diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg new file mode 100644 index 0000000000..f1d36d3e41 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg new file mode 100644 index 0000000000..9fc9fe52c2 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg new file mode 100644 index 0000000000..7a61db38e0 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg new file mode 100644 index 0000000000..c6fb977c52 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Assets/Editor/Translation/scriptcanvas_en_us.ts b/Assets/Editor/Translation/scriptcanvas_en_us.ts index 937f6a4d96..7b8361c879 100644 --- a/Assets/Editor/Translation/scriptcanvas_en_us.ts +++ b/Assets/Editor/Translation/scriptcanvas_en_us.ts @@ -8098,7 +8098,7 @@ COLOR_FROMVALUES_PARAM0_TOOLTIP - The Red value of hte Color [0, 255] + The Red value of the Color [0.0-1.0] COLOR_FROMVALUES_PARAM1_NAME @@ -8107,7 +8107,7 @@ COLOR_FROMVALUES_PARAM1_TOOLTIP - The Green value of the Color [0, 255] + The Green value of the Color [0.0-1.0] COLOR_FROMVALUES_PARAM2_NAME @@ -8116,7 +8116,7 @@ COLOR_FROMVALUES_PARAM2_TOOLTIP - The Blue value of the Color [0, 255] + The Blue value of the Color [0.0-1.0] COLOR_FROMVALUES_PARAM3_NAME @@ -8125,7 +8125,7 @@ COLOR_FROMVALUES_PARAM3_TOOLTIP - The Alpha value of the Color [0, 255] + The Alpha value of the Color [0.0-1.0] diff --git a/AutomatedTesting/Gem/PythonTests/AWS/README.md b/AutomatedTesting/Gem/PythonTests/AWS/README.md index 1429fc487f..1bb36f178d 100644 --- a/AutomatedTesting/Gem/PythonTests/AWS/README.md +++ b/AutomatedTesting/Gem/PythonTests/AWS/README.md @@ -11,7 +11,7 @@ 3. Open a new Command Prompt window at the engine root and set the following environment variables: Set O3DE_AWS_PROJECT_NAME=AWSAUTO Set O3DE_AWS_DEPLOY_REGION=us-east-1 - Set ASSUME_ROLE_ARN="arn:aws:iam::{your_aws_account_id}:role/o3de-automation-tests" + Set ASSUME_ROLE_ARN=arn:aws:iam::{your_aws_account_id}:role/o3de-automation-tests Set COMMIT_ID=HEAD 4. In the same Command Prompt window, Deploy the CDK applications for AWS gems by running deploy_cdk_applications.cmd. diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/aws_metrics_automation_test.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/aws_metrics_automation_test.py index a0a53f92b5..34b2217916 100644 --- a/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/aws_metrics_automation_test.py +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/aws_metrics_automation_test.py @@ -31,7 +31,7 @@ def setup(launcher: pytest.fixture, Set up the resource mapping configuration and start the log monitor. :param launcher: Client launcher for running the test level. :param asset_processor: asset_processor fixture. - :return log monitor object, metrics file path and the metrics stack name. + :return log monitor object. """ asset_processor.start() asset_processor.wait_for_idle() @@ -73,12 +73,11 @@ def monitor_metrics_submission(log_monitor: pytest.fixture) -> None: f'unexpected_lines values: {unexpected_lines}') -def query_metrics_from_s3(aws_metrics_utils: pytest.fixture, resource_mappings: pytest.fixture, stack_name: str) -> None: +def query_metrics_from_s3(aws_metrics_utils: pytest.fixture, resource_mappings: pytest.fixture) -> None: """ Verify that the metrics events are delivered to the S3 bucket and can be queried. :param aws_metrics_utils: aws_metrics_utils fixture. :param resource_mappings: resource_mappings fixture. - :param stack_name: name of the CloudFormation stack. """ aws_metrics_utils.verify_s3_delivery( resource_mappings.get_resource_name_id('AWSMetrics.AnalyticsBucketName') @@ -89,23 +88,24 @@ def query_metrics_from_s3(aws_metrics_utils: pytest.fixture, resource_mappings: resource_mappings.get_resource_name_id('AWSMetrics.EventsCrawlerName')) # Remove the events_json table if exists so that the sample query can create a table with the same name. - aws_metrics_utils.delete_table(f'{stack_name}-eventsdatabase', 'events_json') - aws_metrics_utils.run_named_queries(f'{stack_name}-AthenaWorkGroup') + aws_metrics_utils.delete_table(resource_mappings.get_resource_name_id('AWSMetrics.EventDatabaseName'), 'events_json') + aws_metrics_utils.run_named_queries(resource_mappings.get_resource_name_id('AWSMetrics.AthenaWorkGroupName')) logger.info('Query metrics from S3 successfully.') -def verify_operational_metrics(aws_metrics_utils: pytest.fixture, stack_name: str, start_time: datetime) -> None: +def verify_operational_metrics(aws_metrics_utils: pytest.fixture, + resource_mappings: pytest.fixture, start_time: datetime) -> None: """ Verify that operational health metrics are delivered to CloudWatch. - aws_metrics_utils: aws_metrics_utils fixture. - stack_name: name of the CloudFormation stack. - start_time: Time when the game launcher starts. + :param aws_metrics_utils: aws_metrics_utils fixture. + :param resource_mappings: resource_mappings fixture. + :param start_time: Time when the game launcher starts. """ aws_metrics_utils.verify_cloud_watch_delivery( 'AWS/Lambda', 'Invocations', [{'Name': 'FunctionName', - 'Value': f'{stack_name}-AnalyticsProcessingLambda'}], + 'Value': resource_mappings.get_resource_name_id('AWSMetrics.AnalyticsProcessingLambdaName')}], start_time) logger.info('AnalyticsProcessingLambda metrics are sent to CloudWatch.') @@ -113,7 +113,7 @@ def verify_operational_metrics(aws_metrics_utils: pytest.fixture, stack_name: st 'AWS/Lambda', 'Invocations', [{'Name': 'FunctionName', - 'Value': f'{stack_name}-EventsProcessingLambda'}], + 'Value': resource_mappings.get_resource_name_id('AWSMetrics.EventProcessingLambdaName')}], start_time) logger.info('EventsProcessingLambda metrics are sent to CloudWatch.') @@ -139,7 +139,6 @@ def update_kinesis_analytics_application_status(aws_metrics_utils: pytest.fixtur @pytest.mark.usefixtures('resource_mappings') @pytest.mark.parametrize('assume_role_arn', [constants.ASSUME_ROLE_ARN]) @pytest.mark.parametrize('feature_name', [AWS_METRICS_FEATURE_NAME]) -@pytest.mark.parametrize('level', ['AWS/Metrics']) @pytest.mark.parametrize('profile_name', ['AWSAutomationTest']) @pytest.mark.parametrize('project', ['AutomatedTesting']) @pytest.mark.parametrize('region_name', [constants.AWS_REGION]) @@ -150,6 +149,7 @@ class TestAWSMetricsWindows(object): """ Test class to verify the real-time and batch analytics for metrics. """ + @pytest.mark.parametrize('level', ['AWS/Metrics']) def test_realtime_and_batch_analytics(self, level: str, launcher: pytest.fixture, @@ -157,7 +157,6 @@ class TestAWSMetricsWindows(object): workspace: pytest.fixture, aws_utils: pytest.fixture, resource_mappings: pytest.fixture, - stacks: typing.List, aws_metrics_utils: pytest.fixture): """ Verify that the metrics events are sent to CloudWatch and S3 for analytics. @@ -189,10 +188,10 @@ class TestAWSMetricsWindows(object): operational_threads = list() operational_threads.append( AWSMetricsThread(target=query_metrics_from_s3, - args=(aws_metrics_utils, resource_mappings, stacks[0]))) + args=(aws_metrics_utils, resource_mappings))) operational_threads.append( AWSMetricsThread(target=verify_operational_metrics, - args=(aws_metrics_utils, stacks[0], start_time))) + args=(aws_metrics_utils, resource_mappings, start_time))) operational_threads.append( AWSMetricsThread(target=update_kinesis_analytics_application_status, args=(aws_metrics_utils, resource_mappings, False))) @@ -201,10 +200,7 @@ class TestAWSMetricsWindows(object): for thread in operational_threads: thread.join() - # Clear the analytics bucket objects so that the S3 bucket can be destroyed during tear down. - aws_metrics_utils.empty_bucket( - resource_mappings.get_resource_name_id('AWSMetrics.AnalyticsBucketName')) - + @pytest.mark.parametrize('level', ['AWS/Metrics']) def test_unauthorized_user_request_rejected(self, level: str, launcher: pytest.fixture, @@ -227,3 +223,13 @@ class TestAWSMetricsWindows(object): halt_on_unexpected=True) assert result, 'Metrics events are sent successfully by unauthorized user' logger.info('Unauthorized user is rejected to send metrics.') + + def test_clean_up_s3_bucket(self, + aws_utils: pytest.fixture, + resource_mappings: pytest.fixture, + aws_metrics_utils: pytest.fixture): + """ + Clear the analytics bucket objects so that the S3 bucket can be destroyed during tear down. + """ + aws_metrics_utils.empty_bucket( + resource_mappings.get_resource_name_id('AWSMetrics.AnalyticsBucketName')) diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt similarity index 70% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/CMakeLists.txt rename to AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt index 992420904c..02aaa42597 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt @@ -13,48 +13,56 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedTesting IN_LIST LY_PROJECTS) ly_add_pytest( - NAME AutomatedTesting::AtomRenderer_HydraTests_Main + NAME AutomatedTesting::Atom_TestSuite_Main TEST_SUITE main - PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_MainSuite.py + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py TEST_SERIAL TIMEOUT 600 RUNTIME_DEPENDENCIES AssetProcessor AutomatedTesting.Assets Editor + COMPONENT + Atom ) ly_add_pytest( - NAME AutomatedTesting::AtomRenderer_HydraTests_Sandbox + NAME AutomatedTesting::Atom_TestSuite_Main_Optimized + TEST_SUITE main + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_Optimized.py + TEST_SERIAL + TIMEOUT 600 + RUNTIME_DEPENDENCIES + AssetProcessor + AutomatedTesting.Assets + Editor + COMPONENT + Atom + ) + ly_add_pytest( + NAME AutomatedTesting::Atom_TestSuite_Sandbox TEST_SUITE sandbox - PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_SandboxSuite.py + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Sandbox.py TEST_SERIAL TIMEOUT 400 RUNTIME_DEPENDENCIES AssetProcessor AutomatedTesting.Assets Editor + COMPONENT + Atom ) ly_add_pytest( - NAME AutomatedTesting::AtomRenderer_HydraTests_GPUTests + NAME AutomatedTesting::Atom_TestSuite_Main_GPU TEST_SUITE main TEST_REQUIRES gpu TEST_SERIAL TIMEOUT 1200 - PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_GPUTests.py - RUNTIME_DEPENDENCIES - AssetProcessor - AutomatedTesting.Assets - Editor - ) - ly_add_pytest( - NAME AutomatedTesting::AtomRenderer_HydraTests_ShaderBuildPipeline - TEST_SUITE main - PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_ShaderBuildPipelineSuite.py - TEST_SERIAL - TIMEOUT 600 + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_GPU.py RUNTIME_DEPENDENCIES AssetProcessor AutomatedTesting.Assets Editor + COMPONENT + Atom ) endif() diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py similarity index 94% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py rename to AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py index 16e281e494..4c5716d365 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py @@ -13,10 +13,10 @@ import pytest import ly_test_tools.environment.file_system as file_system import editor_python_test_tools.hydra_test_utils as hydra -from atom_renderer.atom_utils.atom_constants import LIGHT_TYPES +from Atom.atom_utils.atom_constants import LIGHT_TYPES logger = logging.getLogger(__name__) -TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts") +TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests") @pytest.mark.parametrize("project", ["AutomatedTesting"]) @@ -38,24 +38,24 @@ class TestAtomEditorComponentsMain(object): 7. Exposure Control 8. Directional Light 9. DepthOfField - 10. Decal (Atom) + 10. Decal """ cfg_args = [level] expected_lines = [ - # Decal (Atom) Component - "Decal (Atom) Entity successfully created", - "Decal (Atom)_test: Component added to the entity: True", - "Decal (Atom)_test: Component removed after UNDO: True", - "Decal (Atom)_test: Component added after REDO: True", - "Decal (Atom)_test: Entered game mode: True", - "Decal (Atom)_test: Exit game mode: True", - "Decal (Atom) Controller|Configuration|Material: SUCCESS", - "Decal (Atom)_test: Entity is hidden: True", - "Decal (Atom)_test: Entity is shown: True", - "Decal (Atom)_test: Entity deleted: True", - "Decal (Atom)_test: UNDO entity deletion works: True", - "Decal (Atom)_test: REDO entity deletion works: True", + # Decal Component + "Decal Entity successfully created", + "Decal_test: Component added to the entity: True", + "Decal_test: Component removed after UNDO: True", + "Decal_test: Component added after REDO: True", + "Decal_test: Entered game mode: True", + "Decal_test: Exit game mode: True", + "Decal Controller|Configuration|Material: SUCCESS", + "Decal_test: Entity is hidden: True", + "Decal_test: Entity is shown: True", + "Decal_test: Entity deleted: True", + "Decal_test: UNDO entity deletion works: True", + "Decal_test: REDO entity deletion works: True", # DepthOfField Component "DepthOfField Entity successfully created", "DepthOfField_test: Component added to the entity: True", diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_GPUTests.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py similarity index 98% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_GPUTests.py rename to AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py index e62ab5e5dc..249b9c7096 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_GPUTests.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py @@ -3,8 +3,6 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. SPDX-License-Identifier: Apache-2.0 OR MIT - -Tests that require a GPU in order to run. """ import datetime @@ -22,7 +20,7 @@ import editor_python_test_tools.hydra_test_utils as hydra logger = logging.getLogger(__name__) DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots' -TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts") +TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests") def golden_images_directory(): diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py new file mode 100644 index 0000000000..47b2204d56 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py @@ -0,0 +1,46 @@ +""" +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 pytest + +from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite + + +@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.") +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +class TestAutomation(EditorTestSuite): + + class AtomEditorComponents_DecalAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_DecalAdded as test_module + + class AtomEditorComponents_DepthOfFieldAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module + + class AtomEditorComponents_DirectionalLightAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_DirectionalLightAdded as test_module + + class AtomEditorComponents_ExposureControlAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_ExposureControlAdded as test_module + + class AtomEditorComponents_GlobalSkylightIBLAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_GlobalSkylightIBLAdded as test_module + + class AtomEditorComponents_PhysicalSkyAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_PhysicalSkyAdded as test_module + + class AtomEditorComponents_PostFXRadiusWeightModifierAdded(EditorSharedTest): + from Atom.tests import ( + hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded as test_module) + + class AtomEditorComponents_LightAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_LightAdded as test_module + + class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module + + class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest): + from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_SandboxSuite.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_SandboxSuite.py rename to AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/__init__.py b/AutomatedTesting/Gem/PythonTests/Atom/__init__.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/__init__.py rename to AutomatedTesting/Gem/PythonTests/Atom/__init__.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/__init__.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/__init__.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/__init__.py rename to AutomatedTesting/Gem/PythonTests/Atom/atom_utils/__init__.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/atom_component_helper.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/atom_component_helper.py rename to AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/atom_constants.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/atom_constants.py rename to AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/benchmark_utils.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/benchmark_utils.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/benchmark_utils.py rename to AutomatedTesting/Gem/PythonTests/Atom/atom_utils/benchmark_utils.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/material_editor_utils.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/material_editor_utils.py rename to AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/screenshot_utils.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/screenshot_utils.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/screenshot_utils.py rename to AutomatedTesting/Gem/PythonTests/Atom/atom_utils/screenshot_utils.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_1.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_1.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_1.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_1.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_2.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_2.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_2.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_2.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_3.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_3.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_3.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_3.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_4.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_4.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_4.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_4.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_5.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_5.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_5.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_5.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AtomBasicLevelSetup.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/AtomBasicLevelSetup.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AtomBasicLevelSetup.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/AtomBasicLevelSetup.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_1.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_1.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_1.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_1.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_2.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_2.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_2.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_2.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_3.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_3.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_3.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_3.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_4.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_4.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_4.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_4.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_5.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_5.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_5.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_5.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_6.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_6.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_6.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_6.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/DependencyValidation.azsl.txt b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/DependencyValidation.azsl.txt similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/DependencyValidation.azsl.txt rename to AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/DependencyValidation.azsl.txt diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/DependencyValidation.shader.txt b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/DependencyValidation.shader.txt similarity index 93% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/DependencyValidation.shader.txt rename to AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/DependencyValidation.shader.txt index b0eac1783e..4439ec0352 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/DependencyValidation.shader.txt +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/DependencyValidation.shader.txt @@ -2,7 +2,7 @@ { "Source" : "DependencyValidation.azsl", - "DepthStencilState" : { + "DepthStencilState" : { "Depth" : { "Enable" : false, "CompareFunc" : "GreaterEqual" } }, @@ -22,5 +22,5 @@ } ] } - + } diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test1Color.azsli.txt b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test1Color.azsli.txt similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test1Color.azsli.txt rename to AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test1Color.azsli.txt diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test2Color.azsli.txt b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test2Color.azsli.txt similarity index 93% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test2Color.azsli.txt rename to AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test2Color.azsli.txt index 2ef946b947..565493a0ab 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test2Color.azsli.txt +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test2Color.azsli.txt @@ -12,5 +12,5 @@ float4 GetTest2Color(float4 color) { - return color * 0.5; + return color * 0.5; } diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test3Color.azsli.txt b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test3Color.azsli.txt similarity index 92% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test3Color.azsli.txt rename to AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test3Color.azsli.txt index 73b0cca434..7c1ff2be42 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test3Color.azsli.txt +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test3Color.azsli.txt @@ -12,5 +12,5 @@ float4 GetTest3Color(float4 color) { - return color * 0.13; + return color * 0.13; } diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/__init__.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/__init__.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/__init__.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/__init__.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_AddedToEntity.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py similarity index 99% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_AddedToEntity.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py index a2e950e1dc..602e7564b3 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_AddedToEntity.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py @@ -184,7 +184,7 @@ def run(): material_asset = asset.AssetCatalogRequestBus( bus.Broadcast, "GetAssetIdByPath", material_asset_path, math.Uuid(), False) ComponentTests( - "Decal (Atom)", lambda entity_obj: verify_set_property( + "Decal", lambda entity_obj: verify_set_property( entity_obj, "Controller|Configuration|Material", material_asset)) # Directional Light Component diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py new file mode 100644 index 0000000000..fa02b75fe2 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py @@ -0,0 +1,151 @@ +""" +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 +""" + +# fmt: off +class Tests: + camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created") + camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity") + camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component") + creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed") + creation_redo = ("REDO Entity creation success", "REDO Entity creation failed") + decal_creation = ("Decal Entity successfully created", "Decal Entity failed to be created") + decal_component = ("Entity has a Decal component", "Entity failed to find Decal component") + material_property_set = ("Material property set on Decal component", "Couldn't set Material property on Decal component") + enter_game_mode = ("Entered game mode", "Failed to enter game mode") + exit_game_mode = ("Exited game mode", "Couldn't exit game mode") + is_visible = ("Entity is visible", "Entity was not visible") + is_hidden = ("Entity is hidden", "Entity was not hidden") + entity_deleted = ("Entity deleted", "Entity was not deleted") + deletion_undo = ("UNDO deletion success", "UNDO deletion failed") + deletion_redo = ("REDO deletion success", "REDO deletion failed") + no_error_occurred = ("No errors detected", "Errors were detected") +# fmt: on + + +def AtomEditorComponents_Decal_AddedToEntity(): + """ + Summary: + Tests the Decal component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a Decal entity with no components. + 2) Add Decal component to Decal entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Enter/Exit game mode. + 6) Test IsHidden. + 7) Test IsVisible. + 8) Set Material property on Decal component. + 9) Delete Decal entity. + 10) UNDO deletion. + 11) REDO deletion. + 12) Look for errors. + + :return: None + """ + import os + + import azlmbr.asset as asset + import azlmbr.bus as bus + import azlmbr.legacy.general as general + import azlmbr.math as math + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + helper.init_idle() + helper.open_level("", "Base") + + # Test steps begin. + # 1. Create a Decal entity with no components. + decal_name = "Decal" + decal_entity = EditorEntity.create_editor_entity_at(math.Vector3(512.0, 512.0, 34.0), decal_name) + Report.critical_result(Tests.decal_creation, decal_entity.exists()) + + # 2. Add Decal component to Decal entity. + decal_component = decal_entity.add_component(decal_name) + Report.critical_result(Tests.decal_component, decal_entity.has_component(decal_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not decal_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, decal_entity.exists()) + + # 5. Enter/Exit game mode. + helper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + helper.exit_game_mode(Tests.exit_game_mode) + + # 6. Test IsHidden. + decal_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, decal_entity.is_hidden() is True) + + # 7. Test IsVisible. + decal_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, decal_entity.is_visible() is True) + + # 8. Set Material property on Decal component. + decal_material_property_path = "Controller|Configuration|Material" + decal_material_asset_path = os.path.join("AutomatedTesting", "Materials", "basic_grey.material") + decal_material_asset = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", decal_material_asset_path, math.Uuid(), False) + decal_component.set_component_property_value(decal_material_property_path, decal_material_asset) + get_material_property = decal_component.get_component_property_value(decal_material_property_path) + Report.result(Tests.material_property_set, get_material_property == decal_material_asset) + + # 9. Delete Decal entity. + decal_entity.delete() + Report.result(Tests.entity_deleted, not decal_entity.exists()) + + # 10. UNDO deletion. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.deletion_undo, decal_entity.exists()) + + # 11. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not decal_entity.exists()) + + # 12. Look for errors. + helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_Decal_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py new file mode 100644 index 0000000000..80284902ea --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py @@ -0,0 +1,173 @@ +""" +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 +""" + +# fmt: off +class Tests: + camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created") + camera_component_added = ("Camera component was added to Camera entity", "Camera component failed to be added to entity") + camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component") + camera_property_set = ("DepthOfField Entity set Camera Entity", "DepthOfField Entity could not set Camera Entity") + creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed") + creation_redo = ("REDO Entity creation success", "REDO Entity creation failed") + depth_of_field_creation = ("DepthOfField Entity successfully created", "DepthOfField Entity failed to be created") + depth_of_field_component = ("Entity has a DepthOfField component", "Entity failed to find DepthOfField component") + depth_of_field_disabled = ("DepthOfField component disabled", "DepthOfField component was not disabled.") + post_fx_component = ("Entity has a Post FX Layer component", "Entity did not have a Post FX Layer component") + depth_of_field_enabled = ("DepthOfField component enabled", "DepthOfField component was not enabled.") + enter_game_mode = ("Entered game mode", "Failed to enter game mode") + exit_game_mode = ("Exited game mode", "Couldn't exit game mode") + is_visible = ("Entity is visible", "Entity was not visible") + is_hidden = ("Entity is hidden", "Entity was not hidden") + entity_deleted = ("Entity deleted", "Entity was not deleted") + deletion_undo = ("UNDO deletion success", "UNDO deletion failed") + deletion_redo = ("REDO deletion success", "REDO deletion failed") + no_error_occurred = ("No errors detected", "Errors were detected") +# fmt: on + + +def AtomEditorComponents_DepthOfField_AddedToEntity(): + """ + Summary: + Tests the DepthOfField component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a DepthOfField entity with no components. + 2) Add a DepthOfField component to DepthOfField entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Verify DepthOfField component not enabled. + 6) Add Post FX Layer component since it is required by the DepthOfField component. + 7) Verify DepthOfField component is enabled. + 8) Enter/Exit game mode. + 9) Test IsHidden. + 10) Test IsVisible. + 11) Add Camera entity. + 12) Add Camera component to Camera entity. + 13) Set the DepthOfField components's Camera Entity to the newly created Camera entity. + 14) Delete DepthOfField entity. + 15) UNDO deletion. + 16) REDO deletion. + 17) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + import azlmbr.math as math + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + helper.init_idle() + helper.open_level("", "Base") + + # Test steps begin. + # 1. Create a DepthOfField entity with no components. + depth_of_field_name = "DepthOfField" + depth_of_field_entity = EditorEntity.create_editor_entity_at( + math.Vector3(512.0, 512.0, 34.0), depth_of_field_name) + Report.critical_result(Tests.depth_of_field_creation, depth_of_field_entity.exists()) + + # 2. Add a DepthOfField component to DepthOfField entity. + depth_of_field_component = depth_of_field_entity.add_component(depth_of_field_name) + Report.critical_result(Tests.depth_of_field_component, depth_of_field_entity.has_component(depth_of_field_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not depth_of_field_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, depth_of_field_entity.exists()) + + # 5. Verify DepthOfField component not enabled. + Report.result(Tests.depth_of_field_disabled, not depth_of_field_component.is_enabled()) + + # 6. Add Post FX Layer component since it is required by the DepthOfField component. + post_fx_layer = "PostFX Layer" + depth_of_field_entity.add_component(post_fx_layer) + Report.result(Tests.post_fx_component, depth_of_field_entity.has_component(post_fx_layer)) + + # 7. Verify DepthOfField component is enabled. + Report.result(Tests.depth_of_field_enabled, depth_of_field_component.is_enabled()) + + # 8. Enter/Exit game mode. + helper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + helper.exit_game_mode(Tests.exit_game_mode) + + # 9. Test IsHidden. + depth_of_field_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, depth_of_field_entity.is_hidden() is True) + + # 10. Test IsVisible. + depth_of_field_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, depth_of_field_entity.is_visible() is True) + + # 11. Add Camera entity. + camera_name = "Camera" + camera_entity = EditorEntity.create_editor_entity_at(math.Vector3(512.0, 512.0, 34.0), camera_name) + Report.result(Tests.camera_creation, camera_entity.exists()) + + # 12. Add Camera component to Camera entity. + camera_entity.add_component(camera_name) + Report.result(Tests.camera_component_added, camera_entity.has_component(camera_name)) + + # 13. Set the DepthOfField components's Camera Entity to the newly created Camera entity. + depth_of_field_camera_property_path = "Controller|Configuration|Camera Entity" + depth_of_field_component.set_component_property_value(depth_of_field_camera_property_path, camera_entity.id) + camera_entity_set = depth_of_field_component.get_component_property_value(depth_of_field_camera_property_path) + Report.result(Tests.camera_property_set, camera_entity.id == camera_entity_set) + + # 14. Delete DepthOfField entity. + depth_of_field_entity.delete() + Report.result(Tests.entity_deleted, not depth_of_field_entity.exists()) + + # 15. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, depth_of_field_entity.exists()) + + # 16. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not depth_of_field_entity.exists()) + + # 17. Look for errors. + helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_DepthOfField_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py new file mode 100644 index 0000000000..048e132df4 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py @@ -0,0 +1,157 @@ +""" +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 +""" + +# fmt: off +class Tests: + camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created") + camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity") + camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component") + creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed") + creation_redo = ("REDO Entity creation success", "REDO Entity creation failed") + directional_light_creation = ("Directional Light Entity successfully created", "Directional Light Entity failed to be created") + directional_light_component = ("Entity has a Directional Light component", "Entity failed to find Directional Light component") + shadow_camera_check = ("Directional Light component Shadow camera set", "Directional Light component Shadow camera was not set") + enter_game_mode = ("Entered game mode", "Failed to enter game mode") + exit_game_mode = ("Exited game mode", "Couldn't exit game mode") + is_visible = ("Entity is visible", "Entity was not visible") + is_hidden = ("Entity is hidden", "Entity was not hidden") + entity_deleted = ("Entity deleted", "Entity was not deleted") + deletion_undo = ("UNDO deletion success", "UNDO deletion failed") + deletion_redo = ("REDO deletion success", "REDO deletion failed") + no_error_occurred = ("No errors detected", "Errors were detected") +# fmt: on + + +def AtomEditorComponents_DirectionalLight_AddedToEntity(): + """ + Summary: + Tests the Directional Light component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a Directional Light entity with no components. + 2) Add Directional Light component to Directional Light entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Enter/Exit game mode. + 6) Test IsHidden. + 7) Test IsVisible. + 8) Add Camera entity. + 9) Add Camera component to Camera entity + 10) Set the Directional Light component property Shadow|Camera to the Camera entity. + 11) Delete Directional Light entity. + 12) UNDO deletion. + 13) REDO deletion. + 14) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + import azlmbr.math as math + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + helper.init_idle() + helper.open_level("", "Base") + + # Test steps begin. + # 1. Create a Directional Light entity with no components. + directional_light_name = "Directional Light" + directional_light_entity = EditorEntity.create_editor_entity_at( + math.Vector3(512.0, 512.0, 34.0), directional_light_name) + Report.critical_result(Tests.directional_light_creation, directional_light_entity.exists()) + + # 2. Add Directional Light component to Directional Light entity. + directional_light_component = directional_light_entity.add_component(directional_light_name) + Report.critical_result( + Tests.directional_light_component, directional_light_entity.has_component(directional_light_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not directional_light_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, directional_light_entity.exists()) + + # 5. Enter/Exit game mode. + helper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + helper.exit_game_mode(Tests.exit_game_mode) + + # 6. Test IsHidden. + directional_light_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, directional_light_entity.is_hidden() is True) + + # 7. Test IsVisible. + directional_light_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, directional_light_entity.is_visible() is True) + + # 8. Add Camera entity. + camera_name = "Camera" + camera_entity = EditorEntity.create_editor_entity_at(math.Vector3(512.0, 512.0, 34.0), camera_name) + Report.result(Tests.camera_creation, camera_entity.exists()) + + # 9. Add Camera component to Camera entity. + camera_entity.add_component(camera_name) + Report.result(Tests.camera_component_added, camera_entity.has_component(camera_name)) + + # 10. Set the Directional Light component property Shadow|Camera to the Camera entity. + shadow_camera_property_path = "Controller|Configuration|Shadow|Camera" + directional_light_component.set_component_property_value(shadow_camera_property_path, camera_entity.id) + shadow_camera_set = directional_light_component.get_component_property_value(shadow_camera_property_path) + Report.result(Tests.shadow_camera_check, camera_entity.id == shadow_camera_set) + + # 11. Delete DirectionalLight entity. + directional_light_entity.delete() + Report.result(Tests.entity_deleted, not directional_light_entity.exists()) + + # 12. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, directional_light_entity.exists()) + + # 13. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not directional_light_entity.exists()) + + # 14. Look for errors. + helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_DirectionalLight_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py new file mode 100644 index 0000000000..39d7acf4f4 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py @@ -0,0 +1,137 @@ +""" +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 +""" + +# fmt: off +class Tests: + camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created") + camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity") + camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component") + creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed") + creation_redo = ("REDO Entity creation success", "REDO Entity creation failed") + display_mapper_creation = ("Display Mapper Entity successfully created", "Display Mapper Entity failed to be created") + display_mapper_component = ("Entity has a Display Mapper component", "Entity failed to find Display Mapper component") + enter_game_mode = ("Entered game mode", "Failed to enter game mode") + exit_game_mode = ("Exited game mode", "Couldn't exit game mode") + is_visible = ("Entity is visible", "Entity was not visible") + is_hidden = ("Entity is hidden", "Entity was not hidden") + entity_deleted = ("Entity deleted", "Entity was not deleted") + deletion_undo = ("UNDO deletion success", "UNDO deletion failed") + deletion_redo = ("REDO deletion success", "REDO deletion failed") + no_error_occurred = ("No errors detected", "Errors were detected") +# fmt: on + + +def AtomEditorComponents_DisplayMapper_AddedToEntity(): + """ + Summary: + Tests the Display Mapper component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a Display Mapper entity with no components. + 2) Add Display Mapper component to Display Mapper entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Enter/Exit game mode. + 6) Test IsHidden. + 7) Test IsVisible. + 8) Delete Display Mapper entity. + 9) UNDO deletion. + 10) REDO deletion. + 11) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + import azlmbr.math as math + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + helper.init_idle() + helper.open_level("", "Base") + + # Test steps begin. + # 1. Create a Display Mapper entity with no components. + display_mapper = "Display Mapper" + display_mapper_entity = EditorEntity.create_editor_entity_at( + math.Vector3(512.0, 512.0, 34.0), f"{display_mapper}") + Report.critical_result(Tests.display_mapper_creation, display_mapper_entity.exists()) + + # 2. Add Display Mapper component to Display Mapper entity. + display_mapper_entity.add_component(display_mapper) + Report.critical_result(Tests.display_mapper_component, display_mapper_entity.has_component(display_mapper)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not display_mapper_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, display_mapper_entity.exists()) + + # 5. Enter/Exit game mode. + helper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + helper.exit_game_mode(Tests.exit_game_mode) + + # 6. Test IsHidden. + display_mapper_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, display_mapper_entity.is_hidden() is True) + + # 7. Test IsVisible. + display_mapper_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, display_mapper_entity.is_visible() is True) + + # 8. Delete Display Mapper entity. + display_mapper_entity.delete() + Report.result(Tests.entity_deleted, not display_mapper_entity.exists()) + + # 9. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, display_mapper_entity.exists()) + + # 10. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not display_mapper_entity.exists()) + + # 11. Look for errors. + helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_DisplayMapper_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py new file mode 100644 index 0000000000..23a84435f7 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py @@ -0,0 +1,145 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +# fmt: off +class Tests: + camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created") + camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity") + camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component") + creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed") + creation_redo = ("REDO Entity creation success", "REDO Entity creation failed") + exposure_control_creation = ("ExposureControl Entity successfully created", "ExposureControl Entity failed to be created") + exposure_control_component = ("Entity has a Exposure Control component", "Entity failed to find Exposure Control component") + post_fx_component = ("Entity has a Post FX Layer component", "Entity did not have a Post FX Layer component") + enter_game_mode = ("Entered game mode", "Failed to enter game mode") + exit_game_mode = ("Exited game mode", "Couldn't exit game mode") + is_visible = ("Entity is visible", "Entity was not visible") + is_hidden = ("Entity is hidden", "Entity was not hidden") + entity_deleted = ("Entity deleted", "Entity was not deleted") + deletion_undo = ("UNDO deletion success", "UNDO deletion failed") + deletion_redo = ("REDO deletion success", "REDO deletion failed") + no_error_occurred = ("No errors detected", "Errors were detected") +# fmt: on + + +def AtomEditorComponents_ExposureControl_AddedToEntity(): + """ + Summary: + Tests the Exposure Control component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create an Exposure Control entity with no components. + 2) Add Exposure Control component to Exposure Control entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Enter/Exit game mode. + 6) Test IsHidden. + 7) Test IsVisible. + 8) Add Post FX Layer component. + 9) Delete Exposure Control entity. + 10) UNDO deletion. + 11) REDO deletion. + 12) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + import azlmbr.math as math + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + helper.init_idle() + helper.open_level("", "Base") + + # Test steps begin. + # 1. Creation of Exposure Control entity with no components. + exposure_control_name = "Exposure Control" + exposure_control_entity = EditorEntity.create_editor_entity_at( + math.Vector3(512.0, 512.0, 34.0), f"{exposure_control_name}") + Report.critical_result(Tests.exposure_control_creation, exposure_control_entity.exists()) + + # 2. Add Exposure Control component to Exposure Control entity. + exposure_control_entity.add_component(exposure_control_name) + Report.critical_result( + Tests.exposure_control_component, exposure_control_entity.has_component(exposure_control_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not exposure_control_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, exposure_control_entity.exists()) + + # 5. Enter/Exit game mode. + helper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + helper.exit_game_mode(Tests.exit_game_mode) + + # 6. Test IsHidden. + exposure_control_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, exposure_control_entity.is_hidden() is True) + + # 7. Test IsVisible. + exposure_control_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, exposure_control_entity.is_visible() is True) + + # 8. Add Post FX Layer component. + post_fx_layer_name = "PostFX Layer" + exposure_control_entity.add_component(post_fx_layer_name) + Report.result(Tests.post_fx_component, exposure_control_entity.has_component(post_fx_layer_name)) + + # 9. Delete ExposureControl entity. + exposure_control_entity.delete() + Report.result(Tests.entity_deleted, not exposure_control_entity.exists()) + + # 10. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, exposure_control_entity.exists()) + + # 11. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not exposure_control_entity.exists()) + + # 12. Look for errors. + helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_ExposureControl_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py new file mode 100644 index 0000000000..cc891f5929 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py @@ -0,0 +1,164 @@ +""" +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 +""" + +# fmt: off +class Tests: + camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created") + camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity") + camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component") + creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed") + creation_redo = ("REDO Entity creation success", "REDO Entity creation failed") + global_skylight_creation = ("Global Skylight (IBL) Entity successfully created", "Global Skylight (IBL) Entity failed to be created") + global_skylight_component = ("Entity has a Global Skylight (IBL) component", "Entity failed to find Global Skylight (IBL) component") + diffuse_image_set = ("Entity has the Diffuse Image set", "Entity did not the Diffuse Image set") + specular_image_set = ("Entity has the Specular Image set", "Entity did not the Specular Image set") + enter_game_mode = ("Entered game mode", "Failed to enter game mode") + exit_game_mode = ("Exited game mode", "Couldn't exit game mode") + is_visible = ("Entity is visible", "Entity was not visible") + is_hidden = ("Entity is hidden", "Entity was not hidden") + entity_deleted = ("Entity deleted", "Entity was not deleted") + deletion_undo = ("UNDO deletion success", "UNDO deletion failed") + deletion_redo = ("REDO deletion success", "REDO deletion failed") + no_error_occurred = ("No errors detected", "Errors were detected") +# fmt: on + + +def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity(): + """ + Summary: + Tests the Global Skylight (IBL) component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a Global Skylight (IBL) entity with no components. + 2) Add Global Skylight (IBL) component to Global Skylight (IBL) entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Enter/Exit game mode. + 6) Test IsHidden. + 7) Test IsVisible. + 8) Add Post FX Layer component. + 9) Add Camera component + 10) Delete Global Skylight (IBL) entity. + 11) UNDO deletion. + 12) REDO deletion. + 13) Look for errors. + + :return: None + """ + import os + + import azlmbr.legacy.general as general + import azlmbr.math as math + + from editor_python_test_tools.asset_utils import Asset + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + helper.init_idle() + helper.open_level("", "Base") + + # Test steps begin. + # 1. Create a Global Skylight (IBL) entity with no components. + global_skylight_name = "Global Skylight (IBL)" + global_skylight_entity = EditorEntity.create_editor_entity_at( + math.Vector3(512.0, 512.0, 34.0), global_skylight_name) + Report.critical_result(Tests.global_skylight_creation, global_skylight_entity.exists()) + + # 2. Add Global Skylight (IBL) component to Global Skylight (IBL) entity. + global_skylight_component = global_skylight_entity.add_component(global_skylight_name) + Report.critical_result( + Tests.global_skylight_component, global_skylight_entity.has_component(global_skylight_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not global_skylight_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, global_skylight_entity.exists()) + + # 5. Enter/Exit game mode. + helper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + helper.exit_game_mode(Tests.exit_game_mode) + + # 6. Test IsHidden. + global_skylight_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, global_skylight_entity.is_hidden() is True) + + # 7. Test IsVisible. + global_skylight_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, global_skylight_entity.is_visible() is True) + + # 8. Set the Diffuse Image asset on the Global Skylight (IBL) entity. + global_skylight_diffuse_image_property = "Controller|Configuration|Diffuse Image" + diffuse_image_path = os.path.join("LightingPresets", "greenwich_park_02_4k_iblskyboxcm.exr.streamingimage") + diffuse_image_asset = Asset.find_asset_by_path(diffuse_image_path, False) + global_skylight_component.set_component_property_value( + global_skylight_diffuse_image_property, diffuse_image_asset.id) + diffuse_image_set = global_skylight_component.get_component_property_value( + global_skylight_diffuse_image_property) + Report.result(Tests.diffuse_image_set, diffuse_image_set == diffuse_image_asset.id) + + # 9. Set the Specular Image asset on the Global Light (IBL) entity. + global_skylight_specular_image_property = "Controller|Configuration|Specular Image" + specular_image_path = os.path.join("LightingPresets", "greenwich_park_02_4k_iblskyboxcm.exr.streamingimage") + specular_image_asset = Asset.find_asset_by_path(specular_image_path, False) + global_skylight_component.set_component_property_value( + global_skylight_specular_image_property, specular_image_asset.id) + specular_image_added = global_skylight_component.get_component_property_value( + global_skylight_specular_image_property) + Report.result(Tests.specular_image_set, specular_image_added == specular_image_asset.id) + + # 10. Delete Global Skylight (IBL) entity. + global_skylight_entity.delete() + Report.result(Tests.entity_deleted, not global_skylight_entity.exists()) + + # 11. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, global_skylight_entity.exists()) + + # 12. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not global_skylight_entity.exists()) + + # 13. Look for errors. + helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_GlobalSkylightIBL_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py new file mode 100644 index 0000000000..8b1432f1f7 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py @@ -0,0 +1,136 @@ +""" +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 +""" + +# fmt: off +class Tests: + camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created") + camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity") + camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component") + creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed") + creation_redo = ("REDO Entity creation success", "REDO Entity creation failed") + light_creation = ("Light Entity successfully created", "Light Entity failed to be created") + light_component = ("Entity has a Light component", "Entity failed to find Light component") + enter_game_mode = ("Entered game mode", "Failed to enter game mode") + exit_game_mode = ("Exited game mode", "Couldn't exit game mode") + is_visible = ("Entity is visible", "Entity was not visible") + is_hidden = ("Entity is hidden", "Entity was not hidden") + entity_deleted = ("Entity deleted", "Entity was not deleted") + deletion_undo = ("UNDO deletion success", "UNDO deletion failed") + deletion_redo = ("REDO deletion success", "REDO deletion failed") + no_error_occurred = ("No errors detected", "Errors were detected") +# fmt: on + + +def AtomEditorComponents_Light_AddedToEntity(): + """ + Summary: + Tests the Light component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a Light entity with no components. + 2) Add Light component to the Light entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Enter/Exit game mode. + 6) Test IsHidden. + 7) Test IsVisible. + 8) Delete Light entity. + 9) UNDO deletion. + 10) REDO deletion. + 11) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + import azlmbr.math as math + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + helper.init_idle() + helper.open_level("", "Base") + + # Test steps begin. + # 1. Create a Light entity with no components. + light_name = "Light" + light_entity = EditorEntity.create_editor_entity_at(math.Vector3(512.0, 512.0, 34.0), light_name) + Report.critical_result(Tests.light_creation, light_entity.exists()) + + # 2. Add Light component to the Light entity. + light_entity.add_component(light_name) + Report.critical_result(Tests.light_component, light_entity.has_component(light_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not light_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, light_entity.exists()) + + # 5. Enter/Exit game mode. + helper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + helper.exit_game_mode(Tests.exit_game_mode) + + # 6. Test IsHidden. + light_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, light_entity.is_hidden() is True) + + # 7. Test IsVisible. + light_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, light_entity.is_visible() is True) + + # 8. Delete Light entity. + light_entity.delete() + Report.result(Tests.entity_deleted, not light_entity.exists()) + + # 9. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, light_entity.exists()) + + # 10. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not light_entity.exists()) + + # 11. Look for errors. + helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_Light_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_LightComponent.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py similarity index 95% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_LightComponent.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py index 8d138e67b8..751f425916 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_LightComponent.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py @@ -1,10 +1,8 @@ """ -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. +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 - -Hydra script that creates an entity, attaches the Light component to it for test verifications. -The test verifies that each light type option is available and can be selected without errors. """ import os @@ -19,7 +17,7 @@ import azlmbr.legacy.general as general sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests")) import editor_python_test_tools.hydra_editor_utils as hydra -from atom_renderer.atom_utils.atom_constants import LIGHT_TYPES +from Atom.atom_utils.atom_constants import LIGHT_TYPES LIGHT_TYPE_PROPERTY = 'Controller|Configuration|Light type' SPHERE_AND_SPOT_DISK_LIGHT_PROPERTIES = [ diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py new file mode 100644 index 0000000000..04441d5b2c --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py @@ -0,0 +1,136 @@ +""" +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 +""" + +# fmt: off +class Tests: + camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created") + camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity") + camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component") + creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed") + creation_redo = ("REDO Entity creation success", "REDO Entity creation failed") + physical_sky_creation = ("Physical Sky Entity successfully created", "Physical Sky Entity failed to be created") + physical_sky_component = ("Entity has a Physical Sky component", "Entity failed to find Physical Sky component") + enter_game_mode = ("Entered game mode", "Failed to enter game mode") + exit_game_mode = ("Exited game mode", "Couldn't exit game mode") + is_visible = ("Entity is visible", "Entity was not visible") + is_hidden = ("Entity is hidden", "Entity was not hidden") + entity_deleted = ("Entity deleted", "Entity was not deleted") + deletion_undo = ("UNDO deletion success", "UNDO deletion failed") + deletion_redo = ("REDO deletion success", "REDO deletion failed") + no_error_occurred = ("No errors detected", "Errors were detected") +# fmt: on + + +def AtomEditorComponents_PhysicalSky_AddedToEntity(): + """ + Summary: + Tests the Physical Sky component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a Physical Sky entity with no components. + 2) Add Physical Sky component to Physical Sky entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Enter/Exit game mode. + 6) Test IsHidden. + 7) Test IsVisible. + 8) Delete Physical Sky entity. + 9) UNDO deletion. + 10) REDO deletion. + 11) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + import azlmbr.math as math + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + helper.init_idle() + helper.open_level("", "Base") + + # Test steps begin. + # 1. Create a Physical Sky entity with no components. + physical_sky_name = "Physical Sky" + physical_sky_entity = EditorEntity.create_editor_entity_at(math.Vector3(512.0, 512.0, 34.0), physical_sky_name) + Report.critical_result(Tests.physical_sky_creation, physical_sky_entity.exists()) + + # 2. Add Physical Sky component to Physical Sky entity. + physical_sky_entity.add_component(physical_sky_name) + Report.critical_result(Tests.physical_sky_component, physical_sky_entity.has_component(physical_sky_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not physical_sky_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, physical_sky_entity.exists()) + + # 5. Enter/Exit game mode. + helper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + helper.exit_game_mode(Tests.exit_game_mode) + + # 6. Test IsHidden. + physical_sky_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, physical_sky_entity.is_hidden() is True) + + # 7. Test IsVisible. + physical_sky_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, physical_sky_entity.is_visible() is True) + + # 8. Delete Physical Sky entity. + physical_sky_entity.delete() + Report.result(Tests.entity_deleted, not physical_sky_entity.exists()) + + # 9. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, physical_sky_entity.exists()) + + # 10. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not physical_sky_entity.exists()) + + # 11. Look for errors. + helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_PhysicalSky_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py new file mode 100644 index 0000000000..8914ab9e7e --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py @@ -0,0 +1,138 @@ +""" +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 +""" + +# fmt: off +class Tests: + camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created") + camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity") + camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component") + creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed") + creation_redo = ("REDO Entity creation success", "REDO Entity creation failed") + postfx_radius_weight_creation = ("PostFX Radius Weight Modifier Entity successfully created", "PostFX Radius Weight Modifier Entity failed to be created") + postfx_radius_weight_component = ("Entity has a PostFX Radius Weight Modifier component", "Entity failed to find PostFX Radius Weight Modifier component") + enter_game_mode = ("Entered game mode", "Failed to enter game mode") + exit_game_mode = ("Exited game mode", "Couldn't exit game mode") + is_visible = ("Entity is visible", "Entity was not visible") + is_hidden = ("Entity is hidden", "Entity was not hidden") + entity_deleted = ("Entity deleted", "Entity was not deleted") + deletion_undo = ("UNDO deletion success", "UNDO deletion failed") + deletion_redo = ("REDO deletion success", "REDO deletion failed") + no_error_occurred = ("No errors detected", "Errors were detected") +# fmt: on + + +def AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity(): + """ + Summary: + Tests the PostFX Radius Weight Modifier component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a Post FX Radius Weight Modifier entity with no components. + 2) Add Post FX Radius Weight Modifier component to Post FX Radius Weight Modifier entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Enter/Exit game mode. + 6) Test IsHidden. + 7) Test IsVisible. + 8) Delete PostFX Radius Weight Modifier entity. + 9) UNDO deletion. + 10) REDO deletion. + 11) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + import azlmbr.math as math + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + helper.init_idle() + helper.open_level("", "Base") + + # Test steps begin. + # 1. Create a Post FX Radius Weight Modifier entity with no components. + postfx_radius_weight_name = "PostFX Radius Weight Modifier" + postfx_radius_weight_entity = EditorEntity.create_editor_entity_at( + math.Vector3(512.0, 512.0, 34.0), postfx_radius_weight_name) + Report.critical_result(Tests.postfx_radius_weight_creation, postfx_radius_weight_entity.exists()) + + # 2. Add Post FX Radius Weight Modifier component to Post FX Radius Weight Modifier entity. + postfx_radius_weight_entity.add_component(postfx_radius_weight_name) + Report.critical_result( + Tests.postfx_radius_weight_component, postfx_radius_weight_entity.has_component(postfx_radius_weight_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not postfx_radius_weight_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, postfx_radius_weight_entity.exists()) + + # 5. Enter/Exit game mode. + helper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + helper.exit_game_mode(Tests.exit_game_mode) + + # 6. Test IsHidden. + postfx_radius_weight_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, postfx_radius_weight_entity.is_hidden() is True) + + # 7. Test IsVisible. + postfx_radius_weight_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, postfx_radius_weight_entity.is_visible() is True) + + # 8. Delete PostFX Radius Weight Modifier entity. + postfx_radius_weight_entity.delete() + Report.result(Tests.entity_deleted, not postfx_radius_weight_entity.exists()) + + # 9. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, postfx_radius_weight_entity.exists()) + + # 10. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not postfx_radius_weight_entity.exists()) + + # 11. Look for errors. + helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomMaterialEditor_BasicTests.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py similarity index 95% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomMaterialEditor_BasicTests.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py index 9047b4c871..88a1ef4c7b 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomMaterialEditor_BasicTests.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py @@ -3,12 +3,12 @@ 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 azlmbr.materialeditor will fail with a ModuleNotFound error when using this script with Editor.exe -This is because azlmbr.materialeditor only binds to MaterialEditor.exe and not Editor.exe -You need to launch this script with MaterialEditor.exe in order for azlmbr.materialeditor to appear. """ +# import azlmbr.materialeditor will fail with a ModuleNotFound error when using this script with Editor.exe +# This is because azlmbr.materialeditor only binds to MaterialEditor.exe and not Editor.exe +# You need to launch this script with MaterialEditor.exe in order for azlmbr.materialeditor to appear. + import os import sys import time @@ -18,7 +18,7 @@ import azlmbr.paths sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests")) -import atom_renderer.atom_utils.material_editor_utils as material_editor +import Atom.atom_utils.material_editor_utils as material_editor NEW_MATERIAL = "test_material.material" NEW_MATERIAL_1 = "test_material_1.material" diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py similarity index 92% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py index 3aa9fe660c..4f7edeba75 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py @@ -3,11 +3,6 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. SPDX-License-Identifier: Apache-2.0 OR MIT - -Hydra script that is used to create a new level with a default rendering setup. -After the level is setup, screenshots are diffed against golden images are used to verify pass/fail results of the test. - -See the run() function for more in-depth test info. """ import os @@ -19,7 +14,7 @@ sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "P import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.editor_test_helper import EditorTestHelper -from atom_renderer.atom_utils.benchmark_utils import BenchmarkHelper +from Atom.atom_utils.benchmark_utils import BenchmarkHelper SCREEN_WIDTH = 1280 SCREEN_HEIGHT = 720 diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_BasicLevelSetup.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py similarity index 97% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_BasicLevelSetup.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py index 920c044be0..62a122a723 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_BasicLevelSetup.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py @@ -3,11 +3,6 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. SPDX-License-Identifier: Apache-2.0 OR MIT - -Hydra script that is used to create a new level with a default rendering setup. -After the level is setup, screenshots are diffed against golden images are used to verify pass/fail results of the test. - -See the run() function for more in-depth test info. """ import os @@ -26,7 +21,7 @@ sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "P import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.editor_test_helper import EditorTestHelper -from atom_renderer.atom_utils.screenshot_utils import ScreenshotHelper +from Atom.atom_utils.screenshot_utils import ScreenshotHelper SCREEN_WIDTH = 1280 SCREEN_HEIGHT = 720 diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_LightComponent.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py similarity index 97% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_LightComponent.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py index 8063445608..4a3ae8c85d 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_LightComponent.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py @@ -3,12 +3,6 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. SPDX-License-Identifier: Apache-2.0 OR MIT - -Hydra script that is used to create an entity with a Light component attached. -It then updates the property values of the Light component and takes a screenshot. -The screenshot is compared against an expected golden image for test verification. - -See the run() function for more in-depth test info. """ import os import sys @@ -23,7 +17,7 @@ import azlmbr.legacy.general as general sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) import editor_python_test_tools.hydra_editor_utils as hydra -from atom_renderer.atom_utils import atom_component_helper, atom_constants, screenshot_utils +from Atom.atom_utils import atom_component_helper, atom_constants, screenshot_utils from editor_python_test_tools.editor_test_helper import EditorTestHelper helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper") diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py similarity index 60% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py index a05420d960..f13227aa9d 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py @@ -3,78 +3,19 @@ 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 os -import shutil - -def _copy_file(src_file, src_path, target_file, target_path): - # type: (str, str, str, str) -> None - """ - Copies the [src_file] located in [src_path] to the [target_file] located at [target_path]. - Leaves the [target_file] unlocked for reading and writing privileges - :param src_file: The source file to copy (file name) - :param src_path: The source file's path - :param target_file: The target file to copy into (file name) - :param target_path: The target file's path - :return: None - """ - target_file_path = os.path.join(target_path, target_file) - src_file_path = os.path.join(src_path, src_file) - if os.path.exists(target_file_path): - fs.unlock_file(target_file_path) - shutil.copyfile(src_file_path, target_file_path) - -def _copy_tmp_files_in_order(src_directory, file_list, dst_directory, wait_time_in_between = 0.0): - # type: (str, list, str, float) -> None - """ - This function assumes that for each file name listed in @file_list - there's file named "@filename.txt" which the original source file - but they will be copied with just the @filename (.txt removed). - """ - for filename in file_list: - src_name = f"{filename}.txt" - _copy_file(src_name, src_directory, filename, dst_directory) - if wait_time_in_between > 0.0: - print(f"Created {filename} in {dst_directory}") - general.idle_wait(wait_time_in_between) - - -def _remove_file(src_file, src_path): - # type: (str, str) -> None - """ - Removes the [src_file] located in [src_path]. - :param src_file: The source file to copy (file name) - :param src_path: The source file's path - :return: None - """ - src_file_path = os.path.join(src_path, src_file) - if os.path.exists(src_file_path): - fs.unlock_file(src_file_path) - os.remove(src_file_path) - - -def _remove_files(directory, file_list): - for filename in file_list: - _remove_file(filename, directory) - - -def _asset_exists(cache_relative_path): - asset_id = azasset.AssetCatalogRequestBus(azbus.Broadcast, "GetAssetIdByPath", cache_relative_path, azmath.Uuid(), False) - return asset_id.is_valid() - -# List of results that we want to check, this is not 100% necessary but it's a good -# practice to make it easier to debug tests. -# Here we define a tuple of tests -class Results(): - azshader_was_removed = ("azshader was removed", "Failed to remove azshader") - azshader_was_compiled = ("azshader was compiled", "Failed to compile azshader") +# fmt: off +class Tests(): + azshader_was_removed = ("azshader was removed", "Failed to remove azshader") + azshader_was_compiled = ("azshader was compiled", "Failed to compile azshader") + no_error_occurred = ("No errors detected", "Errors were detected") +# fmt: on def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): """ - This test validates [ATOM-5441] Shader Builders May Fail When Multiple New Files Are Added + This test validates: "Shader Builders May Fail When Multiple New Files Are Added" It creates source assets to compile a particular shader. 1- The first phase generates the source assets out of order and slowly. The AP should wakeup each time one of the source dependencies appears but will fail each time. Only when the @@ -82,6 +23,71 @@ def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): 2- The second phase is similar as above, except that all source assets will be created at once and We also expect that in the end the shader is built successfully. """ + import os + import shutil + + import azlmbr.asset as azasset + import azlmbr.bus as azbus + import azlmbr.legacy.general as general + import azlmbr.math as azmath + + from editor_python_test_tools.utils import TestHelper as helper + from editor_python_test_tools.utils import Tracer + import ly_test_tools.environment.file_system as fs + + def _copy_file(src_file, src_path, target_file, target_path): + # type: (str, str, str, str) -> None + """ + Copies the [src_file] located in [src_path] to the [target_file] located at [target_path]. + Leaves the [target_file] unlocked for reading and writing privileges + :param src_file: The source file to copy (file name) + :param src_path: The source file's path + :param target_file: The target file to copy into (file name) + :param target_path: The target file's path + :return: None + """ + target_file_path = os.path.join(target_path, target_file) + src_file_path = os.path.join(src_path, src_file) + if os.path.exists(target_file_path): + fs.unlock_file(target_file_path) + shutil.copyfile(src_file_path, target_file_path) + + def _copy_tmp_files_in_order(src_directory, file_list, dst_directory, wait_time_in_between=0.0): + # type: (str, list, str, float) -> None + """ + This function assumes that for each file name listed in @file_list + there's file named "@filename.txt" which the original source file + but they will be copied with just the @filename (.txt removed). + """ + for filename in file_list: + src_name = f"{filename}.txt" + _copy_file(src_name, src_directory, filename, dst_directory) + if wait_time_in_between > 0.0: + print(f"Created {filename} in {dst_directory}") + general.idle_wait(wait_time_in_between) + + def _remove_file(src_file, src_path): + # type: (str, str) -> None + """ + Removes the [src_file] located in [src_path]. + :param src_file: The source file to copy (file name) + :param src_path: The source file's path + :return: None + """ + src_file_path = os.path.join(src_path, src_file) + if os.path.exists(src_file_path): + fs.unlock_file(src_file_path) + os.remove(src_file_path) + + def _remove_files(directory, file_list): + for filename in file_list: + _remove_file(filename, directory) + + def _asset_exists(cache_relative_path): + asset_id = azasset.AssetCatalogRequestBus(azbus.Broadcast, "GetAssetIdByPath", cache_relative_path, + azmath.Uuid(), False) + return asset_id.is_valid() + # Required for automated tests helper.init_idle() @@ -115,14 +121,14 @@ def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): azshader_name = "assets/dependencyvalidation.azshader" helper.wait_for_condition(lambda: not _asset_exists(azshader_name), 5.0) - Report.critical_result(Results.azshader_was_removed, not _asset_exists(azshader_name)) + Report.critical_result(Tests.azshader_was_removed, not _asset_exists(azshader_name)) _copy_tmp_files_in_order(src_assets_subdir, file_list, game_asset_path, 1.0) # Give enough time to AP to compile the shader helper.wait_for_condition(lambda: _asset_exists(azshader_name), 60.0) - Report.critical_result(Results.azshader_was_compiled, _asset_exists(azshader_name)) + Report.critical_result(Tests.azshader_was_compiled, _asset_exists(azshader_name)) # The first part was about compiling the shader under normal conditions. # Let's remove the files from the previous phase and will proceed @@ -130,7 +136,7 @@ def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): # ShaderAssetBuilder will only succeed when the last file becomes visible. _remove_files(game_asset_path, reverse_file_list) helper.wait_for_condition(lambda: not _asset_exists(azshader_name), 5.0) - Report.critical_result(Results.azshader_was_removed, not _asset_exists(azshader_name)) + Report.critical_result(Tests.azshader_was_removed, not _asset_exists(azshader_name)) # Remark, if you are running this test manually from the Editor with "pyRunFile", # You'll notice how the AP issues notifications that it fails to compile the shader @@ -148,7 +154,7 @@ def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): # Give enough time to AP to compile the shader helper.wait_for_condition(lambda: _asset_exists(azshader_name), 60.0) - Report.critical_result(Results.azshader_was_compiled, _asset_exists(azshader_name)) + Report.critical_result(Tests.azshader_was_compiled, _asset_exists(azshader_name)) # The last phase of the test puts stress on potential race conditions # when all required files appear as soon as possible. @@ -157,7 +163,7 @@ def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): # Remove left over files. _remove_files(game_asset_path, reverse_file_list) helper.wait_for_condition(lambda: not _asset_exists(azshader_name), 5.0) - Report.critical_result(Results.azshader_was_removed, not _asset_exists(azshader_name)) + Report.critical_result(Tests.azshader_was_removed, not _asset_exists(azshader_name)) # Now let's copy all the source files to the "Assets" folder as fast as possible. _copy_tmp_files_in_order(src_assets_subdir, reverse_file_list, game_asset_path) @@ -165,24 +171,17 @@ def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): # Give enough time to AP to compile the shader helper.wait_for_condition(lambda: _asset_exists(azshader_name), 60.0) - Report.critical_result(Results.azshader_was_compiled, _asset_exists(azshader_name)) + Report.critical_result(Tests.azshader_was_compiled, _asset_exists(azshader_name)) # All good, let's cleanup leftover files before closing the test. _remove_files(game_asset_path, reverse_file_list) helper.wait_for_condition(lambda: not _asset_exists(azshader_name), 5.0) + # Look for errors to raise. + helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + if __name__ == "__main__": - # All exposed python bindings are in azlmbr - import azlmbr.legacy.general as general - import azlmbr.bus as azbus - import azlmbr.asset as azasset - import azlmbr.math as azmath - - # Import report and test helper utilities from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper - from editor_python_test_tools.utils import Tracer - import ly_test_tools.environment.file_system as fs - - Report.start_test(ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges) \ No newline at end of file + Report.start_test(ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges) diff --git a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt index 7fd3b3a241..466a4b1679 100644 --- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt @@ -18,7 +18,7 @@ include(${pal_dir}/PAL_traits_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) add_subdirectory(assetpipeline) ## Atom Renderer ## -add_subdirectory(atom_renderer) +add_subdirectory(Atom) ## Physics ## add_subdirectory(Physics) diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py index fec9d9880b..154b5730d7 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py @@ -20,6 +20,7 @@ import azlmbr.legacy.general as general # Helper file Imports from editor_python_test_tools.utils import Report + class EditorComponent: """ EditorComponent class used to set and get the component property value using path @@ -28,7 +29,6 @@ class EditorComponent: which also assigns self.id and self.type_id to the EditorComponent object. """ - # Methods def get_component_name(self) -> str: """ Used to get name of component @@ -87,6 +87,13 @@ class EditorComponent: outcome.IsSuccess() ), f"Failure: Could not set value to '{self.get_component_name()}' : '{component_property_path}'" + def is_enabled(self): + """ + Used to verify if the component is enabled. + :return: True if enabled, otherwise False. + """ + return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", self.id) + @staticmethod def get_type_ids(component_names: list) -> list: """ @@ -254,7 +261,7 @@ class EditorEntity: def get_components_of_type(self, component_names: list) -> List[EditorComponent]: """ Used to get components of type component_name that already exists on Entity - :param component_name: Name to component to check + :param component_names: List of names of components to check :return: List of Entity Component objects of given component name """ component_list = [] @@ -318,3 +325,39 @@ class EditorEntity: editor.EditorEntityAPIBus(bus.Event, "SetStartStatus", self.id, status_to_set) set_status = self.get_start_status() assert set_status == status_to_set, f"Failed to set start status of {desired_start_status} to {self.get_name}" + + def delete(self) -> None: + """ + Used to delete the Entity. + :return: None + """ + editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntityById", self.id) + + def set_visibility_state(self, is_visible: bool) -> None: + """ + Sets the visibility state on the object to visible or not visible. + :param is_visible: True for making visible, False to make not visible. + :return: None + """ + editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", self.id, is_visible) + + def exists(self) -> bool: + """ + Used to verify if the Entity exists. + :return: True if the Entity exists, False otherwise. + """ + return editor.ToolsApplicationRequestBus(bus.Broadcast, "EntityExists", self.id) + + def is_hidden(self) -> bool: + """ + Gets the "isHidden" value from the Entity. + :return: True if "isHidden" is enabled, False otherwise. + """ + return editor.EditorEntityInfoRequestBus(bus.Event, "IsHidden", self.id) + + def is_visible(self) -> bool: + """ + Gets the "isVisible" value from the Entity. + :return: True if "isVisible" is enabled, False otherwise. + """ + return editor.EditorEntityInfoRequestBus(bus.Event, "IsVisible", self.id) diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_test_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_test_utils.py index 3d4d9ea419..54bc118f48 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_test_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_test_utils.py @@ -81,7 +81,8 @@ def launch_and_validate_results(request, test_directory, editor, editor_script, def launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, null_renderer=True, - port_listener_timeout=120, log_monitor_timeout=300, remote_console_port=4600): + port_listener_timeout=120, log_monitor_timeout=300, remote_console_port=4600, + launch_ap=True): """ Runs the launcher with the specified level, and monitors Game.log for expected lines. :param launcher: Configured launcher object to run test against. @@ -92,6 +93,7 @@ def launch_and_validate_results_launcher(launcher, level, remote_console_instanc :param port_listener_timeout: Timeout for verifying successful connection to Remote Console. :param log_monitor_timeout: Timeout for monitoring for lines in Game.log :param remote_console_port: The port used to communicate with the Remote Console. + :param launch_ap: Whether or not to launch AP. Defaults to True. """ def _check_for_listening_port(port): @@ -110,7 +112,7 @@ def launch_and_validate_results_launcher(launcher, level, remote_console_instanc launcher.args.extend(["-rhi=Null"]) # Start the Launcher - with launcher.start(): + with launcher.start(launch_ap=launch_ap): # Ensure Remote Console can be reached waiter.wait_for( diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt index 4a26500ee2..52682e70bb 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt @@ -17,5 +17,14 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AssetProcessorBatch AZ::AssetProcessor ) + + ly_add_pytest( + NAME AssetPipelineTests.Fbx_Tests + PATH ${CMAKE_CURRENT_LIST_DIR}/fbx_test/fbx_test.py + TEST_SUITE sandbox + RUNTIME_DEPENDENCIES + AZ::AssetProcessorBatch + AZ::AssetProcessor + ) endif() diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py index 2b90f53fc3..5cb61da68e 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py @@ -34,11 +34,13 @@ logger = logging.getLogger(__name__) targetProjects = ["AutomatedTesting"] @pytest.fixture +@pytest.mark.SUITE_sandbox def local_resources(request, workspace, ap_setup_fixture): ap_setup_fixture["tests_dir"] = os.path.dirname(os.path.realpath(__file__)) @dataclass +@pytest.mark.SUITE_sandbox class BlackboxAssetTest: test_name: str asset_folder: str @@ -338,9 +340,11 @@ blackbox_fbx_special_tests = [ @pytest.mark.usefixtures("local_resources") @pytest.mark.parametrize("project", targetProjects) @pytest.mark.assetpipeline +@pytest.mark.SUITE_sandbox class TestsFBX_AllPlatforms(object): @pytest.mark.BAT + @pytest.mark.SUITE_sandbox @pytest.mark.parametrize("blackbox_param", blackbox_fbx_tests) def test_FBXBlackboxTest_SourceFiles_Processed_ResultInExpectedProducts(self, workspace, ap_setup_fixture, asset_processor, project, @@ -359,6 +363,7 @@ class TestsFBX_AllPlatforms(object): asset_processor, project, blackbox_param) @pytest.mark.BAT + @pytest.mark.SUITE_sandbox @pytest.mark.parametrize("blackbox_param", blackbox_fbx_special_tests) def test_FBXBlackboxTest_AssetInfoModified_AssetReprocessed_ResultInExpectedProducts(self, workspace, ap_setup_fixture, diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_ShaderBuildPipelineSuite.py b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_ShaderBuildPipelineSuite.py deleted file mode 100644 index 9ef93ea238..0000000000 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_ShaderBuildPipelineSuite.py +++ /dev/null @@ -1,19 +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 - -Main suite tests for the Shader Build Pipeline. -""" -import pytest -from ly_test_tools import LAUNCHERS -from ly_test_tools.o3de.editor_test import EditorTestSuite, EditorSingleTest - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestShaderBuildPipelineMain(EditorTestSuite): - """Holds tests for Shader Build Pipeline validation""" - - class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSingleTest): - from .atom_hydra_scripts import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt index b3030e84ac..f1299ddc2d 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt @@ -14,8 +14,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ NAME AutomatedTesting::DynamicVegetationTests_Main TEST_SERIAL TEST_SUITE main - PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg - PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark" + PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Main.py RUNTIME_DEPENDENCIES AZ::AssetProcessor Legacy::Editor @@ -27,104 +26,33 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ ly_add_pytest( - NAME AutomatedTesting::DynamicVegetationTests_Sandbox + NAME AutomatedTesting::DynamicVegetationTests_Periodic TEST_SERIAL - TEST_SUITE sandbox - PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg - PYTEST_MARKS "SUITE_sandbox" + TEST_SUITE periodic + PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Periodic.py RUNTIME_DEPENDENCIES AZ::AssetProcessor Legacy::Editor + AutomatedTesting.Assets AutomatedTesting.GameLauncher - AutomatedTesting.Assets COMPONENT LargeWorlds ) ly_add_pytest( - NAME AutomatedTesting::DynamicVegetationFilterTests_Periodic + NAME AutomatedTesting::DynamicVegetationTests_Main_Optimized TEST_SERIAL - TEST_SUITE periodic - PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg - PYTEST_MARKS "SUITE_periodic and dynveg_filter" + TEST_SUITE main + PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Main_Optimized.py RUNTIME_DEPENDENCIES - AZ::AssetProcessor - Legacy::Editor - AutomatedTesting.Assets + AZ::AssetProcessor + Legacy::Editor + AutomatedTesting.Assets + AutomatedTesting.GameLauncher COMPONENT LargeWorlds ) - ly_add_pytest( - NAME AutomatedTesting::DynamicVegetationModifierTests_Periodic - TEST_SERIAL - TEST_SUITE periodic - PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg - PYTEST_MARKS "SUITE_periodic and dynveg_modifier" - RUNTIME_DEPENDENCIES - AZ::AssetProcessor - Legacy::Editor - AutomatedTesting.Assets - COMPONENT - LargeWorlds - ) - - ly_add_pytest( - NAME AutomatedTesting::DynamicVegetationRegressionTests_Periodic - TEST_SERIAL - TEST_SUITE periodic - PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg - PYTEST_MARKS "SUITE_periodic and dynveg_regression" - RUNTIME_DEPENDENCIES - AZ::AssetProcessor - Legacy::Editor - AutomatedTesting.Assets - COMPONENT - LargeWorlds - ) - - ly_add_pytest( - NAME AutomatedTesting::DynamicVegetationAreaTests_Periodic - TEST_SERIAL - TEST_SUITE periodic - PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg - PYTEST_MARKS "SUITE_periodic and dynveg_area" - RUNTIME_DEPENDENCIES - AZ::AssetProcessor - Legacy::Editor - AutomatedTesting.Assets - COMPONENT - LargeWorlds - ) - - ly_add_pytest( - NAME AutomatedTesting::DynamicVegetationMiscTests_Periodic - TEST_SERIAL - TEST_SUITE periodic - PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg - PYTEST_MARKS "SUITE_periodic and dynveg_misc" - RUNTIME_DEPENDENCIES - AZ::AssetProcessor - Legacy::Editor - AutomatedTesting.Assets - COMPONENT - LargeWorlds - ) - - ly_add_pytest( - NAME AutomatedTesting::DynamicVegetationSurfaceTagTests_Periodic - TEST_SERIAL - TEST_SUITE periodic - PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg - PYTEST_MARKS "SUITE_periodic and dynveg_surfacetagemitter" - RUNTIME_DEPENDENCIES - AZ::AssetProcessor - Legacy::Editor - AutomatedTesting.Assets - COMPONENT - LargeWorlds - ) - ## LandscapeCanvas ## ly_add_pytest( diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py index 928d38ac5a..e404624c93 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py @@ -5,119 +5,119 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -""" -C4814463 - Altitude Filter overrides function as expected -C4847477 - Altitude Min/Max can be manually set -""" -import os -import sys - -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + prefilter_instance_count = ( + "Pre-filter instance counts are accurate", + "Unexpected number of pre-filter instances found" + ) + postfilter_instance_count = ( + "Post-filter instance counts are accurate", + "Unexpected number of post-filter instances found" + ) + postfilter_overrides_instance_count = ( + "Override instance counts are accurate", + "Unexpected number of override instances found" + ) -class TestAltitudeFilterComponentAndOverrides(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="AltitudeFilterComponentAndOverrides", args=["level"]) +def AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(): + """ + Summary: + A new level is created. A spawner entity is added, along with a planting surface at 32 on Z, and another at 36 + on Z. An Altitude Filter is added to the spawner entity, and Altitude Min/Max values are set. Instance counts + are validated. The same test is then performed for Altitude Filter overrides. - def run_test(self): - """ - Summary: - A new level is created. A spawner entity is added, along with a planting surface at 32 on Z, and another at 36 - on Z. An Altitude Filter is added to the spawner entity, and Altitude Min/Max values are set. Instance counts - are validated. The same test is then performed for Altitude Filter overrides. + Expected Behavior: + Instances are only spawned within the specified altitude ranges. - Expected Behavior: - Instances are only spawned within the specified altitude ranges. + Test Steps: + 1) Open a simple level + 2) Create an instance spawner entity + 3) Create surfaces to plant on, one at 32 on Z and another at 36 on Z. + 4) Initial instance counts pre-filter are verified. + 5) Altitude Min/Max is set on the Vegetation Altitude Filter component. + 6) Instance counts post-filter are verified. + 7) Altitude Min/Max is set on descriptor overrides. + 8) Instance counts post-filter are verified. - Test Steps: - 1) Create a new level - 2) Create an instance spawner entity - 3) Create surfaces to plant on, one at 32 on Z and another at 36 on Z. - 4) Initial instance counts pre-filter are verified. - 5) Altitude Min/Max is set on the Vegetation Altitude Filter component. - 6) Instance counts post-filter are verified. - 7) Altitude Min/Max is set on descriptor overrides. - 8) Instance counts post-filter are verified. + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import os - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.bus as bus + import azlmbr.math as math - # Set view of planting area for visual debugging - general.set_current_view_position(512.0, 500.0, 38.0) - general.set_current_view_rotation(-20.0, 0.0, 0.0) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # 2) Create a new entity with required vegetation area components - center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Add a Vegetation Altitude Filter - spawner_entity.add_component("Vegetation Altitude Filter") + # Set view of planting area for visual debugging + general.set_current_view_position(512.0, 500.0, 38.0) + general.set_current_view_rotation(-20.0, 0.0, 0.0) - # 3) Add surfaces to plant on - dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0) - elevated_surface_center_point = math.Vector3(512.0, 512.0, 36.0) - dynveg.create_surface_entity("Planting Surface Elevated", elevated_surface_center_point, 32.0, 32.0, 1.0) + # 2) Create a new entity with required vegetation area components + center_point = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path) - # Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape - veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, - 'Configuration|Area System Settings|Sector Point Snap Mode', 1) + # Add a Vegetation Altitude Filter + spawner_entity.add_component("Vegetation Altitude Filter") - # 4) Verify initial instance counts pre-filter - num_expected = (40 * 40) * 2 # 20 instances per 16m per side x 2 surfaces - spawner_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) - self.test_success = self.test_success and spawner_success + # 3) Add surfaces to plant on + dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0) + elevated_surface_center_point = math.Vector3(512.0, 512.0, 36.0) + dynveg.create_surface_entity("Planting Surface Elevated", elevated_surface_center_point, 32.0, 32.0, 1.0) - # 5) Set min/max vegetation altitude, instances should now only appear between 35-37m on the Z-axis - spawner_entity.get_set_test(3, "Configuration|Altitude Min", 35) - spawner_entity.get_set_test(3, "Configuration|Altitude Max", 37) + # Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape + veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, + 'Configuration|Area System Settings|Sector Point Snap Mode', 1) - # 6) Validate expected instance counts - num_expected = 40 * 40 # Instances should now only plant on the elevated surface - altitude_min_max_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) - self.test_success = self.test_success and altitude_min_max_success + # 4) Verify initial instance counts pre-filter + num_expected = (40 * 40) * 2 # 20 instances per 16m per side x 2 surfaces + spawner_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.result(Tests.prefilter_instance_count, spawner_success) - # Resize Spawner Entity's Box Shape component to allow monitoring for a different instance count - box_size = math.Vector3(16.0, 16.0, 16.0) - spawner_entity.get_set_test(1, "Box Shape|Box Configuration|Dimensions", box_size) + # 5) Set min/max vegetation altitude, instances should now only appear between 35-37m on the Z-axis + spawner_entity.get_set_test(3, "Configuration|Altitude Min", 35) + spawner_entity.get_set_test(3, "Configuration|Altitude Max", 37) - # 7) Allow overrides on Altitude Filter and set Altitude Filter Min/Max overrides on descriptor - spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Altitude Filter|Override Enabled", True) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Altitude Filter|Min", 35) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Altitude Filter|Max", 37) + # 6) Validate expected instance counts + num_expected = 40 * 40 # Instances should now only plant on the elevated surface + altitude_min_max_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.result(Tests.postfilter_instance_count, altitude_min_max_success) - # 8) Validate expected instances at specified elevations - num_expected = 20 * 20 # 20 instances per 16m per side - overrides_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) - self.test_success = self.test_success and overrides_success + # Resize Spawner Entity's Box Shape component to allow monitoring for a different instance count + box_size = math.Vector3(16.0, 16.0, 16.0) + spawner_entity.get_set_test(1, "Box Shape|Box Configuration|Dimensions", box_size) + + # 7) Allow overrides on Altitude Filter and set Altitude Filter Min/Max overrides on descriptor + spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Altitude Filter|Override Enabled", True) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Altitude Filter|Min", 35) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Altitude Filter|Max", 37) + + # 8) Validate expected instances at specified elevations + num_expected = 20 * 20 # 20 instances per 16m per side + overrides_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.result(Tests.postfilter_overrides_instance_count, overrides_success) -test = TestAltitudeFilterComponentAndOverrides() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py index 2ef72f72eb..3fc6a0afde 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py @@ -5,90 +5,88 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -import azlmbr.bus as bus -import azlmbr.components as components -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + preprocess_instance_count = ( + "Pre-process instance counts are accurate", + "Unexpected number of pre-process instances found" + ) + postprocess_instance_count = ( + "Post-process instance counts are accurate", + "Unexpected number of post-process instances found" + ) -class TestAltitudeFilterFilterStageToggle(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="AltitudeFilter_FilterStageToggle", args=["level"]) +def AltitudeFilter_FilterStageToggle(): + """ + Summary: + Filter Stage toggle affects final vegetation position - def run_test(self): - """ - Summary: - Filter Stage toggle affects final vegetation position + Expected Result: + Vegetation instances plant differently depending on the Filter Stage setting. + PostProcess should cause some number of plants that appear above and below the desired altitude range to disappear. - Expected Result: - Vegetation instances plant differently depending on the Filter Stage setting. - PostProcess should cause some number of plants that appear above and below the desired altitude range to disappear. + :return: None + """ - :return: None - """ + import os - PREPROCESS_INSTANCE_COUNT = 44 - POSTPROCESS_INSTANCE_COUNT = 34 + import azlmbr.legacy.general as general + import azlmbr.math as math - # Create empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - general.set_current_view_position(512.0, 480.0, 38.0) + PREPROCESS_INSTANCE_COUNT = 44 + POSTPROCESS_INSTANCE_COUNT = 34 - # Create basic vegetation entity - position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path) + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") + general.set_current_view_position(512.0, 480.0, 38.0) - # Add a Vegetation Altitude Filter to the vegetation area entity - vegetation.add_component("Vegetation Altitude Filter") + # Create basic vegetation entity + position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path) - # Create Surface for instances to plant on - dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0) + # Add a Vegetation Altitude Filter to the vegetation area entity + vegetation.add_component("Vegetation Altitude Filter") - # Add entity with Mesh to replicate creation of hills - hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 10.0) + # Create Surface for instances to plant on + dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0) - # Set a Min Altitude of 38 and Max of 40 in Vegetation Altitude Filter - vegetation.get_set_test(3, "Configuration|Altitude Min", 38.0) - vegetation.get_set_test(3, "Configuration|Altitude Max", 40.0) + # Add entity with Mesh to replicate creation of hills + hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 10.0) - # Create a new entity as a child of the vegetation area entity with Random Noise Gradient Generator, Gradient - # Transform Modifier, and Box Shape component - random_noise = hydra.Entity("random_noise") - random_noise.create_entity(position, ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"]) - random_noise.set_test_parent_entity(vegetation) + # Set a Min Altitude of 38 and Max of 40 in Vegetation Altitude Filter + vegetation.get_set_test(3, "Configuration|Altitude Min", 38.0) + vegetation.get_set_test(3, "Configuration|Altitude Max", 40.0) - # Add a Vegetation Position Modifier to the vegetation area entity. - vegetation.add_component("Vegetation Position Modifier") + # Create a new entity as a child of the vegetation area entity with Random Noise Gradient Generator, Gradient + # Transform Modifier, and Box Shape component + random_noise = hydra.Entity("random_noise") + random_noise.create_entity(position, ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"]) + random_noise.set_test_parent_entity(vegetation) - # Pin the Random Noise entity to the Gradient Entity Id field of the Position Modifier's Gradient X - vegetation.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", random_noise.id) + # Add a Vegetation Position Modifier to the vegetation area entity. + vegetation.add_component("Vegetation Position Modifier") - # Toggle between PreProcess and PostProcess in Vegetation Altitude Filter - vegetation.get_set_test(3, "Configuration|Filter Stage", 1) - result = self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 30.0, PREPROCESS_INSTANCE_COUNT), 2.0) - self.log(f"Vegetation instances count equal to expected value for PREPROCESS filter stage: {result}") - vegetation.get_set_test(3, "Configuration|Filter Stage", 2) - result = self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 30.0, POSTPROCESS_INSTANCE_COUNT), 2.0) - self.log(f"Vegetation instances count equal to expected value for POSTPROCESS filter stage: {result}") + # Pin the Random Noise entity to the Gradient Entity Id field of the Position Modifier's Gradient X + vegetation.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", random_noise.id) + + # Toggle between PreProcess and PostProcess in Vegetation Altitude Filter + vegetation.get_set_test(3, "Configuration|Filter Stage", 1) + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 30.0, PREPROCESS_INSTANCE_COUNT), 2.0) + Report.result(Tests.preprocess_instance_count, result) + vegetation.get_set_test(3, "Configuration|Filter Stage", 2) + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 30.0, POSTPROCESS_INSTANCE_COUNT), 2.0) + Report.result(Tests.postprocess_instance_count, result) -test = TestAltitudeFilterFilterStageToggle() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(AltitudeFilter_FilterStageToggle) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py index 8139ec5cac..bcd42b7fbb 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py @@ -5,104 +5,105 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + prefilter_instance_count = ( + "Pre-filter instance counts are accurate", + "Unexpected number of pre-filter instances found" + ) + postfilter_instance_count = ( + "Post-filter instance counts are accurate", + "Unexpected number of post-filter instances found" + ) -class TestAltitudeFilterShapeSample(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="AltitudeFilterShapeSample", args=["level"]) +def AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(): + """ + Summary: + A new level is created. A spawner entity is added, along with a planting surface at 32 on Z, and another at 36 + on Z. An Altitude Filter is added to the spawner entity, and set to sample a shape entity. Instance counts are + validated. - def run_test(self): - """ - Summary: - A new level is created. A spawner entity is added, along with a planting surface at 32 on Z, and another at 36 - on Z. An Altitude Filter is added to the spawner entity, and set to sample a shape entity. Instance counts are - validated. + Expected Behavior: + Instances are only spawned within the altitude range specified by the sampled shape. - Expected Behavior: - Instances are only spawned within the altitude range specified by the sampled shape. + Test Steps: + 1) Open a simple level + 2) Create an instance spawner entity + 3) Create surfaces to plant on, one at 32 on Z and another at 36 on Z. + 4) Initial instance counts pre-filter are verified. + 5) A new entity with shape is created, an sampled on the Vegetation Altitude Filter. + 6) Instance counts post-filter are verified. - Test Steps: - 1) Create a new level - 2) Create an instance spawner entity - 3) Create surfaces to plant on, one at 32 on Z and another at 36 on Z. - 4) Initial instance counts pre-filter are verified. - 5) A new entity with shape is created, an sampled on the Vegetation Altitude Filter. - 6) Instance counts post-filter are verified. + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import os - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.bus as bus + import azlmbr.math as math - # Set view of planting area for visual debugging - general.set_current_view_position(512.0, 500.0, 38.0) - general.set_current_view_rotation(-20.0, 0.0, 0.0) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # 2) Create a new entity with required vegetation area components - center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 16.0, asset_path) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Add a Vegetation Altitude Filter - spawner_entity.add_component("Vegetation Altitude Filter") + # Set view of planting area for visual debugging + general.set_current_view_position(512.0, 500.0, 38.0) + general.set_current_view_rotation(-20.0, 0.0, 0.0) - # 3) Add surfaces to plant on - dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0) - elevated_surface_center_point = math.Vector3(512.0, 512.0, 36.0) - dynveg.create_surface_entity("Planting Surface Elevated", elevated_surface_center_point, 32.0, 32.0, 1.0) + # 2) Create a new entity with required vegetation area components + center_point = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 16.0, asset_path) - # Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape - veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, - 'Configuration|Area System Settings|Sector Point Snap Mode', 1) + # Add a Vegetation Altitude Filter + spawner_entity.add_component("Vegetation Altitude Filter") - # 4) Verify initial instance counts pre-filter - num_expected = (20 * 20) * 2 # 20 instances per 16m per side x 2 surfaces - spawner_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) - self.test_success = self.test_success and spawner_success + # 3) Add surfaces to plant on + dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0) + elevated_surface_center_point = math.Vector3(512.0, 512.0, 36.0) + dynveg.create_surface_entity("Planting Surface Elevated", elevated_surface_center_point, 32.0, 32.0, 1.0) - # 5) Create a new entity with a shape at 36 on the Z-axis, and pin the entity to the Vegetation Altitude Filter - shape_sampler_center_point = math.Vector3(512.0, 512.0, 36.0) - shape_sampler = hydra.Entity("Shape Sampler") - shape_sampler.create_entity( - shape_sampler_center_point, - ["Box Shape"] - ) - if shape_sampler.id.IsValid(): - print(f"'{shape_sampler.name}' created") - spawner_entity.get_set_test(3, 'Configuration|Pin To Shape Entity Id', shape_sampler.id) + # Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape + veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, + 'Configuration|Area System Settings|Sector Point Snap Mode', 1) - # 6) Validate expected instance counts - num_expected = 20 * 20 # Instances should now only plant on the elevated surface - sampler_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) - self.test_success = self.test_success and sampler_success + # 4) Verify initial instance counts pre-filter + num_expected = (20 * 20) * 2 # 20 instances per 16m per side x 2 surfaces + spawner_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.result(Tests.prefilter_instance_count, spawner_success) + + # 5) Create a new entity with a shape at 36 on the Z-axis, and pin the entity to the Vegetation Altitude Filter + shape_sampler_center_point = math.Vector3(512.0, 512.0, 36.0) + shape_sampler = hydra.Entity("Shape Sampler") + shape_sampler.create_entity( + shape_sampler_center_point, + ["Box Shape"] + ) + if shape_sampler.id.IsValid(): + print(f"'{shape_sampler.name}' created") + spawner_entity.get_set_test(3, 'Configuration|Pin To Shape Entity Id', shape_sampler.id) + + # 6) Validate expected instance counts + num_expected = 20 * 20 # Instances should now only plant on the elevated surface + sampler_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.result(Tests.postfilter_instance_count, sampler_success) -test = TestAltitudeFilterShapeSample() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AreaComponentSlices_SliceCreationAndVisibilityToggle.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AreaComponentSlices_SliceCreationAndVisibilityToggle.py deleted file mode 100755 index 095024cca1..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AreaComponentSlices_SliceCreationAndVisibilityToggle.py +++ /dev/null @@ -1,125 +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 -""" - -import os -import sys - -import azlmbr.math as math -import azlmbr.legacy.general as general -import azlmbr.slice as slice -import azlmbr.bus as bus -import azlmbr.editor as editor -import azlmbr.asset as asset -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg - - -class TestAreaComponentsSliceCreationAndVisibilityToggle(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__( - self, log_prefix="AreaComponentSlices_SliceCreationAndVisibilityToggle", args=["level"] - ) - - def run_test(self): - """ - Summary: - C2627900 Verifies if a slice containing the component can be created. - C2627905 A slice containing the Vegetation Layer Blender component can be created. - C2627904: Hiding a slice containing the component clears any visuals from the Viewport. - - Expected Result: - C2627900, C2627905: Slice is created, and is properly processed in the Asset Processor. - C2627904: Vegetation area visuals are hidden from the Viewport. - - :return: None - """ - - def path_is_valid_asset(asset_path): - asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False) - return asset_id.invoke("IsValid") - - # 1) Create empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) - - general.set_current_view_position(512.0, 480.0, 38.0) - - # 2) C2627900 Verifies if a slice containing the Vegetation Layer Spawner component can be created. - # 2.1) Create basic vegetation entity - position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - veg_1 = dynveg.create_vegetation_area("vegetation_1", position, 16.0, 16.0, 16.0, asset_path) - - # 2.2) Create slice from the entity - slice_path = os.path.join("slices", "TestSlice_1.slice") - slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", veg_1.id, slice_path) - - # 2.3) Verify if the slice has been created successfully - self.wait_for_condition(lambda: path_is_valid_asset(slice_path), 5.0) - self.log( - f"Slice has been created successfully (entity with spawner component): {path_is_valid_asset(slice_path)}" - ) - - # 3) C2627904: Hiding a slice containing the component clears any visuals from the Viewport - # 3.1) Create Surface for instances to plant on - dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0) - - # 3.2) Initially verify instance count before hiding slice - self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 400)) - self.log( - f"Vegetation plants initially when slice is shown: {dynveg.validate_instance_count(position, 16.0, 400)}" - ) - - # 3.3) Hide the slice and verify instance count - editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", veg_1.id, False) - self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 0)) - self.log(f"Vegetation is cleared when slice is hidden: {dynveg.validate_instance_count(position, 16.0, 0)}") - - # 3.4) Unhide the slice - editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", veg_1.id, True) - - # 4) C2627905 A slice containing the Vegetation Layer Blender component can be created. - # 4.1) Create another vegetation entity to add to blender component - veg_2 = dynveg.create_vegetation_area("vegetation_2", position, 1.0, 1.0, 1.0, "") - - # 4.2) Create entity with Vegetation Layer Blender - components_to_add = ["Box Shape", "Vegetation Layer Blender"] - blender_entity = hydra.Entity("blender_entity") - blender_entity.create_entity(position, components_to_add) - - # 4.3) Pin both the vegetation areas to the blender entity - pte = hydra.get_property_tree(blender_entity.components[1]) - path = "Configuration|Vegetation Areas" - pte.update_container_item(path, 0, veg_1.id) - pte.add_container_item(path, 1, veg_2.id) - - # 4.4) Drag the simple vegetation areas under the Vegetation Layer Blender entity to create an entity hierarchy. - veg_1.set_test_parent_entity(blender_entity) - veg_2.set_test_parent_entity(blender_entity) - - # 4.5) Create slice from blender entity - slice_path = os.path.join("slices", "TestSlice_2.slice") - slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", blender_entity.id, slice_path) - - # 4.6) Verify if the slice has been created successfully - self.wait_for_condition(lambda: path_is_valid_asset(slice_path), 5.0) - self.log( - f"Slice has been created successfully (entity with blender component): {path_is_valid_asset(slice_path)}" - ) - - -test = TestAreaComponentsSliceCreationAndVisibilityToggle() -test.run() diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py index 5242160b35..7f6ce87110 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py @@ -5,162 +5,167 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.bus as bus -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.math as math -import azlmbr.paths -import azlmbr.vegetation as vegetation - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + combined_instance_count_validation = ( + "Combined instance counts are as expected", + "Found an unexpected number of instances" + ) + replaced_asset_list_combined_instance_count_validation = ( + "Combined instance counts are as expected after replacing an Asset List reference", + "Found an unexpected number of instances after replacing an Asset List reference" + ) + removed_asset_lists_combined_instance_count_validation = ( + "Instance counts are as expected after removing the referenced Asset Lists", + "Found an unexpected number of instances after removing the referenced Asset Lists" + ) -class TestAssetListCombiner(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="AssetListCombiner_CombinedDescriptors", args=["level"]) +def AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(): + """ + Summary: + Combined descriptors appear as expected in a vegetation area. Also verifies remove/replace of assigned Asset + Lists. - def run_test(self): - """ - Summary: - Combined descriptors appear as expected in a vegetation area. Also verifies remove/replace of assigned Asset - Lists. + Expected Behavior: + Vegetation fills in the area using the assets assigned to both Vegetation Asset Lists. - Expected Behavior: - Vegetation fills in the area using the assets assigned to both Vegetation Asset Lists. + Test Steps: + 1) Open a simple level + 2) Create 3 entities with Vegetation Asset List components set to spawn different descriptors + 3) Create a planting surface and add a Vegetation System Settings level component with instances set to spawn + on center instead of corner + 4) Create a spawner using a Vegetation Asset List Combiner component and a Weight Selector, and disallow + spawning empty assets + 5) Add 2 of the Asset List entities to the Vegetation Asset List Combiner component (PinkFlower and Empty) + 6) Create a Constant Gradient entity as a child of the spawner entity, and a Dither Gradient Modifier entity + as a child of the Constant Gradient entity, and configure for a checkerboard pattern + 7) Pin the Dither Gradient Entity to the Asset Weight Selector of the spawner entity + 8) Validate instance count with configured Asset List Combiner + 9) Replace the reference to the 2nd asset list on the Vegetation Asset List Combiner component and validate + instance count + 10) Remove the referenced Asset Lists on the Asset List Combiner, Disable/Re-enable the Asset List + Combiner component to force a refresh, and validate instance count - Test Steps: - 1) Create a new, temporary level - 2) Create 3 entities with Vegetation Asset List components set to spawn different descriptors - 3) Create a planting surface and add a Vegetation System Settings level component with instances set to spawn - on center instead of corner - 4) Create a spawner using a Vegetation Asset List Combiner component and a Weight Selector, and disallow - spawning empty assets - 5) Add 2 of the Asset List entities to the Vegetation Asset List Combiner component (PinkFlower and Empty) - 6) Create a Constant Gradient entity as a child of the spawner entity, and a Dither Gradient Modifier entity - as a child of the Constant Gradient entity, and configure for a checkerboard pattern - 7) Pin the Dither Gradient Entity to the Asset Weight Selector of the spawner entity - 8) Validate instance count with configured Asset List Combiner - 9) Replace the reference to the 2nd asset list on the Vegetation Asset List Combiner component and validate - instance count - 10) Remove the referenced Asset Lists on the Asset List Combiner, Disable/Re-enable the Asset List - Combiner component to force a refresh, and validate instance count + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import os - def create_asset_list_entity(name, center, dynamic_slice_asset_path): - asset_list_entity = hydra.Entity(name) - asset_list_entity.create_entity( - center, - ["Vegetation Asset List"] - ) - if asset_list_entity.id.IsValid(): - print(f"'{asset_list_entity.name}' created") + import azlmbr.bus as bus + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.math as math + import azlmbr.vegetation as vegetation - # Set the Asset List to a Dynamic Slice spawner with a specific slice asset selected - dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner() - dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path) - descriptor = hydra.get_component_property_value(asset_list_entity.components[0], - "Configuration|Embedded Assets|[0]") - descriptor.spawner = dynamic_slice_spawner - asset_list_entity.get_set_test(0, "Configuration|Embedded Assets|[0]", descriptor) - return asset_list_entity + import editor_python_test_tools.hydra_editor_utils as hydra + 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) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, + def create_asset_list_entity(name, center, dynamic_slice_asset_path): + asset_list_entity = hydra.Entity(name) + asset_list_entity.create_entity( + center, + ["Vegetation Asset List"] ) + if asset_list_entity.id.IsValid(): + print(f"'{asset_list_entity.name}' created") - # Set view of planting area for visual debugging - general.set_current_view_position(512.0, 500.0, 38.0) - general.set_current_view_rotation(-20.0, 0.0, 0.0) + # Set the Asset List to a Dynamic Slice spawner with a specific slice asset selected + dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner() + dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path) + descriptor = hydra.get_component_property_value(asset_list_entity.components[0], + "Configuration|Embedded Assets|[0]") + descriptor.spawner = dynamic_slice_spawner + asset_list_entity.get_set_test(0, "Configuration|Embedded Assets|[0]", descriptor) + return asset_list_entity - # 2) Create 3 entities with Vegetation Asset List components set to spawn different descriptors - center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - asset_path2 = os.path.join("Slices", "PurpleFlower.dynamicslice") - asset_list_entity = create_asset_list_entity("Asset List 1", center_point, asset_path) - asset_list_entity2 = create_asset_list_entity("Asset List 2", center_point, None) - asset_list_entity3 = create_asset_list_entity("Asset List 3", center_point, asset_path2) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # 3) Create a planting surface and add a Vegetation System Settings level component with instances set to spawn - # on center instead of corner - dynveg.create_surface_entity("Surface Entity", center_point, 32.0, 32.0, 1.0) - veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, - 'Configuration|Area System Settings|Sector Point Snap Mode', 1) + # Set view of planting area for visual debugging + general.set_current_view_position(512.0, 500.0, 38.0) + general.set_current_view_rotation(-20.0, 0.0, 0.0) - # 4) Create a spawner using a Vegetation Asset List Combiner component and a Weight Selector, and disallow - # spawning empty assets - spawner_entity = dynveg.create_vegetation_area("Spawner Entity", center_point, 16.0, 16.0, 16.0, None) - spawner_entity.remove_component("Vegetation Asset List") - spawner_entity.add_component("Vegetation Asset List Combiner") - spawner_entity.add_component("Vegetation Asset Weight Selector") - spawner_entity.get_set_test(0, "Configuration|Allow Empty Assets", False) + # 2) Create 3 entities with Vegetation Asset List components set to spawn different descriptors + center_point = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + asset_path2 = os.path.join("Slices", "PurpleFlower.dynamicslice") + asset_list_entity = create_asset_list_entity("Asset List 1", center_point, asset_path) + asset_list_entity2 = create_asset_list_entity("Asset List 2", center_point, None) + asset_list_entity3 = create_asset_list_entity("Asset List 3", center_point, asset_path2) - # 5) Add the Asset List entities to the Vegetation Asset List Combiner component - asset_list_entities = [asset_list_entity.id, asset_list_entity2.id] - spawner_entity.get_set_test(2, "Configuration|Descriptor Providers", asset_list_entities) + # 3) Create a planting surface and add a Vegetation System Settings level component with instances set to spawn + # on center instead of corner + dynveg.create_surface_entity("Surface Entity", center_point, 32.0, 32.0, 1.0) + veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, + 'Configuration|Area System Settings|Sector Point Snap Mode', 1) - # 6) Create a Constant Gradient entity as a child of the spawner entity, and a Dither Gradient Modifier entity - # as a child of the Constant Gradient entity, and configure for a checkerboard pattern - components_to_add = ["Constant Gradient"] - constant_gradient_entity = hydra.Entity("Constant Gradient Entity") - constant_gradient_entity.create_entity(center_point, components_to_add, parent_id=spawner_entity.id) - constant_gradient_entity.get_set_test(0, "Configuration|Value", 0.5) + # 4) Create a spawner using a Vegetation Asset List Combiner component and a Weight Selector, and disallow + # spawning empty assets + spawner_entity = dynveg.create_vegetation_area("Spawner Entity", center_point, 16.0, 16.0, 16.0, None) + spawner_entity.remove_component("Vegetation Asset List") + spawner_entity.add_component("Vegetation Asset List Combiner") + spawner_entity.add_component("Vegetation Asset Weight Selector") + spawner_entity.get_set_test(0, "Configuration|Allow Empty Assets", False) - components_to_add = ["Dither Gradient Modifier"] - dither_gradient_entity = hydra.Entity("Dither Gradient Entity") - dither_gradient_entity.create_entity(center_point, components_to_add, parent_id=constant_gradient_entity.id) - dither_gradient_entity.get_set_test(0, "Configuration|Gradient|Gradient Entity Id", constant_gradient_entity.id) + # 5) Add the Asset List entities to the Vegetation Asset List Combiner component + asset_list_entities = [asset_list_entity.id, asset_list_entity2.id] + spawner_entity.get_set_test(2, "Configuration|Descriptor Providers", asset_list_entities) - # 7) Pin the Dither Gradient Entity to the Asset Weight Selector of the spawner entity - spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", dither_gradient_entity.id) + # 6) Create a Constant Gradient entity as a child of the spawner entity, and a Dither Gradient Modifier entity + # as a child of the Constant Gradient entity, and configure for a checkerboard pattern + components_to_add = ["Constant Gradient"] + constant_gradient_entity = hydra.Entity("Constant Gradient Entity") + constant_gradient_entity.create_entity(center_point, components_to_add, parent_id=spawner_entity.id) + constant_gradient_entity.get_set_test(0, "Configuration|Value", 0.5) - # 8) Validate instance count. We should now have 200 instances in the spawner area as every other instance - # should be an empty asset which the spawner is set to disallow - num_expected = 20 * 20 / 2 - success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 5.0) - self.test_success = success and self.test_success + components_to_add = ["Dither Gradient Modifier"] + dither_gradient_entity = hydra.Entity("Dither Gradient Entity") + dither_gradient_entity.create_entity(center_point, components_to_add, parent_id=constant_gradient_entity.id) + dither_gradient_entity.get_set_test(0, "Configuration|Gradient|Gradient Entity Id", constant_gradient_entity.id) - # 9) Replace the reference to the 2nd asset list on the Vegetation Asset List Combiner component and validate - # instance count. Should now be 400 instances as the empty spaces can now be claimed by the new descriptor - spawner_entity.get_set_test(2, "Configuration|Descriptor Providers|[1]", asset_list_entity3.id) - num_expected = 20 * 20 - success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 5.0) - self.test_success = success and self.test_success + # 7) Pin the Dither Gradient Entity to the Asset Weight Selector of the spawner entity + spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", dither_gradient_entity.id) - # 10) Remove the referenced Asset Lists on the Asset List Combiner, Disable/Re-enable the Asset List - # Combiner component to force a refresh, and validate instance count. We should now have 0 instances. - pte = hydra.get_property_tree(spawner_entity.components[2]) - path = "Configuration|Descriptor Providers" - pte.reset_container(path) - # Component refresh is currently necessary due to container operations not causing a refresh (LY-120947) - editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [spawner_entity.components[2]]) - editor.EditorComponentAPIBus(bus.Broadcast, "EnableComponents", [spawner_entity.components[2]]) - num_expected = 0 - success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 5.0) - self.test_success = success and self.test_success + # 8) Validate instance count. We should now have 200 instances in the spawner area as every other instance + # should be an empty asset which the spawner is set to disallow + num_expected = 20 * 20 / 2 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 5.0) + Report.result(Tests.combined_instance_count_validation, success) + + # 9) Replace the reference to the 2nd asset list on the Vegetation Asset List Combiner component and validate + # instance count. Should now be 400 instances as the empty spaces can now be claimed by the new descriptor + spawner_entity.get_set_test(2, "Configuration|Descriptor Providers|[1]", asset_list_entity3.id) + num_expected = 20 * 20 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 5.0) + Report.result(Tests.replaced_asset_list_combined_instance_count_validation, success) + + # 10) Remove the referenced Asset Lists on the Asset List Combiner, Disable/Re-enable the Asset List + # Combiner component to force a refresh, and validate instance count. We should now have 0 instances. + pte = hydra.get_property_tree(spawner_entity.components[2]) + path = "Configuration|Descriptor Providers" + pte.reset_container(path) + # Component refresh is currently necessary due to container operations not causing a refresh (LY-120947) + editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [spawner_entity.components[2]]) + editor.EditorComponentAPIBus(bus.Broadcast, "EnableComponents", [spawner_entity.components[2]]) + num_expected = 0 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 5.0) + Report.result(Tests.removed_asset_lists_combined_instance_count_validation, success) -test = TestAssetListCombiner() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py index 113bbc8ea8..53b45e8470 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py @@ -5,112 +5,110 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -""" -C6269654: Vegetation areas using weight selectors properly distribute instances according to Sort By Weight setting -""" -import os -import sys - -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.legacy.general as general -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + highest_weight_instance_count = ( + "Found the expected number of instances when sorting by highest weight", + "Found an unexpected number of instances when sorting by highest weight" + ) + lowest_weight_instance_count = ( + "Found the expected number of instances when sorting by lowest weight", + "Found an unexpected number of instances when sorting by lowest weight" + ) -class TestAssetWeightSelectorSortByWeight(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="AssetWeightSelector_SortByWeight", args=["level"]) +def AssetWeightSelector_InstancesExpressBasedOnWeight(): + """ + Summary: + Vegetation areas using weight selectors properly distribute instances according to Sort By Weight setting - def run_test(self): - """ - Summary: - Vegetation areas using weight selectors properly distribute instances according to Sort By Weight setting + Expected Behavior: + Vegetation is planted in the area according to the generated gradient pattern. + Higher weight assets are more likely to express when "Descending (highest first)" is selected. + Lower weight assets are more likely to express when "Ascending (lowest first)" is selected. - Expected Behavior: - Vegetation is planted in the area according to the generated gradient pattern. - Higher weight assets are more likely to express when "Descending (highest first)" is selected. - Lower weight assets are more likely to express when "Ascending (lowest first)" is selected. + Test Steps: + 1) Open a simple level + 2) Create instance spawner with 2 descriptors, one with an Empty Asset + 3) Create a planting surface + 4) Create a child entity of the instance spawner with a Constant Gradient component with default values (1.0) + 5) Pin the child entity to Vegetation Asset Weight Selector of the instance spawner entity + 6) Set first descriptor to a higher weight, and toggle off Allow Empty Assets + 7) Validate instance count with initial setup/sort values + 8) Change sort values and validate instance count - Test Steps: - 1) Create new level - 2) Create instance spawner with 2 descriptors, one with an Empty Asset - 3) Create a planting surface - 4) Create a child entity of the instance spawner with a Constant Gradient component with default values (1.0) - 5) Pin the child entity to Vegetation Asset Weight Selector of the instance spawner entity - 6) Set first descriptor to a higher weight, and toggle off Allow Empty Assets - 7) Validate instance count with initial setup/sort values - 8) Change sort values and validate instance count + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import os - # Set view of planting area for visual debugging - general.set_current_view_position(512.0, 500.0, 38.0) - general.set_current_view_rotation(-20.0, 0.0, 0.0) + import azlmbr.legacy.general as general + import azlmbr.math as math - # 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors, one set to a - # valid slice entity, and one set to None - spawner_center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) - desc_asset = hydra.get_component_property_value(spawner_entity.components[2], - "Configuration|Embedded Assets")[0] - desc_list = [desc_asset, desc_asset] - spawner_entity.get_set_test(2, "Configuration|Embedded Assets", desc_list) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[1]|Instance|Slice Asset", None) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # Add an Asset Weight Selector component to the spawner entity - spawner_entity.add_component("Vegetation Asset Weight Selector") + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # 3) Create a planting surface - dynveg.create_surface_entity("Planting Surface", spawner_center_point, 32.0, 32.0, 1.0) + # Set view of planting area for visual debugging + general.set_current_view_position(512.0, 500.0, 38.0) + general.set_current_view_rotation(-20.0, 0.0, 0.0) - # 4) Create a child entity of the spawner entity with a Constant Gradient component - components_to_add = ["Constant Gradient"] - gradient_entity = hydra.Entity("Gradient Entity") - gradient_entity.create_entity(spawner_center_point, components_to_add, parent_id=spawner_entity.id) + # 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors, one set to a + # valid slice entity, and one set to None + spawner_center_point = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, + asset_path) + desc_asset = hydra.get_component_property_value(spawner_entity.components[2], + "Configuration|Embedded Assets")[0] + desc_list = [desc_asset, desc_asset] + spawner_entity.get_set_test(2, "Configuration|Embedded Assets", desc_list) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[1]|Instance|Slice Asset", None) - # 5) Pin the Constant Gradient to the Vegetation Asset Weight Selector - spawner_entity.get_set_test(3, 'Configuration|Gradient|Gradient Entity Id', gradient_entity.id) + # Add an Asset Weight Selector component to the spawner entity + spawner_entity.add_component("Vegetation Asset Weight Selector") - # 6) Set the first descriptor weight to a higher value and toggle off Allow Empty Assets on the Layer Spawner - # component - spawner_entity.get_set_test(2, 'Configuration|Embedded Assets|[0]|Weight', 50) - spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False) + # 3) Create a planting surface + dynveg.create_surface_entity("Planting Surface", spawner_center_point, 32.0, 32.0, 1.0) - # 7) Query for expected instances with default settings. We should have 0 instances with default Constant - # Gradient setup sorting by higher weight first - num_expected = 0 - initial_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) - self.test_success = initial_success and self.test_success + # 4) Create a child entity of the spawner entity with a Constant Gradient component + components_to_add = ["Constant Gradient"] + gradient_entity = hydra.Entity("Gradient Entity") + gradient_entity.create_entity(spawner_center_point, components_to_add, parent_id=spawner_entity.id) - # 8) Sort by lowest weight first, and verify instance counts. We should now have 400 instances as the highest - # priority instance won't be allowed to claim space due to "Allow Empty Assets" being False - spawner_entity.get_set_test(3, 'Configuration|Sort By Weight', 1) - num_expected = 20 * 20 - final_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) - self.test_success = final_success and self.test_success + # 5) Pin the Constant Gradient to the Vegetation Asset Weight Selector + spawner_entity.get_set_test(3, 'Configuration|Gradient|Gradient Entity Id', gradient_entity.id) + + # 6) Set the first descriptor weight to a higher value and toggle off Allow Empty Assets on the Layer Spawner + # component + spawner_entity.get_set_test(2, 'Configuration|Embedded Assets|[0]|Weight', 50) + spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False) + + # 7) Query for expected instances with default settings. We should have 0 instances with default Constant + # Gradient setup sorting by higher weight first + num_expected = 0 + initial_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.result(Tests.highest_weight_instance_count, initial_success) + + # 8) Sort by lowest weight first, and verify instance counts. We should now have 400 instances as the highest + # priority instance won't be allowed to claim space due to "Allow Empty Assets" being False + spawner_entity.get_set_test(3, 'Configuration|Sort By Weight', 1) + num_expected = 20 * 20 + final_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.result(Tests.lowest_weight_instance_count, final_success) -test = TestAssetWeightSelectorSortByWeight() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(AssetWeightSelector_InstancesExpressBasedOnWeight) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py index 12fb6d8eff..c2adffc6ca 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py @@ -5,108 +5,118 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + initial_instance_counts = ( + "Initial instance counts are as expected", + "Unexpected number of initial instances found" + ) + instance_counts_1m = ( + "Instance counts with 1 meters between instances are as expected", + "Unexpected number of instances found with 1 meters between instances" + ) + instance_counts_2m = ( + "Instance counts with 2 meters between instances are as expected", + "Unexpected number of instances found with 2 meters between instances" + ) + instance_counts_16m = ( + "Instance counts with 16 meters between instances are as expected", + "Unexpected number of instances found with 16 meters between instances" + ) -class TestDistanceBetweenFilterComponentOverrides(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="DistanceBetweenFilterComponentOverrides", args=["level"]) +def DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(): + """ + Summary: Creates a level with a simple vegetation area. A Vegetation Distance Between Filter is + added and the min radius is changed as an override on the descriptor. Instance counts at specific points are + validated. - def run_test(self): - """ - Summary: Creates a level with a simple vegetation area. A Vegetation Distance Between Filter is - added and the min radius is changed as an override on the descriptor. Instance counts at specific points are - validated. + Test Steps: + 1) Open a simple level + 2) Create a vegetation area + 3) Create a surface for planting + 4) Add the Vegetation System Settings component and setup for the test + 5-8) Add the Distance Between Filter, setup overrides on both the component and descriptor, and validate + expected instance counts with a few different Radius values - Test Steps: - 1) Create a new level - 2) Create a vegetation area - 3) Create a surface for planting - 4) Add the Vegetation System Settings component and setup for the test - 5-8) Add the Distance Between Filter, setup overrides on both the component and descriptor, and validate - expected instance counts with a few different Radius values + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ - instance_query_point_a = math.Vector3(512.5, 512.5, 32.0) - instance_query_point_b = math.Vector3(514.0, 512.5, 32.0) - instance_query_point_c = math.Vector3(515.0, 512.5, 32.0) + import os - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.bus as bus + import azlmbr.math as math - general.set_current_view_position(512.0, 480.0, 38.0) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # 2) Create a new entity with required vegetation area components - spawner_center_point = math.Vector3(520.0, 520.0, 32.0) - asset_path = os.path.join("Slices", "1m_cube.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + instance_query_point_a = math.Vector3(512.5, 512.5, 32.0) + instance_query_point_b = math.Vector3(514.0, 512.5, 32.0) + instance_query_point_c = math.Vector3(515.0, 512.5, 32.0) - # 3) Create a surface to plant on - surface_center_point = math.Vector3(512.0, 512.0, 32.0) - dynveg.create_surface_entity("Planting Surface", surface_center_point, 128.0, 128.0, 1.0) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # 4) Add a Vegetation System Settings Level component and set Sector Point Snap Mode to Center - veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, - 'Configuration|Area System Settings|Sector Point Snap Mode', 1) - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, - 'Configuration|Area System Settings|Sector Point Density', 16) + general.set_current_view_position(512.0, 480.0, 38.0) - # 5) Add a Vegetation Distance Between Filter, toggle overrides on both the component and descriptor, - # and verify initial instance counts are accurate - spawner_entity.add_component("Vegetation Distance Between Filter") - spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Override Enabled", True) - num_expected = 16 * 16 - initial_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) - self.test_success = self.test_success and initial_success + # 2) Create a new entity with required vegetation area components + spawner_center_point = math.Vector3(520.0, 520.0, 32.0) + asset_path = os.path.join("Slices", "1m_cube.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, + asset_path) - # 6) Change Radius Min to 1.0, refresh, and verify instance counts are accurate - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min", 1.0) - point_a_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0) - point_b_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0) - point_c_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 1), 5.0) - self.test_success = self.test_success and point_a_success and point_b_success and point_c_success + # 3) Create a surface to plant on + surface_center_point = math.Vector3(512.0, 512.0, 32.0) + dynveg.create_surface_entity("Planting Surface", surface_center_point, 128.0, 128.0, 1.0) - # 7) Change Radius Min to 2.0, refresh, and verify instance counts are accurate - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min", 2.0) - point_a_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0) - point_b_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0) - point_c_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 0), 5.0) - self.test_success = self.test_success and point_a_success and point_b_success and point_c_success + # 4) Add a Vegetation System Settings Level component and set Sector Point Snap Mode to Center + veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, + 'Configuration|Area System Settings|Sector Point Snap Mode', 1) + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, + 'Configuration|Area System Settings|Sector Point Density', 16) - # 8) Change Radius Min to 16.0, refresh, and verify instance counts are accurate, only a single instance should plant - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min", 16.0) - num_expected_instances = 1 - final_check_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0) - self.test_success = self.test_success and final_check_success + # 5) Add a Vegetation Distance Between Filter, toggle overrides on both the component and descriptor, + # and verify initial instance counts are accurate + spawner_entity.add_component("Vegetation Distance Between Filter") + spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Override Enabled", True) + num_expected = 16 * 16 + initial_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.result(Tests.initial_instance_counts, initial_success) + + # 6) Change Radius Min to 1.0, refresh, and verify instance counts are accurate + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min", 1.0) + point_a_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0) + point_b_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0) + point_c_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 1), 5.0) + Report.result(Tests.instance_counts_1m, point_a_success and point_b_success and point_c_success) + + # 7) Change Radius Min to 2.0, refresh, and verify instance counts are accurate + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min", 2.0) + point_a_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0) + point_b_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0) + point_c_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 0), 5.0) + Report.result(Tests.instance_counts_2m, point_a_success and point_b_success and point_c_success) + + # 8) Change Radius Min to 16.0, refresh, and verify instance counts are accurate, only a single instance should plant + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min", 16.0) + num_expected_instances = 1 + final_check_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_counts_16m, final_check_success) -test = TestDistanceBetweenFilterComponentOverrides() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py index 5b978c6212..de0d9a14fe 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py @@ -5,104 +5,113 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + initial_instance_counts = ( + "Initial instance counts are as expected", + "Unexpected number of initial instances found" + ) + instance_counts_1m = ( + "Instance counts with 1 meters between instances are as expected", + "Unexpected number of instances found with 1 meters between instances" + ) + instance_counts_2m = ( + "Instance counts with 2 meters between instances are as expected", + "Unexpected number of instances found with 2 meters between instances" + ) + instance_counts_16m = ( + "Instance counts with 16 meters between instances are as expected", + "Unexpected number of instances found with 16 meters between instances" + ) -class TestDistanceBetweenFilterComponent(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="DistanceBetweenFilterComponent", args=["level"]) +def DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(): + """ + Summary: Creates a level with a simple vegetation area. A Vegetation Distance Between Filter is + added and the min radius is changed. Instance counts at specific points are validated. - def run_test(self): - """ - Summary: Creates a level with a simple vegetation area. A Vegetation Distance Between Filter is - added and the min radius is changed. Instance counts at specific points are validated. + Test Steps: + 1) Open a simple level + 2) Create a vegetation area + 3) Create a surface for planting + 4) Add the Vegetation System Settings component and setup for the test + 5-8) Add the Distance Between Filter, and validate expected instance counts with a few different Radius values - Test Steps: - 1) Create a new level - 2) Create a vegetation area - 3) Create a surface for planting - 4) Add the Vegetation System Settings component and setup for the test - 5-8) Add the Distance Between Filter, and validate expected instance counts with a few different Radius values + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ - instance_query_point_a = math.Vector3(512.5, 512.5, 32.0) - instance_query_point_b = math.Vector3(514.0, 512.5, 32.0) - instance_query_point_c = math.Vector3(515.0, 512.5, 32.0) + import os - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.bus as bus + import azlmbr.math as math - general.set_current_view_position(512.0, 480.0, 38.0) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # 2) Create a new entity with required vegetation area components - spawner_center_point = math.Vector3(520.0, 520.0, 32.0) - asset_path = os.path.join("Slices", "1m_cube.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + instance_query_point_a = math.Vector3(512.5, 512.5, 32.0) + instance_query_point_b = math.Vector3(514.0, 512.5, 32.0) + instance_query_point_c = math.Vector3(515.0, 512.5, 32.0) - # 3) Create a surface to plant on - surface_center_point = math.Vector3(512.0, 512.0, 32.0) - dynveg.create_surface_entity("Planting Surface", surface_center_point, 128.0, 128.0, 1.0) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # 4) Add a Vegetation System Settings Level component and set Sector Point Snap Mode to Center - veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, - 'Configuration|Area System Settings|Sector Point Snap Mode', 1) - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, - 'Configuration|Area System Settings|Sector Point Density', 16) + general.set_current_view_position(512.0, 480.0, 38.0) - # 5) Add a Vegetation Distance Between Filter and verify initial instance counts are accurate - spawner_entity.add_component("Vegetation Distance Between Filter") - num_expected = 16 * 16 - num_expected = 16 * 16 - initial_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) - self.test_success = self.test_success and initial_success + # 2) Create a new entity with required vegetation area components + spawner_center_point = math.Vector3(520.0, 520.0, 32.0) + asset_path = os.path.join("Slices", "1m_cube.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, + asset_path) - # 6) Change Radius Min to 1.0, refresh, and verify instance counts are accurate - spawner_entity.get_set_test(3, "Configuration|Radius Min", 1.0) - point_a_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0) - point_b_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0) - point_c_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 1), 5.0) - self.test_success = self.test_success and point_a_success and point_b_success and point_c_success + # 3) Create a surface to plant on + surface_center_point = math.Vector3(512.0, 512.0, 32.0) + dynveg.create_surface_entity("Planting Surface", surface_center_point, 128.0, 128.0, 1.0) - # 7) Change Radius Min to 2.0, refresh, and verify instance counts are accurate - spawner_entity.get_set_test(3, "Configuration|Radius Min", 2.0) - point_a_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0) - point_b_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0) - point_c_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 0), 5.0) - self.test_success = self.test_success and point_a_success and point_b_success and point_c_success + # 4) Add a Vegetation System Settings Level component and set Sector Point Snap Mode to Center + veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, + 'Configuration|Area System Settings|Sector Point Snap Mode', 1) + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, + 'Configuration|Area System Settings|Sector Point Density', 16) - # 8) Change Radius Min to 16.0, refresh, and verify instance counts are accurate - spawner_entity.get_set_test(3, "Configuration|Radius Min", 16.0) - num_expected_instances = 1 - final_check_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0) - self.test_success = final_check_success and self.test_success + # 5) Add a Vegetation Distance Between Filter and verify initial instance counts are accurate + spawner_entity.add_component("Vegetation Distance Between Filter") + num_expected = 16 * 16 + initial_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.result(Tests.initial_instance_counts, initial_success) + + # 6) Change Radius Min to 1.0, refresh, and verify instance counts are accurate + spawner_entity.get_set_test(3, "Configuration|Radius Min", 1.0) + point_a_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0) + point_b_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0) + point_c_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 1), 5.0) + Report.result(Tests.instance_counts_1m, point_a_success and point_b_success and point_c_success) + + # 7) Change Radius Min to 2.0, refresh, and verify instance counts are accurate + spawner_entity.get_set_test(3, "Configuration|Radius Min", 2.0) + point_a_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_a, 0.5, 1), 5.0) + point_b_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_b, 0.5, 0), 5.0) + point_c_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(instance_query_point_c, 0.5, 0), 5.0) + Report.result(Tests.instance_counts_2m, point_a_success and point_b_success and point_c_success) + + # 8) Change Radius Min to 16.0, refresh, and verify instance counts are accurate + spawner_entity.get_set_test(3, "Configuration|Radius Min", 16.0) + num_expected_instances = 1 + final_check_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_counts_16m, final_check_success) -test = TestDistanceBetweenFilterComponent() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py index 173be62829..fcbf85c59d 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py @@ -5,140 +5,178 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class BehaviorContextTests: + spawner_initialized = ( + "Successfully initialized a Dynamic Slice Instance Spawner", + "Failed to initialize a Dynamic Slice Instance Spawner" + ) + spawner_slice_asset_path_set = ( + "Successfully set a Dynamic Slice asset path", + "Failed to set a Dynamic Slice asset path" + ) + spawner_empty_slice_asset_path_set = ( + "Successfully set an empty Dynamic Slice asset path", + "Failed to set an empty Dynamic Slice asset path" + ) + desc_spawnertype_sets_spawner = ( + "Setting spawnerType sets spawner too", + "Setting spawnerType failed to set spawner to expected value" + ) + desc_spawner_sets_spawnertype = ( + "Setting spawner sets spawnerType too", + "Setting spawner failed to set spawnerType to expected value" + ) -class TestDynamicSliceInstanceSpawner(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="DynamicSliceInstanceSpawner", args=["level"]) +class PropertyTreeTests: + entity_created = ( + "Spawner entity created successfully", + "Failed to create spawner entity" + ) + spawner_type_set = ( + "Successfully set spawner type", + "Failed to set spawner type" + ) + empty_instance_count_validation = ( + "Expected number of empty instances planted", + "Unexpected number of empty instances planted" + ) + no_instances_when_empty_disallowed = ( + "No empty instances found when Empty Assets are not allowed", + "Unexpectedly found empty instances when Empty Assets are not allowed" + ) + nonempty_asset_instance_count_validation = ( + "Expected number of instances planted", + "Unexpected number of instances planted" + ) - def run_test(self): - """ - Summary: - Test aspects of the DynamicSliceInstanceSpawner through the BehaviorContext and the Property Tree. - :return: None - """ - # 1) Open an empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, +def DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(): + """ + Summary: + Test aspects of the DynamicSliceInstanceSpawner through the BehaviorContext and the Property Tree. + + :return: None + """ + + import os + + import azlmbr.legacy.general as general + import azlmbr.math as math + + import editor_python_test_tools.hydra_editor_utils as hydra + 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 + helper.init_idle() + helper.open_level("Physics", "Base") + general.set_current_view_position(512.0, 480.0, 38.0) + + # Grab the UUID that we need for creating an Dynamic Slice Instance Spawner + dynamic_slice_spawner_uuid = azlmbr.math.Uuid_CreateString('{BBA5CC1E-B4CA-4792-89F7-93711E98FBD1}', 0) + + # Grab a path to a test dynamic slice asset + test_slice_asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + + # 2) Test DynamicSliceInstanceSpawner BehaviorContext + behavior_context_test_success = True + dynamic_slice_spawner = azlmbr.vegetation.DynamicSliceInstanceSpawner() + behavior_context_test_success = behavior_context_test_success and (dynamic_slice_spawner is not None) + behavior_context_test_success = behavior_context_test_success and (dynamic_slice_spawner.typename == 'DynamicSliceInstanceSpawner') + Report.critical_result(BehaviorContextTests.spawner_initialized, behavior_context_test_success) + # Try to get/set the slice asset path with a valid asset + dynamic_slice_spawner.SetSliceAssetPath(test_slice_asset_path) + validate_path = dynamic_slice_spawner.GetSliceAssetPath() + # We expect the path to get lowercased and normalized with a forward slash, so we compare our result + # vs that instead of directly against test_slice_asset_path. + behavior_context_test_success = behavior_context_test_success and hydra.compare_values('slices/pinkflower.dynamicslice', validate_path, 'GetSliceAssetPath - valid') + Report.result(BehaviorContextTests.spawner_slice_asset_path_set, behavior_context_test_success) + # Try to get/set the slice asset path with an empty path + dynamic_slice_spawner.SetSliceAssetPath('') + validate_path = dynamic_slice_spawner.GetSliceAssetPath() + behavior_context_test_success = behavior_context_test_success and hydra.compare_values('', validate_path, 'GetSliceAssetPath - empty') + Report.result(BehaviorContextTests.spawner_empty_slice_asset_path_set, behavior_context_test_success) + Report.info(f'DynamicSliceInstanceSpawner() BehaviorContext test: {behavior_context_test_success}') + + # 3) Test Descriptor BehaviorContext - setting spawnerType sets spawner too + spawner_type_test_success = True + descriptor = azlmbr.vegetation.Descriptor() + spawner_type_test_success = spawner_type_test_success and hydra.get_set_property_test(descriptor, 'spawnerType', dynamic_slice_spawner_uuid) + spawner_type_test_success = spawner_type_test_success and (descriptor.spawner.typename == 'DynamicSliceInstanceSpawner') + Report.result(BehaviorContextTests.desc_spawnertype_sets_spawner, spawner_type_test_success) + Report.info(f'Descriptor() BehaviorContext spawnerType test: {spawner_type_test_success}') + + # 4) Test Descriptor BehaviorContext - setting spawner sets spawnerType too + spawner_test_success = True + descriptor = azlmbr.vegetation.Descriptor() + descriptor.spawner = dynamic_slice_spawner + spawner_test_success = spawner_test_success and (descriptor.spawnerType.Equal(dynamic_slice_spawner_uuid)) + spawner_test_success = spawner_test_success and (descriptor.spawner.typename == 'DynamicSliceInstanceSpawner') + Report.result(BehaviorContextTests.desc_spawner_sets_spawnertype, spawner_test_success) + Report.info(f'Descriptor() BehaviorContext spawner test: {spawner_test_success}') + + ### Setup for Property Tree set of tests + + # Create a new entity with required vegetation area components + spawner_entity = hydra.Entity("Veg Area") + spawner_entity.create_entity( + math.Vector3(512.0, 512.0, 32.0), + ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"] ) - general.idle_wait(1.0) - general.set_current_view_position(512.0, 480.0, 38.0) + Report.critical_result(PropertyTreeTests.entity_created, spawner_entity.id.IsValid()) - # Grab the UUID that we need for creating an Dynamic Slice Instance Spawner - dynamic_slice_spawner_uuid = azlmbr.math.Uuid_CreateString('{BBA5CC1E-B4CA-4792-89F7-93711E98FBD1}', 0) + # Resize the Box Shape component + new_box_dimensions = math.Vector3(16.0, 16.0, 16.0) + box_dimensions_path = "Box Shape|Box Configuration|Dimensions" + spawner_entity.get_set_test(1, box_dimensions_path, new_box_dimensions) - # Grab a path to a test dynamic slice asset - test_slice_asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + # Create a surface to plant on + dynveg.create_surface_entity("Surface Entity", math.Vector3(512.0, 512.0, 32.0), 1024.0, 1024.0, 1.0) - # 2) Test DynamicSliceInstanceSpawner BehaviorContext - behavior_context_test_success = True - dynamic_slice_spawner = azlmbr.vegetation.DynamicSliceInstanceSpawner() - behavior_context_test_success = behavior_context_test_success and (dynamic_slice_spawner is not None) - behavior_context_test_success = behavior_context_test_success and (dynamic_slice_spawner.typename == 'DynamicSliceInstanceSpawner') - # Try to get/set the slice asset path with a valid asset - dynamic_slice_spawner.SetSliceAssetPath(test_slice_asset_path) - validate_path = dynamic_slice_spawner.GetSliceAssetPath() - # We expect the path to get lowercased and normalized with a forward slash, so we compare our result - # vs that instead of directly against test_slice_asset_path. - behavior_context_test_success = behavior_context_test_success and hydra.compare_values('slices/pinkflower.dynamicslice', validate_path, 'GetSliceAssetPath - valid') - # Try to get/set the slice asset path with an empty path - dynamic_slice_spawner.SetSliceAssetPath('') - validate_path = dynamic_slice_spawner.GetSliceAssetPath() - behavior_context_test_success = behavior_context_test_success and hydra.compare_values('', validate_path, 'GetSliceAssetPath - empty') - self.test_success = self.test_success and behavior_context_test_success - self.log(f'DynamicSliceInstanceSpawner() BehaviorContext test: {behavior_context_test_success}') + # 5) Descriptor Property Tree test: spawner type can be set - # 3) Test Descriptor BehaviorContext - setting spawnerType sets spawner too - spawner_type_test_success = True - descriptor = azlmbr.vegetation.Descriptor() - spawner_type_test_success = spawner_type_test_success and hydra.get_set_property_test(descriptor, 'spawnerType', dynamic_slice_spawner_uuid) - spawner_type_test_success = spawner_type_test_success and (descriptor.spawner.typename == 'DynamicSliceInstanceSpawner') - self.test_success = self.test_success and spawner_type_test_success - self.log(f'Descriptor() BehaviorContext spawnerType test: {spawner_type_test_success}') + # - Validate the dynamic slice spawner type can be set correctly. + property_tree_success = True + property_tree_success = property_tree_success and spawner_entity.get_set_test(2, 'Configuration|Embedded Assets|[0]|Instance Spawner', dynamic_slice_spawner_uuid) + Report.result(PropertyTreeTests.spawner_type_set, property_tree_success) - # 4) Test Descriptor BehaviorContext - setting spawner sets spawnerType too - spawner_test_success = True - descriptor = azlmbr.vegetation.Descriptor() - descriptor.spawner = dynamic_slice_spawner - spawner_test_success = spawner_test_success and (descriptor.spawnerType.Equal(dynamic_slice_spawner_uuid)) - spawner_test_success = spawner_test_success and (descriptor.spawner.typename == 'DynamicSliceInstanceSpawner') - self.test_success = self.test_success and spawner_test_success - self.log(f'Descriptor() BehaviorContext spawner test: {spawner_test_success}') + # This should result in 400 instances, since our box is 16 m x 16 m and by default the veg system plants + # 20 instances per 16 meters + spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', True) + num_expected_instances = 20 * 20 + property_tree_success = property_tree_success and helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0) + Report.result(PropertyTreeTests.empty_instance_count_validation, property_tree_success) + Report.info(f'Property Tree spawner type test: {property_tree_success}') - ### Setup for Property Tree set of tests + # 6) Validate that the "Allow Empty Assets" setting affects the DynamicSliceInstanceSpawner + allow_empty_assets_success = True + # Since we have an empty slice path, we should have 0 instances once we disable 'Allow Empty Assets' + num_expected_instances = 0 + allow_empty_assets_success = allow_empty_assets_success and spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False) + Report.info('Allow Empty Assets test:') + allow_empty_assets_success = allow_empty_assets_success and helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0) + Report.result(PropertyTreeTests.no_instances_when_empty_disallowed, allow_empty_assets_success) + Report.info(f'Allow Empty Assets test: {allow_empty_assets_success}') - # Create a new entity with required vegetation area components - spawner_entity = hydra.Entity("Veg Area") - spawner_entity.create_entity( - math.Vector3(512.0, 512.0, 32.0), - ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"] - ) - if (spawner_entity.id.IsValid()): - self.log(f"'{spawner_entity.name}' created") - - # Resize the Box Shape component - new_box_dimensions = math.Vector3(16.0, 16.0, 16.0) - box_dimensions_path = "Box Shape|Box Configuration|Dimensions" - spawner_entity.get_set_test(1, box_dimensions_path, new_box_dimensions) - - # Create a surface to plant on - dynveg.create_surface_entity("Surface Entity", math.Vector3(512.0, 512.0, 32.0), 1024.0, 1024.0, 1.0) - - # 5) Descriptor Property Tree test: spawner type can be set - - # - Validate the dynamic slice spawner type can be set correctly. - property_tree_success = True - property_tree_success = property_tree_success and spawner_entity.get_set_test(2, 'Configuration|Embedded Assets|[0]|Instance Spawner', dynamic_slice_spawner_uuid) - - # This should result in 400 instances, since our box is 16 m x 16 m and by default the veg system plants - # 20 instances per 16 meters - spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', True) - num_expected_instances = 20 * 20 - property_tree_success = property_tree_success and self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0) - self.test_success = self.test_success and property_tree_success - self.log(f'Property Tree spawner type test: {property_tree_success}') - - # 6) Validate that the "Allow Empty Assets" setting affects the DynamicSliceInstanceSpawner - allow_empty_assets_success = True - # Since we have an empty slice path, we should have 0 instances once we disable 'Allow Empty Assets' - num_expected_instances = 0 - allow_empty_assets_success = allow_empty_assets_success and spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False) - self.log('Allow Empty Assets test:') - allow_empty_assets_success = allow_empty_assets_success and self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0) - self.test_success = self.test_success and allow_empty_assets_success - self.log(f'Allow Empty Assets test: {allow_empty_assets_success}') - - # 7) Validate that with 'Allow Empty Assets' set to False, a non-empty slice asset gives us the number - # of instances we expect. - spawns_slices_success = True - num_expected_instances = 20 * 20 - dynamic_slice_spawner.SetSliceAssetPath(test_slice_asset_path) - spawns_slices_success = spawns_slices_success and spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False) - descriptor = hydra.get_component_property_value(spawner_entity.components[2], 'Configuration|Embedded Assets|[0]') - descriptor.spawner = dynamic_slice_spawner - spawns_slices_success = spawns_slices_success and spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor) - self.log('Spawn dynamic slices test:') - spawns_slices_success = spawns_slices_success and self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0) - self.test_success = self.test_success and spawns_slices_success - self.log(f'Spawn dynamic slices test: {spawns_slices_success}') + # 7) Validate that with 'Allow Empty Assets' set to False, a non-empty slice asset gives us the number + # of instances we expect. + spawns_slices_success = True + num_expected_instances = 20 * 20 + dynamic_slice_spawner.SetSliceAssetPath(test_slice_asset_path) + spawns_slices_success = spawns_slices_success and spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False) + descriptor = hydra.get_component_property_value(spawner_entity.components[2], 'Configuration|Embedded Assets|[0]') + descriptor.spawner = dynamic_slice_spawner + spawns_slices_success = spawns_slices_success and spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor) + Report.info('Spawn dynamic slices test:') + spawns_slices_success = spawns_slices_success and helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0) + Report.result(PropertyTreeTests.nonempty_asset_instance_count_validation, spawns_slices_success) + Report.info(f'Spawn dynamic slices test: {spawns_slices_success}') -test = TestDynamicSliceInstanceSpawner() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py index 6f2e711387..e51be58ec6 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py @@ -5,99 +5,115 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.asset as asset -import azlmbr.components as components -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.entity as entity -import azlmbr.editor as editor -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + level_created = ( + "Successfully created level", + "Failed to create level" + ) + spawner_entity_created = ( + "Spawner entity created successfully", + "Failed to create spawner entity" + ) + surface_entity_created = ( + "Surface entity created successfully", + "Failed to create surface entity" + ) + instance_count = ( + "Found the expected number of instances", + "Found an unexpected number of instances" + ) + saved_and_exported = ( + "Saved and exported level successfully", + "Failed to save and export level" + ) -class TestDynamicSliceInstanceSpawnerEmbeddedEditor(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="DynamicSliceInstanceSpawnerEmbeddedEditor", args=["level"]) +def DynamicSliceInstanceSpawner_Embedded_E2E(): + """ + Summary: + A new temporary level is created. Surface for planting is created. Simple vegetation area is created using + Dynamic Slice Instance Spawner type. - def run_test(self): - """ - Summary: - A new temporary level is created. Surface for planting is created. Simple vegetation area is created using - Dynamic Slice Instance Spawner type. + Expected Behavior: + Instances plant as expected in the assigned area. - Expected Behavior: - Instances plant as expected in the assigned area. + Test Steps: + 1) Create level + 2) Create a Vegetation Layer Spawner setup using Dynamic Slice Instance Spawner type assets + 3) Create a surface to plant on + 4) Verify expected instance counts + 5) Add a camera component looking at the planting area for visual debugging + 6) Save and export to engine - Test Steps: - 1) Create level - 2) Create a Vegetation Layer Spawner setup using Dynamic Slice Instance Spawner type assets - 3) Create a surface to plant on - 4) Verify expected instance counts - 5) Add a camera component looking at the planting area for visual debugging - 6) Save and export to engine + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import os - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import azlmbr.asset as asset + import azlmbr.legacy.general as general + import azlmbr.bus as bus + import azlmbr.components as components + import azlmbr.entity as entity + import azlmbr.math as math + import azlmbr.paths as paths - general.set_current_view_position(512.0, 480.0, 38.0) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # 2) Create a new entity with required vegetation area components and Script Canvas component for launcher test - center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, asset_path) - spawner_entity.add_component("Script Canvas") - instance_counter_path = os.path.join("scriptcanvas", "instance_counter.scriptcanvas") - instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path, - math.Uuid(), False) - spawner_entity.get_set_test(3, "Script Canvas Asset|Script Canvas Asset", instance_counter_script) + # 1) Create a new, temporary level + lvl_name = "tmp_level" + helper.init_idle() + level_created = general.create_level_no_prompt(lvl_name, 1024, 1, 4096, False) + general.idle_wait(1.0) + Report.critical_result(Tests.level_created, level_created == 0) + general.set_current_view_position(512.0, 480.0, 38.0) - # 3) Create a surface to plant on - dynveg.create_surface_entity("Planting Surface", center_point, 128.0, 128.0, 1.0) + # 2) Create a new entity with required vegetation area components and Script Canvas component for launcher test + center_point = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, asset_path) + spawner_entity.add_component("Script Canvas") + instance_counter_path = os.path.join("scriptcanvas", "instance_counter.scriptcanvas") + instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path, + math.Uuid(), False) + spawner_entity.get_set_test(3, "Script Canvas Asset|Script Canvas Asset", instance_counter_script) + Report.result(Tests.spawner_entity_created, spawner_entity.id.IsValid() and hydra.has_components(spawner_entity.id, + ["Script Canvas"])) - # 4) Verify instance counts are accurate - general.idle_wait(3.0) # Allow a few seconds for instances to spawn - num_expected_instances = 20 * 20 - box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id) - num_found = azlmbr.areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstanceCountInAabb', box) - self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances") - self.test_success = self.test_success and num_found == num_expected_instances + # 3) Create a surface to plant on + surface_entity = dynveg.create_surface_entity("Planting Surface", center_point, 128.0, 128.0, 1.0) + Report.result(Tests.surface_entity_created, surface_entity.id.IsValid()) - # 5) Move the default Camera entity for testing in the launcher - cam_position = math.Vector3(512.0, 500.0, 35.0) - search_filter = entity.SearchFilter() - search_filter.names = ["Camera"] - search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter) - components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position) + # 4) Verify instance counts are accurate + num_expected_instances = 20 * 20 + 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_count, success) - # 6) Save and export to engine - general.save_level() - general.idle_wait(1.0) - general.export_to_engine() - general.idle_wait(1.0) + # 5) Move the default Camera entity for testing in the launcher + cam_position = math.Vector3(512.0, 500.0, 35.0) + search_filter = entity.SearchFilter() + search_filter.names = ["Camera"] + search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter) + components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position) + + # 6) Save and export to engine + general.save_level() + general.export_to_engine() + pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak") + Report.result(Tests.saved_and_exported, os.path.exists(pak_path)) -test = TestDynamicSliceInstanceSpawnerEmbeddedEditor() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(DynamicSliceInstanceSpawner_Embedded_E2E) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py index dfa4d8191e..7a0abdd969 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py @@ -5,122 +5,137 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.legacy.general as general -import azlmbr.asset as asset -import azlmbr.bus as bus -import azlmbr.components as components -import azlmbr.entity as entity -import azlmbr.editor as editor -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + level_created = ( + "Successfully created level", + "Failed to create level" + ) + spawner_entity_created = ( + "Spawner entity created successfully", + "Failed to create spawner entity" + ) + surface_entity_created = ( + "Surface entity created successfully", + "Failed to create surface entity" + ) + instance_count = ( + "Found the expected number of instances", + "Found an unexpected number of instances" + ) + saved_and_exported = ( + "Saved and exported level successfully", + "Failed to save and export level" + ) -class TestDynamicSliceInstanceSpawnerExternalEditor(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="DynamicSliceInstanceSpawnerExternalEditor", args=["level"]) +def DynamicSliceInstanceSpawner_External_E2E(): + """ + Summary: + A new temporary level is created. Surface for planting is created. Simple vegetation area is created using + Dynamic Slice Instance Spawner type using external assets. - def run_test(self): - """ - Summary: - A new temporary level is created. Surface for planting is created. Simple vegetation area is created using - Dynamic Slice Instance Spawner type using external assets. + Expected Behavior: + Instances plant as expected in the assigned area. - Expected Behavior: - Instances plant as expected in the assigned area. + Test Steps: + 1) Create level + 2) Create a Vegetation Layer Spawner setup using Dynamic Slice Instance Spawner type assets + 3) Create a surface to plant on + 4) Verify expected instance counts + 5) Add a camera component looking at the planting area for visual debugging + 6) Save and export to engine - Test Steps: - 1) Create level - 2) Create a Vegetation Layer Spawner setup using Dynamic Slice Instance Spawner type assets - 3) Create a surface to plant on - 4) Verify expected instance counts - 5) Add a camera component looking at the planting area for visual debugging - 6) Save and export to engine + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import os - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import azlmbr.asset as asset + import azlmbr.components as components + import azlmbr.editor as editor + import azlmbr.entity as entity + import azlmbr.legacy.general as general + import azlmbr.bus as bus + import azlmbr.math as math + import azlmbr.paths as paths - general.set_current_view_position(512.0, 480.0, 38.0) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # 2) Create a new entity with required vegetation area components and switch the Vegetation Asset List Source - # Type to External - entity_position = math.Vector3(512.0, 512.0, 32.0) - veg_area_required_components = ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List", - "Script Canvas"] - new_entity_id = editor.ToolsApplicationRequestBus( - bus.Broadcast, "CreateNewEntityAtPosition", entity_position, entity.EntityId() - ) - if new_entity_id.IsValid(): - self.log("Spawner entity created") - spawner_entity = hydra.Entity("Spawner Entity", new_entity_id) - spawner_entity.components = [] - for component in veg_area_required_components: - spawner_entity.components.append(hydra.add_component(component, new_entity_id)) - hydra.get_set_test(spawner_entity, 2, "Configuration|Source Type", 1) + # 1) Create a new, temporary level + lvl_name = "tmp_level" + helper.init_idle() + level_created = general.create_level_no_prompt(lvl_name, 1024, 1, 4096, False) + general.idle_wait(1.0) + Report.critical_result(Tests.level_created, level_created == 0) + general.set_current_view_position(512.0, 480.0, 38.0) - # Add a Script Canvas component with instance_counter script for launcher tests - instance_counter_path = os.path.join("scriptcanvas", "instance_counter.scriptcanvas") - instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path, - math.Uuid(), False) - spawner_entity.get_set_test(3, "Script Canvas Asset|Script Canvas Asset", instance_counter_script) + # 2) Create a new entity with required vegetation area components and switch the Vegetation Asset List Source + # Type to External + entity_position = math.Vector3(512.0, 512.0, 32.0) + veg_area_required_components = ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List", + "Script Canvas"] + new_entity_id = editor.ToolsApplicationRequestBus( + bus.Broadcast, "CreateNewEntityAtPosition", entity_position, entity.EntityId() + ) + spawner_entity = hydra.Entity("Spawner Entity", new_entity_id) + spawner_entity.components = [] + for component in veg_area_required_components: + spawner_entity.components.append(hydra.add_component(component, new_entity_id)) + hydra.get_set_test(spawner_entity, 2, "Configuration|Source Type", 1) - # Assign a Vegetation Descriptor List asset to the Vegetation Asset List component - descriptor_asset = asset.AssetCatalogRequestBus( - bus.Broadcast, "GetAssetIdByPath", os.path.join("Assets", "VegDescriptorLists", "flower_pink.vegdescriptorlist"), math.Uuid(), - False) - hydra.get_set_test(spawner_entity, 2, "Configuration|External Assets", descriptor_asset) + # Add a Script Canvas component with instance_counter script for launcher tests + instance_counter_path = os.path.join("scriptcanvas", "instance_counter.scriptcanvas") + instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path, + math.Uuid(), False) + spawner_entity.get_set_test(3, "Script Canvas Asset|Script Canvas Asset", instance_counter_script) + Report.result(Tests.spawner_entity_created, spawner_entity.id.IsValid() and hydra.has_components(spawner_entity.id, + ["Script Canvas"])) - # Resize the Box Shape component - new_box_dimensions = math.Vector3(16.0, 16.0, 16.0) - box_dimensions_path = "Box Shape|Box Configuration|Dimensions" - hydra.get_set_test(spawner_entity, 1, box_dimensions_path, new_box_dimensions) + # Assign a Vegetation Descriptor List asset to the Vegetation Asset List component + descriptor_asset = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", os.path.join("Assets", "VegDescriptorLists", "flower_pink.vegdescriptorlist"), math.Uuid(), + False) + hydra.get_set_test(spawner_entity, 2, "Configuration|External Assets", descriptor_asset) - # 3) Create a surface to plant on - dynveg.create_surface_entity("Planting Surface", entity_position, 128.0, 128.0, 1.0) + # Resize the Box Shape component + new_box_dimensions = math.Vector3(16.0, 16.0, 16.0) + box_dimensions_path = "Box Shape|Box Configuration|Dimensions" + hydra.get_set_test(spawner_entity, 1, box_dimensions_path, new_box_dimensions) - # 4) Verify instance counts are accurate - general.idle_wait(3.0) # Allow a few seconds for instances to spawn - num_expected_instances = 20 * 20 - box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id) - num_found = azlmbr.areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstanceCountInAabb', box) - self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances") - self.test_success = self.test_success and num_found == num_expected_instances + # 3) Create a surface to plant on + surface_entity = dynveg.create_surface_entity("Planting Surface", entity_position, 128.0, 128.0, 1.0) + Report.result(Tests.surface_entity_created, surface_entity.id.IsValid()) - # 5) Move the default Camera entity for testing in the launcher - cam_position = math.Vector3(512.0, 500.0, 35.0) - search_filter = entity.SearchFilter() - search_filter.names = ["Camera"] - search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter) - components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position) + # 4) Verify instance counts are accurate + num_expected_instances = 20 * 20 + 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_count, success) - # 6) Save and export to engine - general.save_level() - general.idle_wait(1.0) - general.export_to_engine() - general.idle_wait(1.0) + # 5) Move the default Camera entity for testing in the launcher + cam_position = math.Vector3(512.0, 500.0, 35.0) + search_filter = entity.SearchFilter() + search_filter.names = ["Camera"] + search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter) + components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position) + + # 6) Save and export to engine + general.save_level() + general.export_to_engine() + pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak") + Report.result(Tests.saved_and_exported, os.path.exists(pak_path)) -test = TestDynamicSliceInstanceSpawnerExternalEditor() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(DynamicSliceInstanceSpawner_External_E2E) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/EmptyInstanceSpawner_EmptySpawnerWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/EmptyInstanceSpawner_EmptySpawnerWorks.py index 721d06f858..d0a51809b4 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/EmptyInstanceSpawner_EmptySpawnerWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/EmptyInstanceSpawner_EmptySpawnerWorks.py @@ -5,109 +5,131 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class BehaviorContextTests: + spawner_initialized = ( + "Successfully initialized an Empty Instance Spawner", + "Failed to initialize an Empty Instance Spawner" + ) + desc_spawnertype_sets_spawner = ( + "Setting spawnerType sets spawner too", + "Setting spawnerType failed to set spawner to expected value" + ) + desc_spawner_sets_spawnertype = ( + "Setting spawner sets spawnerType too", + "Setting spawner failed to set spawnerType to expected value" + ) -class TestEmptyInstanceSpawner(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="EmptyInstanceSpawner", args=["level"]) +class PropertyTreeTests: + entity_created = ( + "Spawner entity created successfully", + "Failed to create spawner entity" + ) + spawner_type_set = ( + "Successfully set spawner type", + "Failed to set spawner type" + ) + empty_instance_count_validation = ( + "Expected number of empty instances planted", + "Unexpected number of empty instances planted" + ) + not_affected_by_allow_empty_assets = ( + "Instance count unaffected by Allow Empty Assets toggle", + "Instance count was unexpectedly affected by Allow Empty Assets toggle" + ) - def run_test(self): - """ - Summary: - Test aspects of the EmptyInstanceSpawner through the BehaviorContext and the Property Tree. - :return: None - """ - # 1) Open an empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, +def EmptyInstanceSpawner_EmptySpawnerWorks(): + """ + Summary: + Test aspects of the EmptyInstanceSpawner through the BehaviorContext and the Property Tree. + + :return: None + """ + + import azlmbr.legacy.general as general + import azlmbr.math as math + + import editor_python_test_tools.hydra_editor_utils as hydra + 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 + helper.init_idle() + helper.open_level("Physics", "Base") + general.set_current_view_position(512.0, 480.0, 38.0) + + # Grab the UUID that we need for creating an Empty Spawner + empty_spawner_uuid = azlmbr.math.Uuid_CreateString('{23C40FD4-A55F-4BD3-BE5B-DC5423F217C2}', 0) + + # 2) Test EmptyInstanceSpawner BehaviorContext + behavior_context_test_success = True + empty_spawner = azlmbr.vegetation.EmptyInstanceSpawner() + behavior_context_test_success = behavior_context_test_success and (empty_spawner is not None) + behavior_context_test_success = behavior_context_test_success and (empty_spawner.typename == 'EmptyInstanceSpawner') + Report.critical_result(BehaviorContextTests.spawner_initialized, behavior_context_test_success) + Report.info(f'EmptyInstanceSpawner() BehaviorContext test: {behavior_context_test_success}') + + # 3) Test Descriptor BehaviorContext - setting spawnerType sets spawner too + spawner_type_test_success = True + descriptor = azlmbr.vegetation.Descriptor() + spawner_type_test_success = spawner_type_test_success and hydra.get_set_property_test(descriptor, 'spawnerType', empty_spawner_uuid) + spawner_type_test_success = spawner_type_test_success and (descriptor.spawner.typename == 'EmptyInstanceSpawner') + Report.result(BehaviorContextTests.desc_spawnertype_sets_spawner, spawner_type_test_success) + Report.info(f'Descriptor() BehaviorContext spawnerType test: {spawner_type_test_success}') + + # 4) Test Descriptor BehaviorContext - setting spawner sets spawnerType too + spawner_test_success = True + descriptor = azlmbr.vegetation.Descriptor() + descriptor.spawner = empty_spawner + spawner_test_success = spawner_test_success and (descriptor.spawnerType.Equal(empty_spawner_uuid)) + spawner_test_success = spawner_test_success and (descriptor.spawner.typename == 'EmptyInstanceSpawner') + Report.result(BehaviorContextTests.desc_spawner_sets_spawnertype, spawner_test_success) + Report.info(f'Descriptor() BehaviorContext spawner test: {spawner_test_success}') + + ### Setup for Property Tree set of tests + + # Create a new entity with required vegetation area components + spawner_entity = hydra.Entity("Veg Area") + spawner_entity.create_entity( + math.Vector3(512.0, 512.0, 32.0), + ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"] ) - general.idle_wait(1.0) - general.set_current_view_position(512.0, 480.0, 38.0) + Report.critical_result(PropertyTreeTests.entity_created, spawner_entity.id.IsValid()) - # Grab the UUID that we need for creating an Empty Spawner - empty_spawner_uuid = azlmbr.math.Uuid_CreateString('{23C40FD4-A55F-4BD3-BE5B-DC5423F217C2}', 0) + # Resize the Box Shape component + new_box_dimensions = math.Vector3(16.0, 16.0, 16.0) + box_dimensions_path = "Box Shape|Box Configuration|Dimensions" + spawner_entity.get_set_test(1, box_dimensions_path, new_box_dimensions) - # 2) Test EmptyInstanceSpawner BehaviorContext - behavior_context_test_success = True - empty_spawner = azlmbr.vegetation.EmptyInstanceSpawner() - behavior_context_test_success = behavior_context_test_success and (empty_spawner is not None) - behavior_context_test_success = behavior_context_test_success and (empty_spawner.typename == 'EmptyInstanceSpawner') - self.test_success = self.test_success and behavior_context_test_success - self.log(f'EmptyInstanceSpawner() BehaviorContext test: {behavior_context_test_success}') + # Create a surface to plant on + dynveg.create_surface_entity("Surface Entity", math.Vector3(512.0, 512.0, 32.0), 1024.0, 1024.0, 1.0) - # 3) Test Descriptor BehaviorContext - setting spawnerType sets spawner too - spawner_type_test_success = True - descriptor = azlmbr.vegetation.Descriptor() - spawner_type_test_success = spawner_type_test_success and hydra.get_set_property_test(descriptor, 'spawnerType', empty_spawner_uuid) - spawner_type_test_success = spawner_type_test_success and (descriptor.spawner.typename == 'EmptyInstanceSpawner') - self.test_success = self.test_success and spawner_type_test_success - self.log(f'Descriptor() BehaviorContext spawnerType test: {spawner_type_test_success}') + # 5) Descriptor Property Tree test: spawner type can be set - # 4) Test Descriptor BehaviorContext - setting spawner sets spawnerType too - spawner_test_success = True - descriptor = azlmbr.vegetation.Descriptor() - descriptor.spawner = empty_spawner - spawner_test_success = spawner_test_success and (descriptor.spawnerType.Equal(empty_spawner_uuid)) - spawner_test_success = spawner_test_success and (descriptor.spawner.typename == 'EmptyInstanceSpawner') - self.test_success = self.test_success and spawner_test_success - self.log(f'Descriptor() BehaviorContext spawner test: {spawner_test_success}') + # - Validate the empty spawner type can be set correctly. + property_tree_success = True + property_tree_success = property_tree_success and spawner_entity.get_set_test(2, 'Configuration|Embedded Assets|[0]|Instance Spawner', empty_spawner_uuid) + Report.result(PropertyTreeTests.spawner_type_set, property_tree_success) - ### Setup for Property Tree set of tests + # This should result in 400 instances, since our box is 16 m x 16 m and by default the veg system plants + # 20 instances per 16 meters + num_expected_instances = 20 * 20 + property_tree_success = property_tree_success and helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0) + Report.result(PropertyTreeTests.empty_instance_count_validation, property_tree_success) + Report.info(f'Property Tree spawner type test: {property_tree_success}') - # Create a new entity with required vegetation area components - spawner_entity = hydra.Entity("Veg Area") - spawner_entity.create_entity( - math.Vector3(512.0, 512.0, 32.0), - ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"] - ) - if spawner_entity.id.IsValid(): - self.log(f"'{spawner_entity.name}' created") - - # Resize the Box Shape component - new_box_dimensions = math.Vector3(16.0, 16.0, 16.0) - box_dimensions_path = "Box Shape|Box Configuration|Dimensions" - spawner_entity.get_set_test(1, box_dimensions_path, new_box_dimensions) - - # Create a surface to plant on - dynveg.create_surface_entity("Surface Entity", math.Vector3(512.0, 512.0, 32.0), 1024.0, 1024.0, 1.0) - - # 5) Descriptor Property Tree test: spawner type can be set - - # - Validate the empty spawner type can be set correctly. - property_tree_success = True - property_tree_success = property_tree_success and spawner_entity.get_set_test(2, 'Configuration|Embedded Assets|[0]|Instance Spawner', empty_spawner_uuid) - - # This should result in 400 instances, since our box is 16 m x 16 m and by default the veg system plants - # 20 instances per 16 meters - num_expected_instances = 20 * 20 - property_tree_success = property_tree_success and self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0) - self.test_success = self.test_success and property_tree_success - self.log(f'Property Tree spawner type test: {property_tree_success}') - - # 6) Validate that the "Allow Empty Assets" setting doesn't affect the EmptyInstanceSpawner - allow_empty_assets_success = True - spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False) - allow_empty_assets_success = allow_empty_assets_success and self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0) - self.test_success = self.test_success and allow_empty_assets_success - self.log(f'Allow Empty Assets test: {allow_empty_assets_success}') + # 6) Validate that the "Allow Empty Assets" setting doesn't affect the EmptyInstanceSpawner + allow_empty_assets_success = True + spawner_entity.get_set_test(0, 'Configuration|Allow Empty Assets', False) + allow_empty_assets_success = allow_empty_assets_success and helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_instances), 5.0) + Report.result(PropertyTreeTests.not_affected_by_allow_empty_assets, allow_empty_assets_success) + Report.info(f'Allow Empty Assets test: {allow_empty_assets_success}') -test = TestEmptyInstanceSpawner() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(EmptyInstanceSpawner_EmptySpawnerWorks) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py index e5ff0bf86f..98418c2432 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py @@ -5,111 +5,115 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.math as math -import azlmbr.paths -import azlmbr.vegetation as vegetation - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + initial_instance_count = ( + "Initial instance count is as expected", + "Initial instance count does not match expected results" + ) + layer_priority_instance_count = ( + "Instance count is as expected after updating layer priorities", + "Instance count does not match expected results after updating layer priorities" + ) + sub_priority_instance_count = ( + "Instance count is as expected after updating sub priorities", + "Instance count does not match expected results after updating sub priorities" + ) -class TestInstanceSpawnerPriority(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="InstanceSpawnerPriority", args=["level"]) +def InstanceSpawnerPriority_LayerAndSubPriority(): + """ + Summary: + A new level is created. An instance spawner area and blocker area are setup to overlap. Instance counts are + verified with the initial setup. Layer priority on the blocker area is set to lower than the instance spawner + area, and instance counts are re-verified. - def run_test(self): - """ - Summary: - A new level is created. An instance spawner area and blocker area are setup to overlap. Instance counts are - verified with the initial setup. Layer priority on the blocker area is set to lower than the instance spawner - area, and instance counts are re-verified. + Expected Behavior: + Vegetation areas with a higher Layer Priority plant over those with a lower Layer Priority - Expected Behavior: - Vegetation areas with a higher Layer Priority plant over those with a lower Layer Priority + Test Steps: + 1) Open a simple level + 2) Create overlapping instance spawner and blocker areas + 3) Create a surface to plant on + 4) Validate initial instance counts in the spawner area + 5) Reduce the Layer Priority of the blocker area + 6) Validate instance counts in the spawner area - Test Steps: - 1) Create a new level - 2) Create overlapping instance spawner and blocker areas - 3) Create a surface to plant on - 4) Validate initial instance counts in the spawner area - 5) Reduce the Layer Priority of the blocker area - 6) Validate instance counts in the spawner area - - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - :return: None - """ + :return: None + """ - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import os - # Set view of planting area for visual debugging - general.set_current_view_position(512.0, 500.0, 38.0) - general.set_current_view_rotation(-20.0, 0.0, 0.0) + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.bus as bus + import azlmbr.math as math - # 2) Create overlapping areas: 1 instance spawner area, and 1 blocker area - spawner_center_point = math.Vector3(508.0, 508.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 1.0, - asset_path) - blocker_center_point = math.Vector3(516.0, 516.0, 32.0) - blocker_entity = dynveg.create_blocker_area("Instance Blocker", blocker_center_point, 16.0, 16.0, 1.0) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # 3) Create a surface for planting - planting_surface_center_point = math.Vector3(512.0, 512.0, 32.0) - dynveg.create_surface_entity("Planting Surface", planting_surface_center_point, 64.0, 64.0, 1.0) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape - veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, - 'Configuration|Area System Settings|Sector Point Snap Mode', 1) + # Set view of planting area for visual debugging + general.set_current_view_position(512.0, 500.0, 38.0) + general.set_current_view_rotation(-20.0, 0.0, 0.0) - # 4) Validate the expected instance count with initial setup. GetAreaProductCount is used as - # GetInstanceCountInAabb does not filter out blocked instances - num_expected = (20 * 20) - (10 * 10) # 20 instances per 16m per side minus 1 blocked quadrant - result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 5.0) - self.test_success = self.test_success and result + # 2) Create overlapping areas: 1 instance spawner area, and 1 blocker area + spawner_center_point = math.Vector3(508.0, 508.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 1.0, + asset_path) + blocker_center_point = math.Vector3(516.0, 516.0, 32.0) + blocker_entity = dynveg.create_blocker_area("Instance Blocker", blocker_center_point, 16.0, 16.0, 1.0) - # 5) Change the Instance Spawner area to a higher layer priority than the Instance Blocker - blocker_entity.get_set_test(0, 'Configuration|Layer Priority', 0) + # 3) Create a surface for planting + planting_surface_center_point = math.Vector3(512.0, 512.0, 32.0) + dynveg.create_surface_entity("Planting Surface", planting_surface_center_point, 64.0, 64.0, 1.0) - # 6) Validate the expected instance count with changed area priorities - num_expected = 20 * 20 # 20 instances per 16m per side, no instances should be blocked at this point - result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 5.0) - self.test_success = self.test_success and result + # Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape + veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, + 'Configuration|Area System Settings|Sector Point Snap Mode', 1) - # 7) Revert Layer Priority changes so both areas are equal, and change Sub Priority to a higher value on the - # Instance Spawner area - blocker_entity.get_set_test(0, 'Configuration|Layer Priority', 1) - spawner_entity.get_set_test(0, 'Configuration|Sub Priority', 100) - blocker_entity.get_set_test(0, 'Configuration|Sub Priority', 1) + # 4) Validate the expected instance count with initial setup. GetAreaProductCount is used as + # GetInstanceCountInAabb does not filter out blocked instances + num_expected = (20 * 20) - (10 * 10) # 20 instances per 16m per side minus 1 blocked quadrant + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 5.0) + Report.result(Tests.initial_instance_count, result) - # 8) Validate the expected instance count with changed area priorities - num_expected = 20 * 20 # 20 instances per 16m per side, no instances should be blocked at this point - result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 5.0) - self.test_success = self.test_success and result + # 5) Change the Instance Spawner area to a higher layer priority than the Instance Blocker + blocker_entity.get_set_test(0, 'Configuration|Layer Priority', 0) + + # 6) Validate the expected instance count with changed area priorities + num_expected = 20 * 20 # 20 instances per 16m per side, no instances should be blocked at this point + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 5.0) + Report.result(Tests.layer_priority_instance_count, result) + + # 7) Revert Layer Priority changes so both areas are equal, and change Sub Priority to a higher value on the + # Instance Spawner area + blocker_entity.get_set_test(0, 'Configuration|Layer Priority', 1) + spawner_entity.get_set_test(0, 'Configuration|Sub Priority', 100) + blocker_entity.get_set_test(0, 'Configuration|Sub Priority', 1) + + # 8) Validate the expected instance count with changed area priorities + num_expected = 20 * 20 # 20 instances per 16m per side, no instances should be blocked at this point + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 5.0) + Report.result(Tests.sub_priority_instance_count, result) -test = TestInstanceSpawnerPriority() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(InstanceSpawnerPriority_LayerAndSubPriority) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py index f03ea29613..f56c0b836e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py @@ -5,151 +5,161 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -""" -C2627906: A simple Vegetation Layer Blender area can be created -""" -import os -from math import radians -import sys - -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.asset as asset -import azlmbr.areasystem as areasystem -import azlmbr.legacy.general as general -import azlmbr -import azlmbr.bus as bus -import azlmbr.components as components -import azlmbr.math as math -import azlmbr.entity as entity -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + level_created = ( + "Successfully created level", + "Failed to create level" + ) + blender_entity_created = ( + "Blender entity created successfully", + "Failed to create Blender entity" + ) + instance_count = ( + "Found the expected number of instances in the Blender area", + "Found an unexpected number of instances in the Blender area" + ) + instances_blended = ( + "Instances from each spawner are blended as expected", + "Found an unexpected number of instances from each spawner" + ) + saved_and_exported = ( + "Saved and exported level successfully", + "Failed to save and export level" + ) -class TestVegLayerBlenderCreated(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="LayerBlender_E2E_Editor", args=["level"]) - self.screenshot_count = 0 +def LayerBlender_E2E_Editor(): + """ + Summary: + A temporary level is loaded. Two vegetation areas with different meshes are added and then + pinned to a vegetation blender. Screenshots are taken in the editor normal mode and in game mode. - def run_test(self): - """ - Summary: - A temporary level is loaded. Two vegetation areas with different meshes are added and then - pinned to a vegetation blender. Screenshots are taken in the editor normal mode and in game mode. + Expected Behavior: + The specified assets plant in the specified blend area and are visible in the Viewport in + Edit Mode, Game Mode. - Expected Behavior: - The specified assets plant in the specified blend area and are visible in the Viewport in - Edit Mode, Game Mode. + Test Steps: + 1) Create level + 2) Create 2 vegetation areas with different meshes + 3) Create Blender entity and pin the vegetation areas + 4) Take screenshot in normal mode + 5) Create a new entity with a Camera component for testing in the launcher + 6) Save level and take screenshot in game mode + 7) Export to engine - Test Steps: - 1) Create level - 2) Create 2 vegetation areas with different meshes - 3) Create Blender entity and pin the vegetation areas - 4) Take screenshot in normal mode - 5) Create a new entity with a Camera component for testing in the launcher - 6) Save level and take screenshot in game mode - 7) Export to engine + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import os + from math import radians - # 1) Create/prepare a new level and set an appropriate view of blender area - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import azlmbr.asset as asset + import azlmbr.areasystem as areasystem + import azlmbr.legacy.general as general + import azlmbr.paths as paths + import azlmbr.bus as bus + import azlmbr.components as components + import azlmbr.math as math + import azlmbr.entity as entity - general.set_current_view_position(500.49, 498.69, 46.66) - general.set_current_view_rotation(-42.05, 0.00, -36.33) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # 2) Create 2 vegetation areas with different meshes - purple_position = math.Vector3(504.0, 512.0, 32.0) - purple_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity_1 = dynveg.create_vegetation_area("Purple Spawner", - purple_position, - 16.0, 16.0, 1.0, - purple_asset_path) + # 1) Create a new, temporary level + lvl_name = "tmp_level" + helper.init_idle() + level_created = general.create_level_no_prompt(lvl_name, 1024, 1, 4096, False) + general.idle_wait(1.0) + Report.critical_result(Tests.level_created, level_created == 0) - pink_position = math.Vector3(520.0, 512.0, 32.0) - pink_asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity_2 = dynveg.create_vegetation_area("Pink Spawner", - pink_position, - 16.0, 16.0, 1.0, - pink_asset_path) + general.set_current_view_position(500.49, 498.69, 46.66) + general.set_current_view_rotation(-42.05, 0.00, -36.33) - base_position = math.Vector3(512.0, 512.0, 32.0) - dynveg.create_surface_entity("Surface Entity", - base_position, - 16.0, 16.0, 1.0) + # 2) Create 2 vegetation areas with different meshes + purple_position = math.Vector3(504.0, 512.0, 32.0) + purple_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") + spawner_entity_1 = dynveg.create_vegetation_area("Purple Spawner", + purple_position, + 16.0, 16.0, 1.0, + purple_asset_path) - hydra.add_level_component("Vegetation Debugger") + pink_position = math.Vector3(520.0, 512.0, 32.0) + pink_asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity_2 = dynveg.create_vegetation_area("Pink Spawner", + pink_position, + 16.0, 16.0, 1.0, + pink_asset_path) - # 3) Create Blender entity and pin the vegetation areas. We also add and attach a Lua script to validate in the - # launcher for the follow-up test - blender_entity = hydra.Entity("Blender") - blender_entity.create_entity( - base_position, - ["Box Shape", "Vegetation Layer Blender", "Lua Script"] - ) - if blender_entity.id.IsValid(): - print(f"'{blender_entity.name}' created") + base_position = math.Vector3(512.0, 512.0, 32.0) + dynveg.create_surface_entity("Surface Entity", + base_position, + 16.0, 16.0, 1.0) - blender_entity.get_set_test(0, "Box Shape|Box Configuration|Dimensions", math.Vector3(16.0, 16.0, 1.0)) - blender_entity.get_set_test(1, "Configuration|Vegetation Areas", [spawner_entity_1.id, spawner_entity_2.id]) - instance_counter_path = os.path.join("luascripts", "instance_counter_blender.lua") - instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path, - math.Uuid(), False) - blender_entity.get_set_test(2, "Script properties|Asset", instance_counter_script) + hydra.add_level_component("Vegetation Debugger") - # 4) Verify instances in blender area are equally represented by both descriptors + # 3) Create Blender entity and pin the vegetation areas. We also add and attach a Lua script to validate in the + # launcher for the follow-up test + blender_entity = hydra.Entity("Blender") + blender_entity.create_entity( + base_position, + ["Box Shape", "Vegetation Layer Blender", "Lua Script"] + ) + Report.result(Tests.blender_entity_created, blender_entity.id.IsValid()) - # Wait for instances to spawn - general.run_console('veg_debugClearAllAreas') - num_expected = 20 * 20 - self.test_success = self.test_success and self.wait_for_condition( - lambda: dynveg.validate_instance_count(base_position, 8.0, num_expected), 5.0) + blender_entity.get_set_test(0, "Box Shape|Box Configuration|Dimensions", math.Vector3(16.0, 16.0, 1.0)) + blender_entity.get_set_test(1, "Configuration|Vegetation Areas", [spawner_entity_1.id, spawner_entity_2.id]) + instance_counter_path = os.path.join("luascripts", "instance_counter_blender.lua") + instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path, + math.Uuid(), False) + blender_entity.get_set_test(2, "Script properties|Asset", instance_counter_script) - if self.test_success: - box = math.Aabb_CreateCenterRadius(base_position, 8.0) - instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) - pink_count = 0 - purple_count = 0 - for instance in instances: - purple_asset_path = purple_asset_path.replace("\\", "/").lower() - pink_asset_path = pink_asset_path.replace("\\", "/").lower() - if instance.descriptor.spawner.GetSliceAssetPath() == pink_asset_path: - pink_count += 1 - elif instance.descriptor.spawner.GetSliceAssetPath() == purple_asset_path: - purple_count += 1 - self.test_success = pink_count == purple_count and (pink_count + purple_count == num_expected) and self.test_success + # 4) Verify instances in blender area are equally represented by both descriptors - # 5) Move the default Camera entity for testing in the launcher - cam_position = math.Vector3(500.0, 500.0, 47.0) - cam_rot_degrees_vector = math.Vector3(radians(-55.0), radians(28.5), radians(-17.0)) - search_filter = entity.SearchFilter() - search_filter.names = ["Camera"] - search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter) - components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position) - azlmbr.components.TransformBus(bus.Event, "SetLocalRotation", search_entity_ids[0], cam_rot_degrees_vector) + # Wait for instances to spawn + general.run_console('veg_debugClearAllAreas') + num_expected = 20 * 20 + success = helper.wait_for_condition( + lambda: dynveg.validate_instance_count(base_position, 8.0, num_expected), 5.0) + Report.critical_result(Tests.instance_count, success) - # 6) Save and export level - general.save_level() - general.idle_wait(1.0) - general.export_to_engine() - general.idle_wait(1.0) + box = math.Aabb_CreateCenterRadius(base_position, 8.0) + instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) + pink_count = 0 + purple_count = 0 + for instance in instances: + purple_asset_path = purple_asset_path.replace("\\", "/").lower() + pink_asset_path = pink_asset_path.replace("\\", "/").lower() + if instance.descriptor.spawner.GetSliceAssetPath() == pink_asset_path: + pink_count += 1 + elif instance.descriptor.spawner.GetSliceAssetPath() == purple_asset_path: + purple_count += 1 + Report.result(Tests.instances_blended, pink_count == purple_count and (pink_count + purple_count == num_expected)) + + # 5) Move the default Camera entity for testing in the launcher + cam_position = math.Vector3(500.0, 500.0, 47.0) + cam_rot_degrees_vector = math.Vector3(radians(-55.0), radians(28.5), radians(-17.0)) + search_filter = entity.SearchFilter() + search_filter.names = ["Camera"] + search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter) + components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position) + azlmbr.components.TransformBus(bus.Event, "SetLocalRotation", search_entity_ids[0], cam_rot_degrees_vector) + + # 6) Save and export to engine + general.save_level() + general.export_to_engine() + pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak") + Report.result(Tests.saved_and_exported, os.path.exists(pak_path)) -test = TestVegLayerBlenderCreated() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(LayerBlender_E2E_Editor) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py index 138ac27700..625b7d2265 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py @@ -5,102 +5,105 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -import azlmbr.bus as bus -import azlmbr.editor as editor -import azlmbr.math as math -import azlmbr.legacy.general as general -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + initial_instance_count = ( + "Initial instance count is as expected", + "Unexpected number of initial instances found" + ) + blocked_instance_count = ( + "Expected number of instances found after configuring Blocker", + "Unexpected number of instances found after configuring Blocker" + ) -class TestLayerBlocker(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="LayerBlocker_InstancesBlocked", args=["level"]) - def run_test(self): - """ - Summary: - An empty level is created. A Vegetation Layer Spawner area is configured. A Vegetation Layer Blocker area is - configured to block instances in the spawner area. +def LayerBlocker_InstancesBlockedInConfiguredArea(): + """ + Summary: + An empty level is created. A Vegetation Layer Spawner area is configured. A Vegetation Layer Blocker area is + configured to block instances in the spawner area. - Expected Behavior: - Vegetation is blocked by the configured Blocker area. + Expected Behavior: + Vegetation is blocked by the configured Blocker area. - Test Steps: - 1. A new level is created - 2. Vegetation Layer Spawner area is created - 3. Planting surface is created - 4. Vegetation System Settings level component is added, and Snap Mode set to center to ensure expected instance - counts are accurate in the configured vegetation area - 5. Initial instance counts pre-blocker are validated - 6. A Vegetation Layer Blocker area is created, overlapping the spawner area - 7. Post-blocker instance counts are validated + Test Steps: + 1. A simple level is opened + 2. Vegetation Layer Spawner area is created + 3. Planting surface is created + 4. Vegetation System Settings level component is added, and Snap Mode set to center to ensure expected instance + counts are accurate in the configured vegetation area + 5. Initial instance counts pre-blocker are validated + 6. A Vegetation Layer Blocker area is created, overlapping the spawner area + 7. Post-blocker instance counts are validated - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - :return: None - """ + :return: None + """ - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import os - # Set view of planting area for visual debugging - general.set_current_view_position(512.0, 500.0, 38.0) - general.set_current_view_rotation(-20.0, 0.0, 0.0) + import azlmbr.bus as bus + import azlmbr.editor as editor + import azlmbr.math as math + import azlmbr.legacy.general as general - # 2) Create a new instance spawner entity - spawner_center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # 3) Create surface for planting on - dynveg.create_surface_entity("Surface Entity", spawner_center_point, 32.0, 32.0, 1.0) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # 4) Add a Vegetation System Settings Level component and set Sector Point Snap Mode to Center - veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, - 'Configuration|Area System Settings|Sector Point Snap Mode', 1) + # Set view of planting area for visual debugging + general.set_current_view_position(512.0, 500.0, 38.0) + general.set_current_view_rotation(-20.0, 0.0, 0.0) - # 5) Verify initial instance counts - num_expected = 20 * 20 - success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 5.0) - self.test_success = success and self.test_success + # 2) Create a new instance spawner entity + spawner_center_point = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, + asset_path) - # 6) Create a new Vegetation Layer Blocker area overlapping the spawner area - blocker_entity = hydra.Entity("Blocker Area") - blocker_entity.create_entity( - spawner_center_point, - ["Vegetation Layer Blocker", "Box Shape"] - ) - if blocker_entity.id.IsValid(): - print(f"'{blocker_entity.name}' created") - blocker_entity.get_set_test(1, "Box Shape|Box Configuration|Dimensions", - math.Vector3(3.0, 3.0, 3.0)) + # 3) Create surface for planting on + dynveg.create_surface_entity("Surface Entity", spawner_center_point, 32.0, 32.0, 1.0) - # 7) Validate instance counts post-blocker. 16 instances should now be blocked in the center of the spawner area - num_expected = (20 * 20) - 16 - success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 5.0) - self.test_success = success and self.test_success + # 4) Add a Vegetation System Settings Level component and set Sector Point Snap Mode to Center + veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, + 'Configuration|Area System Settings|Sector Point Snap Mode', 1) + + # 5) Verify initial instance counts + num_expected = 20 * 20 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 5.0) + Report.result(Tests.initial_instance_count, success) + + # 6) Create a new Vegetation Layer Blocker area overlapping the spawner area + blocker_entity = hydra.Entity("Blocker Area") + blocker_entity.create_entity( + spawner_center_point, + ["Vegetation Layer Blocker", "Box Shape"] + ) + if blocker_entity.id.IsValid(): + print(f"'{blocker_entity.name}' created") + blocker_entity.get_set_test(1, "Box Shape|Box Configuration|Dimensions", + math.Vector3(3.0, 3.0, 3.0)) + + # 7) Validate instance counts post-blocker. 16 instances should now be blocked in the center of the spawner area + num_expected = (20 * 20) - 16 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 5.0) + Report.result(Tests.blocked_instance_count, success) -test = TestLayerBlocker() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(LayerBlocker_InstancesBlockedInConfiguredArea) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py index a52f91ae5f..8592692c4b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py @@ -5,85 +5,83 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -import azlmbr.bus as bus -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + preprocess_instance_count = ( + "Preprocess filter stage vegetation instance count is as expected", + "Preprocess filter stage instance count found an unexpected number of instances" + ) + postprocess_instance_count = ( + "Postprocess filter stage vegetation instance count is as expected", + "Postprocess filter stage instance count found an unexpected number of instances" + ) -class TestLayerSpawnerFilterStageToggle(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="LayerSpawner_FilterStageToggle", args=["level"]) +def LayerSpawner_FilterStageToggle(): + """ + Summary: + Filter Stage toggle affects final vegetation position. - def run_test(self): - """ - Summary: - C4765973 Filter Stage toggle affects final vegetation position. + Expected Result: + Vegetation instances plant differently depending on the Filter Stage setting. - Expected Result: - Vegetation instances plant differently depending on the Filter Stage setting. + :return: None + """ - :return: None - """ + import os - PREPROCESS_INSTANCE_COUNT = 21 - POSTPROCESS_INSTANCE_COUNT = 19 + import azlmbr.legacy.general as general + import azlmbr.math as math - # Create empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - general.set_current_view_position(500.49, 498.69, 46.66) - general.set_current_view_rotation(-42.05, 0.00, -36.33) + PREPROCESS_INSTANCE_COUNT = 21 + POSTPROCESS_INSTANCE_COUNT = 19 - # Create a vegetation area with all needed components - position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - vegetation_entity = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path) - vegetation_entity.add_component("Vegetation Altitude Filter") - vegetation_entity.add_component("Vegetation Position Modifier") + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Create a child entity under vegetation area - child_entity = hydra.Entity("child_entity") - components_to_add = ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"] - child_entity.create_entity(position, components_to_add, vegetation_entity.id) + general.set_current_view_position(500.49, 498.69, 46.66) + general.set_current_view_rotation(-42.05, 0.00, -36.33) - # Set the Gradient Id in X and Y direction - vegetation_entity.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", child_entity.id) - vegetation_entity.get_set_test(4, "Configuration|Position Y|Gradient|Gradient Entity Id", child_entity.id) + # Create a vegetation area with all needed components + position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + vegetation_entity = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path) + vegetation_entity.add_component("Vegetation Altitude Filter") + vegetation_entity.add_component("Vegetation Position Modifier") - # Set the min and max values for Altitude Filter - vegetation_entity.get_set_test(3, "Configuration|Altitude Min", 34.0) - vegetation_entity.get_set_test(3, "Configuration|Altitude Max", 38.0) + # Create a child entity under vegetation area + child_entity = hydra.Entity("child_entity") + components_to_add = ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"] + child_entity.create_entity(position, components_to_add, vegetation_entity.id) - # Add entity with Mesh to replicate creation of hills and a flat surface to plant on - dynveg.create_surface_entity("Flat Surface", position, 32.0, 32.0, 1.0) - hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 4.0) + # Set the Gradient Id in X and Y direction + vegetation_entity.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", child_entity.id) + vegetation_entity.get_set_test(4, "Configuration|Position Y|Gradient|Gradient Entity Id", child_entity.id) - # Set the filter stage to preprocess and postprocess respectively and verify instance count - vegetation_entity.get_set_test(0, "Configuration|Filter Stage", 1) - self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, PREPROCESS_INSTANCE_COUNT), 3.0) - result = dynveg.validate_instance_count(position, 16.0, PREPROCESS_INSTANCE_COUNT) - self.log(f"Preprocess filter stage vegetation instance count is as expected: {result}") - vegetation_entity.get_set_test(0, "Configuration|Filter Stage", 2) - self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, POSTPROCESS_INSTANCE_COUNT), 3.0) - result = dynveg.validate_instance_count(position, 16.0, POSTPROCESS_INSTANCE_COUNT) - self.log(f"Postprocess filter vegetation instance stage count is as expected: {result}") + # Set the min and max values for Altitude Filter + vegetation_entity.get_set_test(3, "Configuration|Altitude Min", 34.0) + vegetation_entity.get_set_test(3, "Configuration|Altitude Max", 38.0) + + # Add entity with Mesh to replicate creation of hills and a flat surface to plant on + dynveg.create_surface_entity("Flat Surface", position, 32.0, 32.0, 1.0) + hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 4.0) + + # Set the filter stage to preprocess and postprocess respectively and verify instance count + vegetation_entity.get_set_test(0, "Configuration|Filter Stage", 1) + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, PREPROCESS_INSTANCE_COUNT), 3.0) + Report.result(Tests.preprocess_instance_count, result) + vegetation_entity.get_set_test(0, "Configuration|Filter Stage", 2) + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, POSTPROCESS_INSTANCE_COUNT), 3.0) + Report.result(Tests.postprocess_instance_count, result) -test = TestLayerSpawnerFilterStageToggle() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(LayerSpawner_FilterStageToggle) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InheritBehaviorFlag.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InheritBehaviorFlag.py index 5e5c6410b0..649c7d0776 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InheritBehaviorFlag.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InheritBehaviorFlag.py @@ -5,118 +5,116 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -import azlmbr.math as math -import azlmbr.legacy.general as general -import azlmbr.paths -import azlmbr.surface_data as surface_data -import azlmbr.vegetation as vegetation - -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + inherit_behavior_checked = ( + "Found no instances with Inherit Behavior checked as expected", + "Unexpectedly found instances with Inherit Behavior checked" + ) + inherit_behavior_unchecked = ( + "Found instances with Inherit Behavior unchecked as expected", + "Unexpectedly found no instances with Inherit Behavior unchecked" + ) -class TestLayerSpawnerInheritBehavior(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="LayerSpawner_InheritBehavior", args=["level"]) +def LayerSpawner_InheritBehaviorFlag(): + """ + Summary: + Verifies if Inherit Behavior Flag works as expected. - def run_test(self): - """ - Summary: - C4762381 Verifies if Inherit Behavior Flag works as expected. + Expected Result: + The spawner with Inherit Behavior toggled off no longer obeys + Vegetation Surface Mask Filter of the Vegetation Layer Blender entity and plants on the surface. - Expected Result: - The spawner with Inherit Behavior toggled off no longer obeys - Vegetation Surface Mask Filter of the Vegetation Layer Blender entity and plants on the surface. + :return: None + """ + import os - :return: None - """ + import azlmbr.math as math + import azlmbr.legacy.general as general + import azlmbr.surface_data as surface_data + import azlmbr.vegetation as vegetation - SURFACE_TAG = "test_tag" + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - def set_dynamic_slice_asset(entity_obj, component_index, dynamic_slice_asset_path): - dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner() - dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path) - descriptor = hydra.get_component_property_value( - entity_obj.components[component_index], "Configuration|Embedded Assets|[0]" - ) - descriptor.spawner = dynamic_slice_spawner - entity_obj.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor) + SURFACE_TAG = "test_tag" - # Create empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, + def set_dynamic_slice_asset(entity_obj, component_index, dynamic_slice_asset_path): + dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner() + dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path) + descriptor = hydra.get_component_property_value( + entity_obj.components[component_index], "Configuration|Embedded Assets|[0]" ) + descriptor.spawner = dynamic_slice_spawner + entity_obj.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor) - general.set_current_view_position(512.0, 480.0, 38.0) + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Create Emitter entity and add the required components - position = math.Vector3(512.0, 512.0, 32.0) - emitter_entity = dynveg.create_surface_entity("emitter_entity", position, 16.0, 16.0, 1.0) + general.set_current_view_position(512.0, 480.0, 38.0) - # Add surface tag to the Surface Tag Emitter - tag = surface_data.SurfaceTag() - tag.SetTag(SURFACE_TAG) - pte = hydra.get_property_tree(emitter_entity.components[1]) - path = "Configuration|Generated Tags" - pte.add_container_item(path, 0, tag) - emitter_entity.get_set_test(1, "Configuration|Generated Tags|[0]", tag) + # Create Emitter entity and add the required components + position = math.Vector3(512.0, 512.0, 32.0) + emitter_entity = dynveg.create_surface_entity("emitter_entity", position, 16.0, 16.0, 1.0) - # Create Blender entity and add required components - components_to_add = ["Box Shape", "Vegetation Layer Blender"] - blender_entity = hydra.Entity("blender_entity") - blender_entity.create_entity(position, components_to_add) - blender_entity.get_set_test(0, "Box Shape|Box Configuration|Dimensions", math.Vector3(16.0, 16.0, 1.0)) + # Add surface tag to the Surface Tag Emitter + tag = surface_data.SurfaceTag() + tag.SetTag(SURFACE_TAG) + pte = hydra.get_property_tree(emitter_entity.components[1]) + path = "Configuration|Generated Tags" + pte.add_container_item(path, 0, tag) + emitter_entity.get_set_test(1, "Configuration|Generated Tags|[0]", tag) - # Create Vegetation area and assign a valid asset - veg_1 = hydra.Entity("veg_1") - veg_1.create_entity( - position, ["Vegetation Layer Spawner", "Vegetation Reference Shape", "Vegetation Asset List"] - ) - set_dynamic_slice_asset(veg_1, 2, os.path.join("Slices", "PinkFlower.dynamicslice")) - veg_1.get_set_test(1, "Configuration|Shape Entity Id", blender_entity.id) + # Create Blender entity and add required components + components_to_add = ["Box Shape", "Vegetation Layer Blender"] + blender_entity = hydra.Entity("blender_entity") + blender_entity.create_entity(position, components_to_add) + blender_entity.get_set_test(0, "Box Shape|Box Configuration|Dimensions", math.Vector3(16.0, 16.0, 1.0)) - # Create second vegetation area and assign a valid asset - veg_2 = hydra.Entity("veg_2") - veg_2.create_entity( - position, ["Vegetation Layer Spawner", "Vegetation Reference Shape", "Vegetation Asset List"] - ) - set_dynamic_slice_asset(veg_2, 2, os.path.join("Slices", "PurpleFlower.dynamicslice")) - veg_2.get_set_test(1, "Configuration|Shape Entity Id", blender_entity.id) + # Create Vegetation area and assign a valid asset + veg_1 = hydra.Entity("veg_1") + veg_1.create_entity( + position, ["Vegetation Layer Spawner", "Vegetation Reference Shape", "Vegetation Asset List"] + ) + set_dynamic_slice_asset(veg_1, 2, os.path.join("Slices", "PinkFlower.dynamicslice")) + veg_1.get_set_test(1, "Configuration|Shape Entity Id", blender_entity.id) - # Assign the vegetation areas to the Blender entity - pte = hydra.get_property_tree(blender_entity.components[1]) - path = "Configuration|Vegetation Areas" - pte.update_container_item(path, 0, veg_1.id) - pte.add_container_item(path, 1, veg_2.id) + # Create second vegetation area and assign a valid asset + veg_2 = hydra.Entity("veg_2") + veg_2.create_entity( + position, ["Vegetation Layer Spawner", "Vegetation Reference Shape", "Vegetation Asset List"] + ) + set_dynamic_slice_asset(veg_2, 2, os.path.join("Slices", "PurpleFlower.dynamicslice")) + veg_2.get_set_test(1, "Configuration|Shape Entity Id", blender_entity.id) - # Add Vegetation Surface Mask Filter to the blender entity and add a Exclusion tag - tag = surface_data.SurfaceTag() - tag.SetTag(SURFACE_TAG) - blender_entity.add_component("Vegetation Surface Mask Filter") - pte = hydra.get_property_tree(blender_entity.components[2]) - path = "Configuration|Exclusion|Surface Tags" - pte.add_container_item(path, 0, tag) - blender_entity.get_set_test(2, "Configuration|Exclusion|Surface Tags|[0]", tag) + # Assign the vegetation areas to the Blender entity + pte = hydra.get_property_tree(blender_entity.components[1]) + path = "Configuration|Vegetation Areas" + pte.update_container_item(path, 0, veg_1.id) + pte.add_container_item(path, 1, veg_2.id) - # Toggle Inherit Behavior flag and verify vegetation instances - self.log( - f"Vegetation is not planted when Inherit Behavior flag is checked: {dynveg.validate_instance_count(position, 16.0, 0)}" - ) - veg_1.get_set_test(0, "Configuration|Inherit Behavior", False) - self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 400), 2.0) - self.log( - f"Vegetation plant when Inherit Behavior flag is unchecked: {dynveg.validate_instance_count(position, 16.0, 400)}" - ) + # Add Vegetation Surface Mask Filter to the blender entity and add a Exclusion tag + tag = surface_data.SurfaceTag() + tag.SetTag(SURFACE_TAG) + blender_entity.add_component("Vegetation Surface Mask Filter") + pte = hydra.get_property_tree(blender_entity.components[2]) + path = "Configuration|Exclusion|Surface Tags" + pte.add_container_item(path, 0, tag) + blender_entity.get_set_test(2, "Configuration|Exclusion|Surface Tags|[0]", tag) + + # Toggle Inherit Behavior flag and verify vegetation instances + flag_checked_instance_count = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 0), 2.0) + Report.result(Tests.inherit_behavior_checked, flag_checked_instance_count) + veg_1.get_set_test(0, "Configuration|Inherit Behavior", False) + flag_unchecked_instance_count = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 400), 2.0) + Report.result(Tests.inherit_behavior_unchecked, flag_unchecked_instance_count) -test = TestLayerSpawnerInheritBehavior() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(LayerSpawner_InheritBehaviorFlag) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py index 8310583fe6..0da200d87a 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py @@ -5,134 +5,128 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr -import azlmbr.legacy.general as general -import azlmbr.entity as EntityId -import azlmbr.math as math +def LayerSpawner_InstancesPlantInAllSupportedShapes(): + """ + Summary: + The level is loaded and vegetation area is created. Then the Vegetation Reference Shape + component of vegetation area is pinned with entities of different shape components to check + if the vegetation plants in different shaped areas. -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg + Expected Behavior: + Vegetation properly plants in areas of any shape. + Test Steps: + 1) Open a level + 2) Create basic vegetation area entity and set the properties + 3) Box Shape Entity: create, set properties and pin to vegetation + 4) Capsule Shape Entity: create, set properties and pin to vegetation + 5) Tube Shape Entity: create, set properties and pin to vegetation + 6) Sphere Shape Entity: create, set properties and pin to vegetation + 7) Cylinder Shape Entity: create, set properties and pin to vegetation + 8) Prism Shape Entity: create, set properties and pin to vegetation + 9) Compound Shape Entity: create, set properties and pin to vegetation -class TestLayerSpawner_AllShapesPlant(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="TestLayerSpawner_AllShapesPlant", args=["level"]) + Note: + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - def run_test(self): - """ - Summary: - The level is loaded and vegetation area is created. Then the Vegetation Reference Shape - component of vegetation area is pinned with entities of different shape components to check - if the vegetation plants in different shaped areas. + :return: None + """ - Expected Behavior: - Vegetation properly plants in areas of any shape. + import os - Test Steps: - 1) Create level - 2) Create basic vegetation area entity and set the properties - 3) Box Shape Entity: create, set properties and pin to vegetation - 4) Capsule Shape Entity: create, set properties and pin to vegetation - 5) Tube Shape Entity: create, set properties and pin to vegetation - 6) Sphere Shape Entity: create, set properties and pin to vegetation - 7) Cylinder Shape Entity: create, set properties and pin to vegetation - 8) Prism Shape Entity: create, set properties and pin to vegetation - 9) Compound Shape Entity: create, set properties and pin to vegetation + import azlmbr.legacy.general as general + import azlmbr.entity as EntityId + import azlmbr.math as math - Note: - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - :return: None - """ - - def pin_shape_and_check_count(entity_id, count): - hydra.get_set_test(vegetation, 2, "Configuration|Shape Entity Id", entity_id) - result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(vegetation.id, - count), 2.0) - self.test_success = self.test_success and result - - # 1) Create level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, + def pin_shape_and_check_count(entity, count): + hydra.get_set_test(vegetation, 2, "Configuration|Shape Entity Id", entity.id) + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(vegetation.id, + count), 2.0) + success = ( + f"Found the expected number of instances in {entity.name} shape", + f"Unexpected number of instances found in {entity.name} shape" ) + Report.result(success, result) - # 2) Create basic vegetation area entity and set the properties - entity_position = math.Vector3(125.0, 136.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - vegetation = dynveg.create_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) - vegetation.remove_component("Box Shape") - vegetation.add_component("Vegetation Reference Shape") + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Create surface for planting on - dynveg.create_surface_entity("Surface Entity", entity_position, 60.0, 60.0, 1.0) + # 2) Create basic vegetation area entity and set the properties + entity_position = math.Vector3(125.0, 136.0, 32.0) + asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") + vegetation = dynveg.create_vegetation_area("Instance Spawner", + entity_position, + 10.0, 10.0, 10.0, + asset_path) + vegetation.remove_component("Box Shape") + vegetation.add_component("Vegetation Reference Shape") - # Adjust camera to be close to the vegetation entity - general.set_current_view_position(135.0, 102.0, 39.0) - general.set_current_view_rotation(-15.0, 0, 0) + # Create surface for planting on + dynveg.create_surface_entity("Surface Entity", entity_position, 60.0, 60.0, 1.0) - # 3) Box Shape Entity: create, set properties and pin to vegetation - box = hydra.Entity("box") - box.create_entity(math.Vector3(124.0, 126.0, 32.0), ["Box Shape"]) - new_box_dimension = math.Vector3(10.0, 10.0, 1.0) - hydra.get_set_test(box, 0, "Box Shape|Box Configuration|Dimensions", new_box_dimension) - # This and subsequent counts are the number of "PurpleFlower" that spawn in the shape with given dimensions - pin_shape_and_check_count(box.id, 156) + # Adjust camera to be close to the vegetation entity + general.set_current_view_position(135.0, 102.0, 39.0) + general.set_current_view_rotation(-15.0, 0, 0) - # 4) Capsule Shape Entity: create, set properties and pin to vegetation - capsule = hydra.Entity("capsule") - capsule.create_entity(math.Vector3(120.0, 142.0, 32.0), ["Capsule Shape"]) - hydra.get_set_test(capsule, 0, "Capsule Shape|Capsule Configuration|Height", 10.0) - hydra.get_set_test(capsule, 0, "Capsule Shape|Capsule Configuration|Radius", 2.0) - pin_shape_and_check_count(capsule.id, 20) + # 3) Box Shape Entity: create, set properties and pin to vegetation + box = hydra.Entity("Box") + box.create_entity(math.Vector3(124.0, 126.0, 32.0), ["Box Shape"]) + new_box_dimension = math.Vector3(10.0, 10.0, 1.0) + hydra.get_set_test(box, 0, "Box Shape|Box Configuration|Dimensions", new_box_dimension) + # This and subsequent counts are the number of "PurpleFlower" that spawn in the shape with given dimensions + pin_shape_and_check_count(box, 156) - # 5) Tube Shape Entity: create, set properties and pin to vegetation - tube = hydra.Entity("tube") - tube.create_entity(math.Vector3(124.0, 136.0, 32.0), ["Tube Shape", "Spline"]) - pin_shape_and_check_count(tube.id, 27) + # 4) Capsule Shape Entity: create, set properties and pin to vegetation + capsule = hydra.Entity("Capsule") + capsule.create_entity(math.Vector3(120.0, 142.0, 32.0), ["Capsule Shape"]) + hydra.get_set_test(capsule, 0, "Capsule Shape|Capsule Configuration|Height", 10.0) + hydra.get_set_test(capsule, 0, "Capsule Shape|Capsule Configuration|Radius", 2.0) + pin_shape_and_check_count(capsule, 20) - # 6) Sphere Shape Entity: create, set properties and pin to vegetation - sphere = hydra.Entity("sphere") - sphere.create_entity(math.Vector3(112.0, 143.0, 32.0), ["Sphere Shape"]) - hydra.get_set_test(sphere, 0, "Sphere Shape|Sphere Configuration|Radius", 5.0) - pin_shape_and_check_count(sphere.id, 122) + # 5) Tube Shape Entity: create, set properties and pin to vegetation + tube = hydra.Entity("Tube") + tube.create_entity(math.Vector3(124.0, 136.0, 32.0), ["Tube Shape", "Spline"]) + pin_shape_and_check_count(tube, 27) - # 7) Cylinder Shape Entity: create, set properties and pin to vegetation - cylinder = hydra.Entity("cylinder") - cylinder.create_entity(math.Vector3(136.0, 143.0, 32.0), ["Cylinder Shape"]) - hydra.get_set_test(cylinder, 0, "Cylinder Shape|Cylinder Configuration|Radius", 5.0) - hydra.get_set_test(cylinder, 0, "Cylinder Shape|Cylinder Configuration|Height", 5.0) - pin_shape_and_check_count(cylinder.id, 124) + # 6) Sphere Shape Entity: create, set properties and pin to vegetation + sphere = hydra.Entity("Sphere") + sphere.create_entity(math.Vector3(112.0, 143.0, 32.0), ["Sphere Shape"]) + hydra.get_set_test(sphere, 0, "Sphere Shape|Sphere Configuration|Radius", 5.0) + pin_shape_and_check_count(sphere, 122) - # 8) Prism Shape Entity: create, set properties and pin to vegetation - polygon_prism = hydra.Entity("polygonprism") - polygon_prism.create_entity(math.Vector3(127.0, 142.0, 32.0), ["Polygon Prism Shape"]) - pin_shape_and_check_count(polygon_prism.id, 20) + # 7) Cylinder Shape Entity: create, set properties and pin to vegetation + cylinder = hydra.Entity("Cylinder") + cylinder.create_entity(math.Vector3(136.0, 143.0, 32.0), ["Cylinder Shape"]) + hydra.get_set_test(cylinder, 0, "Cylinder Shape|Cylinder Configuration|Radius", 5.0) + hydra.get_set_test(cylinder, 0, "Cylinder Shape|Cylinder Configuration|Height", 5.0) + pin_shape_and_check_count(cylinder, 124) - # 9) Compound Shape Entity: create, set properties and pin to vegetation - compound = hydra.Entity("Compound") - compound.create_entity(math.Vector3(125.0, 136.0, 32.0), ["Compound Shape"]) - pte = hydra.get_property_tree(compound.components[0]) - shapes = [box.id, capsule.id, tube.id, sphere.id, cylinder.id, polygon_prism.id] - for index in range(6): - pte.add_container_item("Configuration|Child Shape Entities", index, EntityId.EntityId()) - for index, element in enumerate(shapes): - hydra.get_set_test(compound, 0, f"Configuration|Child Shape Entities|[{index}]", element) - pin_shape_and_check_count(compound.id, 469) + # 8) Prism Shape Entity: create, set properties and pin to vegetation + polygon_prism = hydra.Entity("Polygon Prism") + polygon_prism.create_entity(math.Vector3(127.0, 142.0, 32.0), ["Polygon Prism Shape"]) + pin_shape_and_check_count(polygon_prism, 20) + + # 9) Compound Shape Entity: create, set properties and pin to vegetation + compound = hydra.Entity("Compound") + compound.create_entity(math.Vector3(125.0, 136.0, 32.0), ["Compound Shape"]) + pte = hydra.get_property_tree(compound.components[0]) + shapes = [box.id, capsule.id, tube.id, sphere.id, cylinder.id, polygon_prism.id] + for index in range(6): + pte.add_container_item("Configuration|Child Shape Entities", index, EntityId.EntityId()) + for index, element in enumerate(shapes): + hydra.get_set_test(compound, 0, f"Configuration|Child Shape Entities|[{index}]", element) + pin_shape_and_check_count(compound, 469) -test = TestLayerSpawner_AllShapesPlant() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(LayerSpawner_InstancesPlantInAllSupportedShapes) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py index f17956e066..02d30fb0f3 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py @@ -5,115 +5,131 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr -import azlmbr.legacy.general as general -import azlmbr.math as math - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + viewport_config_updated = ( + "Viewport is now configured for test", + "Failed to configure viewport for test" + ) + first_viewport_active_instance_count = ( + "Expected number of instances found in left viewport", + "Unexpected number of instances found in left viewport" + ) + second_viewport_inactive_instance_count = ( + "No instances found in right viewport", + "Unexpectedly found instances in right viewport while not active" + ) + first_viewport_inactive_instance_count = ( + "No instances found in left viewport", + "Unexpectedly found instances in left viewport while not active" + ) + second_viewport_active_instance_count = ( + "Expected number of instances found in right viewport", + "Unexpected number of instances found in right viewport" + ) -class TestLayerSpawnerInstanceCameraRefresh(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="LayerSpawner_InstanceCameraRefresh", args=["level"]) +def LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(): + """ + Summary: + Test that the Dynamic Vegetation System is using the current Editor viewport camera as the center + of the spawn area for vegetation. To verify this, we create two separate Editor viewports pointed + at two different vegetation areas, and verify that as we switch between active viewports, only the + area directly underneath that viewport's camera has vegetation. + """ - def run_test(self): - """ - Summary: - Test that the Dynamic Vegetation System is using the current Editor viewport camera as the center - of the spawn area for vegetation. To verify this, we create two separate Editor viewports pointed - at two different vegetation areas, and verify that as we switch between active viewports, only the - area directly underneath that viewport's camera has vegetation. - """ - # Create an empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=128, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) - # Set up a test environment to validate that switching viewports correctly changes which camera - # the vegetation system uses. - # The test environment consists of the following: - # - two 32 x 32 x 1 box shapes located far apart that emit a surface with no tags - # - two 32 x 32 x 32 vegetation areas that place vegetation on the boxes + import os - # Initialize some constants for our test. - # The boxes are intentionally shifted by 0.5 meters to ensure that we get a predictable number - # of vegetation points. By default, vegetation plants on grid corners, so if our boxes are aligned - # with grid corner points, the right/bottom edges will include more points than we might intuitively expect. - # By shifting by 0.5 meters, the vegetation grid points don't fall on the box edges, making the total count - # more predictable. - first_entity_center_point = math.Vector3(0.5, 0.5, 100.0) - # The second box needs to be far enough away from the first that the vegetation system will never spawn instances - # in both at the same time. - second_entity_center_point = math.Vector3(1024.5, 1024.5, 100.0) - box_size = 32.0 - surface_height = 1.0 - # By default, vegetation spawns 20 instances per 16 meters, so for our box of 32 meters, we should have - # ((20 instances / 16 m) * 32 m) ^ 2 instances. - filled_vegetation_area_instance_count = (20 * 2) * (20 * 2) + import azlmbr.legacy.general as general + import azlmbr.math as math - # Change the Editor view to contain two viewports - general.set_view_pane_layout(1) - get_view_pane_layout_success = self.wait_for_condition(lambda: (general.get_view_pane_layout() == 1), 2) - get_viewport_count_success = self.wait_for_condition(lambda: (general.get_viewport_count() == 2), 2) - self.test_success = get_view_pane_layout_success and self.test_success - self.test_success = get_viewport_count_success and self.test_success + 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 - # Set the view in the first viewport to point down at the first box - general.set_active_viewport(0) - self.wait_for_condition(lambda: general.get_active_viewport() == 0, 2) - general.set_current_view_position(first_entity_center_point.x, first_entity_center_point.y, - first_entity_center_point.z + 30.0) - general.set_current_view_rotation(-85.0, 0.0, 0.0) + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Set the view in the second viewport to point down at the second box - general.set_active_viewport(1) - self.wait_for_condition(lambda: general.get_active_viewport() == 1, 2) - general.set_current_view_position(second_entity_center_point.x, second_entity_center_point.y, - second_entity_center_point.z + 30.0) - general.set_current_view_rotation(-85.0, 0.0, 0.0) + # Set up a test environment to validate that switching viewports correctly changes which camera + # the vegetation system uses. + # The test environment consists of the following: + # - two 32 x 32 x 1 box shapes located far apart that emit a surface with no tags + # - two 32 x 32 x 32 vegetation areas that place vegetation on the boxes - # Create the "flat surface" entities to use as our vegetation surfaces - first_surface_entity = dynveg.create_surface_entity("Surface 1", first_entity_center_point, box_size, box_size, - surface_height) - second_surface_entity = dynveg.create_surface_entity("Surface 2", second_entity_center_point, box_size, box_size, - surface_height) + # Initialize some constants for our test. + # The boxes are intentionally shifted by 0.5 meters to ensure that we get a predictable number + # of vegetation points. By default, vegetation plants on grid corners, so if our boxes are aligned + # with grid corner points, the right/bottom edges will include more points than we might intuitively expect. + # By shifting by 0.5 meters, the vegetation grid points don't fall on the box edges, making the total count + # more predictable. + first_entity_center_point = math.Vector3(0.5, 0.5, 100.0) + # The second box needs to be far enough away from the first that the vegetation system will never spawn instances + # in both at the same time. + second_entity_center_point = math.Vector3(1024.5, 1024.5, 100.0) + box_size = 32.0 + surface_height = 1.0 + # By default, vegetation spawns 20 instances per 16 meters, so for our box of 32 meters, we should have + # ((20 instances / 16 m) * 32 m) ^ 2 instances. + filled_vegetation_area_instance_count = (20 * 2) * (20 * 2) - # Create the two vegetation areas - test_slice_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - first_veg_entity = dynveg.create_vegetation_area("Veg Area 1", first_entity_center_point, box_size, box_size, - box_size, test_slice_asset_path) - second_veg_entity = dynveg.create_vegetation_area("Veg Area 2", second_entity_center_point, box_size, box_size, - box_size, test_slice_asset_path) + # Change the Editor view to contain two viewports + general.set_view_pane_layout(1) + get_view_pane_layout_success = helper.wait_for_condition(lambda: (general.get_view_pane_layout() == 1), 2) + get_viewport_count_success = helper.wait_for_condition(lambda: (general.get_viewport_count() == 2), 2) + Report.critical_result(Tests.viewport_config_updated, get_view_pane_layout_success and get_viewport_count_success) - # When the first viewport is active, the first area should be full of instances, and the second should be empty - general.set_active_viewport(0) - viewport_0_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(first_entity_center_point, - box_size / 2.0, - filled_vegetation_area_instance_count), 5) - self.test_success = viewport_0_success and self.test_success - viewport_1_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(second_entity_center_point, - box_size / 2.0, 0), 5) - self.test_success = viewport_1_success and self.test_success + # Set the view in the first viewport to point down at the first box + general.set_active_viewport(0) + helper.wait_for_condition(lambda: general.get_active_viewport() == 0, 2) + general.set_current_view_position(first_entity_center_point.x, first_entity_center_point.y, + first_entity_center_point.z + 30.0) + general.set_current_view_rotation(-85.0, 0.0, 0.0) - # When the second viewport is active, the second area should be full of instances, and the first should be empty - general.set_active_viewport(1) - viewport_0_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(first_entity_center_point, - box_size / 2.0, 0), 5) - self.test_success = viewport_0_success and self.test_success - viewport_1_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(second_entity_center_point, - box_size / 2.0, - filled_vegetation_area_instance_count), 5) - self.test_success = viewport_1_success and self.test_success + # Set the view in the second viewport to point down at the second box + general.set_active_viewport(1) + helper.wait_for_condition(lambda: general.get_active_viewport() == 1, 2) + general.set_current_view_position(second_entity_center_point.x, second_entity_center_point.y, + second_entity_center_point.z + 30.0) + general.set_current_view_rotation(-85.0, 0.0, 0.0) + + # Create the "flat surface" entities to use as our vegetation surfaces + first_surface_entity = dynveg.create_surface_entity("Surface 1", first_entity_center_point, box_size, box_size, + surface_height) + second_surface_entity = dynveg.create_surface_entity("Surface 2", second_entity_center_point, box_size, box_size, + surface_height) + + # Create the two vegetation areas + test_slice_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") + first_veg_entity = dynveg.create_vegetation_area("Veg Area 1", first_entity_center_point, box_size, box_size, + box_size, test_slice_asset_path) + second_veg_entity = dynveg.create_vegetation_area("Veg Area 2", second_entity_center_point, box_size, box_size, + box_size, test_slice_asset_path) + + # When the first viewport is active, the first area should be full of instances, and the second should be empty + general.set_active_viewport(0) + helper.wait_for_condition(lambda: general.get_active_viewport() == 0, 2) + viewport_0_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(first_entity_center_point, + box_size / 2.0, + filled_vegetation_area_instance_count), 5) + viewport_1_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(second_entity_center_point, + box_size / 2.0, 0), 5) + Report.result(Tests.first_viewport_active_instance_count, viewport_0_success) + Report.result(Tests.second_viewport_inactive_instance_count, viewport_1_success) + + # When the second viewport is active, the second area should be full of instances, and the first should be empty + general.set_active_viewport(1) + helper.wait_for_condition(lambda: general.get_active_viewport() == 1, 2) + viewport_0_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(first_entity_center_point, + box_size / 2.0, 0), 5) + Report.result(Tests.first_viewport_inactive_instance_count, viewport_0_success) + viewport_1_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(second_entity_center_point, + box_size / 2.0, + filled_vegetation_area_instance_count), 5) + Report.result(Tests.second_viewport_active_instance_count, viewport_1_success) -test = TestLayerSpawnerInstanceCameraRefresh() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(LayerSpawner_InstancesRefreshUsingCorrectViewportCamera) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py index 47e9c857c2..415673c215 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py @@ -5,92 +5,90 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os, sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.asset as asset -import azlmbr.bus as bus -import azlmbr.components as components -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.math as math - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + blocked_instance_count = ( + "Instance count is as expected wih a Blocker setup", + "Found unexpected instances with a Blocker setup" + ) -class test_MeshBlocker_InstancesBlockedByMesh(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="MeshBlocker_InstancesBlockedByMesh", args=["level"]) +def MeshBlocker_InstancesBlockedByMesh(): + """ + Summary: + Level is created. An entity with a vegetation spawner and entity with vegetation blocker (Mesh) component are + added. Finally, the instance counts are checked to verify expected numbers after blocker is applied. - def run_test(self): - """ - Summary: - Level is created. An entity with a vegetation spawner and entity with vegetation blocker (Mesh) component are - added. Finally, the instance counts are checked to verify expected numbers after blocker is applied. + Expected Behavior: + The vegetation planted in the Spawner area is blocked by the Mesh of the Vegetation Blocker Mesh component. - Expected Behavior: - The vegetation planted in the Spawner area is blocked by the Mesh of the Vegetation Blocker Mesh component. + Test Steps: + --> Open a level + --> Create Spawner Entity + --> Create Surface Entity to spawn vegetation instances on + --> Create Blocker Entity with cube mesh + --> Verify spawned vegetation instance counts - Test Steps: - --> Create level - --> Create Spawner Entity - --> Create Surface Entity to spawn vegetation instances on - --> Create Blocker Entity with cube mesh - --> Verify spawned vegetation instance counts + Note: + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import os - # Create a new level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import azlmbr.asset as asset + import azlmbr.bus as bus + import azlmbr.components as components + import azlmbr.legacy.general as general + import azlmbr.math as math - general.set_current_view_position(500.49, 498.69, 46.66) - general.set_current_view_rotation(-42.05, 0.00, -36.33) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" - entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Create surface entity to plant on - dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0) + general.set_current_view_position(500.49, 498.69, 46.66) + general.set_current_view_rotation(-42.05, 0.00, -36.33) - # Create blocker entity with cube mesh - mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId - blocker_entity = hydra.Entity("Blocker Entity") - blocker_entity.create_entity(entity_position, - ["Vegetation Layer Blocker (Mesh)"]) - blocker_entity.add_component_of_type(mesh_type_id) - if blocker_entity.id.IsValid(): - print(f"'{blocker_entity.name}' created") - cubeId = asset.AssetCatalogRequestBus( - bus.Broadcast, "GetAssetIdByPath", os.path.join("objects", "_primitives", "_box_1x1.azmodel"), math.Uuid(), - False) - blocker_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", cubeId) - components.TransformBus(bus.Event, "SetLocalUniformScale", blocker_entity.id, 2.0) + # Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" + entity_position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", + entity_position, + 10.0, 10.0, 10.0, + asset_path) - # Verify spawned instance counts are accurate after addition of Blocker Entity - num_expected = 160 # Number of "PurpleFlower"s that plant on a 10 x 10 surface minus 2m blocker cube - result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 2.0) - self.test_success = self.test_success and result + # Create surface entity to plant on + dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0) + + # Create blocker entity with cube mesh + mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId + blocker_entity = hydra.Entity("Blocker Entity") + blocker_entity.create_entity(entity_position, + ["Vegetation Layer Blocker (Mesh)"]) + blocker_entity.add_component_of_type(mesh_type_id) + if blocker_entity.id.IsValid(): + print(f"'{blocker_entity.name}' created") + cubeId = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", os.path.join("objects", "_primitives", "_box_1x1.azmodel"), math.Uuid(), + False) + blocker_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", cubeId) + components.TransformBus(bus.Event, "SetLocalUniformScale", blocker_entity.id, 2.0) + + # Verify spawned instance counts are accurate after addition of Blocker Entity + num_expected = 160 # Number of "PurpleFlower"s that plant on a 10 x 10 surface minus 2m blocker cube + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 2.0) + Report.result(Tests.blocked_instance_count, result) -test = test_MeshBlocker_InstancesBlockedByMesh() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(MeshBlocker_InstancesBlockedByMesh) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py index f5dad6b64d..be15c9967c 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py @@ -5,104 +5,100 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import math as pymath -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr -import azlmbr.asset as asset -import azlmbr.bus as bus -import azlmbr.components as components -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.math as math +class Tests: + blocked_instance_count = ( + "Instance count is as expected wih a Blocker setup", + "Found unexpected instances with a Blocker setup" + ) -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +def MeshBlocker_InstancesBlockedByMeshHeightTuning(): + """ + Summary: + A temporary level is created, then a simple vegetation area is created. A blocker area is created and it is + verified that the tuning of the height percent blocker setting works as expected. + + Expected Behavior: + Vegetation is blocked only around the trunk of the tree, while it still plants under the areas covered by branches. + + Test Steps: + 1) Open a level + 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" + 3) Create surface entity + 4) Create blocker entity with sphere mesh + 5) Adjust the height Min/Max percentage values of blocker + 6) Verify spawned instance counts are accurate after adjusting height Max percentage of Blocker Entity + + Note: + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ + + import os + import math as pymath + + import azlmbr + import azlmbr.asset as asset + import azlmbr.bus as bus + import azlmbr.components as components + import azlmbr.legacy.general as general + import azlmbr.math as math + + import editor_python_test_tools.hydra_editor_utils as hydra + 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 + helper.init_idle() + helper.open_level("Physics", "Base") + + general.set_current_view_position(500.49, 498.69, 46.66) + general.set_current_view_rotation(-42.05, 0.00, -36.33) + + # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" + entity_position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", + entity_position, + 10.0, 10.0, 10.0, + asset_path) + + # 3) Create surface entity to plant on + dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0) + + # 4) Create blocker entity with rotated cube mesh + y_rotation = pymath.radians(45.0) + mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId + blocker_entity = hydra.Entity("Blocker Entity") + blocker_entity.create_entity(entity_position, + ["Vegetation Layer Blocker (Mesh)"]) + blocker_entity.add_component_of_type(mesh_type_id) + if blocker_entity.id.IsValid(): + Report.info(f"'{blocker_entity.name}' created") + sphere_id = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", os.path.join("objects", "_primitives", "_box_1x1.azmodel"), math.Uuid(), + False) + blocker_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", sphere_id) + components.TransformBus(bus.Event, "SetLocalUniformScale", blocker_entity.id, 5.0) + components.TransformBus(bus.Event, "SetLocalRotation", blocker_entity.id, math.Vector3(0.0, y_rotation, 0.0)) + + # 5) Adjust the height Max percentage values of blocker + blocker_entity.get_set_test(0, "Configuration|Mesh Height Percent Max", 0.8) + + # 6) Verify spawned instance counts are accurate after adjusting height Max percentage of Blocker Entity + # The number of "PurpleFlower" instances that plant on a 10 x 10 surface minus those blocked by the rotated at + # 80% max height factored in. + num_expected = 127 + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 5.0) + Report.result(Tests.blocked_instance_count, result) -class test_MeshBlocker_InstancesBlockedByMeshHeightTuning(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="MeshBlocker_InstancesBlockedByMeshHeightTuning", args=["level"]) +if __name__ == "__main__": - def run_test(self): - """ - Summary: - A temporary level is created, then a simple vegetation area is created. A blocker area is created and it is - verified that the tuning of the height percent blocker setting works as expected. - - Expected Behavior: - Vegetation is blocked only around the trunk of the tree, while it still plants under the areas covered by branches. - - Test Steps: - 1) Create level - 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" - 3) Create surface entity - 4) Create blocker entity with sphere mesh - 5) Adjust the height Min/Max percentage values of blocker - 6) Verify spawned instance counts are accurate after adjusting height Max percentage of Blocker Entity - - Note: - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. - - :return: None - """ - - # 1) Create level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) - - general.set_current_view_position(500.49, 498.69, 46.66) - general.set_current_view_rotation(-42.05, 0.00, -36.33) - - # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" - entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) - - # 3) Create surface entity to plant on - dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0) - - # 4) Create blocker entity with rotated cube mesh - y_rotation = pymath.radians(45.0) - mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId - blocker_entity = hydra.Entity("Blocker Entity") - blocker_entity.create_entity(entity_position, - ["Vegetation Layer Blocker (Mesh)"]) - blocker_entity.add_component_of_type(mesh_type_id) - if blocker_entity.id.IsValid(): - print(f"'{blocker_entity.name}' created") - sphere_id = asset.AssetCatalogRequestBus( - bus.Broadcast, "GetAssetIdByPath", os.path.join("objects", "_primitives", "_box_1x1.azmodel"), math.Uuid(), - False) - blocker_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", sphere_id) - components.TransformBus(bus.Event, "SetLocalUniformScale", blocker_entity.id, 5.0) - components.TransformBus(bus.Event, "SetLocalRotation", blocker_entity.id, math.Vector3(0.0, y_rotation, 0.0)) - - # 5) Adjust the height Max percentage values of blocker - blocker_entity.get_set_test(0, "Configuration|Mesh Height Percent Max", 0.8) - - # 6) Verify spawned instance counts are accurate after adjusting height Max percentage of Blocker Entity - # The number of "PurpleFlower" instances that plant on a 10 x 10 surface minus those blocked by the rotated at - # 80% max height factored in. - num_expected = 127 - result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 5.0) - self.test_success = self.test_success and result - - -test = test_MeshBlocker_InstancesBlockedByMeshHeightTuning() -test.run() + from editor_python_test_tools.utils import Report + Report.start_test(MeshBlocker_InstancesBlockedByMeshHeightTuning) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_DependentOnMeshComponent.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_DependentOnMeshComponent.py index c093529888..cee8ebe5b6 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_DependentOnMeshComponent.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_DependentOnMeshComponent.py @@ -5,93 +5,87 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.bus as bus -import azlmbr.editor as editor -import azlmbr.entity as EntityId -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper +class Tests: + new_entity_created = ( + "Successfully created new entity", + "Failed to create new entity" + ) + emitter_disabled_before_mesh = ( + "Mesh Surface Tag Emitter is disabled without a Mesh component", + "Mesh Surface Tag Emitter is unexpectedly enabled without a Mesh component" + ) + emitter_enabled_after_mesh = ( + "Mesh Surface Tag Emitter is enabled after adding a Mesh component", + "Mesh Surface Tag Emitter is unexpectedly disabled after adding a Mesh component" + ) -class TestMeshSurfaceTagEmitter(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="MeshSurfaceTagEmitter_DependentOnMeshComponent", args=["level"]) +def MeshSurfaceTagEmitter_DependentOnMeshComponent(): + """ + Summary: + A New level is loaded. A New entity is created with component "Mesh Surface Tag Emitter". Adding a component + "Mesh" to the same entity. - def run_test(self): - """ - Summary: - A New level is loaded. A New entity is created with component "Mesh Surface Tag Emitter". Adding a component - "Mesh" to the same entity. + Expected Behavior: + Mesh Surface Tag Emitter is disabled until the required Mesh component is added to the entity. - Expected Behavior: - Mesh Surface Tag Emitter is disabled until the required Mesh component is added to the entity. + Test Steps: + 1) Open level + 2) Create a new entity with component "Mesh Surface Tag Emitter" + 3) Make sure Mesh Surface Tag Emitter is disabled + 4) Add Mesh to the same entity + 5) Make sure Mesh Surface Tag Emitter is enabled after adding Mesh - Test Steps: - 1) Open level - 2) Create a new entity with component "Mesh Surface Tag Emitter" - 3) Make sure Mesh Surface Tag Emitter is disabled - 4) Add Mesh to the same entity - 5) Make sure Mesh Surface Tag Emitter is enabled after adding Mesh + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import azlmbr.bus as bus + import azlmbr.editor as editor + import azlmbr.entity as EntityId + import azlmbr.math as math - def is_component_enabled(EntityComponentIdPair): - return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair) + import editor_python_test_tools.hydra_editor_utils as hydra + from editor_python_test_tools.utils import Report + from editor_python_test_tools.utils import TestHelper as helper - # 1) Open level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + def is_component_enabled(EntityComponentIdPair): + return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair) - # 2) Create a new entity with component "Mesh Surface Tag Emitter" - entity_position = math.Vector3(125.0, 136.0, 32.0) - component_to_add = "Mesh Surface Tag Emitter" - entity_id = editor.ToolsApplicationRequestBus( - bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId() - ) - meshentity = hydra.Entity("meshentity", entity_id) - meshentity.components = [] - meshentity.components.append(hydra.add_component(component_to_add, entity_id)) - if entity_id.IsValid(): - print("New Entity Created") + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # 3) Make sure Mesh Surface Tag Emitter is disabled - is_enabled = is_component_enabled(meshentity.components[0]) - self.test_success = self.test_success and not is_enabled - if not is_enabled: - print(f"{component_to_add} is Disabled") - elif is_enabled: - print(f"{component_to_add} is Enabled. But It should be disabled before adding Mesh") + # 2) Create a new entity with component "Mesh Surface Tag Emitter" + entity_position = math.Vector3(125.0, 136.0, 32.0) + component_to_add = "Mesh Surface Tag Emitter" + entity_id = editor.ToolsApplicationRequestBus( + bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId() + ) + meshentity = hydra.Entity("meshentity", entity_id) + meshentity.components = [] + meshentity.components.append(hydra.add_component(component_to_add, entity_id)) + Report.critical_result(Tests.new_entity_created, entity_id.IsValid()) - # 4) Add Mesh to the same entity - component = "Mesh" - meshentity.components.append(hydra.add_component(component, entity_id)) + # 3) Make sure Mesh Surface Tag Emitter is disabled + is_enabled = is_component_enabled(meshentity.components[0]) + Report.result(Tests.emitter_disabled_before_mesh, not is_enabled) - # 5) Make sure Mesh Surface Tag Emitter is enabled after adding Mesh - is_enabled = is_component_enabled(meshentity.components[0]) - self.test_success = self.test_success and is_enabled - if is_enabled: - print(f"{component_to_add} is Enabled") - elif not is_enabled: - print(f"{component_to_add} is Disabled. But It should be enabled after adding Mesh") + # 4) Add Mesh to the same entity + component = "Mesh" + meshentity.components.append(hydra.add_component(component, entity_id)) + + # 5) Make sure Mesh Surface Tag Emitter is enabled after adding Mesh + is_enabled = is_component_enabled(meshentity.components[0]) + Report.result(Tests.emitter_enabled_after_mesh, is_enabled) -test = TestMeshSurfaceTagEmitter() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(MeshSurfaceTagEmitter_DependentOnMeshComponent) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py index 83beb5d0c2..d7abc66106 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py @@ -5,75 +5,71 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.math as math -import azlmbr.paths -import azlmbr.surface_data as surface_data - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper +class Tests: + add_surface_tag = ( + "Surface Tag added successfully", + "Failed to add Surface Tag" + ) + remove_surface_tag = ( + "Successfully removed Surface Tag", + "Failed to remove Surface Tag" + ) -class TestMeshSurfaceTagEmitter(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSucessfully", - args=["level"]) +def MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(): + """ + Summary: + An enity with Mesh Tag Emitter and a Mesh is added to the viewport to verify if we are able to + add/remove surface tags. - def run_test(self): - """ - Summary: - An enity with Mesh Tag Emitter and a Mesh is added to the viewport to verify if we are able to - add/remove surface tags. + Expected Behavior: + A new Surface Tag can be added and removed from the component. - Expected Behavior: - A new Surface Tag can be added and removed from the component. + Test Steps: + 1) Open level + 2) Create a new entity with components "Mesh Surface Tag Emitter", "Mesh" + 3) Add/ remove Surface Tags - Test Steps: - 1) Open level - 2) Create a new entity with components "Mesh Surface Tag Emitter", "Mesh" - 3) Add/ remove Surface Tags + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import azlmbr.math as math + import azlmbr.surface_data as surface_data - # 1) Open level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import editor_python_test_tools.hydra_editor_utils as hydra + from editor_python_test_tools.utils import Report + from editor_python_test_tools.utils import TestHelper as helper - # 2) Create a new entity with components "Mesh Surface Tag Emitter", "Mesh" - entity_position = math.Vector3(125.0, 136.0, 32.0) - components_to_add = ["Mesh Surface Tag Emitter", "Mesh"] - entity = hydra.Entity("entity") - entity.create_entity(entity_position, components_to_add) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # 3) Add/ remove Surface Tags - tag = surface_data.SurfaceTag() - tag.SetTag("water") - pte = hydra.get_property_tree(entity.components[0]) - path = "Configuration|Generated Tags" - pte.add_container_item(path, 0, tag) - success = self.wait_for_condition(lambda: pte.get_container_count(path).GetValue() == 1, 5.0) - self.test_success = self.test_success and success - print(f"Added SurfaceTag: container count is {pte.get_container_count(path).GetValue()}") - pte.remove_container_item(path, 0) - success = self.wait_for_condition(lambda: pte.get_container_count(path).GetValue() == 0, 5.0) - self.test_success = self.test_success and success - print(f"Removed SurfaceTag: container count is {pte.get_container_count(path).GetValue()}") + # 2) Create a new entity with components "Mesh Surface Tag Emitter", "Mesh" + entity_position = math.Vector3(125.0, 136.0, 32.0) + components_to_add = ["Mesh Surface Tag Emitter", "Mesh"] + entity = hydra.Entity("entity") + entity.create_entity(entity_position, components_to_add) + + # 3) Add/ remove Surface Tags + tag = surface_data.SurfaceTag() + tag.SetTag("water") + pte = hydra.get_property_tree(entity.components[0]) + path = "Configuration|Generated Tags" + pte.add_container_item(path, 0, tag) + success = helper.wait_for_condition(lambda: pte.get_container_count(path).GetValue() == 1, 5.0) + Report.result(Tests.add_surface_tag, success) + pte.remove_container_item(path, 0) + success = helper.wait_for_condition(lambda: pte.get_container_count(path).GetValue() == 0, 5.0) + Report.result(Tests.remove_surface_tag, success) -test = TestMeshSurfaceTagEmitter() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py index 361dbdaea9..b5739c2386 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py @@ -5,27 +5,30 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.asset as asset -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.math as math +def PhysXColliderSurfaceTagEmitter_E2E_Editor(): + """ + Summary: + Test aspects of the PhysX Collider Surface Tag Emitter Component through the BehaviorContext and the Property + Tree. -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg + :return: None + """ + import os -class TestPhysXColliderSurfaceTagEmitter(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="PhysXColliderSurfaceTagEmitter_E2E_Editor", args=["level"]) + import azlmbr.asset as asset + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.bus as bus + import azlmbr.math as math - def validate_behavior_context(self): + import editor_python_test_tools.hydra_editor_utils as hydra + 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 + + def validate_behavior_context(): # Verify that we can create the component through the BehaviorContext behavior_context_test_success = True test_component = azlmbr.surface_data.SurfaceDataColliderComponent() @@ -38,163 +41,181 @@ class TestPhysXColliderSurfaceTagEmitter(EditorTestHelper): provider_tag2 = azlmbr.surface_data.SurfaceTag('provider_tag2') modifier_tag1 = azlmbr.surface_data.SurfaceTag('modifier_tag1') modifier_tag2 = azlmbr.surface_data.SurfaceTag('modifier_tag2') - behavior_context_test_success = behavior_context_test_success and hydra.get_set_property_test(test_component, - 'providerTags', - [provider_tag1, - provider_tag2]) - behavior_context_test_success = behavior_context_test_success and hydra.get_set_property_test(test_component, - 'modifierTags', - [modifier_tag1, - modifier_tag2]) - self.log(f'SurfaceDataColliderComponent() BehaviorContext test: {behavior_context_test_success}') + behavior_context_test_success = behavior_context_test_success and hydra.get_set_property_test( + test_component, + 'providerTags', + [provider_tag1, + provider_tag2]) + behavior_context_test_success = behavior_context_test_success and hydra.get_set_property_test( + test_component, + 'modifierTags', + [modifier_tag1, + modifier_tag2]) + Report.info(f'SurfaceDataColliderComponent() BehaviorContext test: {behavior_context_test_success}') return behavior_context_test_success - def run_test(self): - """ - Summary: - Test aspects of the PhysX Collider Surface Tag Emitter Component through the BehaviorContext and the Property Tree. + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - :return: None - """ - # Create an empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, + # Verify all of the BehaviorContext API: + behavior_context = ( + "SurfaceDataColliderComponent() Behavior Context tests were successful", + "SurfaceDataColliderComponent() Behavior Context tests failed" + ) + Report.result(behavior_context, validate_behavior_context()) + + # Set up a test environment to validate the PhysX Collider Surface Tag Emitter Component. + # The test environment will consist of the following: + # - a 32 x 32 x 1 box shape that emits a surface with no tags + # - a 32 x 32 x 32 vegetation area that will only place vegetation on surfaces with the 'test' tag + # With this setup, no vegetation will appear until a Surface Tag Emitter either emits new points with + # the correct tag, or modifies points on our box shape to emit the correct tag. + + # Initialize some arbitrary constants for our test + entity_center_point = math.Vector3(512.0, 512.0, 100.0) + invalid_tag = azlmbr.surface_data.SurfaceTag('invalid') + surface_tag = azlmbr.surface_data.SurfaceTag('test') + test_box_size = 32.0 + baseline_surface_height = 1.0 + collider_radius = 4.0 + collider_diameter = collider_radius * 2.0 + + # Set viewport view of area under test, and toggle helpers back on + general.set_current_view_position(512.0, 485.0, 110.0) + general.set_current_view_rotation(-35.0, 0.0, 0.0) + general.toggle_helpers() + + # Create the "flat surface" entity to use as our baseline surface + dynveg.create_surface_entity("Baseline Surface", entity_center_point, 32.0, 32.0, 1.0) + + # Create a new entity with required vegetation area components + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Veg Area", entity_center_point, 32.0, 32.0, 32.0, asset_path) + + # Add a Vegetation Surface Mask Filter component to the spawner entity and set it to include the "test" tag + spawner_entity.add_component("Vegetation Surface Mask Filter") + spawner_entity.get_set_test(3, "Configuration|Inclusion|Surface Tags", [surface_tag]) + + # At this point, there should be 0 instances within our entire veg area + initial_instance_count = ( + "Found no instances as expected with initial setup", + "Unexpected found instances with initial setup" + ) + initial_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(entity_center_point, 16.0, 0), + 5.0) + Report.result(initial_instance_count, initial_success) + + # Create an entity with a PhysX Collider and our PhysX Collider Surface Tag Emitter + collider_entity_created = ( + "Successfully created a Collider entity", + "Failed to create Collider entity" + ) + collider_entity = hydra.Entity("Collider Surface") + collider_entity.create_entity( + entity_center_point, + ["PhysX Collider", "PhysX Collider Surface Tag Emitter"] ) + Report.result(collider_entity_created, collider_entity.id.IsValid()) - # Verify all of the BehaviorContext API: - self.test_success = self.test_success and self.validate_behavior_context() + # Set up the PhysX Collider so that each shape type (sphere, box, capsule) has the same test height. + hydra.get_set_test(collider_entity, 0, "Shape Configuration|Sphere|Radius", collider_radius) + hydra.get_set_test(collider_entity, 0, "Shape Configuration|Box|Dimensions", math.Vector3(collider_diameter, + collider_diameter, + collider_diameter)) + hydra.get_set_test(collider_entity, 0, "Shape Configuration|Capsule|Height", collider_diameter) - # Set up a test environment to validate the PhysX Collider Surface Tag Emitter Component. - # The test environment will consist of the following: - # - a 32 x 32 x 1 box shape that emits a surface with no tags - # - a 32 x 32 x 32 vegetation area that will only place vegetation on surfaces with the 'test' tag - # With this setup, no vegetation will appear until a Surface Tag Emitter either emits new points with - # the correct tag, or modifies points on our box shape to emit the correct tag. - - # Initialize some arbitrary constants for our test - entity_center_point = math.Vector3(512.0, 512.0, 100.0) - invalid_tag = azlmbr.surface_data.SurfaceTag('invalid') - surface_tag = azlmbr.surface_data.SurfaceTag('test') - test_box_size = 32.0 - baseline_surface_height = 1.0 - collider_radius = 4.0 - collider_diameter = collider_radius * 2.0 - - # Set viewport view of area under test, and toggle helpers back on - general.set_current_view_position(512.0, 485.0, 110.0) - general.set_current_view_rotation(-35.0, 0.0, 0.0) - general.toggle_helpers() - - # Create the "flat surface" entity to use as our baseline surface - dynveg.create_surface_entity("Baseline Surface", entity_center_point, 32.0, 32.0, 1.0) - - # Create a new entity with required vegetation area components - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Veg Area", entity_center_point, 32.0, 32.0, 32.0, asset_path) - - # Add a Vegetation Surface Mask Filter component to the spawner entity and set it to include the "test" tag - spawner_entity.add_component("Vegetation Surface Mask Filter") - spawner_entity.get_set_test(3, "Configuration|Inclusion|Surface Tags", [surface_tag]) - - # At this point, there should be 0 instances within our entire veg area - initial_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(entity_center_point, 16.0, 0), - 5.0) - self.test_success = self.test_success and initial_success - - # Create an entity with a PhysX Collider and our PhysX Collider Surface Tag Emitter - collider_entity = hydra.Entity("Collider Surface") - collider_entity.create_entity( - entity_center_point, - ["PhysX Collider", "PhysX Collider Surface Tag Emitter"] - ) - if collider_entity.id.IsValid(): - self.log(f"'{collider_entity.name}' created") - - # Set up the PhysX Collider so that each shape type (sphere, box, capsule) has the same test height. - hydra.get_set_test(collider_entity, 0, "Shape Configuration|Sphere|Radius", collider_radius) - hydra.get_set_test(collider_entity, 0, "Shape Configuration|Box|Dimensions", math.Vector3(collider_diameter, - collider_diameter, - collider_diameter)) - hydra.get_set_test(collider_entity, 0, "Shape Configuration|Capsule|Height", collider_diameter) - - # Run through each collider shape type (sphere, box, capsule) and verify the surface generation - # and surface modification of the PhysX Collision Surface Tag Emitter Component. - for collider_shape in range(0, 3): - hydra.get_set_test(collider_entity, 0, "Shape Configuration|Shape", collider_shape) - - # Test: Generate a new surface on the collider. - # There should be one instance at the very top of the collider sphere, and none on the baseline surface - # (We use a small query box to only check for one placed instance point) - hydra.get_set_test(collider_entity, 1, "Configuration|Generated Tags", [surface_tag]) - hydra.get_set_test(collider_entity, 1, "Configuration|Extended Tags", [invalid_tag]) - top_point = math.Vector3(entity_center_point.x, entity_center_point.y, entity_center_point.z + - collider_radius) - baseline_surface_point = math.Vector3(entity_center_point.x, entity_center_point.y, entity_center_point.z + - (baseline_surface_height / 2.0)) - top_point_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 1), 5.0) - self.test_success = self.test_success and top_point_success - baseline_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point, - 0.25, 0), 5.0) - self.test_success = self.test_success and baseline_success - - # Test: Modify an existing surface inside the collider. - # There should be no instances at the very top of the collider sphere, and one on the baseline surface - # within our query box. - # (We use a small query box to only check for one placed instance point) - hydra.get_set_test(collider_entity, 1, "Configuration|Generated Tags", [invalid_tag]) - hydra.get_set_test(collider_entity, 1, "Configuration|Extended Tags", [surface_tag]) - top_point_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 0), 5.0) - self.test_success = self.test_success and top_point_success - baseline_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point, - 0.25, 1), 5.0) - self.test_success = self.test_success and baseline_success - - # Setup collider entity with a PhysX Mesh - test_physx_mesh_asset_id = asset.AssetCatalogRequestBus( - bus.Broadcast, "GetAssetIdByPath", os.path.join("levels", "physics", - "Material_PerFaceMaterialGetsCorrectMaterial", - "test.pxmesh"), math.Uuid(), False) - - # Remove/re-add component due to LYN-5496 - collider_entity.remove_component("PhysX Collider") - collider_entity.add_component("PhysX Collider") - self.wait_for_condition(lambda: editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', - collider_entity.components[1]), 5.0) - hydra.get_set_test(collider_entity, 1, "Shape Configuration|Shape", 7) - hydra.get_set_test(collider_entity, 1, "Shape Configuration|Asset|PhysX Mesh", test_physx_mesh_asset_id) - - # Set the asset scale to match the test heights of the shapes tested - asset_scale = math.Vector3(1.0, 1.0, 9.0) - collider_entity.get_set_test(1, "Shape Configuration|Asset|Configuration|Asset Scale", asset_scale) + # Run through each collider shape type (sphere, box, capsule) and verify the surface generation + # and surface modification of the PhysX Collision Surface Tag Emitter Component. + for collider_shape in range(0, 3): + collider_shapes = {0: "Sphere", 1: "Box", 2: "Capsule"} + hydra.get_set_test(collider_entity, 0, "Shape Configuration|Shape", collider_shape) # Test: Generate a new surface on the collider. - # There should be one instance at the very top of the collider mesh, and none on the baseline surface + # There should be one instance at the very top of the collider sphere, and none on the baseline surface # (We use a small query box to only check for one placed instance point) - self.log("Starting PhysX Mesh Collider Test") - hydra.get_set_test(collider_entity, 0, "Configuration|Generated Tags", [surface_tag]) - hydra.get_set_test(collider_entity, 0, "Configuration|Extended Tags", [invalid_tag]) - top_point_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 1), 5.0) - self.test_success = self.test_success and top_point_success - baseline_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point, - 0.25, 0), 5.0) - self.test_success = self.test_success and baseline_success + on_collider_top_point_count = ( + f"Expected number of instances found on the top point for {collider_shapes[collider_shape]} shape", + f"Found an unexpected number of instances on the top point for {collider_shapes[collider_shape]} shape" + ) + on_collider_baseline_count = ( + f"Expected number of instances found on the baseline point for {collider_shapes[collider_shape]} shape", + f"Found an unexpected number of instances on the baseline point for {collider_shapes[collider_shape]} shape" + ) + hydra.get_set_test(collider_entity, 1, "Configuration|Generated Tags", [surface_tag]) + hydra.get_set_test(collider_entity, 1, "Configuration|Extended Tags", [invalid_tag]) + top_point = math.Vector3(entity_center_point.x, entity_center_point.y, entity_center_point.z + + collider_radius) + baseline_surface_point = math.Vector3(entity_center_point.x, entity_center_point.y, entity_center_point.z + + (baseline_surface_height / 2.0)) + top_point_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 1), 5.0) + Report.result(on_collider_top_point_count, top_point_success) + baseline_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point, + 0.25, 0), 5.0) + Report.result(on_collider_baseline_count, baseline_success) # Test: Modify an existing surface inside the collider. - # There should be no instances at the very top of the collider mesh, and none on the baseline surface within - # our query box as PhysX meshes are treated as hollow shells, not solid volumes. + # There should be no instances at the very top of the collider sphere, and one on the baseline surface + # within our query box. # (We use a small query box to only check for one placed instance point) - hydra.get_set_test(collider_entity, 0, "Configuration|Generated Tags", [invalid_tag]) - hydra.get_set_test(collider_entity, 0, "Configuration|Extended Tags", [surface_tag]) - top_point_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 0), 5.0) - self.test_success = self.test_success and top_point_success - baseline_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point, - 0.25, 0), 5.0) - self.test_success = self.test_success and baseline_success + hydra.get_set_test(collider_entity, 1, "Configuration|Generated Tags", [invalid_tag]) + hydra.get_set_test(collider_entity, 1, "Configuration|Extended Tags", [surface_tag]) + top_point_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 0), 5.0) + Report.result(on_collider_top_point_count, top_point_success) + baseline_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point, + 0.25, 1), 5.0) + Report.result(on_collider_baseline_count, baseline_success) + + # Setup collider entity with a PhysX Mesh + test_physx_mesh_asset_id = asset.AssetCatalogRequestBus( + bus.Broadcast, "GetAssetIdByPath", os.path.join("levels", "physics", + "Material_PerFaceMaterialGetsCorrectMaterial", + "test.pxmesh"), math.Uuid(), False) + collider_entity.remove_component("PhysX Collider") + collider_entity.add_component("PhysX Collider") + helper.wait_for_condition(lambda: editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', + collider_entity.components[1]), 5.0) + hydra.get_set_test(collider_entity, 1, "Shape Configuration|Shape", 7) + hydra.get_set_test(collider_entity, 1, "Shape Configuration|Asset|PhysX Mesh", test_physx_mesh_asset_id) + + # Set the asset scale to match the test heights of the shapes tested + asset_scale = math.Vector3(1.0, 1.0, 9.0) + collider_entity.get_set_test(1, "Shape Configuration|Asset|Configuration|Asset Scale", asset_scale) + + # Test: Generate a new surface on the collider. + # There should be one instance at the very top of the collider mesh, and none on the baseline surface + # (We use a small query box to only check for one placed instance point) + Report.info("Starting PhysX Mesh Collider Test") + on_collider_top_point_count = ( + f"Expected number of instances found on the top point for a PhysX Mesh", + f"Found an unexpected number of instances on the top point for a PhysX Mesh" + ) + on_collider_baseline_count = ( + f"Expected number of instances found on the baseline point for a PhysX Mesh", + f"Found an unexpected number of instances on the baseline point for a PhysX Mesh" + ) + hydra.get_set_test(collider_entity, 0, "Configuration|Generated Tags", [surface_tag]) + hydra.get_set_test(collider_entity, 0, "Configuration|Extended Tags", [invalid_tag]) + top_point_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 1), 5.0) + Report.result(on_collider_top_point_count, top_point_success) + baseline_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point, + 0.25, 0), 5.0) + Report.result(on_collider_baseline_count, baseline_success) + + # Test: Modify an existing surface inside the collider. + # There should be no instances at the very top of the collider mesh, and none on the baseline surface within + # our query box as PhysX meshes are treated as hollow shells, not solid volumes. + # (We use a small query box to only check for one placed instance point) + hydra.get_set_test(collider_entity, 0, "Configuration|Generated Tags", [invalid_tag]) + hydra.get_set_test(collider_entity, 0, "Configuration|Extended Tags", [surface_tag]) + top_point_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 0), 5.0) + Report.result(on_collider_top_point_count, top_point_success) + baseline_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point, + 0.25, 0), 5.0) + Report.result(on_collider_baseline_count, baseline_success) -test = TestPhysXColliderSurfaceTagEmitter() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(PhysXColliderSurfaceTagEmitter_E2E_Editor) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py index a55e88488c..5a3ee70d22 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py @@ -5,136 +5,133 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.bus as bus -import azlmbr.legacy.general as general -import azlmbr.editor as editor -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + initial_instance_count = ( + "Initial instance count is as expected", + "Found an unexpected number of initial instances" + ) + autosnap_enabled_instance_count = ( + "Found the expected number of instances with Auto Snap to Surface enabled", + "Found an unexpected number of instances with Auto Snap to Surface enabled" + ) + autosnap_disabled_instance_count = ( + "Found the expected number of instances with Auto Snap to Surface disabled", + "Found an unexpected number of instances with Auto Snap to Surface disabled" + ) -class TestPositionModifierAutoSnapToSurface(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="PositionModifier_AutoSnapToSurface", args=["level"]) +def PositionModifier_AutoSnapToSurfaceWorks(): + """ + Summary: + Instance spawner is setup to plant on a spherical mesh. Offsets are set on the x-axis, and checks are performed + to ensure instances plant where expected depending on the toggle setting. - def run_test(self): - """ - Summary: - Instance spawner is setup to plant on a spherical mesh. Offsets are set on the x-axis, and checks are performed - to ensure instances plant where expected depending on the toggle setting. + Expected Behavior: + Offset instances snap to the expected surface when Auto Snap to Surface is enabled, and offset away from surface + when it is disabled. - Expected Behavior: - Offset instances snap to the expected surface when Auto Snap to Surface is enabled, and offset away from surface - when it is disabled. + Test Steps: + 1) Open a simple level + 2) Create a new entity with required vegetation area components and a Position Modifier + 3) Create a spherical planting surface + 4) Verify initial instance counts pre-filter + 5) Create a child entity of the spawner entity with a Constant Gradient component and pin to spawner + 6) Set the Position Modifier offset to 5 on the x-axis + 7) Validate instance counts on top of and inside the sphere mesh with Auto Snap to Surface enabled + 8) Validate instance counts on top of and inside the sphere mesh with Auto Snap to Surface disabled - Test Steps: - 1) Create a new, temporary level - 2) Create a new entity with required vegetation area components and a Position Modifier - 3) Create a spherical planting surface - 4) Verify initial instance counts pre-filter - 5) Create a child entity of the spawner entity with a Constant Gradient component and pin to spawner - 6) Set the Position Modifier offset to 5 on the x-axis - 7) Validate instance counts on top of and inside the sphere mesh with Auto Snap to Surface enabled - 8) Validate instance counts on top of and inside the sphere mesh with Auto Snap to Surface disabled + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ - position_modifier_paths = ['Configuration|Position X|Range Min', 'Configuration|Position X|Range Max', - 'Configuration|Position Y|Range Min', 'Configuration|Position Y|Range Max', - 'Configuration|Position Z|Range Min', 'Configuration|Position Z|Range Max'] + import os - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import azlmbr.legacy.general as general + import azlmbr.math as math - # Set view of planting area for visual debugging - general.set_current_view_position(512.0, 500.0, 38.0) - general.set_current_view_rotation(-20.0, 0.0, 0.0) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # 2) Create a new entity with required vegetation area components and a Position Modifier - spawner_center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) + position_modifier_paths = ['Configuration|Position X|Range Min', 'Configuration|Position X|Range Max', + 'Configuration|Position Y|Range Min', 'Configuration|Position Y|Range Max', + 'Configuration|Position Z|Range Min', 'Configuration|Position Z|Range Max'] - # Add a Vegetation Position Modifier and set offset values to 0 - spawner_entity.add_component("Vegetation Position Modifier") - for path in position_modifier_paths: - spawner_entity.get_set_test(3, path, 0) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # 3) Create a spherical planting surface and a flat surface - flat_entity = dynveg.create_surface_entity("Flat Surface", spawner_center_point, 32.0, 32.0, 1.0) - hill_entity = dynveg.create_mesh_surface_entity_with_slopes("Planting Surface", spawner_center_point, 5.0) + # Set view of planting area for visual debugging + general.set_current_view_position(512.0, 500.0, 38.0) + general.set_current_view_rotation(-20.0, 0.0, 0.0) - # Disable the Flat Surface Box Shape component, and temporarily ignore initial instance counts due to LYN-2245 - editor.EditorComponentAPIBus(bus.Broadcast, 'DisableComponents', [flat_entity.components[0]]) - """ - # 4) Verify initial instance counts pre-filter - num_expected = 121 - spawner_success = self.wait_for_condition( - lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) - self.test_success = self.test_success and spawner_success - """ + # 2) Create a new entity with required vegetation area components and a Position Modifier + spawner_center_point = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, + asset_path) - # 5) Create a child entity of the spawner entity with a Constant Gradient component and pin to spawner - components_to_add = ["Constant Gradient"] - gradient_entity = hydra.Entity("Gradient Entity") - gradient_entity.create_entity(spawner_center_point, components_to_add, parent_id=spawner_entity.id) + # Add a Vegetation Position Modifier and set offset values to 0 + spawner_entity.add_component("Vegetation Position Modifier") + for path in position_modifier_paths: + spawner_entity.get_set_test(3, path, 0) - # Pin the Constant Gradient to the X axis of the spawner's Position Modifier component - spawner_entity.get_set_test(3, 'Configuration|Position X|Gradient|Gradient Entity Id', gradient_entity.id) + # 3) Create a spherical planting surface + hill_entity = dynveg.create_mesh_surface_entity_with_slopes("Planting Surface", spawner_center_point, 5.0) - # 6) Set the Position Modifier offset to 2.5 on the x-axis - spawner_entity.get_set_test(3, position_modifier_paths[0], 2.5) - spawner_entity.get_set_test(3, position_modifier_paths[1], 2.5) + # 4) Verify initial instance counts pre-filter + num_expected = 29 + spawner_success = helper.wait_for_condition( + lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.result(Tests.initial_instance_count, spawner_success) - # 7) Validate instance count at the top of the sphere mesh and inside the sphere mesh while Auto Snap to Surface - # is enabled - top_point = math.Vector3(512.0, 512.0, 37.0) - inside_point = math.Vector3(512.0, 512.0, 35.0) - radius = 0.5 - num_expected = 1 - self.log(f"Checking for instances in a {radius * 2}m area at {top_point.ToString()}") - top_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, radius, num_expected), - 5.0) - self.test_success = top_success and self.test_success - num_expected = 0 - self.log(f"Checking for instances in a {radius * 2}m area at {inside_point.ToString()}") - inside_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(inside_point, radius, - num_expected), 5.0) - self.test_success = inside_success and self.test_success + # 5) Create a child entity of the spawner entity with a Constant Gradient component and pin to spawner + components_to_add = ["Constant Gradient"] + gradient_entity = hydra.Entity("Gradient Entity") + gradient_entity.create_entity(spawner_center_point, components_to_add, parent_id=spawner_entity.id) - # 8) Toggle off Auto Snap to Surface. Instances should now plant inside the sphere and no longer on top - spawner_entity.get_set_test(3, "Configuration|Auto Snap To Surface", False) - num_expected = 0 - self.log(f"Checking for instances in a {radius * 2}m area at {top_point.ToString()}") - top_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, radius, num_expected), - 5.0) - self.test_success = top_success and self.test_success - num_expected = 1 - self.log(f"Checking for instances in a {radius * 2}m area at {inside_point.ToString()}") - inside_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(inside_point, radius, - num_expected), 5.0) - self.test_success = inside_success and self.test_success + # Pin the Constant Gradient to the X axis of the spawner's Position Modifier component + spawner_entity.get_set_test(3, 'Configuration|Position X|Gradient|Gradient Entity Id', gradient_entity.id) + + # 6) Set the Position Modifier offset to 2.5 on the x-axis + spawner_entity.get_set_test(3, position_modifier_paths[0], 2.5) + spawner_entity.get_set_test(3, position_modifier_paths[1], 2.5) + + # 7) Validate instance count at the top of the sphere mesh and inside the sphere mesh while Auto Snap to Surface + # is enabled + top_point = math.Vector3(512.0, 512.0, 37.0) + inside_point = math.Vector3(512.0, 512.0, 35.0) + radius = 0.5 + num_expected = 1 + Report.info(f"Checking for instances in a {radius * 2}m area at {top_point.ToString()}") + top_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, radius, num_expected), + 5.0) + num_expected = 0 + Report.info(f"Checking for instances in a {radius * 2}m area at {inside_point.ToString()}") + inside_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(inside_point, radius, + num_expected), 5.0) + Report.result(Tests.autosnap_enabled_instance_count, top_success and inside_success) + + # 8) Toggle off Auto Snap to Surface. Instances should now plant inside the sphere and no longer on top + spawner_entity.get_set_test(3, "Configuration|Auto Snap To Surface", False) + num_expected = 0 + Report.info(f"Checking for instances in a {radius * 2}m area at {top_point.ToString()}") + top_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, radius, num_expected), + 5.0) + num_expected = 1 + Report.info(f"Checking for instances in a {radius * 2}m area at {inside_point.ToString()}") + inside_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(inside_point, radius, + num_expected), 5.0) + Report.result(Tests.autosnap_disabled_instance_count, top_success and inside_success) -test = TestPositionModifierAutoSnapToSurface() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(PositionModifier_AutoSnapToSurfaceWorks) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py index 9597f79339..c4fa91f886 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py @@ -5,159 +5,164 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import random -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + initial_instance_count = ( + "Initial instance count is as expected", + "Found an unexpected number of initial instances" + ) + position_offset = ( + "Found instances at all expected locations with Position Modifier offsets configured", + "Failed to find all expected instances at all locations with Position Modifier offsets configured" + ) + position_offset_overrides = ( + "Found instances at all expected locations with Position Modifier offset overrides configured", + "Failed to find all expected instances at all locations with Position Modifier offset overrides configured" + ) -class TestPositionModifierComponentAndOverrides(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="PositionModifierComponentAndOverrides_InstanceOffset", args=["level"]) +def PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(): + """ + Summary: Range Min/Max in the Vegetation Position Modifier component and component overrides can be set for all + axes, and functions as expected when fed a gradient signal. - def run_test(self): - """ - Summary: Range Min/Max in the Vegetation Position Modifier component and component overrides can be set for all - axes, and functions as expected when fed a gradient signal. + Expected Behavior: Instances are offset by the specified amount. - Expected Behavior: Instances are offset by the specified amount. + Test Steps: + 1) Open an existing level + 2) Spawner area is setup with all necessary components + 3) Surface for planting is created + 4) Initial instance count validation pre-filter is performed + 5) An entity with a Constant Gradient of 1 is added as a child to the spawner entity, and pinned to the Position + Modifier Gradient Entity Id fields + 6) Sector size is adjusted on a Vegetation System Settings component to allow for offset instances to not fall + outside of the queried sector + 7) Random offsets are set for each axis of the Position Modifier component, and instance counts are validated + 8) Overrides are enabled on the Position Modifier component + 9) Random offsets are set for each axis of the descriptor's Position Modifier overrides, and instance counts + are validated - Test Steps: - 1) New test level is created - 2) Spawner area is setup with all necessary components - 3) Surface for planting is created - 4) Initial instance count validation pre-filter is performed - 5) An entity with a Constant Gradient of 1 is added as a child to the spawner entity, and pinned to the Position - Modifier Gradient Entity Id fields - 6) Sector size is adjusted on a Vegetation System Settings component to allow for offset instances to not fall - outside of the queried sector - 7) Random offsets are set for each axis of the Position Modifier component, and instance counts are validated - 8) Overrides are enabled on the Position Modifier component - 9) Random offsets are set for each axis of the descriptor's Position Modifier overrides, and instance counts - are validated + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ - position_modifier_paths = ['Configuration|Position X|Range Min', 'Configuration|Position X|Range Max', - 'Configuration|Position Y|Range Min', 'Configuration|Position Y|Range Max', - 'Configuration|Position Z|Range Min', 'Configuration|Position Z|Range Max'] + import os + import random - override_position_modifier_paths = ['Configuration|Embedded Assets|[0]|Position Modifier|Min X', - 'Configuration|Embedded Assets|[0]|Position Modifier|Max X', - 'Configuration|Embedded Assets|[0]|Position Modifier|Min Y', - 'Configuration|Embedded Assets|[0]|Position Modifier|Max Y', - 'Configuration|Embedded Assets|[0]|Position Modifier|Min Z', - 'Configuration|Embedded Assets|[0]|Position Modifier|Max Z'] + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.bus as bus + import azlmbr.math as math - def generate_random_offset_list(): - offset_list = [] - while len(offset_list) < 10: - offset = round(random.uniform(-8.0, 8.0), 2) - if not -1.0 <= offset <= 1.0: - offset_list.append(offset) - print("List of values to test against = " + str(offset_list)) - return offset_list + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - def set_offset_and_verify_instance_counts(offset_to_test, center, is_override=False): - print(f"Starting test with an offset of {offset_to_test}") - # Set min/max values to the offset value - if not is_override: - for path in position_modifier_paths: - spawner_entity.get_set_test(3, path, offset_to_test) - else: - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Position Modifier|Override Enabled", - True) - for path in override_position_modifier_paths: - spawner_entity.get_set_test(2, path, offset_to_test) - center_point = math.Vector3(center.x + offset_to_test, center.y + offset_to_test, center.z + offset_to_test) - radius = 0.5 - print(f"Querying for instances in a {radius * 2}m area around {center_point.ToString()}") - offset_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(center_point, radius, 1), 5.0) - offset_success2 = self.wait_for_condition(lambda: dynveg.validate_instance_count(center, radius, 0), 5.0) - self.test_success = offset_success and offset_success2 and self.test_success + position_modifier_paths = ['Configuration|Position X|Range Min', 'Configuration|Position X|Range Max', + 'Configuration|Position Y|Range Min', 'Configuration|Position Y|Range Max', + 'Configuration|Position Z|Range Min', 'Configuration|Position Z|Range Max'] - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + override_position_modifier_paths = ['Configuration|Embedded Assets|[0]|Position Modifier|Min X', + 'Configuration|Embedded Assets|[0]|Position Modifier|Max X', + 'Configuration|Embedded Assets|[0]|Position Modifier|Min Y', + 'Configuration|Embedded Assets|[0]|Position Modifier|Max Y', + 'Configuration|Embedded Assets|[0]|Position Modifier|Min Z', + 'Configuration|Embedded Assets|[0]|Position Modifier|Max Z'] - # Set view of planting area for visual debugging - general.set_current_view_position(16.0, -5.0, 32.0) + def generate_random_offset_list(): + offset_list = [] + while len(offset_list) < 10: + offset = round(random.uniform(-8.0, 8.0), 2) + if not -1.0 <= offset <= 1.0: + offset_list.append(offset) + Report.info("List of values to test against = " + str(offset_list)) + return offset_list - # 2) Create a new entity with required vegetation area components - spawner_center_point = math.Vector3(16.0, 16.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 1.0, 1.0, 1.0, asset_path) + def set_offset_and_verify_instance_counts(offset_to_test, center, is_override=False): + Report.info(f"Starting test with an offset of {offset_to_test}") + # Set min/max values to the offset value + if not is_override: + for path in position_modifier_paths: + spawner_entity.get_set_test(3, path, offset_to_test) + else: + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Position Modifier|Override Enabled", + True) + for path in override_position_modifier_paths: + spawner_entity.get_set_test(2, path, offset_to_test) + center_point = math.Vector3(center.x + offset_to_test, center.y + offset_to_test, center.z + offset_to_test) + radius = 0.5 + Report.info(f"Querying for instances in a {radius * 2}m area around {center_point.ToString()}") + offset_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(center_point, radius, 1), 5.0) + offset_success2 = helper.wait_for_condition(lambda: dynveg.validate_instance_count(center, radius, 0), 5.0) + return offset_success and offset_success2 - # Add a Vegetation Position Modifier and set offset values to 0 - spawner_entity.add_component("Vegetation Position Modifier") - for path in position_modifier_paths: - spawner_entity.get_set_test(3, path, 0) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # 3) Add flat surface to plant on - dynveg.create_surface_entity("Planting Surface", spawner_center_point, 32.0, 32.0, 0.0) + # Set view of planting area for visual debugging + general.set_current_view_position(16.0, -5.0, 32.0) - # 4) Verify initial instance counts pre-filter - num_expected = 1 # Single instance planted - spawner_success = self.wait_for_condition( - lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) - self.test_success = self.test_success and spawner_success + # 2) Create a new entity with required vegetation area components + spawner_center_point = math.Vector3(16.0, 16.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 1.0, 1.0, 1.0, asset_path) - # 5) Create a child entity of the spawner entity with a Constant Gradient component - components_to_add = ["Constant Gradient"] - gradient_entity = hydra.Entity("Gradient Entity") - gradient_entity.create_entity(spawner_center_point, components_to_add, parent_id=spawner_entity.id) + # Add a Vegetation Position Modifier and set offset values to 0 + spawner_entity.add_component("Vegetation Position Modifier") + for path in position_modifier_paths: + spawner_entity.get_set_test(3, path, 0) - # Pin the Constant Gradient to each axis of the Position Modifier - position_modifier_gradient_paths = ['Configuration|Position X|Gradient|Gradient Entity Id', - 'Configuration|Position Y|Gradient|Gradient Entity Id', - 'Configuration|Position Z|Gradient|Gradient Entity Id'] - for path in position_modifier_gradient_paths: - spawner_entity.get_set_test(3, path, gradient_entity.id) + # 3) Add flat surface to plant on + dynveg.create_surface_entity("Planting Surface", spawner_center_point, 32.0, 32.0, 0.0) - # 6) Add a Vegetation System Settings Level component and change sector size to 32 sq meters so instances can - # offset to a greater range and still be validated - veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, - "Configuration|Area System Settings|Sector Size In Meters", 32) - sector_size = hydra.get_component_property_value(veg_system_settings_component, - "Configuration|Area System Settings|Sector Size In Meters") - self.test_success = (sector_size == 32) and self.test_success + # 4) Verify initial instance counts pre-filter + num_expected = 1 # Single instance planted + spawner_success = helper.wait_for_condition( + lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.result(Tests.initial_instance_count, spawner_success) - # 7) Set offsets on all axes and verify instance counts - offsets_to_test = generate_random_offset_list() - for offset in offsets_to_test: - if self.test_success: - set_offset_and_verify_instance_counts(offset, spawner_center_point) + # 5) Create a child entity of the spawner entity with a Constant Gradient component + components_to_add = ["Constant Gradient"] + gradient_entity = hydra.Entity("Gradient Entity") + gradient_entity.create_entity(spawner_center_point, components_to_add, parent_id=spawner_entity.id) - # 8) Toggle on allow overrides on the Position Modifier Component - spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) + # Pin the Constant Gradient to each axis of the Position Modifier + position_modifier_gradient_paths = ['Configuration|Position X|Gradient|Gradient Entity Id', + 'Configuration|Position Y|Gradient|Gradient Entity Id', + 'Configuration|Position Z|Gradient|Gradient Entity Id'] + for path in position_modifier_gradient_paths: + spawner_entity.get_set_test(3, path, gradient_entity.id) - # 9) Set offsets on all axes on descriptor overrides and verify instance counts - for offset in offsets_to_test: - if self.test_success: - set_offset_and_verify_instance_counts(offset, spawner_center_point, is_override=True) + # 6) Add a Vegetation System Settings Level component and change sector size to 32 sq meters so instances can + # offset to a greater range and still be validated + veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, + "Configuration|Area System Settings|Sector Size In Meters", 32) + + # 7) Set offsets on all axes and verify instance counts + offsets_to_test = generate_random_offset_list() + success = True + for offset in offsets_to_test: + success = success and set_offset_and_verify_instance_counts(offset, spawner_center_point) + Report.result(Tests.position_offset, success) + + # 8) Toggle on allow overrides on the Position Modifier Component + spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) + + # 9) Set offsets on all axes on descriptor overrides and verify instance counts + success = True + for offset in offsets_to_test: + success = success and set_offset_and_verify_instance_counts(offset, spawner_center_point, is_override=True) + Report.result(Tests.position_offset_overrides, success) -test = TestPositionModifierComponentAndOverrides() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py index b428459032..4d8019bb33 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py @@ -5,139 +5,138 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -""" -C4814460: A level with simple vegetation is created. A child entity with required components is then created, -and pinned to the gradient entity id in Z direction for vegetation entity. The changes in vegetation area are observed. -""" -import os -import sys - -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.legacy.general as general -import azlmbr.math as math -import azlmbr.bus as bus -import azlmbr.areasystem as areasystem - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + gradient_entity_created = ( + "Successfully created new Gradient entity", + "Failed to create Gradient entity" + ) + non_override_rotation_check = ( + "Instances are rotated at expected values after initial setup", + "Found unexpectedly rotated instances after initial setup" + ) + override_rotation_check = ( + "Instances are rotated at expected values after configuring overrides", + "Found unexpectedly rotated instances after configuring overrides" + ) -class TestRotationModifierOverrides_InstancesRotateWithinRange(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="RotationModifierOverrides_InstancesRotateWithinRange", args=["level"]) +def RotationModifierOverrides_InstancesRotateWithinRange(): + """ + Summary: + A level with simple vegetation is created. A child entity with required components is then created, + and pinned to the gradient entity id in Z direction for vegetation entity. The changes in vegetation + area are observed. - def run_test(self): - """ - Summary: - A level with simple vegetation is created. A child entity with required components is then created, - and pinned to the gradient entity id in Z direction for vegetation entity. The changes in vegetation - area are observed. + Expected Behavior: + Vegetation instances all rotate randomly between 0-360 degrees on the Z-axis. - Expected Behavior: - Vegetation instances all rotate randomly between 0-360 degrees on the Z-axis. + Test Steps: + 1) Open a new level + 2) Create vegetation entity and add components + 3) Set properties for vegetation entity + 4) Create new child entity + 5) Pin the child entity to vegetation entity as gradient entity id + 6) Verify rotation without per-item overrides + 7) Verify rotation with per-item overrides - Test Steps: - 1) Create level - 2) Create vegetation entity and add components - 3) Set properties for vegetation entity - 4) Create new child entity - 5) Pin the child entity to vegetation entity as gradient entity id - 6) Verify rotation without per-item overrides - 7) Verify rotation with per-item overrides + Note: + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import os - def get_expected_rotation(min, max, gradient_value): - return min + ((max - min) * gradient_value) + import azlmbr.legacy.general as general + import azlmbr.math as math + import azlmbr.bus as bus + import azlmbr.areasystem as areasystem - def validate_rotation(center, radius, num_expected, rot_degrees_vector): - # Verify that every instance in the given area has the expected rotation. - box = math.Aabb_CreateCenterRadius(center, radius) - instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) - num_found = len(instances) - num_validated = 0 - result = (num_found == num_expected) - print(f'instance count validation: {result} (found={num_found}, expected={num_expected})') - expected_rotation = math.Quaternion() - expected_rotation.SetFromEulerDegrees(rot_degrees_vector) - for instance in instances: - is_close = instance.rotation.IsClose(expected_rotation) - result = result and is_close - if is_close: - num_validated = num_validated + 1 - #else: - # print(f'instance rotation validation: {is_close} (rotation={instance.rotation} expected={expected_rotation})') - print(f'instance rotation validation: {result} (num_validated={num_validated})') - return result + import editor_python_test_tools.hydra_editor_utils as hydra + 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) Create level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) - general.set_current_view_position(512.0, 480.0, 38.0) + def get_expected_rotation(min, max, gradient_value): + return min + ((max - min) * gradient_value) - # 2) Create vegetation entity and add components - entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, asset_path) - spawner_entity.add_component("Vegetation Rotation Modifier") - # Our default vegetation settings places 20 instances per 16 meters, so we expect 20 * 20 total instances. - num_expected = 20 * 20 - # This is technically twice as big as we need, but we want to make sure our query radius is large enough to discover every - # instance we've created. - area_radius = 16.0 + def validate_rotation(center, radius, num_expected, rot_degrees_vector): + # Verify that every instance in the given area has the expected rotation. + box = math.Aabb_CreateCenterRadius(center, radius) + instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) + num_found = len(instances) + num_validated = 0 + result = (num_found == num_expected) + Report.info(f'instance count validation: {result} (found={num_found}, expected={num_expected})') + expected_rotation = math.Quaternion() + expected_rotation.SetFromEulerDegrees(rot_degrees_vector) + for instance in instances: + is_close = instance.rotation.IsClose(expected_rotation) + result = result and is_close + if is_close: + num_validated = num_validated + 1 + Report.info(f'instance rotation validation: {result} (num_validated={num_validated})') + return result - # Create surface to spawn on - dynveg.create_surface_entity("Surface Entity", entity_position, 16.0, 16.0, 1.0) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") + general.set_current_view_position(512.0, 480.0, 38.0) - # 3) Set properties for the rotation override on the descriptor, but don't set "allow overrides" on the rotation modifier yet. - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Rotation Modifier|Override Enabled", True) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Rotation Modifier|Min Z", -70.0) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Rotation Modifier|Max Z", 30.0) + # 2) Create vegetation entity and add components + entity_position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, asset_path) + spawner_entity.add_component("Vegetation Rotation Modifier") + # Our default vegetation settings places 20 instances per 16 meters, so we expect 20 * 20 total instances. + num_expected = 20 * 20 + # This is technically twice as big as we need, but we want to make sure our query radius is large enough to discover + # every instance we've created. + area_radius = 16.0 - # 4) Create new child entity with a constant gradient - constant_gradient_value = 0.25 - gradient_entity = hydra.Entity("Gradient Entity") - gradient_entity.create_entity( - entity_position, - ["Constant Gradient"], - parent_id=spawner_entity.id - ) - if gradient_entity.id.IsValid(): - self.log(f"'{gradient_entity.name}' created") - gradient_entity.get_set_test(0, "Configuration|Value", constant_gradient_value) + # Create surface to spawn on + dynveg.create_surface_entity("Surface Entity", entity_position, 16.0, 16.0, 1.0) - # 5) Pin the child entity to vegetation entity as gradient entity id - spawner_entity.get_set_test(3, "Configuration|Rotation Z|Gradient|Gradient Entity Id", gradient_entity.id) + # 3) Set properties for the rotation override on the descriptor, but don't set "allow overrides" on the rotation + # modifier yet + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Rotation Modifier|Override Enabled", True) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Rotation Modifier|Min Z", -70.0) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Rotation Modifier|Max Z", 30.0) - # 6) Verify that without per-item overrides, the rotation matches the one calculated from the default rotation range. - general.idle_wait(1.0) - rotation_degrees = get_expected_rotation(-180.0, 180.0, constant_gradient_value) - rotation_success = self.wait_for_condition( - lambda: validate_rotation(entity_position, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)), - 5.0) - self.test_success = self.test_success and rotation_success + # 4) Create new child entity with a constant gradient + constant_gradient_value = 0.25 + gradient_entity = hydra.Entity("Gradient Entity") + gradient_entity.create_entity( + entity_position, + ["Constant Gradient"], + parent_id=spawner_entity.id + ) + Report.critical_result(Tests.gradient_entity_created, gradient_entity.id.IsValid()) + gradient_entity.get_set_test(0, "Configuration|Value", constant_gradient_value) - # 7) Verify that with per-item overrides enabled, the rotation matches the one calculated from the override range. - spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) - rotation_degrees = get_expected_rotation(-70.0, 30.0, constant_gradient_value) - rotation_success = self.wait_for_condition( - lambda: validate_rotation(entity_position, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)), - 5.0) - self.test_success = self.test_success and rotation_success + # 5) Pin the child entity to vegetation entity as gradient entity id + spawner_entity.get_set_test(3, "Configuration|Rotation Z|Gradient|Gradient Entity Id", gradient_entity.id) + + # 6) Verify that without per-item overrides, the rotation matches the one calculated from the default rotation range + general.idle_wait(1.0) + rotation_degrees = get_expected_rotation(-180.0, 180.0, constant_gradient_value) + rotation_success = helper.wait_for_condition( + lambda: validate_rotation(entity_position, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)), + 5.0) + Report.result(Tests.non_override_rotation_check, rotation_success) + + # 7) Verify that with per-item overrides enabled, the rotation matches the one calculated from the override range. + spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) + rotation_degrees = get_expected_rotation(-70.0, 30.0, constant_gradient_value) + rotation_success = helper.wait_for_condition( + lambda: validate_rotation(entity_position, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)), + 5.0) + Report.result(Tests.override_rotation_check, rotation_success) -test = TestRotationModifierOverrides_InstancesRotateWithinRange() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(RotationModifierOverrides_InstancesRotateWithinRange) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py index 6b6f66467b..c261415958 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py @@ -5,207 +5,226 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.math as math -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.areasystem as areasystem - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + gradient_entity_created = ( + "Successfully created new Gradient entity", + "Failed to create Gradient entity" + ) + rotation_baseline = ( + "Instances are not rotated after initial setup", + "Unexpectedly found rotated instances after initial setup" + ) + x_axis_rotation_180 = ( + "Instances rotated 180 degrees on X-axis as expected", + "Found unexpected instance rotation on X-axis" + ) + x_axis_rotation_90 = ( + "Instances rotated 90 degrees on X-axis as expected", + "Found unexpected instance rotation on X-axis" + ) + y_axis_rotation_180 = ( + "Instances rotated 180 degrees on Y-axis as expected", + "Found unexpected instance rotation on Y-axis" + ) + y_axis_rotation_90 = ( + "Instances rotated 90 degrees on Y-axis as expected", + "Found unexpected instance rotation on Y-axis" + ) + z_axis_rotation_180 = ( + "Instances rotated 180 degrees on Z-axis as expected", + "Found unexpected instance rotation on Z-axis" + ) + z_axis_rotation_90 = ( + "Instances rotated 90 degrees on Z-axis as expected", + "Found unexpected instance rotation on Z-axis" + ) -class TestRotationModifier_InstancesRotateWithinRange(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="RotationModifier_InstancesRotateWithinRange", args=["level"]) +def RotationModifier_InstancesRotateWithinRange(): + """ + Summary: Range Min/Max in the Vegetation Rotation Modifier component can be set for all axes, + and functions as expected when fed a gradient signal - def run_test(self): - """ - Summary: Range Min/Max in the Vegetation Rotation Modifier component can be set for all axes, - and functions as expected when fed a gradient signal - - Vegetation Entity: Set in the middle of the level it holds a child entity and the following components: - Vegetation Asset List - Box Shape (size: <10, 10, 10>) - Vegetation Layer Spawner - Vegetation Rotation Modifier - Rotation X (gradient: child, Range Min: Variable, Range Max: Variable) - Rotation Y (gradient: child, Range Min: Variable, Range Max: Variable) - Rotation Z (gradient: child, Range Min: Variable, Range Max: Variable) + Vegetation Entity: Set in the middle of the level it holds a child entity and the following components: + Vegetation Asset List + Box Shape (size: <10, 10, 10>) + Vegetation Layer Spawner + Vegetation Rotation Modifier + Rotation X (gradient: child, Range Min: Variable, Range Max: Variable) + Rotation Y (gradient: child, Range Min: Variable, Range Max: Variable) + Rotation Z (gradient: child, Range Min: Variable, Range Max: Variable) - Child Entity: Child to Vegetation Entity has the following components: - Box Shape (size: <10, 10, 10>) - Gradient Transform Modifier - Constant Gradient + Child Entity: Child to Vegetation Entity has the following components: + Box Shape (size: <10, 10, 10>) + Gradient Transform Modifier + Constant Gradient - Expected Behavior: The vegetation area adjusts rotation based on the Constant Gradient component - and the min and max values for each component. Min max of each axis is checked + Expected Behavior: The vegetation area adjusts rotation based on the Constant Gradient component + and the min and max values for each component. Min max of each axis is checked - Test Steps: - 1) Create level - 2) Set up vegetation entities - 3) X-axis Check - 4) Y-axis Check - 5) Z-axis Check + Test Steps: + 1) Open a new level + 2) Set up vegetation entities + 3) X-axis Check + 4) Y-axis Check + 5) Z-axis Check - Note: - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + Note: + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - :return: None - """ + :return: None + """ - # Test Constants - LEVEL_CENTER = math.Vector3(512.0, 512.0, 32.0) - constant_gradient_value = 0.15 + import os - # Helper Functions - def change_range_max(axis, value): - spawner_entity.get_set_test(3, f"Configuration|Rotation {axis}|Range Max", value) + import azlmbr.math as math + import azlmbr.legacy.general as general + import azlmbr.bus as bus + import azlmbr.areasystem as areasystem - def change_range_min(axis, value): - spawner_entity.get_set_test(3, f"Configuration|Rotation {axis}|Range Min", value) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - def get_expected_rotation(min, max, gradient_value): - return min + ((max - min) * gradient_value) + # Test Constants + LEVEL_CENTER = math.Vector3(512.0, 512.0, 32.0) + constant_gradient_value = 0.15 - def validate_rotation(center, radius, num_expected, rot_degrees_vector): - # Verify that every instance in the given area has the expected rotation. - box = math.Aabb_CreateCenterRadius(center, radius) - instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) - num_found = len(instances) - result = (num_found == num_expected) - print(f'instance count validation: {result} (found={num_found}, expected={num_expected})') - expected_rotation = math.Quaternion() - expected_rotation.SetFromEulerDegrees(rot_degrees_vector) - for instance in instances: - result = result and instance.rotation.IsClose(expected_rotation) - print(f'instance rotation validation: {result} (rotation={instance.rotation} expected={expected_rotation})') - return result + # Helper Functions + def change_range_max(axis, value): + spawner_entity.get_set_test(3, f"Configuration|Rotation {axis}|Range Max", value) + def change_range_min(axis, value): + spawner_entity.get_set_test(3, f"Configuration|Rotation {axis}|Range Min", value) - # Main Script - # 1) Create Level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, + def get_expected_rotation(min, max, gradient_value): + return min + ((max - min) * gradient_value) + + def validate_rotation(center, radius, num_expected, rot_degrees_vector): + # Verify that every instance in the given area has the expected rotation. + box = math.Aabb_CreateCenterRadius(center, radius) + instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) + num_found = len(instances) + result = (num_found == num_expected) + Report.info(f'instance count validation: {result} (found={num_found}, expected={num_expected})') + expected_rotation = math.Quaternion() + expected_rotation.SetFromEulerDegrees(rot_degrees_vector) + for instance in instances: + result = result and instance.rotation.IsClose(expected_rotation) + Report.info(f'instance rotation validation: {result} (rotation={instance.rotation} expected={expected_rotation})') + return result + + # Main Script + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") + general.set_current_view_position(512.0, 480.0, 38.0) + + # 2) Set up vegetation entities + asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Spawner Entity", LEVEL_CENTER, 2.0, 2.0, 2.0, asset_path) + + additional_components = [ + "Vegetation Rotation Modifier" + ] + for component in additional_components: + spawner_entity.add_component(component) + + # Create surface to spawn vegetation on + dynveg.create_surface_entity("Surface Entity", LEVEL_CENTER, 10.0, 10.0, 1.0) + + # Create Gradient Entity + gradient_entity = hydra.Entity("Gradient Entity") + gradient_entity.create_entity( + LEVEL_CENTER, + ["Constant Gradient"], + parent_id=spawner_entity.id + ) + Report.critical_result(Tests.gradient_entity_created, gradient_entity.id.IsValid()) + gradient_entity.get_set_test(0, "Configuration|Value", constant_gradient_value) + + # Vegetation Rotation Modifier + for axis in ["X", "Y", "Z"]: + spawner_entity.get_set_test( + 3, f"Configuration|Rotation {axis}|Gradient|Gradient Entity Id", gradient_entity.id ) - general.set_current_view_position(512.0, 480.0, 38.0) - # 2) Set up vegetation entities - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Spawner Entity", LEVEL_CENTER, 2.0, 2.0, 2.0, asset_path) + # Set up constants used across all the rotation checks - additional_components = [ - "Vegetation Rotation Modifier" - ] - for component in additional_components: - spawner_entity.add_component(component) + # Choose an area large enough to contain all of the instances we spawned. + area_center = LEVEL_CENTER + area_radius = 20.0 + # We're spawning a 2x2 area, which will have 3 rows of 3 instances due to default vegetation system spacing, so + # we should have a total of 9 instances. + num_expected = 9 - # Create surface to spawn vegetation on - dynveg.create_surface_entity("Surface Entity", LEVEL_CENTER, 10.0, 10.0, 1.0) + # 3) X-axis check + # baseline, verify that we initially have no rotation + change_range_min("Z", 0.0) + change_range_max("Z", 0.0) + rotation_success = helper.wait_for_condition( + lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, 0.0, 0.0)), 5.0) + Report.result(Tests.rotation_baseline, rotation_success) - # Create Gradient Entity - gradient_entity = hydra.Entity("Gradient Entity") - gradient_entity.create_entity( - LEVEL_CENTER, - ["Constant Gradient"], - parent_id=spawner_entity.id - ) - if gradient_entity.id.IsValid(): - self.log(f"'{gradient_entity.name}' created") - gradient_entity.get_set_test(0, "Configuration|Value", constant_gradient_value) + # Adjust x-axis range min / max to (-180, 0). + # Because we have a constant gradient of 0.25, our actual rotation should be (min + (max - min) * gradient), + # or (-180 + (0 - -180) * 0.25) + change_range_min("X", -180.0) + rotation_degrees = get_expected_rotation(-180.0, 0.0, constant_gradient_value) + rotation_success = helper.wait_for_condition( + lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(rotation_degrees, 0.0, 0.0)), + 5.0) + Report.result(Tests.x_axis_rotation_180, rotation_success) - # Vegetation Rotation Modifier - for axis in ["X", "Y", "Z"]: - spawner_entity.get_set_test( - 3, f"Configuration|Rotation {axis}|Gradient|Gradient Entity Id", gradient_entity.id - ) + # Set the min / max to (0, 90), with an expected result of (0 + (90 - 0) * 0.25) + change_range_min("X", 0.0) + change_range_max("X", 90.0) + rotation_degrees = get_expected_rotation(0.0, 90.0, constant_gradient_value) + rotation_success = helper.wait_for_condition( + lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(rotation_degrees, 0.0, 0.0)), + 5.0) + Report.result(Tests.x_axis_rotation_90, rotation_success) + change_range_max("X", 0.0) - # Set up constants used across all the rotation checks + # 4) Y-axis check + change_range_min("Y", -180.0) + rotation_degrees = get_expected_rotation(-180.0, 0.0, constant_gradient_value) + rotation_success = helper.wait_for_condition( + lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, rotation_degrees, 0.0)), + 5.0) + Report.result(Tests.y_axis_rotation_180, rotation_success) - # Choose an area large enough to contain all of the instances we spawned. - area_center = LEVEL_CENTER - area_radius = 20.0 - # We're spawning a 2x2 area, which will have 3 rows of 3 instances due to default vegetation system spacing, so - # we should have a total of 9 instances. - num_expected = 9 - - # 3) X-axis check - general.idle_wait(3.0) # Allow mesh to load + change_range_min("Y", 0.0) + change_range_max("Y", 90.0) + rotation_degrees = get_expected_rotation(0.0, 90.0, constant_gradient_value) + rotation_success = helper.wait_for_condition( + lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, rotation_degrees, 0.0)), + 5.0) + Report.result(Tests.y_axis_rotation_90, rotation_success) + change_range_max("Y", 0.0) - # baseline, verify that we initially have no rotation - change_range_min("Z", 0.0) - change_range_max("Z", 0.0) - rotation_success = self.wait_for_condition( - lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, 0.0, 0.0)), - 5.0) - self.test_success = self.test_success and rotation_success + # 5) Z-axis check + change_range_min("Z", -180.0) + rotation_degrees = get_expected_rotation(-180.0, 0.0, constant_gradient_value) + rotation_success = helper.wait_for_condition( + lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)), + 5.0) + Report.result(Tests.z_axis_rotation_180, rotation_success) - # Adjust x-axis range min / max to (-180, 0). - # Because we have a constant gradient of 0.25, our actual rotation should be (min + (max - min) * gradient), - # or (-180 + (0 - -180) * 0.25) - change_range_min("X", -180.0) - rotation_degrees = get_expected_rotation(-180.0, 0.0, constant_gradient_value) - rotation_success = self.wait_for_condition( - lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(rotation_degrees, 0.0, 0.0)), - 5.0) - self.test_success = self.test_success and rotation_success - - # Set the min / max to (0, 90), with an expected result of (0 + (90 - 0) * 0.25) - change_range_min("X", 0.0) - change_range_max("X", 90.0) - rotation_degrees = get_expected_rotation(0.0, 90.0, constant_gradient_value) - rotation_success = self.wait_for_condition( - lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(rotation_degrees, 0.0, 0.0)), - 5.0) - self.test_success = self.test_success and rotation_success - - change_range_max("X", 0.0) - - # 4) Y-axis check - change_range_min("Y", -180.0) - rotation_degrees = get_expected_rotation(-180.0, 0.0, constant_gradient_value) - rotation_success = self.wait_for_condition( - lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, rotation_degrees, 0.0)), - 5.0) - self.test_success = self.test_success and rotation_success - - change_range_min("Y", 0.0) - change_range_max("Y", 90.0) - rotation_degrees = get_expected_rotation(0.0, 90.0, constant_gradient_value) - rotation_success = self.wait_for_condition( - lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, rotation_degrees, 0.0)), - 5.0) - self.test_success = self.test_success and rotation_success - - change_range_max("Y", 0.0) - - # 5) Z-axis check - change_range_min("Z", -180.0) - rotation_degrees = get_expected_rotation(-180.0, 0.0, constant_gradient_value) - rotation_success = self.wait_for_condition( - lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)), - 5.0) - self.test_success = self.test_success and rotation_success - - change_range_min("Z", 0.0) - change_range_max("Z", 90.0) - rotation_degrees = get_expected_rotation(0.0, 90.0, constant_gradient_value) - rotation_success = self.wait_for_condition( - lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)), - 5.0) - self.test_success = self.test_success and rotation_success + change_range_min("Z", 0.0) + change_range_max("Z", 90.0) + rotation_degrees = get_expected_rotation(0.0, 90.0, constant_gradient_value) + rotation_success = helper.wait_for_condition( + lambda: validate_rotation(area_center, area_radius, num_expected, math.Vector3(0.0, 0.0, rotation_degrees)), + 5.0) + Report.result(Tests.z_axis_rotation_90, rotation_success) -test = TestRotationModifier_InstancesRotateWithinRange() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(RotationModifier_InstancesRotateWithinRange) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py index d065c821bf..9f2359ebe2 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py @@ -5,153 +5,155 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.areasystem as areasystem -import azlmbr.bus as bus -import azlmbr.legacy.general as general -import azlmbr.math as math - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg - -# Constants -CLOSE_ENOUGH_THRESHOLD = 0.01 +class Tests: + gradient_entity_created = ( + "Successfully created Gradient entity", + "Failed to create Gradient entity" + ) + scale_values_set = ( + "Scale Min and Scale Max are set to 0.1 and 1.0 in Vegetation Asset List", + "Scale Min and Scale Max are not set to 0.1 and 1.0 in Vegetation Asset List" + ) + instance_count = ( + "Found the expected number of instances", + "Found an unexpected number of instances" + ) + instances_properly_scaled = ( + "All instances scaled within appropriate range", + "Found instances scaled outside of the appropriate range" + ) -class TestScaleModifierOverrides_InstancesProperlyScale(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="ScaleModifierOverrides_InstancesProperlyScale", args=["level"]) +def ScaleModifierOverrides_InstancesProperlyScale(): + """ + Summary: + A level is created, then as simple vegetation area is created. Vegetation Scale Modifier component is + added to the vegetation area. A new child entity is created with Random Noise Gradient Generator, + Gradient Transform Modifier, and Box Shape. Child entity is set as gradient entity id in Vegetation + Scale Modifier, and scale of instances is validated to fall within expected range. - def run_test(self): - """ - Summary: - A level is created, then as simple vegetation area is created. Vegetation Scale Modifier component is - added to the vegetation area. A new child entity is created with Random Noise Gradient Generator, - Gradient Transform Modifier, and Box Shape. Child entity is set as gradient entity id in Vegetation - Scale Modifier, and scale of instances is validated to fall within expected range. + Expected Behavior: + Vegetation instances have random scale between Range Min and Range Max applied. - Expected Behavior: - Vegetation instances have random scale between Range Min and Range Max applied. + Test Steps: + 1) Open an existing level + 2) Create a new entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" + 3) Set a valid mesh asset on the Vegetation Asset List + 4) Add Vegetation Scale Modifier component to the vegetation and set the values + 5) Toggle on Scale Modifier Override and verify Scale Min and Scale Max are set 0.1 and 1.0 + 6) Create a new child entity and add components + 7) Add child entity as gradient entity id in Vegetation Scale Modifier + 8) Validate scale of instances with a few different min/max override values - Test Steps: - 1) Create level - 2) Create a new entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" - 3) Set a valid mesh asset on the Vegetation Asset List - 4) Add Vegetation Scale Modifier component to the vegetation and set the values - 5) Toggle on Scale Modifier Override and verify Scale Min and Scale Max are set 0.1 and 1.0 - 6) Create a new child entity and add components - 7) Add child entity as gradient entity id in Vegetation Scale Modifier - 8) Validate scale of instances with a few different min/max override values + Note: + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import os - def set_and_validate_scale(entity, min_scale, max_scale): - # Set Range Min/Max - entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Scale Modifier|Min", min_scale) - entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Scale Modifier|Max", max_scale) + import azlmbr.areasystem as areasystem + import azlmbr.bus as bus + import azlmbr.legacy.general as general + import azlmbr.math as math - # Clear all areas to force a refresh - general.run_console('veg_debugClearAllAreas') + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # Wait for instances to spawn - num_expected = 20 * 20 - self.test_success = self.test_success and self.wait_for_condition( - lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + # Constants + CLOSE_ENOUGH_THRESHOLD = 0.01 - # Validate scale values of instances - box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', entity.id) - instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) - if len(instances) == num_expected: - for instance in instances: - if min_scale <= instance.scale <= max_scale: - self.log("All instances scaled within appropriate range") - return True - self.log(f"Instance at {instance.position} scale is {instance.scale}. Expected between " - f"{min_scale}/{max_scale}") - return False - self.log(f"Failed to find all instances! Found {len(instances)}, expected {num_expected}.") - return False + def set_and_validate_scale(entity, min_scale, max_scale): + # Set Range Min/Max + entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Scale Modifier|Min", min_scale) + entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Scale Modifier|Max", max_scale) - # 1) Create level and set an appropriate view of spawner area - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, + # Refresh the planted instances + general.run_console("veg_DebugClearAllAreas") + + # Check the initial instance count + num_expected = 20 * 20 + success = helper.wait_for_condition( + lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.critical_result(Tests.instance_count, success) + + # Validate scale values of instances + box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', entity.id) + instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) + for instance in instances: + if min_scale <= instance.scale <= max_scale: + return True + return False + + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") + + general.set_current_view_position(500.49, 498.69, 46.66) + general.set_current_view_rotation(-42.05, 0.00, -36.33) + + # 2) Create a new entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" + entity_position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 10.0, asset_path) + + # Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes + dynveg.create_surface_entity("Surface Entity", entity_position, 20.0, 20.0, 1.0) + hydra.add_level_component("Vegetation Debugger") + + # 4) Add Vegetation Scale Modifier component to the vegetation and set the values + spawner_entity.add_component("Vegetation Scale Modifier") + spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) + + # 5) Toggle on Scale Modifier Override and verify Scale Min and Scale Max are set 0.1 and 1.0 + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Scale Modifier|Override Enabled", True) + scale_min = float( + format( + ( + hydra.get_component_property_value( + spawner_entity.components[2], "Configuration|Embedded Assets|[0]|Scale Modifier|Min" + ) + ), + ".1f", ) - - general.set_current_view_position(500.49, 498.69, 46.66) - general.set_current_view_rotation(-42.05, 0.00, -36.33) - - # 2) Create a new entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" - entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 10.0, asset_path) - - # Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes - dynveg.create_surface_entity("Surface Entity", entity_position, 20.0, 20.0, 1.0) - hydra.add_level_component("Vegetation Debugger") - - # 4) Add Vegetation Scale Modifier component to the vegetation and set the values - spawner_entity.add_component("Vegetation Scale Modifier") - spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) - - # 5) Toggle on Scale Modifier Override and verify Scale Min and Scale Max are set 0.1 and 1.0 - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Scale Modifier|Override Enabled", True) - scale_min = float( - format( - ( - hydra.get_component_property_value( - spawner_entity.components[2], "Configuration|Embedded Assets|[0]|Scale Modifier|Min" - ) - ), - ".1f", - ) + ) + scale_max = float( + format( + ( + hydra.get_component_property_value( + spawner_entity.components[2], "Configuration|Embedded Assets|[0]|Scale Modifier|Max" + ) + ), + ".1f", ) - scale_max = float( - format( - ( - hydra.get_component_property_value( - spawner_entity.components[2], "Configuration|Embedded Assets|[0]|Scale Modifier|Max" - ) - ), - ".1f", - ) - ) - if ((scale_max - 1.0) < CLOSE_ENOUGH_THRESHOLD) and ((scale_min - 0.1) < CLOSE_ENOUGH_THRESHOLD): - self.log("Scale Min and Scale Max are set to 0.1 and 1.0 in Vegetation Asset List") - else: - self.log("Scale Min and Scale Max are not set to 0.1 and 1.0 in Vegetation Asset List") + ) + Report.result(Tests.scale_values_set, ((scale_max - 1.0) < CLOSE_ENOUGH_THRESHOLD) and + ((scale_min - 0.1) < CLOSE_ENOUGH_THRESHOLD)) - # 6) Create a new child entity and add components - gradient_entity = hydra.Entity("Gradient Entity") - gradient_entity.create_entity( - entity_position, - ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"], - parent_id=spawner_entity.id - ) - if gradient_entity.id.IsValid(): - self.log(f"'{gradient_entity.name}' created") + # 6) Create a new child entity and add components + gradient_entity = hydra.Entity("Gradient Entity") + gradient_entity.create_entity( + entity_position, + ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"], + parent_id=spawner_entity.id + ) + Report.result(Tests.gradient_entity_created, gradient_entity.id.IsValid()) - # 7) Add child entity as gradient entity id in Vegetation Scale Modifier - spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", gradient_entity.id) + # 7) Add child entity as gradient entity id in Vegetation Scale Modifier + spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", gradient_entity.id) - # 8) Validate instances are scaled properly via a few different Range Min/Max settings on the override - self.test_success = set_and_validate_scale(spawner_entity, 0.1, 1.0) and self.test_success - self.test_success = set_and_validate_scale(spawner_entity, 2.0, 2.5) and self.test_success - self.test_success = set_and_validate_scale(spawner_entity, 1.0, 5.0) and self.test_success + # 8) Validate instances are scaled properly via a few different Range Min/Max settings on the override + Report.result(Tests.instances_properly_scaled, set_and_validate_scale(spawner_entity, 0.1, 1.0)) + Report.result(Tests.instances_properly_scaled, set_and_validate_scale(spawner_entity, 2.0, 2.5)) + Report.result(Tests.instances_properly_scaled, set_and_validate_scale(spawner_entity, 1.0, 5.0)) -test = TestScaleModifierOverrides_InstancesProperlyScale() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(ScaleModifierOverrides_InstancesProperlyScale) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py index e579495e42..b2fadaa703 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py @@ -5,123 +5,123 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.areasystem as areasystem -import azlmbr.bus as bus -import azlmbr.legacy.general as general -import azlmbr.math as math - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + gradient_entity_created = ( + "Successfully created Gradient entity", + "Failed to create Gradient entity" + ) + instance_count = ( + "Found the expected number of instances", + "Found an unexpected number of instances" + ) + instances_properly_scaled = ( + "All instances scaled within appropriate range", + "Found instances scaled outside of the appropriate range" + ) -class TestScaleModifier_InstancesProperlyScale(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="ScaleModifier_InstancesProperlyScale", args=["level"]) +def ScaleModifier_InstancesProperlyScale(): + """ + Summary: + A New level is created. A New entity is created with components Vegetation Layer Spawner, Vegetation Asset List, + Box Shape and Vegetation Scale Modifier. A New child entity is created with components Random Noise Gradient, + Gradient Transform Modifier, and Box Shape. Pin the Random Noise entity to the Gradient Entity Id field for + the Gradient group. Range Min and Range Max are set to few values and values are validated. Range Min and Range + Max are set to few other values and values are validated. - def run_test(self): - """ - Summary: - A New level is created. A New entity is created with components Vegetation Layer Spawner, Vegetation Asset List, - Box Shape and Vegetation Scale Modifier. A New child entity is created with components Random Noise Gradient, - Gradient Transform Modifier, and Box Shape. Pin the Random Noise entity to the Gradient Entity Id field for - the Gradient group. Range Min and Range Max are set to few values and values are validated. Range Min and Range - Max are set to few other values and values are validated. + Expected Behavior: + Vegetation instances are scaled within Range Min/Range Max. - Expected Behavior: - Vegetation instances are scaled within Range Min/Range Max. + Test Steps: + 1) Open an existing level + 2) Create a new entity with components Vegetation Layer Spawner, Vegetation Asset List, Box Shape and + Vegetation Scale Modifier + 3) Create child entity with components Random Noise Gradient, Gradient Transform Modifier and Box Shape + 4) Pin the Random Noise entity to the Gradient Entity Id field for the Gradient group. + 5) Range Min/Max is set to few different values on the Vegetation Scale Modifier component and + scale of instances is validated - Test Steps: - 1) Create level - 2) Create a new entity with components Vegetation Layer Spawner, Vegetation Asset List, Box Shape and - Vegetation Scale Modifier - 3) Create child entity with components Random Noise Gradient, Gradient Transform Modifier and Box Shape - 4) Pin the Random Noise entity to the Gradient Entity Id field for the Gradient group. - 5) Range Min/Max is set to few different values on the Vegetation Scale Modifier component and - scale of instances is validated + Note: + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import os - def set_and_validate_scale(entity, min_scale, max_scale): - # Set Range Min/Max - entity.get_set_test(3, "Configuration|Range Min", min_scale) - entity.get_set_test(3, "Configuration|Range Max", max_scale) + import azlmbr.areasystem as areasystem + import azlmbr.bus as bus + import azlmbr.legacy.general as general + import azlmbr.math as math - # Clear all areas to force a refresh - general.run_console('veg_debugClearAllAreas') + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # Wait for instances to spawn - num_expected = 20 * 20 - self.test_success = self.test_success and self.wait_for_condition( - lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + def set_and_validate_scale(entity, min_scale, max_scale): + # Set Range Min/Max + entity.get_set_test(3, "Configuration|Range Min", min_scale) + entity.get_set_test(3, "Configuration|Range Max", max_scale) - # Validate scale values of instances - box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', entity.id) - instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) - if len(instances) == num_expected: - for instance in instances: - if min_scale <= instance.scale <= max_scale: - self.log("All instances scaled within appropriate range") - return True - self.log(f"Instance at {instance.position} scale is {instance.scale}. Expected between " - f"{min_scale}/{max_scale}") - return False - self.log(f"Failed to find all instances! Found {len(instances)}, expected {num_expected}.") - return False + # Refresh the planted instances + general.run_console("veg_DebugClearAllAreas") - # 1) Create level and set an appropriate view of spawner area - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + # Check the initial instance count + num_expected = 20 * 20 + success = helper.wait_for_condition( + lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.critical_result(Tests.instance_count, success) - general.set_current_view_position(500.49, 498.69, 46.66) - general.set_current_view_rotation(-42.05, 0.00, -36.33) + # Validate scale values of instances + box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', entity.id) + instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) + for instance in instances: + if min_scale <= instance.scale <= max_scale: + return True + return False - # 2) Create a new entity with components Vegetation Layer Spawner, Vegetation Asset List, Box Shape and - # Vegetation Scale Modifier - entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, - asset_path) - spawner_entity.add_component("Vegetation Scale Modifier") + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes - dynveg.create_surface_entity("Surface Entity", entity_position, 20.0, 20.0, 1.0) - hydra.add_level_component("Vegetation Debugger") + general.set_current_view_position(500.49, 498.69, 46.66) + general.set_current_view_rotation(-42.05, 0.00, -36.33) - # 3) Create child entity with components Random Noise Gradient, Gradient Transform Modifier and Box Shape - gradient_entity = hydra.Entity("Gradient Entity") - gradient_entity.create_entity( - entity_position, - ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"], - parent_id=spawner_entity.id - ) - if gradient_entity.id.IsValid(): - self.log(f"'{gradient_entity.name}' created") + # 2) Create a new entity with components Vegetation Layer Spawner, Vegetation Asset List, Box Shape and + # Vegetation Scale Modifier + entity_position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, + asset_path) + spawner_entity.add_component("Vegetation Scale Modifier") - # 4) Pin the Random Noise entity to the Gradient Entity Id field for the Gradient group. - spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", gradient_entity.id) + # Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes + dynveg.create_surface_entity("Surface Entity", entity_position, 20.0, 20.0, 1.0) + hydra.add_level_component("Vegetation Debugger") - # 5) Set Range Min/Max on the Vegetation Scale Modifier component to diff values, and verify instance scale is - # within bounds - self.test_success = set_and_validate_scale(spawner_entity, 2.0, 4.0) and self.test_success - self.test_success = set_and_validate_scale(spawner_entity, 12.0, 40.0) and self.test_success - self.test_success = set_and_validate_scale(spawner_entity, 0.5, 2.5) and self.test_success + # 3) Create child entity with components Random Noise Gradient, Gradient Transform Modifier and Box Shape + gradient_entity = hydra.Entity("Gradient Entity") + gradient_entity.create_entity( + entity_position, + ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"], + parent_id=spawner_entity.id + ) + Report.critical_result(Tests.gradient_entity_created, gradient_entity.id.IsValid()) + + # 4) Pin the Random Noise entity to the Gradient Entity Id field for the Gradient group. + spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", gradient_entity.id) + + # 5) Set Range Min/Max on the Vegetation Scale Modifier component to diff values, and verify instance scale is + # within bounds + Report.result(Tests.instances_properly_scaled, set_and_validate_scale(spawner_entity, 2.0, 4.0)) + Report.result(Tests.instances_properly_scaled, set_and_validate_scale(spawner_entity, 12.0, 40.0)) + Report.result(Tests.instances_properly_scaled, set_and_validate_scale(spawner_entity, 0.5, 2.5)) -test = TestScaleModifier_InstancesProperlyScale() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(ScaleModifier_InstancesProperlyScale) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py new file mode 100644 index 0000000000..1b9c6aa5ea --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py @@ -0,0 +1,113 @@ +""" +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: + instance_count_in_box_shape = ( + "Only found instances in the configured Box Shape intersection area", + "Found instances outside of the configured Box Shape intersection area" + ) + instance_count_in_cylinder_shape = ( + "Only found instances in the configured Cylinder Shape intersection area", + "Found instances outside of the configured Cylinder Shape intersection area" + ) + preprocess_instance_count = ( + "Found the expected number of instances with preprocessing filter stage", + "Found an unexpected number of instances with preprocessing filter stage" + ) + postprocess_instance_count = ( + "Found the expected number of instances with postprocessing filter stage", + "Found an unexpected number of instances with postprocessing filter stage" + ) + + +def ShapeIntersectionFilter_FilterStageToggle(): + """ + Summary: + Filter Stage toggle affects final vegetation position + + Expected Result: + Vegetation instances plant differently depending on the Filter Stage setting. With PreProcess, some vegetation + instances can appear on slopes outside the filtered values. With PostProcess, vegetation instances only appear on + the correct slope values. + + :return: None + """ + + import os + + import azlmbr.math as math + import azlmbr.legacy.general as general + + import editor_python_test_tools.hydra_editor_utils as hydra + 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 + + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") + + general.set_current_view_position(512.0, 480.0, 38.0) + + # Create basic vegetation entity + position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path) + + # Create Surface for instances to plant on + dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0) + + # Add a Vegetation Shape Intersection Filter to the vegetation area entity + vegetation.add_component("Vegetation Shape Intersection Filter") + + # Create a new entity as a child of the vegetation area entity with Box Shape + box = hydra.Entity("box") + box.create_entity(position, ["Box Shape"]) + box.get_set_test(0, "Box Shape|Box Configuration|Dimensions", math.Vector3(8.0, 8.0, 1.0)) + + # Create a new entity as a child of the vegetation area entity with Cylinder Shape. + cylinder = hydra.Entity("cylinder") + cylinder.create_entity(position, ["Cylinder Shape"]) + cylinder.get_set_test(0, "Cylinder Shape|Cylinder Configuration|Radius", 5.0) + cylinder.get_set_test(0, "Cylinder Shape|Cylinder Configuration|Height", 5.0) + box.set_test_parent_entity(vegetation) + cylinder.set_test_parent_entity(vegetation) + + # On the Shape Intersection Filter component, click the crosshair button, and add child entities one by one + vegetation.get_set_test(3, "Configuration|Shape Entity Id", box.id) + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 8.0, 100), 2.0) + Report.result(Tests.instance_count_in_box_shape, result) + vegetation.get_set_test(3, "Configuration|Shape Entity Id", cylinder.id) + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 5.0, 100), 2.0) + Report.result(Tests.instance_count_in_cylinder_shape, result) + + # Create a new entity as a child of the area entity with Random Noise Gradient, Gradient Transform Modifier, + # and Box Shape component + random_noise = hydra.Entity("random_noise") + random_noise.create_entity(position, ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"]) + random_noise.set_test_parent_entity(vegetation) + + # Add a Vegetation Position Modifier to the vegetation area entity + vegetation.add_component("Vegetation Position Modifier") + + # Pin the Random Noise entity to the Gradient Entity Id field of the Position Modifier's Gradient X + vegetation.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", random_noise.id) + + # Toggle between PreProcess and PostProcess + vegetation.get_set_test(3, "Configuration|Filter Stage", 1) + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 5.0, 117), 2.0) + Report.result(Tests.preprocess_instance_count, result) + vegetation.get_set_test(3, "Configuration|Filter Stage", 2) + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 5.0, 122), 2.0) + Report.result(Tests.postprocess_instance_count, result) + + +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(ShapeIntersectionFilter_FilterStageToggle) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py index 83458fe153..e872b23054 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py @@ -5,124 +5,126 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -""" -C4874094: Shape reference can be replaced/removed -""" -import os -import sys - -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + instance_count_in_box_shape = ( + "Only found instances in the configured Box Shape intersection area", + "Found instances outside of the configured Box Shape intersection area" + ) + instance_count_in_cylinder_shape = ( + "Only found instances in the configured Cylinder Shape intersection area", + "Found instances outside of the configured Cylinder Shape intersection area" + ) + unfiltered_instance_count = ( + "Found instances in the entire Spawner area with no filter set", + "Failed to find all expected instances in the Spawner area with no filter set" + ) -class TestShapeIntersectionFilter(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="ShapeIntersectionFilter_InstancePlanting", args=["level"]) +def ShapeIntersectionFilter_InstancesPlantInAssignedShape(): + """ + Summary: + A spawner area is created with a Vegetation Shape Intersection Filter. 2 different shape entities are created, + pinned to the Shape Intersection Filter, and instance counts are verified. - def run_test(self): - """ - Summary: - A spawner area is created with a Vegetation Shape Intersection Filter. 2 different shape entities are created, - pinned to the Shape Intersection Filter, and instance counts are verified. + Expected Behavior: + The Shape Entity Id reference can be successfully set/updated. Instances spawn only in the specified shape area. - Expected Behavior: - The Shape Entity Id reference can be successfully set/updated. Instances spawn only in the specified shape area. + Test Steps: + 1) Open an existing level, and set view for visual debugging + 2) Create an instance spawner and planting surface + 3) Create child entity with Box Shape + 4) Create child entity with Cylinder Shape + 5) Assign the Intersection Filter to the Box Shape and validate instance counts + 6) Assign the Intersection Filter to the Cylinder Shape and validate instance counts + 7) Remove the shape reference on the Intersection Filter and validate instance counts - Test Steps: - 1) Create a new level, and set view for visual debugging - 2) Create an instance spawner and planting surface - 3) Create child entity with Box Shape - 4) Create child entity with Cylinder Shape - 5) Assign the Intersection Filter to the Box Shape and validate instance counts - 6) Assign the Intersection Filter to the Cylinder Shape and validate instance counts - 7) Remove the shape reference on the Intersection Filter and validate instance counts + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import os - # Set view of planting area for visual debugging - general.set_current_view_position(512.0, 500.0, 38.0) - general.set_current_view_rotation(-20.0, 0.0, 0.0) + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.bus as bus + import azlmbr.math as math - # 2) Create a new entity with required vegetation area components and Vegetation Shape Intersection Filter - center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, - asset_path) - spawner_entity.add_component("Vegetation Shape Intersection Filter") + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # Create a planting surface - dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # 3) Create a child entity with Box Shape - components_to_add = ["Box Shape"] - box_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", spawner_entity.id) - box = hydra.Entity("Box", box_id) - box.components = [] - for component in components_to_add: - box.components.append(hydra.add_component(component, box_id)) - new_box_dimension = math.Vector3(5.0, 5.0, 5.0) - hydra.get_set_test(box, 0, "Box Shape|Box Configuration|Dimensions", new_box_dimension) + # Set view of planting area for visual debugging + general.set_current_view_position(512.0, 500.0, 38.0) + general.set_current_view_rotation(-20.0, 0.0, 0.0) - # 4) Create a child entity with Cylinder Shape - components_to_add = ["Cylinder Shape"] - cylinder_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", spawner_entity.id) - cylinder = hydra.Entity("Cylinder", cylinder_id) - cylinder.components = [] - for component in components_to_add: - cylinder.components.append(hydra.add_component(component, cylinder_id)) - hydra.get_set_test(cylinder, 0, "Cylinder Shape|Cylinder Configuration|Radius", 5.0) + # 2) Create a new entity with required vegetation area components and Vegetation Shape Intersection Filter + center_point = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, + asset_path) + spawner_entity.add_component("Vegetation Shape Intersection Filter") - # 5) Set the Intersection Filter's Shape Entity Id to the Box Shape entity - spawner_entity.get_set_test(3, "Configuration|Shape Entity Id", box_id) + # Create a planting surface + dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0) - # Validate instance counts. Instances should only plant in the Box Shape area - num_expected = 49 - success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 5.0) - self.test_success = success and self.test_success - - # 6) Set the Intersection Filter's Shape Entity Id to the Cylinder Shape entity - spawner_entity.get_set_test(3, "Configuration|Shape Entity Id", cylinder_id) + # 3) Create a child entity with Box Shape + components_to_add = ["Box Shape"] + box_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", spawner_entity.id) + box = hydra.Entity("Box", box_id) + box.components = [] + for component in components_to_add: + box.components.append(hydra.add_component(component, box_id)) + new_box_dimension = math.Vector3(5.0, 5.0, 5.0) + hydra.get_set_test(box, 0, "Box Shape|Box Configuration|Dimensions", new_box_dimension) - # Validate instance counts. Instances should only plant in the Cylinder Shape area - num_expected = 121 - success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 5.0) - self.test_success = success and self.test_success + # 4) Create a child entity with Cylinder Shape + components_to_add = ["Cylinder Shape"] + cylinder_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", spawner_entity.id) + cylinder = hydra.Entity("Cylinder", cylinder_id) + cylinder.components = [] + for component in components_to_add: + cylinder.components.append(hydra.add_component(component, cylinder_id)) + hydra.get_set_test(cylinder, 0, "Cylinder Shape|Cylinder Configuration|Radius", 5.0) - # 7) Clear the Intersection Filter's Shape Entity Id reference - spawner_entity.get_set_test(3, "Configuration|Shape Entity Id", None) + # 5) Set the Intersection Filter's Shape Entity Id to the Box Shape entity + spawner_entity.get_set_test(3, "Configuration|Shape Entity Id", box_id) - # Validate instance counts. Instances should now fill the entire spawner_entity's area - num_expected = 20 * 20 - success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 5.0) - self.test_success = success and self.test_success + # Validate instance counts. Instances should only plant in the Box Shape area + num_expected = 49 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 5.0) + Report.result(Tests.instance_count_in_box_shape, success) + + # 6) Set the Intersection Filter's Shape Entity Id to the Cylinder Shape entity + spawner_entity.get_set_test(3, "Configuration|Shape Entity Id", cylinder_id) + + # Validate instance counts. Instances should only plant in the Cylinder Shape area + num_expected = 121 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 5.0) + Report.result(Tests.instance_count_in_cylinder_shape, success) + + # 7) Clear the Intersection Filter's Shape Entity Id reference + spawner_entity.get_set_test(3, "Configuration|Shape Entity Id", None) + + # Validate instance counts. Instances should now fill the entire spawner_entity's area + num_expected = 20 * 20 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 5.0) + Report.result(Tests.unfiltered_instance_count, success) -test = TestShapeIntersectionFilter() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(ShapeIntersectionFilter_InstancesPlantInAssignedShape) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py index abd3dc35d5..5855972f0c 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py @@ -5,121 +5,132 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -from math import radians -import sys -import azlmbr.areasystem as areasystem -import azlmbr.asset as asset -import azlmbr.bus as bus -import azlmbr.components as components -import azlmbr.legacy.general as general -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + surface_entity_created = ( + "Successfully created Surface entity", + "Failed to create Surface entity" + ) + instance_count = ( + "Found the expected number of instances", + "Unexpected number of instances found" + ) + instances_aligned_0 = ( + "All instances are pointed straight up", + "Found instances not aligned to surface pointing straight up" + ) + instances_aligned_1 = ( + "All instances are planted perpendicularly to the surface", + "Found instances not aligned to surface perpendicularly" + ) -class TestSlopeAlignmentModifierOverrides(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="SlopeAlignmentModifierOverrides", args=["level"]) +def SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(): + """ + Summary: + Verifies instances properly align to surfaces based on configuration of descriptor overrides of the + Vegetation Slope Alignment Modifier component. - def run_test(self): - """ - Summary: - C4814459 Verifies instances properly align to surfaces based on configuration of descriptor overrides of the - Vegetation Slope Alignment Modifier component. + :return: None + """ - :return: None - """ + import os + from math import radians - def verify_proper_alignment(instance, rot_degrees_vec): - expected_rotation = math.Quaternion() - expected_rotation.SetFromEulerDegrees(rot_degrees_vec) - if instance.alignment.IsClose(expected_rotation): - return True - self.log(f"Expected rotation of {expected_rotation}, Found {instance.alignment}") - return False + import azlmbr.areasystem as areasystem + import azlmbr.asset as asset + import azlmbr.bus as bus + import azlmbr.components as components + import azlmbr.legacy.general as general + import azlmbr.math as math - # Create empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - general.set_current_view_position(512.0, 480.0, 38.0) + def verify_proper_alignment(instance, rot_degrees_vec): + expected_rotation = math.Quaternion() + expected_rotation.SetFromEulerDegrees(rot_degrees_vec) + if instance.alignment.IsClose(expected_rotation): + return True + Report.info(f"Expected rotation of {expected_rotation}, Found {instance.alignment}") + return False - # Create a spawner entity setup with all needed components - center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path) + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Create a sloped mesh surface for the instances to plant on - center_point = math.Vector3(502.0, 512.0, 24.0) - mesh_asset_path = os.path.join("objects", "_primitives", "_box_1x1.azmodel") - mesh_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", mesh_asset_path, math.Uuid(), - False) - rotation = math.Vector3(0.0, radians(45.0), 0.0) - surface_entity = hydra.Entity("Surface Entity") - surface_entity.create_entity( - center_point, - ["Mesh", "Mesh Surface Tag Emitter"] - ) - if surface_entity.id.IsValid(): - print(f"'{surface_entity.name}' created") - hydra.get_set_test(surface_entity, 0, "Controller|Configuration|Mesh Asset", mesh_asset) - components.TransformBus(bus.Event, "SetLocalRotation", surface_entity.id, rotation) - components.TransformBus(bus.Event, "SetLocalUniformScale", surface_entity.id, 30.0) + general.set_current_view_position(512.0, 480.0, 38.0) - # Add a Vegetation Debugger component to allow refreshing instances - hydra.add_level_component("Vegetation Debugger") + # Create a spawner entity setup with all needed components + center_point = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path) - # Add Vegetation Slope Alignment Modifier to the spawner entity and toggle on Allow Per-Item Overrides - spawner_entity.add_component("Vegetation Slope Alignment Modifier") - spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) + # Create a sloped mesh surface for the instances to plant on + center_point = math.Vector3(502.0, 512.0, 24.0) + mesh_asset_path = os.path.join("objects", "_primitives", "_box_1x1.azmodel") + mesh_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", mesh_asset_path, math.Uuid(), + False) + rotation = math.Vector3(0.0, radians(45.0), 0.0) + surface_entity = hydra.Entity("Surface Entity") + surface_entity.create_entity( + center_point, + ["Mesh", "Mesh Surface Tag Emitter"] + ) + Report.critical_result(Tests.surface_entity_created, surface_entity.id.IsValid()) + hydra.get_set_test(surface_entity, 0, "Controller|Configuration|Mesh Asset", mesh_asset) + components.TransformBus(bus.Event, "SetLocalRotation", surface_entity.id, rotation) + components.TransformBus(bus.Event, "SetLocalUniformScale", surface_entity.id, 30.0) - # Toggle on Surface Slope Alignment Override Enabled on the Vegetation Asset List component - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Override Enabled", - True) + # Add a Vegetation Debugger component to allow refreshing instances + hydra.add_level_component("Vegetation Debugger") - # Set Surface Slope Alignment Override Min and Max to 0 and validate instance alignment - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max", 0.0) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max", 0.0) + # Add Vegetation Slope Alignment Modifier to the spawner entity and toggle on Allow Per-Item Overrides + spawner_entity.add_component("Vegetation Slope Alignment Modifier") + spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) - # Verify instances are have planted and are aligned to slope as expected - num_expected = 20 * 20 - self.test_success = self.test_success and self.wait_for_condition( - lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + # Toggle on Surface Slope Alignment Override Enabled on the Vegetation Asset List component + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Override Enabled", + True) - box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id) - instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) + # Set Surface Slope Alignment Override Min and Max to 0 and validate instance alignment + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max", 0.0) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max", 0.0) - if self.test_success and num_expected == len(instances): - for instance in instances: - self.test_success = verify_proper_alignment(instance, - math.Vector3(0.0, 0.0, 0.0)) and self.test_success + # Verify instances are have planted and are aligned to slope as expected + num_expected = 20 * 20 + instances_planted = helper.wait_for_condition( + lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.critical_result(Tests.instance_count, instances_planted) - # Set Surface Slope Alignment Min and Max to 1 and validate instance alignment - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Min", 1.0) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max", 1.0) - general.run_console('veg_debugClearAllAreas') - self.test_success = self.test_success and self.wait_for_condition( - lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id) + instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) - box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id) - instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) + success = True + for instance in instances: + success = verify_proper_alignment(instance, math.Vector3(0.0, 0.0, 0.0)) + Report.result(Tests.instances_aligned_0, success) - if self.test_success and num_expected == len(instances): - for instance in instances: - self.test_success = verify_proper_alignment(instance, math.Vector3(0.0, 45.0, 0.0)) and \ - self.test_success + # Set Surface Slope Alignment Min and Max to 1 and validate instance alignment + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Min", 1.0) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max", 1.0) + general.run_console('veg_debugClearAllAreas') + instances_planted = helper.wait_for_condition( + lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.critical_result(Tests.instance_count, instances_planted) + + box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id) + instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) + + success = True + for instance in instances: + success = verify_proper_alignment(instance, math.Vector3(0.0, 45.0, 0.0)) + Report.result(Tests.instances_aligned_1, success) -test = TestSlopeAlignmentModifierOverrides() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py index 6e9e36957f..11427b9b0e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py @@ -5,127 +5,139 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -from math import radians -import sys -import azlmbr.areasystem as areasystem -import azlmbr.asset as asset -import azlmbr.bus as bus -import azlmbr.components as components -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + surface_entity_created = ( + "Successfully created Surface entity", + "Failed to create Surface entity" + ) + instance_count = ( + "Found the expected number of instances", + "Unexpected number of instances found" + ) + instances_aligned_1 = ( + "All instances are planted perpendicularly to the surface", + "Found instances not aligned to surface perpendicularly" + ) + instances_aligned_0 = ( + "All instances are pointed straight up", + "Found instances not aligned to surface pointing straight up" + ) -class TestSlopeAlignmentModifier(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="SlopeAlignmentModifier", args=["level"]) +def SlopeAlignmentModifier_InstanceSurfaceAlignment(): + """ + Summary: + Verifies instances properly align to surfaces based on configuration of the Vegetation Slope Alignment + Modifier. - def run_test(self): - """ - Summary: - C4896941 Verifies instances properly align to surfaces based on configuration of the Vegetation Slope Alignment - Modifier. + :return: None + """ - :return: None - """ + import os + from math import radians - def verify_proper_alignment(instance, rot_degrees_vec): - expected_rotation = math.Quaternion() - expected_rotation.SetFromEulerDegrees(rot_degrees_vec) - if instance.alignment.IsClose(expected_rotation): - return True - self.log(f"Expected rotation of {expected_rotation}, Found {instance.alignment}") - return False + import azlmbr.areasystem as areasystem + import azlmbr.asset as asset + import azlmbr.bus as bus + import azlmbr.components as components + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.math as math - # Create empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - general.set_current_view_position(512.0, 480.0, 38.0) + def verify_proper_alignment(instance, rot_degrees_vec): + expected_rotation = math.Quaternion() + expected_rotation.SetFromEulerDegrees(rot_degrees_vec) + if instance.alignment.IsClose(expected_rotation): + return True + Report.info(f"Expected rotation of {expected_rotation}, Found {instance.alignment}") + return False - # Create a spawner entity setup with all needed components - center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path) + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Create a sloped mesh surface for the instances to plant on - center_point = math.Vector3(502.0, 512.0, 24.0) - mesh_asset_path = os.path.join("objects", "_primitives", "_box_1x1.azmodel") - mesh_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", mesh_asset_path, math.Uuid(), - False) - rotation = math.Vector3(0.0, radians(45.0), 0.0) - surface_entity = hydra.Entity("Surface Entity") - surface_entity.create_entity( - center_point, - ["Mesh", "Mesh Surface Tag Emitter"] - ) - if surface_entity.id.IsValid(): - print(f"'{surface_entity.name}' created") - hydra.get_set_test(surface_entity, 0, "Controller|Configuration|Mesh Asset", mesh_asset) - components.TransformBus(bus.Event, "SetLocalRotation", surface_entity.id, rotation) - components.TransformBus(bus.Event, "SetLocalUniformScale", surface_entity.id, 30.0) + general.set_current_view_position(512.0, 480.0, 38.0) - # Add a Vegetation Debugger component to allow refreshing instances - hydra.add_level_component("Vegetation Debugger") + # Create a spawner entity setup with all needed components + center_point = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path) - # Add Vegetation Slope Alignment Modifier to the spawner entity - spawner_entity.add_component("Vegetation Slope Alignment Modifier") + # Create a sloped mesh surface for the instances to plant on + center_point = math.Vector3(502.0, 512.0, 24.0) + mesh_asset_path = os.path.join("objects", "_primitives", "_box_1x1.azmodel") + mesh_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", mesh_asset_path, math.Uuid(), + False) + rotation = math.Vector3(0.0, radians(45.0), 0.0) + surface_entity = hydra.Entity("Surface Entity") + surface_entity.create_entity( + center_point, + ["Mesh", "Mesh Surface Tag Emitter"] + ) + Report.critical_result(Tests.surface_entity_created, surface_entity.id.IsValid()) + hydra.get_set_test(surface_entity, 0, "Controller|Configuration|Mesh Asset", mesh_asset) + components.TransformBus(bus.Event, "SetLocalRotation", surface_entity.id, rotation) + components.TransformBus(bus.Event, "SetLocalUniformScale", surface_entity.id, 30.0) - # Set Alignment Coefficient Min/Max to 1 on the Slope Alignment Modifier - spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Min", 1.0) - spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Max", 1.0) + # Add a Vegetation Debugger component to allow refreshing instances + hydra.add_level_component("Vegetation Debugger") - # Create new child entity with a Constant Gradient - child_vegetation_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", spawner_entity.id) - child_vegetation = hydra.Entity("Child Vegetation Entity", child_vegetation_id) - components_to_add = ["Constant Gradient"] - child_vegetation.components = [] - for component in components_to_add: - child_vegetation.components.append(hydra.add_component(component, child_vegetation_id)) + # Add Vegetation Slope Alignment Modifier to the spawner entity + spawner_entity.add_component("Vegetation Slope Alignment Modifier") - # Reference the Constant Gradient on the Slope Alignment Modifier component - spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", child_vegetation_id) + # Set Alignment Coefficient Min/Max to 1 on the Slope Alignment Modifier + spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Min", 1.0) + spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Max", 1.0) - # Verify instances are have planted and are aligned to slope as expected - num_expected = 20 * 20 - self.test_success = self.test_success and self.wait_for_condition( - lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + # Create new child entity with a Constant Gradient + child_vegetation_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", spawner_entity.id) + child_vegetation = hydra.Entity("Child Vegetation Entity", child_vegetation_id) + components_to_add = ["Constant Gradient"] + child_vegetation.components = [] + for component in components_to_add: + child_vegetation.components.append(hydra.add_component(component, child_vegetation_id)) - box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id) - instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) + # Reference the Constant Gradient on the Slope Alignment Modifier component + spawner_entity.get_set_test(3, "Configuration|Gradient|Gradient Entity Id", child_vegetation_id) - if self.test_success and num_expected == len(instances): - for instance in instances: - self.test_success = verify_proper_alignment(instance, math.Vector3(0.0, 45.0, 0.0)) and \ - self.test_success + # Verify instances are have planted and are aligned to slope as expected + num_expected = 20 * 20 + instances_planted = helper.wait_for_condition( + lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.critical_result(Tests.instance_count, instances_planted) - # Change Min/Max to 0.0 and verify proper alignment - spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Min", 0.0) - spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Max", 0.0) - general.run_console('veg_debugClearAllAreas') - self.test_success = self.test_success and self.wait_for_condition( - lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id) + instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) - box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id) - instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) + success = True + for instance in instances: + success = verify_proper_alignment(instance, math.Vector3(0.0, 45.0, 0.0)) + Report.result(Tests.instances_aligned_1, success) - if self.test_success and num_expected == len(instances): - for instance in instances: - self.test_success = verify_proper_alignment(instance, math.Vector3(0.0, 0.0, 0.0)) and self.test_success + # Change Min/Max to 0.0 and verify proper alignment + spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Min", 0.0) + spawner_entity.get_set_test(3, "Configuration|Alignment Coefficient Max", 0.0) + general.run_console('veg_debugClearAllAreas') + instances_planted = helper.wait_for_condition( + lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.critical_result(Tests.instance_count, instances_planted) + + box = azlmbr.shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id) + instances = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', box) + + success = True + for instance in instances: + success = verify_proper_alignment(instance, math.Vector3(0.0, 0.0, 0.0)) + Report.result(Tests.instances_aligned_0, success) -test = TestSlopeAlignmentModifier() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(SlopeAlignmentModifier_InstanceSurfaceAlignment) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py index d66716cee3..ee0851d552 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py @@ -5,121 +5,122 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -""" -C4874096 - Slope Min/Max properties can be set, and properly affect planted vegetation -C4814464 - Slope Filter overrides function as expected -""" -import os -import sys - -import azlmbr.bus as bus -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.math as math -import azlmbr.paths - -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + prefilter_instance_count = ( + "Found the expected number of instances before applying the Slope Filter", + "Found an unexpected number of instances before applying the Slope Filter" + ) + postfilter_instance_count = ( + "Found the expected number of instances after applying the Slope Filter", + "Found an unexpected number of instances after applying the Slope Filter" + ) + postfilter_overrides_instance_count = ( + "Found the expected number of instances after applying descriptor overrides to the Slope Filter", + "Found an unexpected number of instances after applying descriptor overrides to the Slope Filter" + ) -class TestSlopeFilterComponentAndOverrides(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="SlopeFilter_InstancesPlantOnValidSlope", args=["level"]) +def SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(): + """ + Summary: + An existing level is opened. A spawner entity is added, along with a flat planting surface at 32 on Z, and sphere + mesh at 38 on Z to provide a sloped surface. A Slope Filter is added to the spawner entity, and Slope Min/Max + values are set. Instance counts are validated. The same test is then performed for Slope Filter overrides. - def run_test(self): - """ - Summary: - A new level is created. A spawner entity is added, along with a flat planting surface at 32 on Z, and sphere - mesh at 38 on Z to provide a sloped surface. A Slope Filter is added to the spawner entity, and Slope Min/Max - values are set. Instance counts are validated. The same test is then performed for Slope Filter overrides. + Expected Behavior: + Instances plant only on surfaces that fall between the Slope Filter Min/Max settings - Expected Behavior: - Instances plant only on surfaces that fall between the Slope Filter Min/Max settings + Test Steps: + 1) Open an existing level + 2) Create an instance spawner entity + 3) Create surfaces to plant on, one at 32 on Z and another sloped surface at 38 on Z. + 4) Initial instance counts pre-filter are verified. + 5) Slope Min/Max values are set on the Slope Filter component + 6) Instance counts are validated + 7) Setup for overrides tests + 8) Slope Min/Max values are set on the descriptor overrides + 9) Instance counts are validated - Test Steps: - 1) Create a new level - 2) Create an instance spawner entity - 3) Create surfaces to plant on, one at 32 on Z and another sloped surface at 38 on Z. - 4) Initial instance counts pre-filter are verified. - 5) Slope Min/Max values are set on the Slope Filter component - 6) Instance counts are validated - 7) Setup for overrides tests - 8) Slope Min/Max values are set on the descriptor overrides - 9) Instance counts are validated + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import os - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import azlmbr.bus as bus + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.math as math - # Set view of planting area for visual debugging - general.set_current_view_position(512.0, 475.0, 38.0) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # 2) Create a new entity with required vegetation area components - center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Add a Vegetation Slope Filter - spawner_entity.add_component("Vegetation Slope Filter") + # Set view of planting area for visual debugging + general.set_current_view_position(512.0, 475.0, 38.0) - # 3) Add surfaces to plant on. This will include a flat surface and a sphere mesh to provide a sloped surface - dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0) - sloped_surface_center = math.Vector3(512.0, 512.0, 38.0) - dynveg.create_mesh_surface_entity_with_slopes("Sloped Planting Surface", sloped_surface_center, 10.0) + # 2) Create a new entity with required vegetation area components + center_point = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path) - # Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape - veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, - 'Configuration|Area System Settings|Sector Point Snap Mode', 1) + # Add a Vegetation Slope Filter + spawner_entity.add_component("Vegetation Slope Filter") - # 4) Validate instance counts pre-filter - num_expected_flat_surface = 40 * 40 # 20x20 instances per 16m - num_expected_slopes_pre_filter = 120 # Unfiltered planting on the top of the sphere mesh - num_expected = num_expected_flat_surface + num_expected_slopes_pre_filter - initial_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape( - spawner_entity.id, num_expected), 5.0) - self.test_success = initial_success and self.test_success + # 3) Add surfaces to plant on. This will include a flat surface and a sphere mesh to provide a sloped surface + dynveg.create_surface_entity("Planting Surface", center_point, 32.0, 32.0, 1.0) + sloped_surface_center = math.Vector3(512.0, 512.0, 38.0) + dynveg.create_mesh_surface_entity_with_slopes("Sloped Planting Surface", sloped_surface_center, 10.0) - # 5) Change Slope Min/Max on the Vegetation Slope Filter component - spawner_entity.get_set_test(3, "Configuration|Slope Min", 20) - spawner_entity.get_set_test(3, "Configuration|Slope Max", 45) + # Set instances to spawn on a center snap point to avoid unexpected instances around the edges of the box shape + veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", veg_system_settings_component, + 'Configuration|Area System Settings|Sector Point Snap Mode', 1) - # 6) Validate instance counts post-filter: instances should only plant on slopes between 20-45 degrees - num_expected_slopes_post_filter = 48 - slope_min_max_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape( - spawner_entity.id, num_expected_slopes_post_filter), 5.0) - self.test_success = slope_min_max_success and self.test_success + # 4) Validate instance counts pre-filter + num_expected_flat_surface = 40 * 40 # 20x20 instances per 16m + num_expected_slopes_pre_filter = 120 # Unfiltered planting on the top of the sphere mesh + num_expected = num_expected_flat_surface + num_expected_slopes_pre_filter + initial_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape( + spawner_entity.id, num_expected), 5.0) + Report.result(Tests.prefilter_instance_count, initial_success) - # 7) Setup for overrides on the Slope Filter component and the spawner entity's descriptor - spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Slope Filter|Override Enabled", True) + # 5) Change Slope Min/Max on the Vegetation Slope Filter component + spawner_entity.get_set_test(3, "Configuration|Slope Min", 20) + spawner_entity.get_set_test(3, "Configuration|Slope Max", 45) - # 8) Set Slope Filter Min/Max overrides on the spawner entity's descriptor - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Slope Filter|Min", 5) - spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Slope Filter|Max", 20) + # 6) Validate instance counts post-filter: instances should only plant on slopes between 20-45 degrees + num_expected_slopes_post_filter = 48 + slope_min_max_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape( + spawner_entity.id, num_expected_slopes_post_filter), 5.0) + Report.result(Tests.postfilter_instance_count, slope_min_max_success) - # 9) Validate instance counts post-filter: instances should only plant on slopes between 5-20 degrees - num_expected_slopes_post_filter_overrides = 12 - overrides_min_max_success = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape( - spawner_entity.id, num_expected_slopes_post_filter_overrides), 5.0) - self.test_success = overrides_min_max_success and self.test_success + # 7) Setup for overrides on the Slope Filter component and the spawner entity's descriptor + spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Slope Filter|Override Enabled", True) + + # 8) Set Slope Filter Min/Max overrides on the spawner entity's descriptor + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Slope Filter|Min", 5) + spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]|Slope Filter|Max", 20) + + # 9) Validate instance counts post-filter: instances should only plant on slopes between 5-20 degrees + num_expected_slopes_post_filter_overrides = 12 + overrides_min_max_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape( + spawner_entity.id, num_expected_slopes_post_filter_overrides), 5.0) + Report.result(Tests.postfilter_overrides_instance_count, overrides_min_max_success) -test = TestSlopeFilterComponentAndOverrides() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_FilterStageToggle.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_FilterStageToggle.py deleted file mode 100755 index 33275f2992..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_FilterStageToggle.py +++ /dev/null @@ -1,103 +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 -""" - -import os -import sys -import azlmbr.math as math -import azlmbr.bus as bus -import azlmbr.paths -import azlmbr.editor as editor -import azlmbr.entity as EntityId -import azlmbr.components as components -import azlmbr.legacy.general as general - -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg - -class TestSlopeFilterFilterStageToggle(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="SlopeFilter_FilterStageToggle", args=["level"]) - - def run_test(self): - """ - Summary: - Filter Stage toggle affects final vegetation position - - Expected Result: - Vegetation instances plant differently depending on the Filter Stage setting. With PreProcess, some vegetation instances can - appear on slopes outside the filtered values. With PostProcess, vegetation instances only appear on the correct slope values. - - :return: None - """ - - # Create empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) - - general.set_current_view_position(512.0, 480.0, 38.0) - - # Create basic vegetation entity - position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path) - - # Create Surface for instances to plant on - dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0) - - # Add a Vegetation Shape Intersection Filter to the vegetation area entity - vegetation.add_component("Vegetation Shape Intersection Filter") - - # Create a new entity as a child of the vegetation area entity with Box Shape - box = hydra.Entity("box") - box.create_entity(position, ["Box Shape"]) - box.get_set_test(0, "Box Shape|Box Configuration|Dimensions", math.Vector3(8.0, 8.0, 1.0)) - - # Create a new entity as a child of the vegetation area entity with Cylinder Shape. - cylinder = hydra.Entity("cylinder") - cylinder.create_entity(position, ["Cylinder Shape"]) - cylinder.get_set_test(0, "Cylinder Shape|Cylinder Configuration|Radius", 5.0) - cylinder.get_set_test(0, "Cylinder Shape|Cylinder Configuration|Height", 5.0) - box.set_test_parent_entity(vegetation) - cylinder.set_test_parent_entity(vegetation) - - # # On the Vegetation Shape Intersection Filter component, click the crosshair button, and add child entities one by one - vegetation.get_set_test(3, "Configuration|Shape Entity Id", box.id) - result = self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 8.0, 100), 2.0) - self.log(f"Vegetation plant only in the areas where the Box overlaps with the vegetation area's boundaries: {result}") - vegetation.get_set_test(3, "Configuration|Shape Entity Id", cylinder.id) - result = self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 5.0, 100), 2.0) - self.log(f"Vegetation plant only in the areas where the Cylinder overlaps with the vegetation area's boundaries: {result}") - - # Create a new entity as a child of the vegetation area entity with Random Noise Gradient Generator, Gradient Transform Modifier, - # and Box Shape component - random_noise = hydra.Entity("random_noise") - random_noise.create_entity(position, ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"]) - random_noise.set_test_parent_entity(vegetation) - - # Add a Vegetation Position Modifier to the vegetation area entity - vegetation.add_component("Vegetation Position Modifier") - - # Pin the Random Noise entity to the Gradient Entity Id field of the Position Modifier's Gradient X - vegetation.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", random_noise.id) - - # Toggle between PreProcess and PostProcess - vegetation.get_set_test(3, "Configuration|Filter Stage", 1) - result = self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 5.0, 117), 2.0) - self.log(f"Vegetation instances count equal to expected value for PREPROCESS filter stage: {result}") - vegetation.get_set_test(3, "Configuration|Filter Stage", 2) - result = self.wait_for_condition(lambda: dynveg.validate_instance_count(position, 5.0, 122), 2.0) - self.log(f"Vegetation instances count equal to expected value for POSTPROCESS filter stage: {result}") - -test = TestSlopeFilterFilterStageToggle() -test.run() \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerSlices_SliceCreationAndVisibilityToggleWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerSlices_SliceCreationAndVisibilityToggleWorks.py new file mode 100644 index 0000000000..1658ffc532 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerSlices_SliceCreationAndVisibilityToggleWorks.py @@ -0,0 +1,126 @@ +""" +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: + spawner_slice_created = ( + "Spawner slice created successfully", + "Failed to create Spawner slice" + ) + instance_count_unhidden = ( + "Initial instance counts are as expected", + "Found an unexpected number of initial instances" + ) + instance_count_hidden = ( + "Instance counts upon hiding the Spawner slice are as expected", + "Unexpectedly found instances with the Spawner slice hidden" + ) + blender_slice_created = ( + "Blender slice created successfully", + "Failed to create Blender slice" + ) + + +def SpawnerSlices_SliceCreationAndVisibilityToggleWorks(): + """ + Summary: + C2627900 Verifies if a slice containing the component can be created. + C2627905 A slice containing the Vegetation Layer Blender component can be created. + C2627904: Hiding a slice containing the component clears any visuals from the Viewport. + + Expected Result: + C2627900, C2627905: Slice is created, and is properly processed in the Asset Processor. + C2627904: Vegetation area visuals are hidden from the Viewport. + + :return: None + """ + + import os + + import azlmbr.math as math + import azlmbr.legacy.general as general + import azlmbr.slice as slice + import azlmbr.bus as bus + import azlmbr.editor as editor + import azlmbr.asset as asset + + import editor_python_test_tools.hydra_editor_utils as hydra + 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 + + def path_is_valid_asset(asset_path): + asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False) + return asset_id.invoke("IsValid") + + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") + general.set_current_view_position(512.0, 480.0, 38.0) + + # 2) C2627900 Verifies if a slice containing the Vegetation Layer Spawner component can be created. + # 2.1) Create basic vegetation entity + position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + veg_1 = dynveg.create_vegetation_area("vegetation_1", position, 16.0, 16.0, 16.0, asset_path) + + # 2.2) Create slice from the entity + slice_path = os.path.join("slices", "TestSlice_1.slice") + slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", veg_1.id, slice_path) + + # 2.3) Verify if the slice has been created successfully + spawner_slice_success = helper.wait_for_condition(lambda: path_is_valid_asset(slice_path), 5.0) + Report.result(Tests.spawner_slice_created, spawner_slice_success) + + # 3) C2627904: Hiding a slice containing the component clears any visuals from the Viewport + # 3.1) Create Surface for instances to plant on + dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0) + + # 3.2) Initially verify instance count before hiding slice + initial_count_success = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 400), 5.0) + Report.result(Tests.instance_count_unhidden, initial_count_success) + + # 3.3) Hide the slice and verify instance count + editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", veg_1.id, False) + hidden_instance_count = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 16.0, 0), 5.0) + Report.result(Tests.instance_count_hidden, hidden_instance_count) + + # 3.4) Unhide the slice + editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", veg_1.id, True) + + # 4) C2627905 A slice containing the Vegetation Layer Blender component can be created. + # 4.1) Create another vegetation entity to add to blender component + veg_2 = dynveg.create_vegetation_area("vegetation_2", position, 1.0, 1.0, 1.0, "") + + # 4.2) Create entity with Vegetation Layer Blender + components_to_add = ["Box Shape", "Vegetation Layer Blender"] + blender_entity = hydra.Entity("blender_entity") + blender_entity.create_entity(position, components_to_add) + + # 4.3) Pin both the vegetation areas to the blender entity + pte = hydra.get_property_tree(blender_entity.components[1]) + path = "Configuration|Vegetation Areas" + pte.update_container_item(path, 0, veg_1.id) + pte.add_container_item(path, 1, veg_2.id) + + # 4.4) Drag the simple vegetation areas under the Vegetation Layer Blender entity to create an entity hierarchy. + veg_1.set_test_parent_entity(blender_entity) + veg_2.set_test_parent_entity(blender_entity) + + # 4.5) Create slice from blender entity + slice_path = os.path.join("slices", "TestSlice_2.slice") + slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", blender_entity.id, slice_path) + + # 4.6) Verify if the slice has been created successfully + blender_slice_success = helper.wait_for_condition(lambda: path_is_valid_asset(slice_path), 5.0) + Report.result(Tests.blender_slice_created, blender_slice_success) + + +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(SpawnerSlices_SliceCreationAndVisibilityToggleWorks) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceDataRefreshes_RemainsStable.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceDataRefreshes_RemainsStable.py index 2f08d3963d..1ad4f305c3 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceDataRefreshes_RemainsStable.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceDataRefreshes_RemainsStable.py @@ -5,99 +5,97 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.legacy.general as general -import azlmbr.math as math +class Tests: + editor_remains_stable = ( + "Editor did not crash following rapid surface data updates", + "Editor crashed" + ) -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +def SurfaceDataRefreshes_RemainsStable(): + """ + Summary: + The Vegetation Area System can intermittently crash when updating surface data and moving the camera + around rapidly. The situation occurs across multiple frames - the surface data updates, which triggers a bunch + of sector updates getting added to the update queue. Then in a subsequent frame, there is no active vegetation + area or surface data updates, which triggers "delete all sectors". The "delete all" wasn't deleting entries from + the update queue, so any unprocessed updates would continue to get processed. If any of those updates referenced + a sector that no longer exists, because the camera changed position, then it would assert and crash. + + To repro this bug, this test loads an empty level with a large box shape emitting a surface, and then runs a tight + loop of camera movements and "surface changed" events that invalidate all surface points. Because this is a timing + issue, there's no guarantee that the test below will successfully cause the condition to occur, but it successfully + crashed every time it was tested locally prior to the bugfix. + + :return: None + """ + + import azlmbr.legacy.general as general + import azlmbr.math as math + + 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 + helper.init_idle() + helper.open_level("Physics", "Base") + + world_center = math.Vector3(512.0, 512.0, 32.0) + + # Add an entity with a 1024 x 1024 box centered at 512,512. + surface_entity = dynveg.create_surface_entity("Surface Data", world_center, 1024.0, 1024.0, 1.0) + + # Move the camera to the world center + general.set_current_view_position(world_center.x, world_center.y, world_center.z) + + # 2) Perform the test. Since the conditions are extremely timing related, and every machine + # running the test can have different timing conditions, we run through a set of different + # combinations to try and cause the crash under as many scenarios as possible + + loops_per_surface_changed = [3, 5, 5] + loops_per_camera_reset = [20, 20, 20] + camera_speed_per_loop = [10.0, 10.0, 15.0] + + # Setting test success to false to make sure the toggle at the end accurately conveys the loop being successful + test_success = False + + # Loop through all our attempted timing test cases to cause the crash pretty consistently. + for test_case in range(0,3): + Report.info(f'Starting test case {test_case}') + Report.info(f'Loops per surface changed: {loops_per_surface_changed[test_case]}') + Report.info(f'Loops per camera reset: {loops_per_camera_reset[test_case]}') + Report.info(f'Camera speed per loop: {camera_speed_per_loop[test_case]}') + for test_counter in range(0, 100): + + # Every N loops, invalidate the entire set of surface data. It's mostly just important for this + # not to happen *every* iteration, since we need the vegetation system to bounce between having + # dirty surface points that cause sectors to be refreshed, and having no dirty surface points or + # active surface areas to trigger a "delete all sectors" condition. + if (test_counter % loops_per_surface_changed[test_case]) == 0: + azlmbr.surface_data.SurfaceDataSystemNotificationBus(azlmbr.bus.Broadcast, + 'OnSurfaceChanged', + surface_entity.id, + azlmbr.math.Aabb(), + azlmbr.math.Aabb()) + + # Move the camera back and forth along the X axis at just the right speed to invalidate sectors that are + # queued for updating but haven't updated yet, so that when they try to update they crash. + x_pos = world_center.x + ((test_counter % loops_per_camera_reset[test_case]) * camera_speed_per_loop[test_case]) + general.set_current_view_position(x_pos, world_center.y, world_center.z) + + Report.info(f'{test_counter}: {x_pos}') + + # Give a little processing time each iteration. + general.idle_wait(0.01) + + # If we haven't crashed, then we've succeeded. + test_success = True + Report.result(Tests.editor_remains_stable, test_success) -class TestSurfaceDataRefreshes_RemainsStable(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="SurfaceDataRefreshes_RemainsStable", args=["level"]) +if __name__ == "__main__": - def run_test(self): - """ - Summary: - The Vegetation Area System can intermittently crash when updating surface data and moving the camera - around rapidly. The situation occurs across multiple frames - the surface data updates, which triggers a bunch - of sector updates getting added to the update queue. Then in a subsequent frame, there is no active vegetation - area or surface data updates, which triggers "delete all sectors". The "delete all" wasn't deleting entries from - the update queue, so any unprocessed updates would continue to get processed. If any of those updates referenced - a sector that no longer exists, because the camera changed position, then it would assert and crash. - - To repro this bug, this test creates an empty level with a large box shape emitting a surface, and then runs a tight - loop of camera movements and "surface changed" events that invalidate all surface points. Because this is a timing - issue, there's no guarantee that the test below will successfully cause the condition to occur, but it successfully - crashed every time it was tested locally prior to the bugfix. - - :return: None - """ - # 1) Create a test level with the needed test setup - self.test_success = self.create_level( - self.get_arg('level'), - heightmap_resolution=128, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=128, - use_terrain=False) - - world_center = math.Vector3(512.0, 512.0, 32.0) - - # Add an entity with a 1024 x 1024 box centered at 512,512. - surface_entity = dynveg.create_surface_entity("Surface Data", world_center, 1024.0, 1024.0, 1.0) - - # Move the camera to the world center - general.set_current_view_position(world_center.x, world_center.y, world_center.z) - - # 2) Perform the test. Since the conditions are extremely timing related, and every machine - # running the test can have different timing conditions, we run through a set of different - # combinations to try and cause the crash under as many scenarios as possible - - loops_per_surface_changed = [3, 5, 5] - loops_per_camera_reset = [20, 20, 20] - camera_speed_per_loop = [10.0, 10.0, 15.0] - - # Setting test success to false to make sure the toggle at the end accurately conveys the loop being successful - self.test_success = False - - # Loop through all our attempted timing test cases to cause the crash pretty consistently. - for test_case in range(0,3): - self.log(f'Starting test case {test_case}') - self.log(f'Loops per surface changed: {loops_per_surface_changed[test_case]}') - self.log(f'Loops per camera reset: {loops_per_camera_reset[test_case]}') - self.log(f'Camera speed per loop: {camera_speed_per_loop[test_case]}') - for test_counter in range (0,100): - - # Every N loops, invalidate the entire set of surface data. It's mostly just important for this - # not to happen *every* iteration, since we need the vegetation system to bounce between having - # dirty surface points that cause sectors to be refreshed, and having no dirty surface points or - # active surface areas to trigger a "delete all sectors" condition. - if (test_counter % loops_per_surface_changed[test_case]) == 0: - azlmbr.surface_data.SurfaceDataSystemNotificationBus(azlmbr.bus.Broadcast, - 'OnSurfaceChanged', - surface_entity.id, - azlmbr.math.Aabb(), - azlmbr.math.Aabb()) - - # Move the camera back and forth along the X axis at just the right speed to invalidate sectors that are - # queued for updating but haven't updated yet, so that when they try to update they crash. - x_pos = world_center.x + ((test_counter % loops_per_camera_reset[test_case]) * camera_speed_per_loop[test_case]) - general.set_current_view_position(x_pos, world_center.y, world_center.z) - - self.log(f'{test_counter}: {x_pos}') - - # Give a little processing time each iteration. - general.idle_wait(0.01) - - # If we haven't crashed, then we've succeeded. - self.test_success = True - - -test = TestSurfaceDataRefreshes_RemainsStable() -test.run() + from editor_python_test_tools.utils import Report + Report.start_test(SurfaceDataRefreshes_RemainsStable) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py index 87aa7946b3..ba0f3e05e3 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py @@ -5,153 +5,160 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -""" -C3711666: Multiple Descriptors with different Surface Mask Filter overrides plant as expected. -""" -import os -import sys - -import azlmbr.legacy.general as general -import azlmbr.math as math -import azlmbr.paths -import azlmbr.surface_data as surface_data - -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + initial_surface_validation = ( + "Found all expected instances on all surfaces with initial setup", + "Found an unexpected number of instances on all surfaces with initial setup" + ) + surface_a_validation = ( + "Found the expected number of instances on Surface A", + "Found an unexpected number of instances on Surface A" + ) + surface_b_validation = ( + "Found the expected number of instances on Surface B", + "Found an unexpected number of instances on Surface B" + ) + surface_c_validation = ( + "Found the expected number of instances on Surface C", + "Found an unexpected number of instances on Surface C" + ) -class TestSurfaceMaskFilterMultipleOverrides(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="SurfaceMaskFilter_MultipleDescriptorOverrides", args=["level"]) +def SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(): + """ + Summary: + A new level is created. An instance spawner with 3 descriptors is created. 3 planting surfaces of different + sizes are created and different surface tags are applied to each. Descriptor surface mask filter overrides are + set and instance counts are validated. - def run_test(self): - """ - Summary: - A new level is created. An instance spawner with 3 descriptors is created. 3 planting surfaces of different - sizes are created and different surface tags are applied to each. Descriptor surface mask filter overrides are - set and instance counts are validated. + Expected Behavior: + Instances plant on surfaces based on surface mask filter overrides. - Expected Behavior: - Instances plant on surfaces based on surface mask filter overrides. + Test Steps: + 1) Open an existing level + 2) An instance spawner with 3 descriptors is created, and a Surface Mask Filter is added to the entity + 3) 3 surfaces of different sizes are created, and set to emit different tags + 4) Pre-test validation of instances + 5) Test 1 setup and validation: Inclusion tag matching surface a is set on a single descriptor + 6) Test 2 setup and validation: Inclusion tag matching surface b is set on a single descriptor + 7) Test 3 setup and validation: Inclusion tag matching surface c is set on a single descriptor - Test Steps: - 1) A new level is created - 2) An instance spawner with 3 descriptors is created, and a Surface Mask Filter is added to the entity - 3) 3 surfaces of different sizes are created, and set to emit different tags - 4) Pre-test validation of instances - 5) Test 1 setup and validation: Inclusion tag matching surface a is set on a single descriptor - 6) Test 2 setup and validation: Inclusion tag matching surface b is set on a single descriptor - 7) Test 3 setup and validation: Inclusion tag matching surface c is set on a single descriptor + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ - surface_tag_list = [surface_data.SurfaceTag("test_tag"), surface_data.SurfaceTag("test_tag2"), - surface_data.SurfaceTag("test_tag3")] + import os - # 1) Create a new, temporary level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import azlmbr.legacy.general as general + import azlmbr.math as math + import azlmbr.surface_data as surface_data - # Set view of planting area for visual debugging - general.set_current_view_position(512.0, 500.0, 38.0) - general.set_current_view_rotation(-20.0, 0.0, 0.0) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - # 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors - spawner_center_point = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, - asset_path) - asset_list_component = spawner_entity.components[2] - desc_asset = hydra.get_component_property_value(asset_list_component, - "Configuration|Embedded Assets")[0] - desc_list = [desc_asset, desc_asset, desc_asset] - spawner_entity.get_set_test(2, "Configuration|Embedded Assets", desc_list) - - # Add a Surface Mask Filter component to the spawner entity and toggle on Allow Overrides - spawner_entity.add_component("Vegetation Surface Mask Filter") - spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) + surface_tag_list = [surface_data.SurfaceTag("test_tag"), surface_data.SurfaceTag("test_tag2"), + surface_data.SurfaceTag("test_tag3")] - # 3) Create 3 surfaces for planting, spaced out vertically, and set expected instance counts for each surface - surface_entity_a = dynveg.create_surface_entity("Surface Entity A", math.Vector3(512.0, 512.0, 32.0), - 16.0, 16.0, 1.0) - num_expected_surface_a = 20 * 20 # 20x20 instances on a 16x16 meter surface - surface_entity_b = dynveg.create_surface_entity("Surface Entity B", math.Vector3(512.0, 512.0, 35.0), - 12.0, 12.0, 1.0) - num_expected_surface_b = 15 * 15 # 15x15 instances on a 12x12 meter surface - surface_entity_c = dynveg.create_surface_entity("Surface Entity C", math.Vector3(512.0, 512.0, 38.0), - 8.0, 8.0, 1.0) - num_expected_surface_c = 10 * 10 # 10x10 instances on a 8x8 meter surface + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Set each surface to emit a different tag - surface_entity_a.get_set_test(1, "Configuration|Generated Tags", [surface_tag_list[0]]) - surface_entity_b.get_set_test(1, "Configuration|Generated Tags", [surface_tag_list[1]]) - surface_entity_c.get_set_test(1, "Configuration|Generated Tags", [surface_tag_list[2]]) + # Set view of planting area for visual debugging + general.set_current_view_position(512.0, 500.0, 38.0) + general.set_current_view_rotation(-20.0, 0.0, 0.0) - # 4) Initial Validation: Validate instance count in the spawner area. Instances should plant on all surfaces - num_expected = num_expected_surface_a + num_expected_surface_b + num_expected_surface_c - initial_success = self.wait_for_condition( - lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) - self.test_success = initial_success and self.test_success + # 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors + spawner_center_point = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0, + asset_path) + asset_list_component = spawner_entity.components[2] + desc_asset = hydra.get_component_property_value(asset_list_component, + "Configuration|Embedded Assets")[0] + desc_list = [desc_asset, desc_asset, desc_asset] + spawner_entity.get_set_test(2, "Configuration|Embedded Assets", desc_list) - # 5) - # Test #1 Setup: Set test_tag to inclusion list for descriptor 1. Set other descriptors to exclude all surfaces + # Add a Surface Mask Filter component to the spawner entity and toggle on Allow Overrides + spawner_entity.add_component("Vegetation Surface Mask Filter") + spawner_entity.get_set_test(3, "Configuration|Allow Per-Item Overrides", True) - # Toggle on Display Per-Item Overrides and Surface Mask Filter Override for each descriptor - for index in range(3): - spawner_entity.get_set_test(2, f"Configuration|Embedded Assets|[{index}]|Display Per-Item Overrides", True) - spawner_entity.get_set_test(2, - f"Configuration|Embedded Assets|[{index}]|Surface Mask Filter|Override Mode", 1) + # 3) Create 3 surfaces for planting, spaced out vertically, and set expected instance counts for each surface + surface_entity_a = dynveg.create_surface_entity("Surface Entity A", math.Vector3(512.0, 512.0, 32.0), + 16.0, 16.0, 1.0) + num_expected_surface_a = 20 * 20 # 20x20 instances on a 16x16 meter surface + surface_entity_b = dynveg.create_surface_entity("Surface Entity B", math.Vector3(512.0, 512.0, 35.0), + 12.0, 12.0, 1.0) + num_expected_surface_b = 15 * 15 # 15x15 instances on a 12x12 meter surface + surface_entity_c = dynveg.create_surface_entity("Surface Entity C", math.Vector3(512.0, 512.0, 38.0), + 8.0, 8.0, 1.0) + num_expected_surface_c = 10 * 10 # 10x10 instances on a 8x8 meter surface + # Set each surface to emit a different tag + surface_entity_a.get_set_test(1, "Configuration|Generated Tags", [surface_tag_list[0]]) + surface_entity_b.get_set_test(1, "Configuration|Generated Tags", [surface_tag_list[1]]) + surface_entity_c.get_set_test(1, "Configuration|Generated Tags", [surface_tag_list[2]]) + + # 4) Initial Validation: Validate instance count in the spawner area. Instances should plant on all surfaces + num_expected = num_expected_surface_a + num_expected_surface_b + num_expected_surface_c + initial_success = helper.wait_for_condition( + lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected), 5.0) + Report.result(Tests.initial_surface_validation, initial_success) + + # 5) + # Test #1 Setup: Set test_tag to inclusion list for descriptor 1. Set other descriptors to exclude all surfaces + + # Toggle on Display Per-Item Overrides and Surface Mask Filter Override for each descriptor + for index in range(3): + spawner_entity.get_set_test(2, f"Configuration|Embedded Assets|[{index}]|Display Per-Item Overrides", True) spawner_entity.get_set_test(2, - "Configuration|Embedded Assets|[0]|Surface Mask Filter|Inclusion Tags", - [surface_tag_list[0]]) - spawner_entity.get_set_test(2, - "Configuration|Embedded Assets|[1]|Surface Mask Filter|Exclusion Tags", - surface_tag_list) - spawner_entity.get_set_test(2, - "Configuration|Embedded Assets|[2]|Surface Mask Filter|Exclusion Tags", - surface_tag_list) + f"Configuration|Embedded Assets|[{index}]|Surface Mask Filter|Override Mode", 1) - # Test #1 Validation: Validate instance count. Should only plant on a single surface for 400 instances - test_1_success = self.wait_for_condition( - lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_surface_a), 5.0) - self.test_success = test_1_success and self.test_success + spawner_entity.get_set_test(2, + "Configuration|Embedded Assets|[0]|Surface Mask Filter|Inclusion Tags", + [surface_tag_list[0]]) + spawner_entity.get_set_test(2, + "Configuration|Embedded Assets|[1]|Surface Mask Filter|Exclusion Tags", + surface_tag_list) + spawner_entity.get_set_test(2, + "Configuration|Embedded Assets|[2]|Surface Mask Filter|Exclusion Tags", + surface_tag_list) - # 6) - # Test #2 Setup: Set test_tag2 to inclusion for descriptor 1. - spawner_entity.get_set_test(2, - "Configuration|Embedded Assets|[0]|Surface Mask Filter|Inclusion Tags", - [surface_tag_list[1]]) + # Test #1 Validation: Validate instance count. Should only plant on a single surface for 400 instances + test_1_success = helper.wait_for_condition( + lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_surface_a), 5.0) + Report.result(Tests.surface_a_validation, test_1_success) - # Test #2 Validation: Validate instance count. Should only plant on a single surface for 225 instances - test_2_success = self.wait_for_condition( - lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_surface_b), 5.0) - self.test_success = test_2_success and self.test_success + # 6) + # Test #2 Setup: Set test_tag2 to inclusion for descriptor 1. + spawner_entity.get_set_test(2, + "Configuration|Embedded Assets|[0]|Surface Mask Filter|Inclusion Tags", + [surface_tag_list[1]]) - # 7) - # Test #3 Setup: Set test_tag3 to inclusion for descriptor 1. - spawner_entity.get_set_test(2, - "Configuration|Embedded Assets|[0]|Surface Mask Filter|Inclusion Tags", - [surface_tag_list[2]]) + # Test #2 Validation: Validate instance count. Should only plant on a single surface for 225 instances + test_2_success = helper.wait_for_condition( + lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_surface_b), 5.0) + Report.result(Tests.surface_b_validation, test_2_success) - # Test #3 Validation: Validate instance count. Should only plant on a single surface for 100 instances - test_3_success = self.wait_for_condition( - lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_surface_c), 5.0) - self.test_success = test_3_success and self.test_success + # 7) + # Test #3 Setup: Set test_tag3 to inclusion for descriptor 1. + spawner_entity.get_set_test(2, + "Configuration|Embedded Assets|[0]|Surface Mask Filter|Inclusion Tags", + [surface_tag_list[2]]) + + # Test #3 Validation: Validate instance count. Should only plant on a single surface for 100 instances + test_3_success = helper.wait_for_condition( + lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, num_expected_surface_c), 5.0) + Report.result(Tests.surface_c_validation, test_3_success) -test = TestSurfaceMaskFilterMultipleOverrides() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py index 1baec7694d..fee62c04d1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py @@ -5,66 +5,61 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -import azlmbr.surface_data as surface_data - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -from editor_python_test_tools.editor_test_helper import EditorTestHelper +class Tests: + tags_same_value_equal = ( + "Two Surface Tags of the same value evaluated as equal", + "Two Surface Tags of the same value unexpectedly evaluated as unequal" + ) + tags_different_value_unequal = ( + "Two Surface Tags of different values evaluated as unequal", + "Two Surface Tags of different values unexpectedly evaluated as equal" + ) -class TestSurfaceMaskFilter_BasicSurfaceTagCreation(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="TestSurfaceMaskFilter_BasicSurfaceTagCreation", args=["level"]) - - def run_test(self): - """ - Summary: - Verifies basic surface tag value equality +def SurfaceMaskFilter_BasicSurfaceTagCreation(): + """ + Summary: + Verifies basic surface tag value equality - Expected Behavior: - Surface tags of the same name are equal, and different names aren't. + Expected Behavior: + Surface tags of the same name are equal, and different names aren't. - Test Steps: - 1) Open level - 2) Create 2 new surface tags of identical names and verify they resolve as equal. - 3) Create another new tag of a different name and verify they resolve as different. + Test Steps: + 1) Open level + 2) Create 2 new surface tags of identical names and verify they resolve as equal. + 3) Create another new tag of a different name and verify they resolve as different. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - :return: None - """ - self.log("SurfaceTag test started") - - # Create a level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) - - tag1 = surface_data.SurfaceTag() - tag2 = surface_data.SurfaceTag() - - # Test 1: Verify that two tags with the same value are equal - tag1.SetTag('equal_test') - tag2.SetTag('equal_test') - self.log("SurfaceTag equal tag comparison is {} expected True".format(tag1.Equal(tag2))) - self.test_success = self.test_success and tag1.Equal(tag2) - - # Test 2: Verify that two tags with different values are not equal - tag2.SetTag('not_equal_test') - self.log("SurfaceTag not equal tag comparison is {} expected False".format(tag1.Equal(tag2))) - self.test_success = self.test_success and not tag1.Equal(tag2) - - self.log("SurfaceTag test finished") + :return: None + """ + + import azlmbr.surface_data as surface_data + from editor_python_test_tools.utils import Report + from editor_python_test_tools.utils import TestHelper as helper + + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") + + tag1 = surface_data.SurfaceTag() + tag2 = surface_data.SurfaceTag() + + # Test 1: Verify that two tags with the same value are equal + tag1.SetTag('equal_test') + tag2.SetTag('equal_test') + Report.result(Tests.tags_same_value_equal, tag1.Equal(tag2)) + + # Test 2: Verify that two tags with different values are not equal + tag2.SetTag('not_equal_test') + Report.result(Tests.tags_different_value_unequal, not tag1.Equal(tag2)) -test = TestSurfaceMaskFilter_BasicSurfaceTagCreation() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(SurfaceMaskFilter_BasicSurfaceTagCreation) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py index 1ce7962e6a..6438124698 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py @@ -4,147 +4,141 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -""" -C2561342: Exclusive Surface Masks tags function -""" -import os -import sys - -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.areasystem as areasystem -import azlmbr.bus as bus -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.math as math -import azlmbr.shape as shape -import azlmbr.surface_data as surface_data -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + default_exclusion_weight = ( + "Found the expected number of instances with Exclusion Weight set to defaults", + "Found an unexpected number of instances with Exclusion Weight set to defaults" + ) + exclusion_weight_below_one = ( + "Found the expected number of instances with Exclusion Weight set below 1", + "Found an unexpected number of instances with Exclusion Weight set below 1" + ) -class TestExclusiveSurfaceMasksTag(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="SurfaceMaskFilter_ExclusionList", args=["level"]) +def SurfaceMaskFilter_ExclusionList(): + """ + Summary: + New level is created and set up with surface shapes with varying surface tags. A simple vegetation area has been + created and Vegetation Surface Mask Filter component is added to entity with terrain hole exclusion tag. - def run_test(self): - """ - Summary: - New level is created and set up with surface shapes with varying surface tags. A simple vegetation area has been - created and Vegetation Surface Mask Filter component is added to entity with terrain hole exclusion tag. + Expected Behavior: + With default Exclusion settings, vegetation does not plant over the terrain holes. + With Exclusion Weight Max below 1.0, vegetation plants over the terrain holes. - Expected Behavior: - With default Exclusion settings, vegetation does not plant over the terrain holes. - With Exclusion Weight Max below 1.0, vegetation plants over the terrain holes. + Test Steps: + 1) Open an existing level + 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" + 3) Add a Vegetation Surface Mask Filter component to the entity. + 4) Create 2 surface entities to represent terrain and terrain hole surfaces + 5) Add an Exclusion List tag to the component, and set it to terrainHole. + 6) Check spawn count with default Exclusion Weights + 7) Check spawn count with Exclusion Weight Max set below 1.0 - Test Steps: - 1) Create a new level. - 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" - 3) Add a Vegetation Surface Mask Filter component to the entity. - 4) Create 2 surface entities to represent terrain and terrain hole surfaces - 5) Add an Exclusion List tag to the component, and set it to terrainHole. - 6) Check spawn count with default Exclusion Weights - 7) Check spawn count with Exclusion Weight Max set below 1.0 - - Note: - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + Note: + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - :return: None - """ + :return: None + """ - def update_surface_tag_exclusion_list(Entity, component_index, surface_tag): - tag_list = [surface_data.SurfaceTag()] + import os - # assign list with one surface tag to exclusion list - hydra.get_set_test(Entity, component_index, "Configuration|Exclusion|Surface Tags", tag_list) + import azlmbr.bus as bus + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.math as math + import azlmbr.surface_data as surface_data - # set that one surface tag element to required surface tag - component = Entity.components[component_index] - path = "Configuration|Exclusion|Surface Tags|[0]|Surface Tag" - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag) - new_value = hydra.get_component_property_value(component, path) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - if new_value == surface_tag: - self.log(f"Exclusive surface mask filter of {surface_tag} is added successfully") - else: - self.log(f"Failed to add an Exclusive surface mask filter of {surface_tag}") + def update_surface_tag_exclusion_list(Entity, component_index, surface_tag): + tag_list = [surface_data.SurfaceTag()] - def update_generated_surface_tag(Entity, component_index, surface_tag): - tag_list = [surface_data.SurfaceTag()] + # assign list with one surface tag to exclusion list + hydra.get_set_test(Entity, component_index, "Configuration|Exclusion|Surface Tags", tag_list) - # assign list with one surface tag to Generated Tags list - hydra.get_set_test(Entity, component_index, "Configuration|Generated Tags", tag_list) + # set that one surface tag element to required surface tag + component = Entity.components[component_index] + path = "Configuration|Exclusion|Surface Tags|[0]|Surface Tag" + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag) + new_value = hydra.get_component_property_value(component, path) - # set that one surface tag element to required surface tag - component = Entity.components[component_index] - path = "Configuration|Generated Tags|[0]|Surface Tag" - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag) - new_value = hydra.get_component_property_value(component, path) + if new_value == surface_tag: + Report.info(f"Exclusive surface mask filter of {surface_tag} is added successfully") + else: + Report.info(f"Failed to add an Exclusive surface mask filter of {surface_tag}") - if new_value == surface_tag: - self.log(f"Generated surface tag of {surface_tag} is added successfully") - else: - self.log(f"Failed to add Generated surface tag of {surface_tag}") + def update_generated_surface_tag(Entity, component_index, surface_tag): + tag_list = [surface_data.SurfaceTag()] - # 1) Create a new level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + # assign list with one surface tag to Generated Tags list + hydra.get_set_test(Entity, component_index, "Configuration|Generated Tags", tag_list) - general.set_current_view_position(512.0, 480.0, 38.0) + # set that one surface tag element to required surface tag + component = Entity.components[component_index] + path = "Configuration|Generated Tags|[0]|Surface Tag" + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag) + new_value = hydra.get_component_property_value(component, path) - # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" - entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + if new_value == surface_tag: + Report.info(f"Generated surface tag of {surface_tag} is added successfully") + else: + Report.info(f"Failed to add Generated surface tag of {surface_tag}") - # 3) Add a Vegetation Surface Mask Filter component to the entity. - spawner_entity.add_component("Vegetation Surface Mask Filter") + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # 4) Create 2 surface entities to represent terrain and terrain hole surfaces - surface_tags: dict = {"terrainHole": 1327698037, "terrain": 3363197873} - entity_position = math.Vector3(510.0, 512.0, 32.0) - surface_entity_1 = dynveg.create_surface_entity("Surface Entity 1", - entity_position, - 10.0, 10.0, 1.0) - update_generated_surface_tag(surface_entity_1, 1, surface_tags["terrainHole"]) + general.set_current_view_position(512.0, 480.0, 38.0) - entity_position = math.Vector3(520.0, 512.0, 32.0) - surface_entity_2 = dynveg.create_surface_entity("Surface Entity 2", - entity_position, - 10.0, 10.0, 1.0) - update_generated_surface_tag(surface_entity_2, 1, surface_tags["terrain"]) + # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" + entity_position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", + entity_position, + 10.0, 10.0, 10.0, + asset_path) - # 5) Add an Exclusion List tag to the component, and set it to "terrainHole". - update_surface_tag_exclusion_list(spawner_entity, 3, surface_tags["terrainHole"]) + # 3) Add a Vegetation Surface Mask Filter component to the entity. + spawner_entity.add_component("Vegetation Surface Mask Filter") - # 6) Check spawn count with default Exclusion Weights - general.idle_wait(2.0) # Allow a few seconds for instances to spawn - num_expected_instances = 39 - box = shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id) - num_found = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstanceCountInAabb', box) - self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances") - self.test_success = self.test_success and num_found == num_expected_instances + # 4) Create 2 surface entities to represent terrain and terrain hole surfaces + surface_tags: dict = {"terrainHole": 1327698037, "terrain": 3363197873} + entity_position = math.Vector3(510.0, 512.0, 32.0) + surface_entity_1 = dynveg.create_surface_entity("Surface Entity 1", + entity_position, + 10.0, 10.0, 1.0) + update_generated_surface_tag(surface_entity_1, 1, surface_tags["terrainHole"]) - # 7) Check spawn count with Exclusion Weight Max set below 1.0 - hydra.get_set_test(spawner_entity, 3, "Configuration|Exclusion|Weight Max", 0.9) - general.idle_wait(2.0) # Allow a few seconds for instances to spawn - num_expected_instances = 169 - num_found = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstanceCountInAabb', box) - self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances") - self.test_success = self.test_success and num_found == num_expected_instances + entity_position = math.Vector3(520.0, 512.0, 32.0) + surface_entity_2 = dynveg.create_surface_entity("Surface Entity 2", + entity_position, + 10.0, 10.0, 1.0) + update_generated_surface_tag(surface_entity_2, 1, surface_tags["terrain"]) + + # 5) Add an Exclusion List tag to the component, and set it to "terrainHole". + update_surface_tag_exclusion_list(spawner_entity, 3, surface_tags["terrainHole"]) + + # 6) Check spawn count with default Exclusion Weights + num_expected_instances = 39 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected_instances), 2.0) + Report.result(Tests.default_exclusion_weight, success) + + # 7) Check spawn count with Exclusion Weight Max set below 1.0 + hydra.get_set_test(spawner_entity, 3, "Configuration|Exclusion|Weight Max", 0.9) + num_expected_instances = 169 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected_instances), 2.0) + Report.result(Tests.exclusion_weight_below_one, success) -test = TestExclusiveSurfaceMasksTag() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(SurfaceMaskFilter_ExclusionList) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py index e394bc0cde..bbd1235abc 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py @@ -4,148 +4,142 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -""" -C2561341: Inclusive Surface Masks tags function -""" -import os -import sys - -sys.path.append(os.path.dirname(os.path.abspath(__file__))) -import azlmbr.areasystem as areasystem -import azlmbr.bus as bus -import azlmbr.editor as editor -import azlmbr.legacy.general as general -import azlmbr.math as math -import azlmbr.shape as shape -import azlmbr.surface_data as surface_data -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + default_inclusion_weight = ( + "Found the expected number of instances with Inclusion Weight set to defaults", + "Found an unexpected number of instances with Inclusion Weight set to defaults" + ) + inclusion_weight_below_one = ( + "Found the expected number of instances with Inclusion Weight set below 1", + "Found an unexpected number of instances with Inclusion Weight set below 1" + ) -class TestInclusiveSurfaceMasksTag(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="SurfaceMaskFilter_InclusionList", args=["level"]) - def run_test(self): - """ - Summary: - New level is created and set up with surface shapes with varying surface tags. A simple vegetation area has been - created and Vegetation Surface Mask Filter component is added to entity with terrain hole inclusion tag. +def SurfaceMaskFilter_InclusionList(): + """ + Summary: + New level is created and set up with surface shapes with varying surface tags. A simple vegetation area has been + created and Vegetation Surface Mask Filter component is added to entity with terrain hole inclusion tag. - Expected Behavior: - With default Inclusion Weights, vegetation draws over the terrain holes. - With Inclusion Weight Max set below 1.0, vegetation stops drawing over the terrain holes. + Expected Behavior: + With default Inclusion Weights, vegetation draws over the terrain holes. + With Inclusion Weight Max set below 1.0, vegetation stops drawing over the terrain holes. - Test Steps: - 1) Create a new level - 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" - 3) Add a Vegetation Surface Mask Filter component to the entity. - 4) Create 2 surface entities to represent terrain and terrain hole surfaces - 5) Add an Inclusion List tag to the component, and set it to "terrainHole". - 6) Check spawn count with default Inclusion Weights - 7) Check spawn count with Inclusion Weight Max set below 1.0 - - Note: - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + Test Steps: + 1) Open an existing level + 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" + 3) Add a Vegetation Surface Mask Filter component to the entity. + 4) Create 2 surface entities to represent terrain and terrain hole surfaces + 5) Add an Inclusion List tag to the component, and set it to "terrainHole". + 6) Check spawn count with default Inclusion Weights + 7) Check spawn count with Inclusion Weight Max set below 1.0 - :return: None - """ + Note: + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - def update_surface_tag_inclusion_list(Entity, component_index, surface_tag): - tag_list = [surface_data.SurfaceTag()] + :return: None + """ - # assign list with one surface tag to inclusion list - hydra.get_set_test(Entity, component_index, "Configuration|Inclusion|Surface Tags", tag_list) + import os - # set that one surface tag element to required surface tag - component = Entity.components[component_index] - path = "Configuration|Inclusion|Surface Tags|[0]|Surface Tag" - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag) - new_value = hydra.get_component_property_value(component, path) + import azlmbr.bus as bus + import azlmbr.editor as editor + import azlmbr.legacy.general as general + import azlmbr.math as math + import azlmbr.surface_data as surface_data - if new_value == surface_tag: - print("Inclusive surface mask filter of terrainHole is added successfully") - else: - print("Failed to add an Inclusive surface mask filter of terrainHole") - general.idle_wait(2.0) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - def update_generated_surface_tag(Entity, component_index, surface_tag): - tag_list = [surface_data.SurfaceTag()] + def update_surface_tag_inclusion_list(Entity, component_index, surface_tag): + tag_list = [surface_data.SurfaceTag()] - # assign list with one surface tag to Generated Tags list - hydra.get_set_test(Entity, component_index, "Configuration|Generated Tags", tag_list) + # assign list with one surface tag to inclusion list + hydra.get_set_test(Entity, component_index, "Configuration|Inclusion|Surface Tags", tag_list) - # set that one surface tag element to required surface tag - component = Entity.components[component_index] - path = "Configuration|Generated Tags|[0]|Surface Tag" - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag) - new_value = hydra.get_component_property_value(component, path) + # set that one surface tag element to required surface tag + component = Entity.components[component_index] + path = "Configuration|Inclusion|Surface Tags|[0]|Surface Tag" + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag) + new_value = hydra.get_component_property_value(component, path) - if new_value == surface_tag: - self.log(f"Generated surface tag of {surface_tag} is added successfully") - else: - self.log(f"Failed to add Generated surface tag of {surface_tag}") + if new_value == surface_tag: + Report.info("Inclusive surface mask filter of terrainHole is added successfully") + else: + Report.info("Failed to add an Inclusive surface mask filter of terrainHole") - # 1) Create a new level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + def update_generated_surface_tag(Entity, component_index, surface_tag): + tag_list = [surface_data.SurfaceTag()] - general.set_current_view_position(512.0, 480.0, 38.0) + # assign list with one surface tag to Generated Tags list + hydra.get_set_test(Entity, component_index, "Configuration|Generated Tags", tag_list) - # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" - entity_position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Instance Spawner", - entity_position, - 10.0, 10.0, 10.0, - asset_path) + # set that one surface tag element to required surface tag + component = Entity.components[component_index] + path = "Configuration|Generated Tags|[0]|Surface Tag" + editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, surface_tag) + new_value = hydra.get_component_property_value(component, path) - # 3) Add a Vegetation Surface Mask Filter component to the entity. - spawner_entity.add_component("Vegetation Surface Mask Filter") + if new_value == surface_tag: + Report.info(f"Generated surface tag of {surface_tag} is added successfully") + else: + Report.info(f"Failed to add Generated surface tag of {surface_tag}") - # 4) Create 2 surface entities to represent terrain and terrain hole surfaces - surface_tags: dict = {"terrainHole": 1327698037, "terrain": 3363197873} - entity_position = math.Vector3(510.0, 512.0, 32.0) - surface_entity_1 = dynveg.create_surface_entity("Surface Entity 1", - entity_position, - 10.0, 10.0, 1.0) - update_generated_surface_tag(surface_entity_1, 1, surface_tags["terrainHole"]) + # 1) Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - entity_position = math.Vector3(520.0, 512.0, 32.0) - surface_entity_2 = dynveg.create_surface_entity("Surface Entity 2", - entity_position, - 10.0, 10.0, 1.0) - update_generated_surface_tag(surface_entity_2, 1, surface_tags["terrain"]) + general.set_current_view_position(512.0, 480.0, 38.0) - # 5) Add an Inclusion List tag to the component, and set it to "terrainHole". - update_surface_tag_inclusion_list(spawner_entity, 3, surface_tags["terrainHole"]) + # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape" + entity_position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Instance Spawner", + entity_position, + 10.0, 10.0, 10.0, + asset_path) - # 6) Check spawn count with default Inclusion Weights - general.idle_wait(2.0) # Allow a few seconds for instances to spawn - num_expected_instances = 130 - box = shape.ShapeComponentRequestsBus(bus.Event, 'GetEncompassingAabb', spawner_entity.id) - num_found = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstanceCountInAabb', box) - self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances") - self.test_success = self.test_success and num_found == num_expected_instances + # 3) Add a Vegetation Surface Mask Filter component to the entity. + spawner_entity.add_component("Vegetation Surface Mask Filter") - # 7) Check spawn count with Inclusion Weight Max set below 1.0 - hydra.get_set_test(spawner_entity, 3, "Configuration|Inclusion|Weight Max", 0.9) - general.idle_wait(2.0) # Allow a few seconds for instances to update - num_expected_instances = 0 - num_found = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstanceCountInAabb', box) - self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances") - self.test_success = self.test_success and num_found == num_expected_instances + # 4) Create 2 surface entities to represent terrain and terrain hole surfaces + surface_tags: dict = {"terrainHole": 1327698037, "terrain": 3363197873} + entity_position = math.Vector3(510.0, 512.0, 32.0) + surface_entity_1 = dynveg.create_surface_entity("Surface Entity 1", + entity_position, + 10.0, 10.0, 1.0) + update_generated_surface_tag(surface_entity_1, 1, surface_tags["terrainHole"]) + + entity_position = math.Vector3(520.0, 512.0, 32.0) + surface_entity_2 = dynveg.create_surface_entity("Surface Entity 2", + entity_position, + 10.0, 10.0, 1.0) + update_generated_surface_tag(surface_entity_2, 1, surface_tags["terrain"]) + + # 5) Add an Inclusion List tag to the component, and set it to "terrainHole". + update_surface_tag_inclusion_list(spawner_entity, 3, surface_tags["terrainHole"]) + + # 6) Check spawn count with default Inclusion Weights + num_expected_instances = 130 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected_instances), 2.0) + Report.result(Tests.default_inclusion_weight, success) + + # 7) Check spawn count with Inclusion Weight Max set below 1.0 + hydra.get_set_test(spawner_entity, 3, "Configuration|Inclusion|Weight Max", 0.9) + num_expected_instances = 0 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected_instances), 2.0) + Report.result(Tests.inclusion_weight_below_one, success) -test = TestInclusiveSurfaceMasksTag() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(SurfaceMaskFilter_InclusionList) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py index f690e175de..d808d3f20b 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py @@ -5,89 +5,90 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -import azlmbr.math as math -import azlmbr.paths -import azlmbr.editor as editor -import azlmbr.bus as bus -import azlmbr.legacy.general as general -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + initial_density_instance_count = ( + "Found the expected number of instances with default sector density", + "Found an unexpected number of instances with default sector density" + ) + configured_density_instance_count = ( + "Found the expected number of instances with a sector density of 10", + "Found an unexpected number of instances with a sector density of 10" + ) -class TestSystemSettingsSectorPointDensity(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="SystemSettings_SectorPointDensity", args=["level"]) +def SystemSettings_SectorPointDensity(): + """ + Summary: + Sector Point Density increases/reduces the number of vegetation points within a sector - def run_test(self): - """ - Summary: - Sector Point Density increases/reduces the number of vegetation points within a sector + Expected Result: + Default value for Sector Point Density is 20. + 20 vegetation meshes appear on each side of the established vegetation area with the default value. + When altered, the specified number of vegetation meshes along a side of a vegetation area matches the value set + in Sector Point Density. - Expected Result: - Default value for Sector Point Density is 20. - 20 vegetation meshes appear on each side of the established vegetation area with the default value. - When altered, the specified number of vegetation meshes along a side of a vegetation area matches the value set - in Sector Point Density. + :return: None + """ - :return: None - """ + import os - INSTANCE_COUNT_BEFORE_DENSITY_CHANGE = 400 - INSTANCE_COUNT_AFTER_DENSITY_CHANGE = 100 + import azlmbr.math as math + import azlmbr.editor as editor + import azlmbr.bus as bus + import azlmbr.legacy.general as general - # Create empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - general.set_current_view_position(512.0, 480.0, 38.0) + INSTANCE_COUNT_BEFORE_DENSITY_CHANGE = 400 + INSTANCE_COUNT_AFTER_DENSITY_CHANGE = 100 - # Create basic vegetation entity - position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path) - dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0) + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Count the number of vegetation meshes along one side of the new vegetation area. # - result = self.wait_for_condition( - lambda: dynveg.validate_instance_count(position, 8.0, INSTANCE_COUNT_BEFORE_DENSITY_CHANGE), 2.0 - ) - self.log(f"Vegetation instances count equal to expected value before changing sector point density: {result}") + general.set_current_view_position(512.0, 480.0, 38.0) - # Add the Vegetation Debugger component to the Level Inspector - veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") + # Create basic vegetation entity + position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path) + dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0) - # Change Sector Point Density to 10 - editor.EditorComponentAPIBus( - bus.Broadcast, - "SetComponentProperty", - veg_system_settings_component, - "Configuration|Area System Settings|Sector Point Snap Mode", - 1, - ) - editor.EditorComponentAPIBus( - bus.Broadcast, - "SetComponentProperty", - veg_system_settings_component, - "Configuration|Area System Settings|Sector Point Density", - 10, - ) + # Count the number of vegetation instances in the vegetation area + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 8.0, + INSTANCE_COUNT_BEFORE_DENSITY_CHANGE), 2.0) + Report.result(Tests.initial_density_instance_count, result) - # Count the number of vegetation meshes along one side of the new vegetation area. - result = self.wait_for_condition( - lambda: dynveg.validate_instance_count(position, 8.0, INSTANCE_COUNT_AFTER_DENSITY_CHANGE), 2.0 - ) - self.log(f"Vegetation instances count equal to expected value after changing sector point density: {result}") + # Add the Vegetation Debugger component to the Level Inspector + veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") + + # Change Sector Point Density to 10 + editor.EditorComponentAPIBus( + bus.Broadcast, + "SetComponentProperty", + veg_system_settings_component, + "Configuration|Area System Settings|Sector Point Snap Mode", + 1, + ) + editor.EditorComponentAPIBus( + bus.Broadcast, + "SetComponentProperty", + veg_system_settings_component, + "Configuration|Area System Settings|Sector Point Density", + 10, + ) + + # Count the number of vegetation instances in the vegetation area + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count(position, 8.0, + INSTANCE_COUNT_AFTER_DENSITY_CHANGE), 2.0) + Report.result(Tests.configured_density_instance_count, result) -test = TestSystemSettingsSectorPointDensity() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(SystemSettings_SectorPointDensity) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py index 61aaedcf7c..fb660c964a 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py @@ -5,88 +5,91 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -import os -import sys -import azlmbr.math as math -import azlmbr.paths -import azlmbr.editor as editor -import azlmbr.bus as bus -import azlmbr.legacy.general as general -sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) -import editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + initial_sector_size_instance_count = ( + "Found the expected number of instances with default sector size", + "Found an unexpected number of instances with default sector size" + ) + configured_sector_size_instance_count = ( + "Found the expected number of instances with a sector size of 10", + "Found an unexpected number of instances with a sector size of 10" + ) -class TestSystemSettingsSectorSize(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix="SystemSettings_SectorSize", args=["level"]) +def SystemSettings_SectorSize(): + """ + Summary: + Sector Size In Meters increases/reduces the size of a sector - def run_test(self): - """ - Summary: - Sector Size In Meters increases/reduces the size of a sector + Expected Result: + The number of spawned vegetation meshes inside the vegetation area is identical after updating the Sector Size - Expected Result: - The number of spawned vegetation meshes inside the vegetation area is identical after updating the Sector Size + :return: None + """ - :return: None - """ + import os - VEGETATION_INSTANCE_COUNT = 400 + import azlmbr.math as math + import azlmbr.editor as editor + import azlmbr.bus as bus + import azlmbr.legacy.general as general - # Create empty level - self.test_success = self.create_level( - self.args["level"], - heightmap_resolution=1024, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=4096, - use_terrain=False, - ) + import editor_python_test_tools.hydra_editor_utils as hydra + 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 - general.set_current_view_position(512.0, 480.0, 38.0) + VEGETATION_INSTANCE_COUNT = 400 - # Create basic vegetation entity - position = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") - vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path) - dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0) + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Add the Vegetation Debugger component to the Level Inspector - veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") + general.set_current_view_position(512.0, 480.0, 38.0) - # Count the number of vegetation meshes along one side of the new vegetation area. - result = self.wait_for_condition( - lambda: dynveg.validate_instance_count(position, 8.0, VEGETATION_INSTANCE_COUNT), 2.0 - ) - self.log(f"Vegetation instances count equal to expected value before changing sector size: {result}") + # Create basic vegetation entity + position = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PinkFlower.dynamicslice") + vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path) + dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0) - # Change Sector Size in Meters to 10. - editor.EditorComponentAPIBus( - bus.Broadcast, - "SetComponentProperty", - veg_system_settings_component, - "Configuration|Area System Settings|Sector Point Snap Mode", - 1, - ) - editor.EditorComponentAPIBus( - bus.Broadcast, - "SetComponentProperty", - veg_system_settings_component, - "Configuration|Area System Settings|Sector Size In Meters", - 10, - ) + # Add the Vegetation Debugger component to the Level Inspector + veg_system_settings_component = hydra.add_level_component("Vegetation System Settings") - # Alter the Box Shape to be 10,10,1 - vegetation.get_set_test(1, "Box Shape|Box Configuration|Dimensions", math.Vector3(10.0, 10.0, 1.0)) + # Count the number of vegetation instances in the vegetation area + result = helper.wait_for_condition( + lambda: dynveg.validate_instance_count(position, 8.0, VEGETATION_INSTANCE_COUNT), 2.0 + ) + Report.result(Tests.initial_sector_size_instance_count, result) - # Count the number of vegetation meshes along one side of the new vegetation area. - result = self.wait_for_condition( - lambda: dynveg.validate_instance_count(position, 5.0, VEGETATION_INSTANCE_COUNT), 2.0 - ) - self.log(f"Vegetation instances count equal to expected value after changing sector size: {result}") + # Change Sector Size in Meters to 10. + editor.EditorComponentAPIBus( + bus.Broadcast, + "SetComponentProperty", + veg_system_settings_component, + "Configuration|Area System Settings|Sector Point Snap Mode", + 1, + ) + editor.EditorComponentAPIBus( + bus.Broadcast, + "SetComponentProperty", + veg_system_settings_component, + "Configuration|Area System Settings|Sector Size In Meters", + 10, + ) + + # Alter the Box Shape to be 10,10,1 + vegetation.get_set_test(1, "Box Shape|Box Configuration|Dimensions", math.Vector3(10.0, 10.0, 1.0)) + + # Count the number of vegetation instances in the vegetation area + result = helper.wait_for_condition( + lambda: dynveg.validate_instance_count(position, 5.0, VEGETATION_INSTANCE_COUNT), 2.0 + ) + Report.result(Tests.configured_sector_size_instance_count, result) -test = TestSystemSettingsSectorSize() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(SystemSettings_SectorSize) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py index 6f40d04854..a0657f3949 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py @@ -5,89 +5,85 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ -""" -This script tests for regressions of "vegetation instances don't despawn correctly -when the camera moves beyond the range of all active vegetation areas". -This creates a new level and a vegetation area with 400 instances. -The expectation is that we will have 400 instances in that area when the camera is centered on it, -and 0 instances when the camera is moved sufficiently far away. -""" - -import sys, os - -import azlmbr.legacy.general as general -import azlmbr.math as math - -sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests')) -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg +class Tests: + instance_validation_close = ( + "Instance count is as expected when within range of Spawner", + "Instance count was unexpected when within range of Spawner" + ) + instance_validation_far = ( + "No instances found when out of range of Spawner", + "Instances still found when out of range of Spawner" + ) -class TestVegetationInstances_DespawnWhenOutOfRange(EditorTestHelper): - def __init__(self): - EditorTestHelper.__init__(self, log_prefix='VegetationInstances_DespawnWhenOutOfRange', args=['level']) +def VegetationInstances_DespawnWhenOutOfRange(): + """ + Summary: + Verifies that vegetation instances properly spawn/despawn based on camera range. - def run_test(self): - """ - Summary: - Verifies that vegetation instances properly spawn/despawn based on camera range. + Expected Behavior: + Vegetation instances despawn when out of camera range. - Expected Behavior: - Vegetation instances despawn when out of camera range. + Test Steps: + 1) Open a simple level + 2) Create a simple vegetation area, and set the view position near the spawner. Verify instances plant. + 3) Move the view position away from the spawner. Verify instances despawn. - Test Steps: - 1) Create a new level - 2) Create a simple vegetation area, and set the view position near the spawner. Verify instances plant. - 3) Move the view position away from the spawner. Verify instances despawn. + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. - Note: - - This test file must be called from the Open 3D Engine Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. + :return: None + """ - :return: None - """ + import os - # Create a new level - self.test_success = self.create_level( - self.get_arg('level'), - heightmap_resolution=128, - heightmap_meters_per_pixel=1, - terrain_texture_resolution=128, - use_terrain=False) + import azlmbr.legacy.general as general + import azlmbr.math as math - # Create vegetation layer spawner - world_center = math.Vector3(512.0, 512.0, 32.0) - asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") - spawner_entity = dynveg.create_vegetation_area("Spawner Instance", world_center, 16.0, 16.0, 16.0, asset_path) + 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 - # Create a surface to spawn on - dynveg.create_surface_entity("Spawner Entity", world_center, 16.0, 16.0, 1.0) + # Open an existing simple level + helper.init_idle() + helper.open_level("Physics", "Base") - # Get the root position of our veg area and use it to position our camera. - # This is useful both to ensure that vegetation is spawned where we're querying and to - # visually verify the number of instances in each box - position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", spawner_entity.id) - general.set_current_view_position(position.x, position.y, position.z + 30.0) - general.set_current_view_rotation(-90.0, 0.0, 0.0) + # Create vegetation layer spawner + world_center = math.Vector3(512.0, 512.0, 32.0) + asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice") + spawner_entity = dynveg.create_vegetation_area("Spawner Instance", world_center, 16.0, 16.0, 16.0, asset_path) - # When centered over the veg area, we expect to find 400 instances. - # (16x16 area, 20 points per 16 meters) - num_expected = 400 - result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 2.0) - self.test_success = self.test_success and result + # Create a surface to spawn on + dynveg.create_surface_entity("Spawner Entity", world_center, 16.0, 16.0, 1.0) - # Move sufficiently far away from the veg area that it should all despawn. - general.set_current_view_position(position.x - 1000.0, position.y - 1000.0, position.z + 30.0) + # Get the root position of our veg area and use it to position our camera. + # This is useful both to ensure that vegetation is spawned where we're querying and to + # visually verify the number of instances in each box + position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", spawner_entity.id) + general.set_current_view_position(position.x, position.y, position.z + 30.0) + general.set_current_view_rotation(-90.0, 0.0, 0.0) - # We now expect to find 0 instances. If the bug exists, we will find 400 still. - num_expected = 0 - result = self.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, - num_expected), 2.0) - self.test_success = self.test_success and result + # When centered over the veg area, we expect to find 400 instances. + # (16x16 area, 20 points per 16 meters) + num_expected = 400 + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 2.0) + Report.result(Tests.instance_validation_close, result) + + # Move sufficiently far away from the veg area that it should all despawn. + general.set_current_view_position(position.x - 1000.0, position.y - 1000.0, position.z + 30.0) + + # We now expect to find 0 instances. If the bug exists, we will find 400 still. + num_expected = 0 + result = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape(spawner_entity.id, + num_expected), 2.0) + Report.result(Tests.instance_validation_far, result) -test = TestVegetationInstances_DespawnWhenOutOfRange() -test.run() +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(VegetationInstances_DespawnWhenOutOfRange) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main.py new file mode 100644 index 0000000000..4c02c887ef --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main.py @@ -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 +""" + +import os +import pytest +import sys + +sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../automatedtesting_shared') +from base import TestAutomationBase + + +@pytest.mark.SUITE_main +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +class TestAutomation(TestAutomationBase): + + def test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(self, request, workspace, editor, launcher_platform): + from .EditorScripts import DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks as test_module + self._run_test(request, workspace, editor, test_module) + + def test_EmptyInstanceSpawner_EmptySpawnerWorks(self, request, workspace, editor, launcher_platform): + from .EditorScripts import EmptyInstanceSpawner_EmptySpawnerWorks as test_module + self._run_test(request, workspace, editor, test_module) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main_Optimized.py new file mode 100644 index 0000000000..ded2dda4e9 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main_Optimized.py @@ -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 +""" + +import os +import pytest + +import ly_test_tools.environment.file_system as file_system +from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite + + +@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.") +@pytest.mark.SUITE_main +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +class TestAutomation(EditorTestSuite): + + class test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(EditorParallelTest): + from .EditorScripts import DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks as test_module + + class test_EmptyInstanceSpawner_EmptySpawnerWorks(EditorParallelTest): + from .EditorScripts import EmptyInstanceSpawner_EmptySpawnerWorks as test_module + + class test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(EditorParallelTest): + from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module + + class test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(EditorParallelTest): + from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module + + class test_AltitudeFilter_FilterStageToggle(EditorParallelTest): + from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module + + class test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(EditorSingleTest): + # Custom teardown to remove slice asset created during test + def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): + file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "slices", + "TestSlice_1.slice")], True, True) + file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "slices", + "TestSlice_2.slice")], True, True) + from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module + + class test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(EditorParallelTest): + from .EditorScripts import AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea as test_module + + class test_AssetWeightSelector_InstancesExpressBasedOnWeight(EditorParallelTest): + from .EditorScripts import AssetWeightSelector_InstancesExpressBasedOnWeight as test_module + + @pytest.mark.skip(reason="https://github.com/o3de/o3de/issues/4155") + class test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(EditorParallelTest): + from .EditorScripts import DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius as test_module + + @pytest.mark.skip(reason="https://github.com/o3de/o3de/issues/4155") + class test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(EditorParallelTest): + from .EditorScripts import DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius as test_module + + class test_SurfaceDataRefreshes_RemainsStable(EditorParallelTest): + from .EditorScripts import SurfaceDataRefreshes_RemainsStable as test_module + + class test_VegetationInstances_DespawnWhenOutOfRange(EditorParallelTest): + from .EditorScripts import VegetationInstances_DespawnWhenOutOfRange as test_module + + class test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(EditorParallelTest): + from .EditorScripts import InstanceSpawnerPriority_LayerAndSubPriority as test_module + + class test_LayerBlocker_InstancesBlockedInConfiguredArea(EditorParallelTest): + from .EditorScripts import LayerBlocker_InstancesBlockedInConfiguredArea as test_module + + class test_LayerSpawner_InheritBehaviorFlag(EditorParallelTest): + from .EditorScripts import LayerSpawner_InheritBehaviorFlag as test_module + + class test_LayerSpawner_InstancesPlantInAllSupportedShapes(EditorParallelTest): + from .EditorScripts import LayerSpawner_InstancesPlantInAllSupportedShapes as test_module + + class test_LayerSpawner_FilterStageToggle(EditorParallelTest): + from .EditorScripts import LayerSpawner_FilterStageToggle as test_module + + @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2038") + class test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(EditorParallelTest): + from .EditorScripts import LayerSpawner_InstancesRefreshUsingCorrectViewportCamera as test_module + + class test_MeshBlocker_InstancesBlockedByMesh(EditorParallelTest): + from .EditorScripts import MeshBlocker_InstancesBlockedByMesh as test_module + + class test_MeshBlocker_InstancesBlockedByMeshHeightTuning(EditorParallelTest): + from .EditorScripts import MeshBlocker_InstancesBlockedByMeshHeightTuning as test_module + + class test_MeshSurfaceTagEmitter_DependentOnMeshComponent(EditorParallelTest): + from .EditorScripts import MeshSurfaceTagEmitter_DependentOnMeshComponent as test_module + + class test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(EditorParallelTest): + from .EditorScripts import MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module + + class test_PhysXColliderSurfaceTagEmitter_E2E_Editor(EditorParallelTest): + from .EditorScripts import PhysXColliderSurfaceTagEmitter_E2E_Editor as test_module + + class test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(EditorParallelTest): + from .EditorScripts import PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets as test_module + + class test_PositionModifier_AutoSnapToSurfaceWorks(EditorParallelTest): + from .EditorScripts import PositionModifier_AutoSnapToSurfaceWorks as test_module + + class test_RotationModifier_InstancesRotateWithinRange(EditorParallelTest): + from .EditorScripts import RotationModifier_InstancesRotateWithinRange as test_module + + class test_RotationModifierOverrides_InstancesRotateWithinRange(EditorParallelTest): + from .EditorScripts import RotationModifierOverrides_InstancesRotateWithinRange as test_module + + class test_ScaleModifier_InstancesProperlyScale(EditorParallelTest): + from .EditorScripts import ScaleModifier_InstancesProperlyScale as test_module + + class test_ScaleModifierOverrides_InstancesProperlyScale(EditorParallelTest): + from .EditorScripts import ScaleModifierOverrides_InstancesProperlyScale as test_module + + class test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(EditorParallelTest): + from .EditorScripts import ShapeIntersectionFilter_InstancesPlantInAssignedShape as test_module + + class test_ShapeIntersectionFilter_FilterStageToggle(EditorParallelTest): + from .EditorScripts import ShapeIntersectionFilter_FilterStageToggle as test_module + + class test_SlopeAlignmentModifier_InstanceSurfaceAlignment(EditorParallelTest): + from .EditorScripts import SlopeAlignmentModifier_InstanceSurfaceAlignment as test_module + + class test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(EditorParallelTest): + from .EditorScripts import SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment as test_module + + class test_SurfaceMaskFilter_BasicSurfaceTagCreation(EditorParallelTest): + from .EditorScripts import SurfaceMaskFilter_BasicSurfaceTagCreation as test_module + + class test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(EditorParallelTest): + from .EditorScripts import SurfaceMaskFilter_ExclusionList as test_module + + class test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(EditorParallelTest): + from .EditorScripts import SurfaceMaskFilter_InclusionList as test_module + + class test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(EditorParallelTest): + from .EditorScripts import SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected as test_module + + class test_SystemSettings_SectorPointDensity(EditorParallelTest): + from .EditorScripts import SystemSettings_SectorPointDensity as test_module + + class test_SystemSettings_SectorSize(EditorParallelTest): + from .EditorScripts import SystemSettings_SectorSize as test_module + + class test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(EditorParallelTest): + from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module + + class test_DynamicSliceInstanceSpawner_Embedded_E2E_Editor(EditorSingleTest): + from .EditorScripts import DynamicSliceInstanceSpawner_Embedded_E2E as test_module + + # Custom teardown to remove test level created during test + def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): + file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], + True, True) + + class test_DynamicSliceInstanceSpawner_External_E2E_Editor(EditorSingleTest): + from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module + + # Custom teardown to remove test level created during test + def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): + file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], + True, True) + + class test_LayerBlender_E2E_Editor(EditorSingleTest): + from .EditorScripts import LayerBlender_E2E_Editor as test_module + + # Custom teardown to remove test level created during test + def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): + file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], + True, True) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py new file mode 100644 index 0000000000..2780c0f471 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py @@ -0,0 +1,283 @@ +""" +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 os +import pytest +import sys + +import ly_test_tools.environment.waiter as waiter +import ly_test_tools.environment.file_system as file_system +import editor_python_test_tools.hydra_test_utils as hydra +from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole + +sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../automatedtesting_shared') +from base import TestAutomationBase + + +@pytest.fixture +def remove_test_slice(request, workspace, project): + file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_1.slice")], True, + True) + file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_2.slice")], True, + True) + + def teardown(): + file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_1.slice")], True, + True) + file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_2.slice")], True, + True) + request.addfinalizer(teardown) + + +@pytest.fixture +def remote_console_instance(request): + console = RemoteConsole() + + def teardown(): + if console.connected: + console.stop() + + request.addfinalizer(teardown) + return console + + +@pytest.mark.SUITE_periodic +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +class TestAutomation(TestAutomationBase): + + def test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform): + from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module + self._run_test(request, workspace, editor, test_module) + + def test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform): + from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module + self._run_test(request, workspace, editor, test_module) + + def test_AltitudeFilter_FilterStageToggle(self, request, workspace, editor, launcher_platform): + from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module + self._run_test(request, workspace, editor, test_module) + + def test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(self, request, workspace, editor, remove_test_slice, launcher_platform): + from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module + self._run_test(request, workspace, editor, test_module) + + def test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(self, request, workspace, editor, launcher_platform): + from .EditorScripts import AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea as test_module + self._run_test(request, workspace, editor, test_module) + + def test_AssetWeightSelector_InstancesExpressBasedOnWeight(self, request, workspace, editor, launcher_platform): + from .EditorScripts import AssetWeightSelector_InstancesExpressBasedOnWeight as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155") + def test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(self, request, workspace, editor, launcher_platform): + from .EditorScripts import DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155") + def test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(self, request, workspace, editor, launcher_platform): + from .EditorScripts import DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius as test_module + self._run_test(request, workspace, editor, test_module) + + def test_SurfaceDataRefreshes_RemainsStable(self, request, workspace, editor, launcher_platform): + from .EditorScripts import SurfaceDataRefreshes_RemainsStable as test_module + self._run_test(request, workspace, editor, test_module) + + def test_VegetationInstances_DespawnWhenOutOfRange(self, request, workspace, editor, launcher_platform): + from .EditorScripts import VegetationInstances_DespawnWhenOutOfRange as test_module + self._run_test(request, workspace, editor, test_module) + + def test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(self, request, workspace, editor, launcher_platform): + from .EditorScripts import InstanceSpawnerPriority_LayerAndSubPriority as test_module + self._run_test(request, workspace, editor, test_module) + + def test_LayerBlocker_InstancesBlockedInConfiguredArea(self, request, workspace, editor, launcher_platform): + from .EditorScripts import LayerBlocker_InstancesBlockedInConfiguredArea as test_module + self._run_test(request, workspace, editor, test_module) + + def test_LayerSpawner_InheritBehaviorFlag(self, request, workspace, editor, launcher_platform): + from .EditorScripts import LayerSpawner_InheritBehaviorFlag as test_module + self._run_test(request, workspace, editor, test_module) + + def test_LayerSpawner_InstancesPlantInAllSupportedShapes(self, request, workspace, editor, launcher_platform): + from .EditorScripts import LayerSpawner_InstancesPlantInAllSupportedShapes as test_module + self._run_test(request, workspace, editor, test_module) + + def test_LayerSpawner_FilterStageToggle(self, request, workspace, editor, launcher_platform): + from .EditorScripts import LayerSpawner_FilterStageToggle as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2038") + def test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(self, request, workspace, editor, launcher_platform): + from .EditorScripts import LayerSpawner_InstancesRefreshUsingCorrectViewportCamera as test_module + self._run_test(request, workspace, editor, test_module) + + def test_MeshBlocker_InstancesBlockedByMesh(self, request, workspace, editor, launcher_platform): + from .EditorScripts import MeshBlocker_InstancesBlockedByMesh as test_module + self._run_test(request, workspace, editor, test_module) + + def test_MeshBlocker_InstancesBlockedByMeshHeightTuning(self, request, workspace, editor, launcher_platform): + from .EditorScripts import MeshBlocker_InstancesBlockedByMeshHeightTuning as test_module + self._run_test(request, workspace, editor, test_module) + + def test_MeshSurfaceTagEmitter_DependentOnMeshComponent(self, request, workspace, editor, launcher_platform): + from .EditorScripts import MeshSurfaceTagEmitter_DependentOnMeshComponent as test_module + self._run_test(request, workspace, editor, test_module) + + def test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform): + from .EditorScripts import MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module + self._run_test(request, workspace, editor, test_module) + + def test_PhysXColliderSurfaceTagEmitter_E2E_Editor(self, request, workspace, editor, launcher_platform): + from .EditorScripts import PhysXColliderSurfaceTagEmitter_E2E_Editor as test_module + self._run_test(request, workspace, editor, test_module) + + def test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(self, request, workspace, editor, launcher_platform): + from .EditorScripts import PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets as test_module + self._run_test(request, workspace, editor, test_module) + + def test_PositionModifier_AutoSnapToSurfaceWorks(self, request, workspace, editor, launcher_platform): + from .EditorScripts import PositionModifier_AutoSnapToSurfaceWorks as test_module + self._run_test(request, workspace, editor, test_module) + + def test_RotationModifier_InstancesRotateWithinRange(self, request, workspace, editor, launcher_platform): + from .EditorScripts import RotationModifier_InstancesRotateWithinRange as test_module + self._run_test(request, workspace, editor, test_module) + + def test_RotationModifierOverrides_InstancesRotateWithinRange(self, request, workspace, editor, launcher_platform): + from .EditorScripts import RotationModifierOverrides_InstancesRotateWithinRange as test_module + self._run_test(request, workspace, editor, test_module) + + def test_ScaleModifier_InstancesProperlyScale(self, request, workspace, editor, launcher_platform): + from .EditorScripts import ScaleModifier_InstancesProperlyScale as test_module + self._run_test(request, workspace, editor, test_module) + + def test_ScaleModifierOverrides_InstancesProperlyScale(self, request, workspace, editor, launcher_platform): + from .EditorScripts import ScaleModifierOverrides_InstancesProperlyScale as test_module + self._run_test(request, workspace, editor, test_module) + + def test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(self, request, workspace, editor, launcher_platform): + from .EditorScripts import ShapeIntersectionFilter_InstancesPlantInAssignedShape as test_module + self._run_test(request, workspace, editor, test_module) + + def test_ShapeIntersectionFilter_FilterStageToggle(self, request, workspace, editor, launcher_platform): + from .EditorScripts import ShapeIntersectionFilter_FilterStageToggle as test_module + self._run_test(request, workspace, editor, test_module) + + def test_SlopeAlignmentModifier_InstanceSurfaceAlignment(self, request, workspace, editor, launcher_platform): + from .EditorScripts import SlopeAlignmentModifier_InstanceSurfaceAlignment as test_module + self._run_test(request, workspace, editor, test_module) + + def test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(self, request, workspace, editor, launcher_platform): + from .EditorScripts import SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment as test_module + self._run_test(request, workspace, editor, test_module) + + def test_SurfaceMaskFilter_BasicSurfaceTagCreation(self, request, workspace, editor, launcher_platform): + from .EditorScripts import SurfaceMaskFilter_BasicSurfaceTagCreation as test_module + self._run_test(request, workspace, editor, test_module) + + def test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(self, request, workspace, editor, launcher_platform): + from .EditorScripts import SurfaceMaskFilter_ExclusionList as test_module + self._run_test(request, workspace, editor, test_module) + + def test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(self, request, workspace, editor, launcher_platform): + from .EditorScripts import SurfaceMaskFilter_InclusionList as test_module + self._run_test(request, workspace, editor, test_module) + + def test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(self, request, workspace, editor, launcher_platform): + from .EditorScripts import SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected as test_module + self._run_test(request, workspace, editor, test_module) + + def test_SystemSettings_SectorPointDensity(self, request, workspace, editor, launcher_platform): + from .EditorScripts import SystemSettings_SectorPointDensity as test_module + self._run_test(request, workspace, editor, test_module) + + def test_SystemSettings_SectorSize(self, request, workspace, editor, launcher_platform): + from .EditorScripts import SystemSettings_SectorSize as test_module + self._run_test(request, workspace, editor, test_module) + + def test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(self, request, workspace, editor, launcher_platform): + from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module + self._run_test(request, workspace, editor, test_module) + + +@pytest.mark.SUITE_periodic +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +@pytest.mark.parametrize("level", ["tmp_level"]) +class TestAutomationE2E(TestAutomationBase): + + # The following tests must run in order, please do not move tests out of order + + @pytest.mark.parametrize("launcher_platform", ['windows_editor']) + def test_DynamicSliceInstanceSpawner_Embedded_E2E_Editor(self, request, workspace, project, level, editor, launcher_platform): + # Ensure our test level does not already exist + file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) + + from .EditorScripts import DynamicSliceInstanceSpawner_Embedded_E2E as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.parametrize("launcher_platform", ['windows']) + def test_DynamicSliceInstanceSpawner_Embedded_E2E_Launcher(self, workspace, launcher, level, + remote_console_instance, project, launcher_platform): + + expected_lines = [ + "Instances found in area = 400" + ] + + hydra.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, launch_ap=False) + + # Cleanup our temp level + file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) + + @pytest.mark.parametrize("launcher_platform", ['windows_editor']) + def test_DynamicSliceInstanceSpawner_External_E2E_Editor(self, request, workspace, project, level, editor, launcher_platform): + # Ensure our test level does not already exist + file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) + + from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.parametrize("launcher_platform", ['windows']) + def test_DynamicSliceInstanceSpawner_External_E2E_Launcher(self, workspace, launcher, level, + remote_console_instance, project, launcher_platform): + + expected_lines = [ + "Instances found in area = 400" + ] + + hydra.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, launch_ap=False) + + # Cleanup our temp level + file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) + + @pytest.mark.parametrize("launcher_platform", ['windows_editor']) + def test_LayerBlender_E2E_Editor(self, request, workspace, project, level, editor, launcher_platform): + # Ensure our test level does not already exist + file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) + + from .EditorScripts import LayerBlender_E2E_Editor as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.parametrize("launcher_platform", ['windows']) + @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4170") + def test_LayerBlender_E2E_Launcher(self, workspace, launcher, level, + remote_console_instance, project, launcher_platform): + + launcher.args.extend(["-rhi=Null"]) + launcher.start(launch_ap=False) + assert launcher.is_alive(), "Launcher failed to start" + + # Wait for test script to quit the launcher. If wait_for returns exc, test was not successful + waiter.wait_for(lambda: not launcher.is_alive(), timeout=300) + + # Verify launcher quit successfully and did not crash + ret_code = launcher.get_returncode() + assert ret_code == 0, "Test failed. See Game.log for details" + + # Cleanup our temp level + file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_AltitudeFilter.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_AltitudeFilter.py deleted file mode 100755 index ec8c225d3b..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_AltitudeFilter.py +++ /dev/null @@ -1,111 +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 -""" - -import os -import pytest -import logging -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip('ly_test_tools') -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestAltitudeFilter(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id('C4814463', 'C4847477') - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_filter - def test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(self, request, editor, level, - launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "'Planting Surface' created", - "'Planting Surface Elevated' created", - "instance count validation: True (found=3200, expected=3200)", - "instance count validation: True (found=1600, expected=1600)", - "instance count validation: True (found=400, expected=400)", - "AltitudeFilterComponentAndOverrides: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py", - expected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C4847476") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_filter - def test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(self, request, editor, level, - launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "'Planting Surface' created", - "'Planting Surface Elevated' created", - "instance count validation: True (found=800, expected=800)", - "'Shape Sampler' created", - "instance count validation: True (found=400, expected=400)", - "AltitudeFilterShapeSample: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py", - expected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C4847478") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_filter - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2303") - def test_AltitudeFilter_FilterStageToggle(self, request, editor, level, workspace, launcher_platform): - cfg_args = [level] - - expected_lines = [ - "AltitudeFilter_FilterStageToggle: test started", - "AltitudeFilter_FilterStageToggle: Vegetation instances count equal to expected value for PREPROCESS filter stage: True", - "AltitudeFilter_FilterStageToggle: Vegetation instances count equal to expected value for POSTPROCESS filter stage: True", - "AltitudeFilter_FilterStageToggle: result=SUCCESS", - ] - - unexpected_lines = [ - "AltitudeFilter_FilterStageToggle: Vegetation instances count equal to expected value for PREPROCESS filter stage: False", - "AltitudeFilter_FilterStageToggle: Vegetation instances count equal to expected value for POSTPROCESS filter stage: False", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "AltitudeFilter_FilterStageToggle.py", - expected_lines=expected_lines, - unexpected_lines=unexpected_lines, - cfg_args=cfg_args - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_AreaComponentSlices.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_AreaComponentSlices.py deleted file mode 100755 index 7c72105957..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_AreaComponentSlices.py +++ /dev/null @@ -1,72 +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 -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip("ly_test_tools") -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestAreaComponents(object): - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - # Cleanup the test slices - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_1.slice")], True, True) - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_2.slice")], True, True) - - def teardown(): - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - # Cleanup the test slices - file_system.delete( - [os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_1.slice")], True, True - ) - file_system.delete( - [os.path.join(workspace.paths.engine_root(), project, "slices", "TestSlice_2.slice")], True, True - ) - - request.addfinalizer(teardown) - - @pytest.mark.test_case_id("C2627900", "C2627905", "C2627904") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_misc - def test_AreaComponents_SliceCreationVisibilityToggleWorks(self, request, editor, level, workspace, - launcher_platform): - cfg_args = [level] - - expected_lines = [ - "AreaComponentSlices_SliceCreationAndVisibilityToggle: test started", - "AreaComponentSlices_SliceCreationAndVisibilityToggle: Slice has been created successfully (entity with spawner component): True", - "AreaComponentSlices_SliceCreationAndVisibilityToggle: Vegetation plants initially when slice is shown: True", - "AreaComponentSlices_SliceCreationAndVisibilityToggle: Vegetation is cleared when slice is hidden: True", - "AreaComponentSlices_SliceCreationAndVisibilityToggle: Slice has been created successfully (entity with blender component): True", - "AreaComponentSlices_SliceCreationAndVisibilityToggle: result=SUCCESS", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "AreaComponentSlices_SliceCreationAndVisibilityToggle.py", - expected_lines=expected_lines, - cfg_args=cfg_args - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_AssetListCombiner.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_AssetListCombiner.py deleted file mode 100755 index 5deaa9199e..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_AssetListCombiner.py +++ /dev/null @@ -1,63 +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 -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip("ly_test_tools") -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") - - -@pytest.mark.parametrize('project', ['AutomatedTesting']) -@pytest.mark.parametrize('level', ['tmp_level']) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestAssetListCombiner(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C4762374", "C4762373") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_misc - def test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(self, request, editor, level, - launcher_platform): - - expected_lines = [ - "'Asset List 1' created", - "'Asset List 2' created", - "'Asset List 3' created", - "'Surface Entity' created", - "'Spawner Entity' created", - "Spawner Entity Configuration|Descriptor Providers: SUCCESS", - "Spawner Entity Configuration|Gradient|Gradient Entity Id: SUCCESS", - "instance count validation: True (found=200, expected=200.0)", - "Spawner Entity Configuration|Descriptor Providers|[1]: SUCCESS", - "instance count validation: True (found=400, expected=400)", - "instance count validation: True (found=0, expected=0)", - "AssetListCombiner_CombinedDescriptors: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py", - expected_lines=expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_AssetWeightSelector.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_AssetWeightSelector.py deleted file mode 100755 index f3cb2f4647..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_AssetWeightSelector.py +++ /dev/null @@ -1,60 +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 -""" - -""" -C6269654: Vegetation areas using weight selectors properly distribute instances according to Sort By Weight setting -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip('ly_test_tools') -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestAssetWeightSelector(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C6269654", "C4762368") - @pytest.mark.SUITE_sandbox - @pytest.mark.dynveg_filter - def test_AssetWeightSelector_InstancesExpressBasedOnWeight(self, request, editor, level, launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "Instance Spawner Configuration|Embedded Assets|[1]|Instance|Slice Asset: SUCCESS", - "'Planting Surface' created", - "Configuration|Embedded Assets|[0]|Weight set to 50.0", - "Instance Spawner Configuration|Allow Empty Assets: SUCCESS", - "AssetWeightSelector_SortByWeight: result=SUCCESS", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "AssetWeightSelector_InstancesExpressBasedOnWeight.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DistanceBetweenFilter.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DistanceBetweenFilter.py deleted file mode 100755 index 6d1b688315..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DistanceBetweenFilter.py +++ /dev/null @@ -1,76 +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 -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip('ly_test_tools') -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestDistanceBetweenFilter(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C4851066") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_filter - def test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(self, request, editor, level, launcher_platform): - - expected_lines = [ - "Configuration|Radius Min set to 1.0", - "Configuration|Radius Min set to 2.0", - "Configuration|Radius Min set to 16.0", - "DistanceBetweenFilterComponent: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py", - expected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C4814458") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_filter - def test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(self, request, editor, level, - launcher_platform): - - expected_lines = [ - "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min set to 1.0", - "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min set to 2.0", - "Configuration|Embedded Assets|[0]|Distance Between Filter (Radius)|Radius Min set to 16.0", - "DistanceBetweenFilterComponentOverrides: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynVeg_Regressions.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynVeg_Regressions.py deleted file mode 100755 index b15e1d552d..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynVeg_Regressions.py +++ /dev/null @@ -1,78 +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 -""" - -import os -import pytest - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip('ly_test_tools') - -import editor_python_test_tools.hydra_test_utils as hydra -import ly_test_tools.environment.file_system as file_system - -test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') - - -@pytest.mark.parametrize('project', ['AutomatedTesting']) -@pytest.mark.parametrize('level', ['tmp_level']) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class Test_DynVeg_Regressions(object): - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - # delete temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - # Setup - add the teardown finalizer - request.addfinalizer(teardown) - - # Make sure the temp level doesn't already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C29470845") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_regression - def test_SurfaceDataRefreshes_RemainsStable(self, request, editor, level, launcher_platform): - - expected_lines = [ - "SurfaceDataRefreshes_RemainsStable: test started", - "SurfaceDataRefreshes_RemainsStable: test finished", - "SurfaceDataRefreshes_RemainsStable: result=SUCCESS" - ] - - unexpected_lines = [ - "Sector update mode is 'RebuildSurfaceCache' but sector doesn't exist" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - 'SurfaceDataRefreshes_RemainsStable.py', - expected_lines=expected_lines, - unexpected_lines=unexpected_lines, - cfg_args=[level] - ) - - @pytest.mark.SUITE_periodic - def test_VegetationInstances_DespawnWhenOutOfRange(self, request, editor, level, launcher_platform): - - expected_lines = [ - "VegetationInstances_DespawnWhenOutOfRange: test started", - "VegetationInstances_DespawnWhenOutOfRange: test finished", - "VegetationInstances_DespawnWhenOutOfRange: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - 'VegetationInstances_DespawnWhenOutOfRange.py', - expected_lines=expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py deleted file mode 100755 index 0f83f44094..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py +++ /dev/null @@ -1,140 +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 -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip('ly_test_tools') -import ly_test_tools.environment.file_system as file_system -import ly_test_tools._internal.pytest_plugin as internal_plugin -import editor_python_test_tools.hydra_test_utils as hydra -from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') - - -@pytest.mark.parametrize('project', ['AutomatedTesting']) -@pytest.mark.parametrize('level', ['tmp_level']) -@pytest.mark.usefixtures("automatic_process_killer") -class TestDynamicSliceInstanceSpawner(object): - - @pytest.fixture - def remote_console_instance(self, request): - console = RemoteConsole() - - def teardown(): - if console.connected: - console.stop() - - request.addfinalizer(teardown) - - return console - - @pytest.mark.test_case_id("C28851763") - @pytest.mark.SUITE_main - @pytest.mark.dynveg_area - @pytest.mark.parametrize("launcher_platform", ['windows_editor']) - def test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(self, request, editor, level, workspace, project, - launcher_platform): - - # Skip test if running against Debug build - if "debug" in internal_plugin.build_directory: - pytest.skip("Does not execute against debug builds.") - - # Ensure temp level does not already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - cfg_args = [level] - - expected_lines = [ - "DynamicSliceInstanceSpawner: test started", - "DynamicSliceInstanceSpawner: test finished", - "DynamicSliceInstanceSpawner: result=SUCCESS" - ] - - hydra.launch_and_validate_results(request, test_directory, editor, - 'DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks.py', - expected_lines=expected_lines, cfg_args=cfg_args) - - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id('C2574330') - @pytest.mark.BAT - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_area - @pytest.mark.parametrize("launcher_platform", ['windows_editor']) - def test_DynamicSliceInstanceSpawner_Embedded_E2E_Editor(self, workspace, request, editor, level, project, - launcher_platform): - # Ensure temp level does not already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - expected_lines = [ - "'Instance Spawner' created", - "'Planting Surface' created", - "DynamicSliceInstanceSpawnerEmbeddedEditor: Expected 400 instances - Found 400 instances", - "DynamicSliceInstanceSpawnerEmbeddedEditor: result=SUCCESS" - ] - - hydra.launch_and_validate_results(request, test_directory, editor, "DynamicSliceInstanceSpawner_Embedded_E2E.py", - expected_lines, cfg_args=[level]) - - @pytest.mark.test_case_id('C2574330') - @pytest.mark.BAT - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_area - @pytest.mark.parametrize("launcher_platform", ['windows']) - def test_DynamicSliceInstanceSpawner_Embedded_E2E_Launcher(self, workspace, launcher, level, - remote_console_instance, project, launcher_platform): - - expected_lines = [ - "Instances found in area = 400" - ] - - hydra.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines) - - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id('C4762367') - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_area - @pytest.mark.parametrize("launcher_platform", ['windows_editor']) - def test_DynamicSliceInstanceSpawner_External_E2E_Editor(self, workspace, request, editor, level, project, - launcher_platform): - # Ensure temp level does not already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - expected_lines = [ - "Spawner entity created", - "'Planting Surface' created", - "DynamicSliceInstanceSpawnerExternalEditor: Expected 400 instances - Found 400 instances", - "DynamicSliceInstanceSpawnerExternalEditor: result=SUCCESS" - ] - - hydra.launch_and_validate_results(request, test_directory, editor, "DynamicSliceInstanceSpawner_External_E2E.py", - expected_lines, cfg_args=[level]) - - @pytest.mark.test_case_id('C4762367') - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_area - @pytest.mark.parametrize("launcher_platform", ['windows']) - def test_DynamicSliceInstanceSpawner_External_E2E_Launcher(self, workspace, launcher, level, - remote_console_instance, project, launcher_platform): - - expected_lines = [ - "Instances found in area = 400" - ] - - hydra.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines) - - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py deleted file mode 100755 index 1fc88b816f..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py +++ /dev/null @@ -1,54 +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 -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip('ly_test_tools') -import ly_test_tools.environment.file_system as file_system -import ly_test_tools._internal.pytest_plugin as internal_plugin -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') - - -@pytest.mark.parametrize('project', ['AutomatedTesting']) -@pytest.mark.parametrize('level', ['tmp_level']) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestEmptyInstanceSpawner(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C28851762") - @pytest.mark.SUITE_main - @pytest.mark.dynveg_area - def test_EmptyInstanceSpawner_EmptySpawnerWorks(self, request, editor, level, launcher_platform): - - # Skip test if running against Debug build - if "debug" in internal_plugin.build_directory: - pytest.skip("Does not execute against debug builds.") - - cfg_args = [level] - - expected_lines = [ - "EmptyInstanceSpawner: test started", - "EmptyInstanceSpawner: test finished", - "EmptyInstanceSpawner: result=SUCCESS" - ] - - hydra.launch_and_validate_results(request, test_directory, editor, 'EmptyInstanceSpawner_EmptySpawnerWorks.py', - expected_lines=expected_lines, cfg_args=cfg_args) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_InstanceSpawnerPriority.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_InstanceSpawnerPriority.py deleted file mode 100755 index 955c20a37d..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_InstanceSpawnerPriority.py +++ /dev/null @@ -1,63 +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 -""" - -""" -C5747383: Vegetation areas with a higher Layer Priority plant over those with a lower Layer Priority -C4762382: Vegetation areas with a higher Sub Priority plant over those with a lower Sub Priority -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip('ly_test_tools') -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestInstanceSpawnerPriority(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C5747383", "C4762382") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_misc - def test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(self, request, editor, level, - launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "'Instance Blocker' created", - "'Planting Surface' created", - "Instance Blocker Configuration|Layer Priority: SUCCESS", - "Instance Spawner Configuration|Sub Priority: SUCCESS", - "Instance Blocker Configuration|Sub Priority: SUCCESS", - "InstanceSpawnerPriority: result=SUCCESS", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "InstanceSpawnerPriority_LayerAndSubPriority.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerBlender.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerBlender.py deleted file mode 100755 index 13f2a569c1..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerBlender.py +++ /dev/null @@ -1,104 +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 -""" - -""" -C2627906: A simple Vegetation Layer Blender area can be created. -The specified assets plant in the specified blend area and are visible in the Viewport in -Edit Mode, Game Mode. -""" - -import os -import pytest - -pytest.importorskip("ly_test_tools") - -import ly_remote_console.remote_console_commands as remote_console_commands -import ly_test_tools.environment.waiter as waiter -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - - -test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") -remote_console_port = 4600 -listener_timeout = 120 - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -class TestLayerBlender(object): - - @pytest.fixture - def remote_console_instance(self, request): - console = remote_console_commands.RemoteConsole() - - def teardown(): - if console.connected: - console.stop() - - request.addfinalizer(teardown) - - return console - - @pytest.mark.test_case_id("C2627906") - @pytest.mark.BAT - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_area - @pytest.mark.parametrize("launcher_platform", ['windows_editor']) - def test_LayerBlender_E2E_Editor(self, workspace, request, editor, project, level, launcher_platform): - # Make sure temp level doesn't already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - expected_lines = [ - "'Purple Spawner' created", - "'Pink Spawner' created", - "'Surface Entity' created", - "Entity has a Vegetation Layer Spawner component", - "Entity has a Vegetation Asset List component", - "Entity has a Box Shape component", - "Purple Spawner Box Shape|Box Configuration|Dimensions: SUCCESS", - "Pink Spawner Box Shape|Box Configuration|Dimensions: SUCCESS", - "Purple Spawner Configuration|Embedded Assets|[0]: SUCCESS", - "Pink Spawner Configuration|Embedded Assets|[0]: SUCCESS", - "'Blender' created", - "Entity has a Vegetation Layer Blender component", - "Entity has a Box Shape component", - "Blender Configuration|Vegetation Areas: SUCCESS", - "Blender Box Shape|Box Configuration|Dimensions: SUCCESS", - "LayerBlender_E2E_Editor: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "LayerBlender_E2E_Editor.py", - expected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C2627906") - @pytest.mark.BAT - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_area - @pytest.mark.parametrize("launcher_platform", ['windows']) - def test_LayerBlender_E2E_Launcher(self, workspace, project, launcher, level, remote_console_instance, - launcher_platform): - - launcher.args.extend(["-rhi=Null"]) - launcher.start() - assert launcher.is_alive(), "Launcher failed to start" - - # Wait for test script to quit the launcher. If wait_for returns exc, test was not successful - waiter.wait_for(lambda: not launcher.is_alive(), timeout=300) - - # Verify launcher quit successfully and did not crash - ret_code = launcher.get_returncode() - assert ret_code == 0, "Test failed. See Game.log for details" - - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerBlocker.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerBlocker.py deleted file mode 100755 index d0889f17a4..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerBlocker.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Copyright (c) Contributors to the Open 3D Engine Project. -For complete copyright and license terms please see the LICENSE at the root of this distribution. - -SPDX-License-Identifier: Apache-2.0 OR MIT -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip('ly_test_tools') -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestLayerBlocker(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C2793772") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_area - def test_LayerBlocker_InstancesBlockedInConfiguredArea(self, request, editor, level, launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "'Surface Entity' created", - "instance count validation: True (found=400, expected=400)", - "'Blocker Area' created", - "instance count validation: True (found=384, expected=384)", - "LayerBlocker_InstancesBlocked: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "LayerBlocker_InstancesBlockedInConfiguredArea.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerSpawner.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerSpawner.py deleted file mode 100755 index 65d994f841..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerSpawner.py +++ /dev/null @@ -1,142 +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 -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip("ly_test_tools") -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestLayerSpawner(object): - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - def teardown(): - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - request.addfinalizer(teardown) - - @pytest.mark.test_case_id("C4762381") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_misc - def test_LayerSpawner_InheritBehaviorFlag(self, request, editor, level, workspace, launcher_platform): - - expected_lines = [ - "LayerSpawner_InheritBehavior: test started", - "LayerSpawner_InheritBehavior: Vegetation is not planted when Inherit Behavior flag is checked: True", - "LayerSpawner_InheritBehavior: Vegetation plant when Inherit Behavior flag is unchecked: True", - "LayerSpawner_InheritBehavior: result=SUCCESS", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "LayerSpawner_InheritBehaviorFlag.py", - expected_lines=expected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C2802020") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_misc - def test_LayerSpawner_InstancesPlantInAllSupportedShapes(self, request, editor, level, launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "'Surface Entity' created", - "Entity has a Vegetation Reference Shape component", - "Entity has a Box Shape component", - "box Box Shape|Box Configuration|Dimensions: SUCCESS", - "Entity has a Capsule Shape component", - "capsule Capsule Shape|Capsule Configuration|Height: SUCCESS", - "capsule Capsule Shape|Capsule Configuration|Radius: SUCCESS", - "Entity has a Tube Shape component", - "Entity has a Spline component", - "Entity has a Sphere Shape component", - "sphere Sphere Shape|Sphere Configuration|Radius: SUCCESS", - "Entity has a Cylinder Shape component", - "cylinder Cylinder Shape|Cylinder Configuration|Radius: SUCCESS", - "cylinder Cylinder Shape|Cylinder Configuration|Height: SUCCESS", - "Entity has a Polygon Prism Shape component", - "Entity has a Compound Shape component", - "Compound Configuration|Child Shape Entities|[0]: SUCCESS", - "Compound Configuration|Child Shape Entities|[1]: SUCCESS", - "Compound Configuration|Child Shape Entities|[2]: SUCCESS", - "Compound Configuration|Child Shape Entities|[3]: SUCCESS", - "Compound Configuration|Child Shape Entities|[4]: SUCCESS", - "Compound Configuration|Child Shape Entities|[5]: SUCCESS", - "Instance Spawner Configuration|Shape Entity Id: SUCCESS", - "TestLayerSpawner_AllShapesPlant: result=SUCCESS", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "LayerSpawner_InstancesPlantInAllSupportedShapes.py", - expected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C4765973") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_misc - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2303") - def test_LayerSpawner_FilterStageToggle(self, request, editor, level, workspace, launcher_platform): - - expected_lines = [ - "LayerSpawner_FilterStageToggle: test started", - "LayerSpawner_FilterStageToggle: Preprocess filter stage vegetation instance count is as expected: True", - "LayerSpawner_FilterStageToggle: Postprocess filter vegetation instance stage count is as expected: True", - "LayerSpawner_FilterStageToggle: result=SUCCESS", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "LayerSpawner_FilterStageToggle.py", - expected_lines=expected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C30000751") - @pytest.mark.SUITE_sandbox - @pytest.mark.dynveg_misc - @pytest.mark.skip # https://github.com/o3de/o3de/issues/2038 - def test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(self, request, editor, level, launcher_platform): - - expected_lines = [ - "LayerSpawner_InstanceCameraRefresh: test started", - "LayerSpawner_InstanceCameraRefresh: test finished", - "LayerSpawner_InstanceCameraRefresh: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py", - expected_lines, - cfg_args=[level], - null_renderer=False - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_MeshBlocker.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_MeshBlocker.py deleted file mode 100755 index 197ab6e585..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_MeshBlocker.py +++ /dev/null @@ -1,89 +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 -""" - - -import logging -import os -import pytest -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip('ly_test_tools') - -import editor_python_test_tools.hydra_test_utils as hydra -import ly_test_tools.environment.file_system as file_system - -test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') -logger = logging.getLogger(__name__) - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestMeshBlocker(object): - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, editor, project, level): - pass - - def teardown(): - # delete temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - # Setup - add the teardown finalizer - request.addfinalizer(teardown) - # Make sure the temp level doesn't already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - """ - C3980834: A simple Vegetation Blocker Mesh can be created - """ - @pytest.mark.test_case_id("C3980834") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_area - @pytest.mark.xfail # LYN-3273 - def test_MeshBlocker_InstancesBlockedByMesh(self, request, editor, level, launcher_platform): - expected_lines = [ - "'Instance Spawner' created", - "'Surface Entity' created", - "'Blocker Entity' created", - "instance count validation: True (found=160, expected=160)", - "MeshBlocker_InstancesBlockedByMesh: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "MeshBlocker_InstancesBlockedByMesh.py", - expected_lines, - cfg_args=[level] - ) - - """ - C4766030: Mesh Height Percent Min/Max values can be set to fine tune the blocked area - """ - @pytest.mark.test_case_id("C4766030") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_area - @pytest.mark.xfail # LYN-3273 - def test_MeshBlocker_InstancesBlockedByMeshHeightTuning(self, request, editor, level, launcher_platform): - expected_lines = [ - "'Instance Spawner' created", - "'Surface Entity' created", - "'Blocker Entity' created", - "Blocker Entity Configuration|Mesh Height Percent Max: SUCCESS", - "instance count validation: True (found=127, expected=127)", - "MeshBlocker_InstancesBlockedByMeshHeightTuning: result=SUCCESS", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "MeshBlocker_InstancesBlockedByMeshHeightTuning.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_MeshSurfaceTagEmitter.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_MeshSurfaceTagEmitter.py deleted file mode 100755 index fd3a2f6514..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_MeshSurfaceTagEmitter.py +++ /dev/null @@ -1,82 +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 -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip("ly_test_tools") -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") - - -@pytest.mark.parametrize('project', ['AutomatedTesting']) -@pytest.mark.parametrize('level', ['tmp_level']) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestMeshSurfaceTagEmitter(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C2908172") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_surfacetagemitter - def test_MeshSurfaceTagEmitter_DependentOnMeshComponent(self, request, editor, level, launcher_platform): - - expected_lines = [ - "Entity has a Mesh Surface Tag Emitter component", - "New Entity Created", - "Mesh Surface Tag Emitter is Disabled", - "Entity has a Mesh component", - "Mesh Surface Tag Emitter is Enabled", - "MeshSurfaceTagEmitter_DependentOnMeshComponent: result=SUCCESS" - ] - - unexpected_lines = [ - "Mesh Surface Tag Emitter is Enabled. But It should be disabled before adding Mesh", - "Mesh Surface Tag Emitter is Disabled. But It should be enabled after adding Mesh", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "MeshSurfaceTagEmitter_DependentOnMeshComponent.py", - expected_lines, - unexpected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C2908174") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_surfacetagemitter - def test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(self, request, editor, level, launcher_platform): - - expected_lines = [ - "Added SurfaceTag: container count is 1", - "Removed SurfaceTag: container count is 0", - "MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSucessfully: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_PhysXColliderSurfaceTagEmitter.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_PhysXColliderSurfaceTagEmitter.py deleted file mode 100755 index 03b7a968f8..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_PhysXColliderSurfaceTagEmitter.py +++ /dev/null @@ -1,53 +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 -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip("ly_test_tools") -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") - - -@pytest.mark.parametrize('project', ['AutomatedTesting']) -@pytest.mark.parametrize('level', ['tmp_level']) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestPhysXColliderSurfaceTagEmitter(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C29053640") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_surfacetagemitter - def test_PhysXColliderSurfaceTagEmitter_E2E_Editor(self, request, editor, level, launcher_platform): - - expected_lines = [ - "PhysXColliderSurfaceTagEmitter_E2E_Editor: test started", - "PhysXColliderSurfaceTagEmitter_E2E_Editor: test finished", - "PhysXColliderSurfaceTagEmitter_E2E_Editor: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "PhysXColliderSurfaceTagEmitter_E2E_Editor.py", - expected_lines=expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_PositionModifier.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_PositionModifier.py deleted file mode 100755 index ebbd58557e..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_PositionModifier.py +++ /dev/null @@ -1,79 +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 -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip('ly_test_tools') -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestPositionModifier(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C4874099", "C4814461") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_modifier - def test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(self, request, editor, level, - launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "Vegetation Position Modifier component was added to entity", - "'Planting Surface' created", - "Entity has a Constant Gradient component", - "PositionModifierComponentAndOverrides_InstanceOffset: result=SUCCESS", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py", - expected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C4874100") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_modifier - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2303") - def test_PositionModifier_AutoSnapToSurfaceWorks(self, request, editor, level, launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "'Planting Surface' created", - "Instance Spawner Configuration|Position X|Range Min: SUCCESS", - "Instance Spawner Configuration|Position X|Range Max: SUCCESS", - "PositionModifier_AutoSnapToSurface: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "PositionModifier_AutoSnapToSurfaceWorks.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_RotationModifier.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_RotationModifier.py deleted file mode 100755 index 1dea332f7d..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_RotationModifier.py +++ /dev/null @@ -1,97 +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 -""" - -import logging -import os -import pytest - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip("ly_test_tools") - -import editor_python_test_tools.hydra_test_utils as hydra -import ly_test_tools.environment.file_system as file_system - -logger = logging.getLogger(__name__) - -test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestRotationModifier(object): - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - # delete temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - # Setup - add the teardown finalizer - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C4896922") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_modifier - def test_RotationModifier_InstancesRotateWithinRange(self, request, editor, level, launcher_platform) -> None: - """ - Launches editor and run test script to test that rotation modifier works for all axis. - Manual test case: C4896922 - """ - - expected_lines = [ - "'Spawner Entity' created", - "'Surface Entity' created", - "'Gradient Entity' created", - "Entity has a Vegetation Asset List component", - "Entity has a Vegetation Layer Spawner component", - "Entity has a Vegetation Rotation Modifier component", - "Entity has a Box Shape component", - "Entity has a Constant Gradient component", - "RotationModifier_InstancesRotateWithinRange: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "RotationModifier_InstancesRotateWithinRange.py", - expected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C4814460") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_modifier - def test_RotationModifierOverrides_InstancesRotateWithinRange(self, request, editor, level, launcher_platform) -> None: - - expected_lines = [ - "'Spawner Entity' created", - "'Surface Entity' created", - "'Gradient Entity' created", - "Entity has a Vegetation Layer Spawner component", - "Entity has a Vegetation Asset List component", - "Spawner Entity Box Shape|Box Configuration|Dimensions: SUCCESS", - "Entity has a Vegetation Rotation Modifier component", - "Spawner Entity Configuration|Embedded Assets|[0]|Rotation Modifier|Override Enabled: SUCCESS", - "Spawner Entity Configuration|Allow Per-Item Overrides: SUCCESS", - "Entity has a Constant Gradient component", - "Entity has a Box Shape component", - "Spawner Entity Configuration|Rotation Z|Gradient|Gradient Entity Id: SUCCESS", - "RotationModifierOverrides_InstancesRotateWithinRange: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "RotationModifierOverrides_InstancesRotateWithinRange.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_ScaleModifier.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_ScaleModifier.py deleted file mode 100755 index 62f6c0bbad..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_ScaleModifier.py +++ /dev/null @@ -1,91 +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 -""" - -""" -C4814462: Vegetation instances have random scale between 0.1 and 1.0 applied. -""" - -import os -import pytest - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip("ly_test_tools") - -import editor_python_test_tools.hydra_test_utils as hydra -import ly_test_tools.environment.file_system as file_system - -test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestScaleOverrideWorksSuccessfully(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C4814462") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_modifier - def test_ScaleModifierOverrides_InstancesProperlyScale(self, request, editor, level, launcher_platform): - - expected_lines = [ - "'Spawner Entity' created", - "'Surface Entity' created", - "Entity has a Vegetation Scale Modifier component", - "'Gradient Entity' created", - "Scale Min and Scale Max are set to 0.1 and 1.0 in Vegetation Asset List", - "Entity has a Random Noise Gradient component", - "Entity has a Gradient Transform Modifier component", - "Entity has a Box Shape component", - "Spawner Entity Configuration|Gradient|Gradient Entity Id: SUCCESS", - "ScaleModifierOverrides_InstancesProperlyScale: result=SUCCESS" - ] - - unexpected_lines = ["Scale Min and Scale Max are not set to 0.1 and 1.0 in Vegetation Asset List"] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "ScaleModifierOverrides_InstancesProperlyScale.py", - expected_lines, - unexpected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C4896937") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_modifier - def test_ScaleModifier_InstancesProperlyScale(self, request, editor, level, launcher_platform): - - expected_lines = [ - "'Spawner Entity' created", - "Entity has a Vegetation Scale Modifier component", - "'Surface Entity' created", - "'Gradient Entity' created", - "Spawner Entity Configuration|Gradient|Gradient Entity Id: SUCCESS", - "Spawner Entity Configuration|Range Min: SUCCESS", - "Spawner Entity Configuration|Range Max: SUCCESS", - "ScaleModifier_InstancesProperlyScale: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "ScaleModifier_InstancesProperlyScale.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_ShapeIntersectionFilter.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_ShapeIntersectionFilter.py deleted file mode 100755 index d2ed0318ca..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_ShapeIntersectionFilter.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Copyright (c) Contributors to the Open 3D Engine Project. -For complete copyright and license terms please see the LICENSE at the root of this distribution. - -SPDX-License-Identifier: Apache-2.0 OR MIT -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip('ly_test_tools') -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestShapeIntersectionFilter(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C4874094") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_filter - def test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(self, request, editor, level, launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "'Planting Surface' created", - "instance count validation: True (found=49, expected=49)", - "instance count validation: True (found=121, expected=121)", - "instance count validation: True (found=400, expected=400)", - "ShapeIntersectionFilter_InstancePlanting: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "ShapeIntersectionFilter_InstancesPlantInAssignedShape.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_SlopeAlignmentModifier.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_SlopeAlignmentModifier.py deleted file mode 100755 index 3a01521d8e..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_SlopeAlignmentModifier.py +++ /dev/null @@ -1,82 +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 -""" - -""" -C4896941 - Surface Alignment functions as expected -C4814459 - Surface Alignment overrides function as expected -""" - -import os -import pytest - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip("ly_test_tools") -import editor_python_test_tools.hydra_test_utils as hydra -import ly_test_tools.environment.file_system as file_system - -test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestSlopeAlignmentModifier(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C4896941") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_modifier - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2303") - def test_SlopeAlignmentModifier_InstanceSurfaceAlignment(self, request, editor, level, launcher_platform): - - expected_lines = [ - "Vegetation Slope Alignment Modifier component was added to entity", - "Instance Spawner Configuration|Alignment Coefficient Min: SUCCESS", - "Constant Gradient component was added to entity", - "Instance Spawner Configuration|Gradient|Gradient Entity Id: SUCCESS", - "SlopeAlignmentModifier: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "SlopeAlignmentModifier_InstanceSurfaceAlignment.py", - expected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C4814459") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_modifier - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2303") - def test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(self, request, editor, level, launcher_platform): - - expected_lines = [ - "Instance Spawner Configuration|Allow Per-Item Overrides: SUCCESS", - "Instance Spawner Configuration|Embedded Assets|[0]|Surface Slope Alignment|Override Enabled: SUCCESS", - "Instance Spawner Configuration|Embedded Assets|[0]|Surface Slope Alignment|Max: SUCCESS", - "Instance Spawner Configuration|Embedded Assets|[0]|Surface Slope Alignment|Min: SUCCESS", - "SlopeAlignmentModifierOverrides: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_SlopeFilter.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_SlopeFilter.py deleted file mode 100755 index 3065b2df61..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_SlopeFilter.py +++ /dev/null @@ -1,96 +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 -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip("ly_test_tools") -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestSlopeFilter(object): - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - def teardown(): - # Cleanup our temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - request.addfinalizer(teardown) - - @pytest.mark.test_case_id("C4874097") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_filter - def test_SlopeFilter_FilterStageToggle(self, request, editor, level, workspace, launcher_platform): - cfg_args = [level] - - expected_lines = [ - "SlopeFilter_FilterStageToggle: test started", - "SlopeFilter_FilterStageToggle: Vegetation plant only in the areas where the Box overlaps with the vegetation area's boundaries: True", - "SlopeFilter_FilterStageToggle: Vegetation plant only in the areas where the Cylinder overlaps with the vegetation area's boundaries: True", - "SlopeFilter_FilterStageToggle: Vegetation instances count equal to expected value for PREPROCESS filter stage: True", - "SlopeFilter_FilterStageToggle: Vegetation instances count equal to expected value for POSTPROCESS filter stage: True", - "SlopeFilter_FilterStageToggle: result=SUCCESS", - ] - - unexpected_lines = [ - "SlopeFilter_FilterStageToggle: Vegetation plant only in the areas where the Box overlaps with the vegetation area's boundaries: False", - "SlopeFilter_FilterStageToggle: Vegetation plant only in the areas where the Cylinder overlaps with the vegetation area's boundaries: False", - "SlopeFilter_FilterStageToggle: Vegetation instances count equal to expected value for PREPROCESS filter stage: False", - "SlopeFilter_FilterStageToggle: Vegetation instances count equal to expected value for POSTPROCESS filter stage: False", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "SlopeFilter_FilterStageToggle.py", - expected_lines=expected_lines, - unexpected_lines=unexpected_lines, - cfg_args=cfg_args - ) - - @pytest.mark.test_case_id("C4814464", "C4874096") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_filter - @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2303") - def test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(self, request, editor, level, - launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "'Planting Surface' created", - "'Sloped Planting Surface' created", - "instance count validation: True (found=1720, expected=1720)", - "Instance Spawner Configuration|Slope Min: SUCCESS", - "Instance Spawner Configuration|Slope Max: SUCCESS", - "instance count validation: True (found=48, expected=48)", - "Instance Spawner Configuration|Embedded Assets|[0]|Slope Filter|Min: SUCCESS", - "Instance Spawner Configuration|Embedded Assets|[0]|Slope Filter|Max: SUCCESS", - "instance count validation: True (found=12, expected=12)", - "SlopeFilter_InstancesPlantOnValidSlope: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_SurfaceMaskFilter.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_SurfaceMaskFilter.py deleted file mode 100755 index 7101a8e286..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_SurfaceMaskFilter.py +++ /dev/null @@ -1,150 +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 -""" - -import os -import pytest - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip("ly_test_tools") - -import editor_python_test_tools.hydra_test_utils as hydra -import ly_test_tools.environment.file_system as file_system - -test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestSurfaceMaskFilter(object): - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - # delete temp level - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - # Setup - add the teardown finalizer - request.addfinalizer(teardown) - - # Make sure the temp level doesn't already exist - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - # Simple validation test to ensure that SurfaceTag can be created, set to a value, and compared to another SurfaceTag. - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_filter - def test_SurfaceMaskFilter_BasicSurfaceTagCreation(self, request, level, editor, launcher_platform): - - expected_lines = [ - "SurfaceTag test started", - "SurfaceTag equal tag comparison is True expected True", - "SurfaceTag not equal tag comparison is False expected False", - "SurfaceTag test finished", - "TestSurfaceMaskFilter_BasicSurfaceTagCreation: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - 'SurfaceMaskFilter_BasicSurfaceTagCreation.py', - expected_lines=expected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C2561342") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_filter - def test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(self, request, editor, level, launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "Instance Spawner Box Shape|Box Configuration|Dimensions: SUCCESS", - "Instance Spawner Configuration|Embedded Assets|[0]: SUCCESS", - "'Surface Entity 1' created", - "Surface Entity 1 Box Shape|Box Configuration|Dimensions: SUCCESS", - "Surface Entity 1 Configuration|Generated Tags: SUCCESS", - "'Surface Entity 2' created", - "Surface Entity 2 Box Shape|Box Configuration|Dimensions: SUCCESS", - "Surface Entity 2 Configuration|Generated Tags: SUCCESS", - "SurfaceMaskFilter_ExclusionList: Expected 39 instances - Found 39 instances", - "Instance Spawner Configuration|Exclusion|Weight Max: SUCCESS", - "SurfaceMaskFilter_ExclusionList: Expected 169 instances - Found 169 instances", - "SurfaceMaskFilter_ExclusionList: result=SUCCESS" - ] - - unexpected_lines = ["Failed to add an Exclusive surface mask filter of terrainHole"] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "SurfaceMaskFilter_ExclusionList.py", - expected_lines, - unexpected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C2561341") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_filter - def test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(self, request, editor, level, launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "Instance Spawner Box Shape|Box Configuration|Dimensions: SUCCESS", - "Instance Spawner Configuration|Embedded Assets|[0]: SUCCESS", - "'Surface Entity 1' created", - "Surface Entity 1 Box Shape|Box Configuration|Dimensions: SUCCESS", - "Surface Entity 1 Configuration|Generated Tags: SUCCESS", - "'Surface Entity 2' created", - "Surface Entity 2 Box Shape|Box Configuration|Dimensions: SUCCESS", - "Surface Entity 2 Configuration|Generated Tags: SUCCESS", - "SurfaceMaskFilter_InclusionList: Expected 130 instances - Found 130 instances", - "Instance Spawner Configuration|Inclusion|Weight Max: SUCCESS", - "SurfaceMaskFilter_InclusionList: Expected 0 instances - Found 0 instances", - "SurfaceMaskFilter_InclusionList: result=SUCCESS" - ] - - unexpected_lines = ["Failed to add an Inclusive surface mask filter of terrainHole"] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "SurfaceMaskFilter_InclusionList.py", - expected_lines, - unexpected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C3711666") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_filter - def test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(self, request, editor, level, - launcher_platform): - - expected_lines = [ - "'Instance Spawner' created", - "'Surface Entity A' created", - "'Surface Entity B' created", - "'Surface Entity C' created", - "instance count validation: True (found=725, expected=725)", - "instance count validation: True (found=400, expected=400)", - "instance count validation: True (found=225, expected=225)", - "instance count validation: True (found=100, expected=100)", - "SurfaceMaskFilter_MultipleDescriptorOverrides: result=SUCCESS" - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py", - expected_lines, - cfg_args=[level] - ) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_SystemSettings.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_SystemSettings.py deleted file mode 100755 index 0b00a1fa83..0000000000 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_SystemSettings.py +++ /dev/null @@ -1,87 +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 -""" - -import os -import pytest -import logging - -# Bail on the test if ly_test_tools doesn't exist. -pytest.importorskip("ly_test_tools") -import ly_test_tools.environment.file_system as file_system -import editor_python_test_tools.hydra_test_utils as hydra - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") - - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["tmp_level"]) -@pytest.mark.usefixtures("automatic_process_killer") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestSystemSettings(object): - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, project, level): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - request.addfinalizer(teardown) - - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - @pytest.mark.test_case_id("C2646869") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_misc - def test_SystemSettings_SectorPointDensity(self, request, editor, level, launcher_platform): - - expected_lines = [ - "SystemSettings_SectorPointDensity: test started", - "SystemSettings_SectorPointDensity: Vegetation instances count equal to expected value before changing sector point density: True", - "SystemSettings_SectorPointDensity: Vegetation instances count equal to expected value after changing sector point density: True", - "SystemSettings_SectorPointDensity: result=SUCCESS", - ] - - unexpected_lines = [ - "SystemSettings_SectorPointDensity: Vegetation instances count equal to expected value before changing sector point density: False", - "SystemSettings_SectorPointDensity: Vegetation instances count equal to expected value after changing sector point density: False", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "SystemSettings_SectorPointDensity.py", - expected_lines, - unexpected_lines=unexpected_lines, - cfg_args=[level] - ) - - @pytest.mark.test_case_id("C2646870") - @pytest.mark.SUITE_periodic - @pytest.mark.dynveg_misc - def test_SystemSettings_SectorSize(self, request, editor, level, launcher_platform): - - expected_lines = [ - "SystemSettings_SectorSize: test started", - "SystemSettings_SectorSize: Vegetation instances count equal to expected value before changing sector size: True", - "SystemSettings_SectorSize: Vegetation instances count equal to expected value after changing sector size: True", - "SystemSettings_SectorSize: result=SUCCESS", - ] - - unexpected_lines = [ - "SystemSettings_SectorSize: Vegetation instances count equal to expected value before changing sector size: False", - "SystemSettings_SectorSize: Vegetation instances count equal to expected value after changing sector size: False", - ] - - hydra.launch_and_validate_results( - request, - test_directory, - editor, - "SystemSettings_SectorSize.py", - expected_lines, - unexpected_lines=unexpected_lines, - cfg_args=[level] - ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43b0dd240e..e659270f84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,11 +29,11 @@ include(cmake/PAL.cmake) include(cmake/PALTools.cmake) include(cmake/RuntimeDependencies.cmake) include(cmake/Configurations.cmake) # Requires to be after PAL so we get platform variable definitions -include(cmake/Install.cmake) include(cmake/Dependencies.cmake) include(cmake/Deployment.cmake) include(cmake/3rdParty.cmake) include(cmake/LYPython.cmake) +include(cmake/Install.cmake) include(cmake/LYWrappers.cmake) include(cmake/Gems.cmake) include(cmake/UnitTest.cmake) diff --git a/Code/Editor/2DViewport.h b/Code/Editor/2DViewport.h index 4ffda18514..007c1a47d3 100644 --- a/Code/Editor/2DViewport.h +++ b/Code/Editor/2DViewport.h @@ -35,32 +35,32 @@ public: Q2DViewport(QWidget* parent = nullptr); virtual ~Q2DViewport(); - virtual void SetType(EViewportType type); - virtual EViewportType GetType() const { return m_viewType; } - virtual float GetAspectRatio() const { return 1.0f; }; + void SetType(EViewportType type) override; + EViewportType GetType() const override { return m_viewType; } + float GetAspectRatio() const override { return 1.0f; }; - virtual void ResetContent(); - virtual void UpdateContent(int flags); + void ResetContent() override; + void UpdateContent(int flags) override; public slots: // Called every frame to update viewport. - virtual void Update(); + void Update() override; public: //! Map world space position to viewport position. - virtual QPoint WorldToView(const Vec3& wp) const; + QPoint WorldToView(const Vec3& wp) const override; - virtual QPoint WorldToViewParticleEditor(const Vec3& wp, int width, int height) const; //Eric@conffx + QPoint WorldToViewParticleEditor(const Vec3& wp, int width, int height) const override; //Eric@conffx //! Map viewport position to world space position. - virtual Vec3 ViewToWorld(const QPoint& vp, bool* collideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const override; + Vec3 ViewToWorld(const QPoint& vp, bool* collideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const override; //! Map viewport position to world space ray from camera. - virtual void ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3& rayDir) const; + void ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3& rayDir) const override; void OnTitleMenu(QMenu* menu) override; - virtual bool HitTest(const QPoint& point, HitContext& hitInfo) override; - virtual bool IsBoundsVisible(const AABB& box) const; + bool HitTest(const QPoint& point, HitContext& hitInfo) override; + bool IsBoundsVisible(const AABB& box) const override; // ovverided from CViewport. float GetScreenScaleFactor(const Vec3& worldPoint) const override; @@ -111,8 +111,8 @@ protected: virtual void SetZoom(float fZoomFactor, const QPoint& center); // overrides from CViewport. - virtual void MakeConstructionPlane(int axis); - virtual const Matrix34& GetConstructionMatrix(RefCoordSys coordSys); + void MakeConstructionPlane(int axis) override; + const Matrix34& GetConstructionMatrix(RefCoordSys coordSys) override; //! Calculate view transformation matrix. virtual void CalculateViewTM(); @@ -146,9 +146,9 @@ protected: void showEvent(QShowEvent* event) override; void paintEvent(QPaintEvent* event) override; int OnCreate(); - void OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point); - void OnRButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point); - void OnMouseWheel(Qt::KeyboardModifiers modifiers, short zDelta, const QPoint& pt); + void OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point) override; + void OnRButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point) override; + void OnMouseWheel(Qt::KeyboardModifiers modifiers, short zDelta, const QPoint& pt) override; void OnDestroy(); protected: diff --git a/Code/Editor/ActionManager.h b/Code/Editor/ActionManager.h index 2481b7e3ef..8879bdc1fb 100644 --- a/Code/Editor/ActionManager.h +++ b/Code/Editor/ActionManager.h @@ -353,7 +353,7 @@ public: m_actionHandlers[id] = std::bind(method, object, id); } - bool eventFilter(QObject* watched, QEvent* event); + bool eventFilter(QObject* watched, QEvent* event) override; // returns false if the action was already inserted, indicating that the action should not be processed again bool InsertActionExecuting(int id); diff --git a/Code/Editor/AnimationContext.h b/Code/Editor/AnimationContext.h index 98c951eda7..62ffa26634 100644 --- a/Code/Editor/AnimationContext.h +++ b/Code/Editor/AnimationContext.h @@ -197,7 +197,7 @@ private: virtual void OnSequenceRemoved(CTrackViewSequence* pSequence) override; - virtual void OnEditorNotifyEvent(EEditorNotifyEvent event); + virtual void OnEditorNotifyEvent(EEditorNotifyEvent event) override; void AnimateActiveSequence(); diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 18946c7874..9fe4cfd0d2 100644 --- a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -141,6 +141,10 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, &AzAssetBrowser::SearchWidget::ClearTypeFilter); + connect( + this, &AzAssetBrowserWindow::SizeChangedSignal, m_ui->m_assetBrowserTableViewWidget, + &AzAssetBrowser::AssetBrowserTableView::UpdateSizeSlot); + m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main"); } @@ -164,6 +168,25 @@ QObject* AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(QObject* pa return listener; } +void AzAssetBrowserWindow::resizeEvent(QResizeEvent* resizeEvent) +{ + // leftLayout is the parent of the tableView + // rightLayout is the parent of the preview window. + // Workaround: When docking windows this event keeps holding the old size of the widgets instead of the new one + // but the resizeEvent holds the new size of the whole widget + // So we have to save the proportions somehow + const QWidget* leftLayout = m_ui->m_leftLayout; + const QVBoxLayout* rightLayout = m_ui->m_rightLayout; + + const float oldLeftLayoutWidth = aznumeric_cast(leftLayout->geometry().width()); + const float oldWidth = aznumeric_cast(leftLayout->geometry().width() + rightLayout->geometry().width()); + + const float newWidth = oldLeftLayoutWidth * aznumeric_cast(resizeEvent->size().width()) / oldWidth; + + emit SizeChangedSignal(aznumeric_cast(newWidth)); + QWidget::resizeEvent(resizeEvent); +} + void AzAssetBrowserWindow::OnInitViewToggleButton() { CreateSwitchViewMenu(); diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h index bf742b0cf2..753f000300 100644 --- a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h +++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h @@ -53,9 +53,17 @@ public: static QObject* createListenerForShowAssetEditorEvent(QObject* parent); + +Q_SIGNALS: + void SizeChangedSignal(int newWidth); + +protected: + void resizeEvent(QResizeEvent* resizeEvent) override; + private: void OnInitViewToggleButton(); void UpdateDisplayInfo(); + protected slots: void CreateSwitchViewMenu(); void SetExpandedAssetBrowserMode(); diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui index df7474d9d4..a345438aed 100644 --- a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui +++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui @@ -140,9 +140,6 @@ QAbstractItemView::ScrollPerPixel - - false - true @@ -201,6 +198,11 @@
AzToolsFramework/AssetBrowser/Search/SearchWidget.h
1 + + AzQtComponents::TableView + QTreeView +
AzQtComponents/Components/Widgets/TableView.h
+
AzToolsFramework::AssetBrowser::AssetBrowserTreeView QTreeView @@ -214,7 +216,7 @@ AzToolsFramework::AssetBrowser::AssetBrowserTableView - QTableView + AzQtComponents::TableView
AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h
diff --git a/Code/Editor/BaseLibrary.h b/Code/Editor/BaseLibrary.h index 6c807df56b..55079d3fde 100644 --- a/Code/Editor/BaseLibrary.h +++ b/Code/Editor/BaseLibrary.h @@ -40,57 +40,57 @@ public: //! Set library name. virtual void SetName(const QString& name); //! Get library name. - const QString& GetName() const; + const QString& GetName() const override; //! Set new filename for this library. virtual bool SetFilename(const QString& filename, [[maybe_unused]] bool checkForUnique = true) { m_filename = filename.toLower(); return true; }; - const QString& GetFilename() const { return m_filename; }; + const QString& GetFilename() const override { return m_filename; }; - virtual bool Save() = 0; - virtual bool Load(const QString& filename) = 0; - virtual void Serialize(XmlNodeRef& node, bool bLoading) = 0; + bool Save() override = 0; + bool Load(const QString& filename) override = 0; + void Serialize(XmlNodeRef& node, bool bLoading) override = 0; //! Mark library as modified. - void SetModified(bool bModified = true); + void SetModified(bool bModified = true) override; //! Check if library was modified. - bool IsModified() const { return m_bModified; }; + bool IsModified() const override { return m_bModified; }; ////////////////////////////////////////////////////////////////////////// // Working with items. ////////////////////////////////////////////////////////////////////////// //! Add a new prototype to library. - void AddItem(IDataBaseItem* item, bool bRegister = true); + void AddItem(IDataBaseItem* item, bool bRegister = true) override; //! Get number of known prototypes. - int GetItemCount() const { return static_cast(m_items.size()); } + int GetItemCount() const override { return static_cast(m_items.size()); } //! Get prototype by index. - IDataBaseItem* GetItem(int index); + IDataBaseItem* GetItem(int index) override; //! Delete item by pointer of item. - void RemoveItem(IDataBaseItem* item); + void RemoveItem(IDataBaseItem* item) override; //! Delete all items from library. - void RemoveAllItems(); + void RemoveAllItems() override; //! Find library item by name. //! Using linear search. - IDataBaseItem* FindItem(const QString& name); + IDataBaseItem* FindItem(const QString& name) override; //! Check if this library is local level library. - bool IsLevelLibrary() const { return m_bLevelLib; }; + bool IsLevelLibrary() const override { return m_bLevelLib; }; //! Set library to be level library. - void SetLevelLibrary(bool bEnable) { m_bLevelLib = bEnable; }; + void SetLevelLibrary(bool bEnable) override { m_bLevelLib = bEnable; }; ////////////////////////////////////////////////////////////////////////// //! Return manager for this library. - IBaseLibraryManager* GetManager(); + IBaseLibraryManager* GetManager() override; // Saves the library with the main tag defined by the parameter name bool SaveLibrary(const char* name, bool saveEmptyLibrary = false); //CONFETTI BEGIN // Used to change the library item order - virtual void ChangeItemOrder(CBaseLibraryItem* item, unsigned int newLocation) override; + void ChangeItemOrder(CBaseLibraryItem* item, unsigned int newLocation) override; //CONFETTI END signals: diff --git a/Code/Editor/BaseLibraryManager.h b/Code/Editor/BaseLibraryManager.h index 6f0b905760..118c7ef1f0 100644 --- a/Code/Editor/BaseLibraryManager.h +++ b/Code/Editor/BaseLibraryManager.h @@ -35,112 +35,112 @@ public: ~CBaseLibraryManager(); //! Clear all libraries. - virtual void ClearAll() override; + void ClearAll() override; ////////////////////////////////////////////////////////////////////////// // IDocListener implementation. ////////////////////////////////////////////////////////////////////////// - virtual void OnEditorNotifyEvent(EEditorNotifyEvent event) override; + void OnEditorNotifyEvent(EEditorNotifyEvent event) override; ////////////////////////////////////////////////////////////////////////// // Library items. ////////////////////////////////////////////////////////////////////////// //! Make a new item in specified library. - virtual IDataBaseItem* CreateItem(IDataBaseLibrary* pLibrary) override; + IDataBaseItem* CreateItem(IDataBaseLibrary* pLibrary) override; //! Delete item from library and manager. - virtual void DeleteItem(IDataBaseItem* pItem) override; + void DeleteItem(IDataBaseItem* pItem) override; //! Find Item by its GUID. - virtual IDataBaseItem* FindItem(REFGUID guid) const; - virtual IDataBaseItem* FindItemByName(const QString& fullItemName); - virtual IDataBaseItem* LoadItemByName(const QString& fullItemName); + IDataBaseItem* FindItem(REFGUID guid) const override; + IDataBaseItem* FindItemByName(const QString& fullItemName) override; + IDataBaseItem* LoadItemByName(const QString& fullItemName) override; virtual IDataBaseItem* FindItemByName(const char* fullItemName); virtual IDataBaseItem* LoadItemByName(const char* fullItemName); - virtual IDataBaseItemEnumerator* GetItemEnumerator() override; + IDataBaseItemEnumerator* GetItemEnumerator() override; ////////////////////////////////////////////////////////////////////////// // Set item currently selected. - virtual void SetSelectedItem(IDataBaseItem* pItem) override; + void SetSelectedItem(IDataBaseItem* pItem) override; // Get currently selected item. - virtual IDataBaseItem* GetSelectedItem() const override; - virtual IDataBaseItem* GetSelectedParentItem() const override; + IDataBaseItem* GetSelectedItem() const override; + IDataBaseItem* GetSelectedParentItem() const override; ////////////////////////////////////////////////////////////////////////// // Libraries. ////////////////////////////////////////////////////////////////////////// //! Add Item library. - virtual IDataBaseLibrary* AddLibrary(const QString& library, bool bIsLevelLibrary = false, bool bIsLoading = true) override; - virtual void DeleteLibrary(const QString& library, bool forceDeleteLevel = false) override; + IDataBaseLibrary* AddLibrary(const QString& library, bool bIsLevelLibrary = false, bool bIsLoading = true) override; + void DeleteLibrary(const QString& library, bool forceDeleteLevel = false) override; //! Get number of libraries. - virtual int GetLibraryCount() const override { return static_cast(m_libs.size()); }; + int GetLibraryCount() const override { return static_cast(m_libs.size()); }; //! Get number of modified libraries. - virtual int GetModifiedLibraryCount() const override; + int GetModifiedLibraryCount() const override; //! Get Item library by index. - virtual IDataBaseLibrary* GetLibrary(int index) const override; + IDataBaseLibrary* GetLibrary(int index) const override; //! Get Level Item library. - virtual IDataBaseLibrary* GetLevelLibrary() const override; + IDataBaseLibrary* GetLevelLibrary() const override; //! Find Items Library by name. - virtual IDataBaseLibrary* FindLibrary(const QString& library) override; + IDataBaseLibrary* FindLibrary(const QString& library) override; //! Find Items Library's index by name. int FindLibraryIndex(const QString& library) override; //! Load Items library. - virtual IDataBaseLibrary* LoadLibrary(const QString& filename, bool bReload = false) override; + IDataBaseLibrary* LoadLibrary(const QString& filename, bool bReload = false) override; //! Save all modified libraries. - virtual void SaveAllLibs() override; + void SaveAllLibs() override; //! Serialize property manager. - virtual void Serialize(XmlNodeRef& node, bool bLoading) override; + void Serialize(XmlNodeRef& node, bool bLoading) override; //! Export items to game. - virtual void Export([[maybe_unused]] XmlNodeRef& node) override {}; + void Export([[maybe_unused]] XmlNodeRef& node) override {}; //! Returns unique name base on input name. - virtual QString MakeUniqueItemName(const QString& name, const QString& libName = "") override; - virtual QString MakeFullItemName(IDataBaseLibrary* pLibrary, const QString& group, const QString& itemName) override; + QString MakeUniqueItemName(const QString& name, const QString& libName = "") override; + QString MakeFullItemName(IDataBaseLibrary* pLibrary, const QString& group, const QString& itemName) override; //! Root node where this library will be saved. - virtual QString GetRootNodeName() override = 0; + QString GetRootNodeName() override = 0; //! Path to libraries in this manager. - virtual QString GetLibsPath() override = 0; + QString GetLibsPath() override = 0; ////////////////////////////////////////////////////////////////////////// //! Validate library items for errors. - virtual void Validate() override; + void Validate() override; ////////////////////////////////////////////////////////////////////////// - virtual void GatherUsedResources(CUsedResources& resources) override; + void GatherUsedResources(CUsedResources& resources) override; - virtual void AddListener(IDataBaseManagerListener* pListener) override; - virtual void RemoveListener(IDataBaseManagerListener* pListener) override; + void AddListener(IDataBaseManagerListener* pListener) override; + void RemoveListener(IDataBaseManagerListener* pListener) override; ////////////////////////////////////////////////////////////////////////// - virtual void RegisterItem(CBaseLibraryItem* pItem, REFGUID newGuid) override; - virtual void RegisterItem(CBaseLibraryItem* pItem) override; - virtual void UnregisterItem(CBaseLibraryItem* pItem) override; + void RegisterItem(CBaseLibraryItem* pItem, REFGUID newGuid) override; + void RegisterItem(CBaseLibraryItem* pItem) override; + void UnregisterItem(CBaseLibraryItem* pItem) override; // Only Used internally. - virtual void OnRenameItem(CBaseLibraryItem* pItem, const QString& oldName) override; + void OnRenameItem(CBaseLibraryItem* pItem, const QString& oldName) override; // Called by items to indicated that they have been modified. // Sends item changed event to listeners. - virtual void OnItemChanged(IDataBaseItem* pItem) override; - virtual void OnUpdateProperties(IDataBaseItem* pItem, bool bRefresh) override; + void OnItemChanged(IDataBaseItem* pItem) override; + void OnUpdateProperties(IDataBaseItem* pItem, bool bRefresh) override; QString MakeFilename(const QString& library); - virtual bool IsUniqueFilename(const QString& library) override; + bool IsUniqueFilename(const QString& library) override; //CONFETTI BEGIN // Used to change the library item order - virtual void ChangeLibraryOrder(IDataBaseLibrary* lib, unsigned int newLocation) override; + void ChangeLibraryOrder(IDataBaseLibrary* lib, unsigned int newLocation) override; - virtual bool SetLibraryName(CBaseLibrary* lib, const QString& name) override; + bool SetLibraryName(CBaseLibrary* lib, const QString& name) override; protected: void SplitFullItemName(const QString& fullItemName, QString& libraryName, QString& itemName); @@ -199,8 +199,8 @@ public: m_pMap = pMap; m_iterator = m_pMap->begin(); } - virtual void Release() { delete this; }; - virtual IDataBaseItem* GetFirst() + void Release() override { delete this; }; + IDataBaseItem* GetFirst() override { m_iterator = m_pMap->begin(); if (m_iterator == m_pMap->end()) @@ -209,7 +209,7 @@ public: } return m_iterator->second; } - virtual IDataBaseItem* GetNext() + IDataBaseItem* GetNext() override { if (m_iterator != m_pMap->end()) { diff --git a/Code/Editor/CMakeLists.txt b/Code/Editor/CMakeLists.txt index 5158ebc6d5..5884795413 100644 --- a/Code/Editor/CMakeLists.txt +++ b/Code/Editor/CMakeLists.txt @@ -254,4 +254,35 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_add_googletest( NAME Legacy::EditorLib.Tests ) + + ly_add_target( + NAME EditorLib.Camera.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Legacy + FILES_CMAKE + Lib/Tests/Camera/editor_lib_camera_test_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + . + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzTest + AZ::AzToolsFramework + AZ::AzTestShared + Legacy::EditorLib + Gem::Camera.Editor + Gem::AtomToolsFramework.Static + RUNTIME_DEPENDENCIES + Legacy::EditorLib + ) + + ly_add_source_properties( + SOURCES Lib/Tests/Camera/test_EditorCamera.cpp + PROPERTY COMPILE_DEFINITIONS + VALUES CAMERA_EDITOR_MODULE="$" + ) + + ly_add_googletest( + NAME Legacy::EditorLib.Camera.Tests + ) endif() diff --git a/Code/Editor/Controls/ColorGradientCtrl.h b/Code/Editor/Controls/ColorGradientCtrl.h index a3f95fcd8c..bb1a83b0c1 100644 --- a/Code/Editor/Controls/ColorGradientCtrl.h +++ b/Code/Editor/Controls/ColorGradientCtrl.h @@ -81,7 +81,7 @@ protected: HIT_SPLINE, }; - void paintEvent(QPaintEvent* e); + void paintEvent(QPaintEvent* e) override; void resizeEvent(QResizeEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; diff --git a/Code/Editor/Controls/FolderTreeCtrl.h b/Code/Editor/Controls/FolderTreeCtrl.h index 48eff10a92..f74cca6143 100644 --- a/Code/Editor/Controls/FolderTreeCtrl.h +++ b/Code/Editor/Controls/FolderTreeCtrl.h @@ -83,7 +83,7 @@ protected Q_SLOTS: void OnIndexDoubleClicked(const QModelIndex& index); protected: - virtual void OnFileMonitorChange(const SFileChangeInfo& rChange); + void OnFileMonitorChange(const SFileChangeInfo& rChange) override; void contextMenuEvent(QContextMenuEvent* e) override; void InitTree(); diff --git a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.h b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.h index 9c49f1ae1a..4bcd6dcaa3 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.h +++ b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.h @@ -123,7 +123,7 @@ public: void SetVariable(IVariable* pVariable) override; void SyncReflectedVarToIVar(IVariable* pVariable) override; void SyncIVarToReflectedVar(IVariable* pVariable) override; - virtual void OnVariableChange(IVariable* var); + void OnVariableChange(IVariable* var) override; CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); } protected: diff --git a/Code/Editor/Controls/SplineCtrlEx.cpp b/Code/Editor/Controls/SplineCtrlEx.cpp index 9e0a954c79..1dc2ff19e1 100644 --- a/Code/Editor/Controls/SplineCtrlEx.cpp +++ b/Code/Editor/Controls/SplineCtrlEx.cpp @@ -70,7 +70,7 @@ protected: m_splineEntries.resize(m_splineEntries.size() + 1); SplineEntry& entry = m_splineEntries.back(); ISplineSet* pSplineSet = (pCtrl ? pCtrl->m_pSplineSet : nullptr); - entry.id = (pSplineSet ? pSplineSet->GetIDFromSpline(pSpline) : nullptr); + entry.id = (pSplineSet ? pSplineSet->GetIDFromSpline(pSpline) : AZStd::string{}); entry.pSpline = pSpline; const int numKeys = pSpline->GetKeyCount(); diff --git a/Code/Editor/Controls/SplineCtrlEx.h b/Code/Editor/Controls/SplineCtrlEx.h index 711cbcf8d4..9bf412f056 100644 --- a/Code/Editor/Controls/SplineCtrlEx.h +++ b/Code/Editor/Controls/SplineCtrlEx.h @@ -159,15 +159,15 @@ public: ////////////////////////////////////////////////////////////////////////// // IKeyTimeSet Implementation - virtual int GetKeyTimeCount() const; - virtual float GetKeyTime(int index) const; - virtual void MoveKeyTimes(int numChanges, int* indices, float scale, float offset, bool copyKeys); - virtual bool GetKeyTimeSelected(int index) const; - virtual void SetKeyTimeSelected(int index, bool selected); - virtual int GetKeyCount(int index) const; - virtual int GetKeyCountBound() const; - virtual void BeginEdittingKeyTimes(); - virtual void EndEdittingKeyTimes(); + int GetKeyTimeCount() const override; + float GetKeyTime(int index) const override; + void MoveKeyTimes(int numChanges, int* indices, float scale, float offset, bool copyKeys) override; + bool GetKeyTimeSelected(int index) const override; + void SetKeyTimeSelected(int index, bool selected) override; + int GetKeyCount(int index) const override; + int GetKeyCountBound() const override; + void BeginEdittingKeyTimes() override; + void EndEdittingKeyTimes() override; void SetEditLock(bool bLock) { m_bEditLock = bLock; } @@ -361,8 +361,8 @@ public: SplineWidget(QWidget* parent); virtual ~SplineWidget(); - void update() { QWidget::update(); } - void update(const QRect& rect) { QWidget::update(rect); } + void update() override { QWidget::update(); } + void update(const QRect& rect) override { QWidget::update(rect); } QPoint mapFromGlobal(const QPoint& point) const override { return QWidget::mapFromGlobal(point); } diff --git a/Code/Editor/Controls/TimelineCtrl.h b/Code/Editor/Controls/TimelineCtrl.h index f87bdf3410..7e1fc6fcb2 100644 --- a/Code/Editor/Controls/TimelineCtrl.h +++ b/Code/Editor/Controls/TimelineCtrl.h @@ -56,7 +56,7 @@ public: void setGeometry(const QRect& r) override { QWidget::setGeometry(r); } void SetTimeRange(const Range& r) { m_timeRange = r; } - void SetTimeMarker(float fTime); + void SetTimeMarker(float fTime) override; float GetTimeMarker() const { return m_fTimeMarker; } void SetZoom(float fZoom); @@ -113,7 +113,7 @@ protected: void OnLButtonUp(const QPoint& point, Qt::KeyboardModifiers modifiers); void OnRButtonDown(const QPoint& point, Qt::KeyboardModifiers modifiers); void OnRButtonUp(const QPoint& point, Qt::KeyboardModifiers modifiers); - void keyPressEvent(QKeyEvent* event); + void keyPressEvent(QKeyEvent* event) override; // Drawing functions float ClientToTime(int x); diff --git a/Code/Editor/Core/QtEditorApplication_linux.cpp b/Code/Editor/Core/QtEditorApplication_linux.cpp index fa39609308..2fd4ef629e 100644 --- a/Code/Editor/Core/QtEditorApplication_linux.cpp +++ b/Code/Editor/Core/QtEditorApplication_linux.cpp @@ -8,11 +8,21 @@ #include "QtEditorApplication.h" +#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB +#include +#endif + namespace Editor { - bool EditorQtApplication::nativeEventFilter(const QByteArray& , void* , long* ) + bool EditorQtApplication::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long*) { - // TODO_KDAB_LINUX + if (GetIEditor()->IsInGameMode()) + { +#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::HandleXcbEvent, static_cast(message)); +#endif + return true; + } return false; } } diff --git a/Code/Editor/CryEdit.h b/Code/Editor/CryEdit.h index 4ab37ac1e5..730af7c034 100644 --- a/Code/Editor/CryEdit.h +++ b/Code/Editor/CryEdit.h @@ -462,6 +462,7 @@ class CCryDocManager CCrySingleDocTemplate* m_pDefTemplate = nullptr; public: CCryDocManager(); + virtual ~CCryDocManager() = default; CCrySingleDocTemplate* SetDefaultTemplate(CCrySingleDocTemplate* pNew); // Copied from MFC to get rid of the silly ugly unoverridable doc-type pick dialog virtual void OnFileNew(); diff --git a/Code/Editor/CryEditDoc.cpp b/Code/Editor/CryEditDoc.cpp index a61429fc87..07d946c609 100644 --- a/Code/Editor/CryEditDoc.cpp +++ b/Code/Editor/CryEditDoc.cpp @@ -1135,7 +1135,6 @@ bool CCryEditDoc::SaveLevel(const QString& filename) { // if we're saving to a new folder, we need to copy the old folder tree. auto pIPak = GetIEditor()->GetSystem()->GetIPak(); - pIPak->Lock(); const QString oldLevelPattern = QDir(oldLevelFolder).absoluteFilePath("*.*"); const QString oldLevelName = Path::GetFile(GetLevelPathName()); @@ -1199,7 +1198,6 @@ bool CCryEditDoc::SaveLevel(const QString& filename) QFile(filePath).setPermissions(QFile::ReadOther | QFile::WriteOther); }); - pIPak->Unlock(); } // Save level to XML archive. @@ -1813,8 +1811,8 @@ bool CCryEditDoc::BackupBeforeSave(bool force) QString subFolder = theTime.toString("yyyy-MM-dd [HH.mm.ss]"); QString levelName = GetIEditor()->GetGameEngine()->GetLevelName(); - QString backupPath = saveBackupPath + "/" + subFolder + "/"; - gEnv->pCryPak->MakeDir(backupPath.toUtf8().data()); + QString backupPath = saveBackupPath + "/" + subFolder; + AZ::IO::FileIOBase::GetDirectInstance()->CreatePath(backupPath.toUtf8().data()); QString sourcePath = QString::fromUtf8(resolvedLevelPath) + "/"; @@ -2028,7 +2026,7 @@ const char* CCryEditDoc::GetTemporaryLevelName() const void CCryEditDoc::DeleteTemporaryLevel() { QString tempLevelPath = (Path::GetEditingGameDataFolder() + "/Levels/" + GetTemporaryLevelName()).c_str(); - GetIEditor()->GetSystem()->GetIPak()->ClosePacks(tempLevelPath.toUtf8().data(), AZ::IO::IArchive::EPathResolutionRules::FLAGS_ADD_TRAILING_SLASH); + GetIEditor()->GetSystem()->GetIPak()->ClosePacks(tempLevelPath.toUtf8().data()); CFileUtil::Deltree(tempLevelPath.toUtf8().data(), true); } diff --git a/Code/Editor/Dialogs/PythonScriptsDialog.cpp b/Code/Editor/Dialogs/PythonScriptsDialog.cpp index 7c6b445387..e95fb90c0a 100644 --- a/Code/Editor/Dialogs/PythonScriptsDialog.cpp +++ b/Code/Editor/Dialogs/PythonScriptsDialog.cpp @@ -79,6 +79,8 @@ CPythonScriptsDialog::CPythonScriptsDialog(QWidget* parent) GetGemSourcePathsVisitor(AZ::SettingsRegistryInterface& settingsRegistry) : m_settingsRegistry(settingsRegistry) {} + + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override { diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp index 498d6f3353..e375e98fb6 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.cpp +++ b/Code/Editor/EditorModularViewportCameraComposer.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -34,10 +35,12 @@ namespace SandboxEditor : m_viewportId(viewportId) { EditorModularViewportCameraComposerNotificationBus::Handler::BusConnect(viewportId); + Camera::EditorCameraNotificationBus::Handler::BusConnect(); } EditorModularViewportCameraComposer::~EditorModularViewportCameraComposer() { + Camera::EditorCameraNotificationBus::Handler::BusDisconnect(); EditorModularViewportCameraComposerNotificationBus::Handler::BusDisconnect(); } @@ -283,4 +286,22 @@ namespace SandboxEditor m_orbitCamera->SetOrbitInputChannelId(SandboxEditor::CameraOrbitChannelId()); m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId()); } + + void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) + { + if (viewEntityId.IsValid()) + { + AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(worldFromLocal, viewEntityId, &AZ::TransformBus::Events::GetWorldTM); + + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, + worldFromLocal); + } + else + { + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame); + } + } } // namespace SandboxEditor diff --git a/Code/Editor/EditorModularViewportCameraComposer.h b/Code/Editor/EditorModularViewportCameraComposer.h index cb223d39e6..e6e71c976c 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.h +++ b/Code/Editor/EditorModularViewportCameraComposer.h @@ -10,13 +10,16 @@ #include #include +#include #include #include namespace SandboxEditor { //! Type responsible for building the editor's modular viewport camera controller. - class EditorModularViewportCameraComposer : private EditorModularViewportCameraComposerNotificationBus::Handler + class EditorModularViewportCameraComposer + : private EditorModularViewportCameraComposerNotificationBus::Handler + , private Camera::EditorCameraNotificationBus::Handler { public: SANDBOX_API explicit EditorModularViewportCameraComposer(AzFramework::ViewportId viewportId); @@ -32,6 +35,9 @@ namespace SandboxEditor // EditorModularViewportCameraComposerNotificationBus overrides ... void OnEditorModularViewportCameraComposerSettingsChanged() override; + // EditorCameraNotificationBus overrides ... + void OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) override; + AZStd::shared_ptr m_firstPersonRotateCamera; AZStd::shared_ptr m_firstPersonPanCamera; AZStd::shared_ptr m_firstPersonTranslateCamera; diff --git a/Code/Editor/EditorPreferencesPageViewportGeneral.cpp b/Code/Editor/EditorPreferencesPageViewportGeneral.cpp index a9eec22e69..77560e24f8 100644 --- a/Code/Editor/EditorPreferencesPageViewportGeneral.cpp +++ b/Code/Editor/EditorPreferencesPageViewportGeneral.cpp @@ -5,9 +5,11 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #include "EditorDefs.h" #include "EditorPreferencesPageViewportGeneral.h" +#include "EditorViewportSettings.h" #include @@ -15,7 +17,6 @@ #include "DisplaySettings.h" #include "Settings.h" - void CEditorPreferencesPage_ViewportGeneral::Reflect(AZ::SerializeContext& serialize) { serialize.Class() @@ -23,7 +24,8 @@ void CEditorPreferencesPage_ViewportGeneral::Reflect(AZ::SerializeContext& seria ->Field("Sync2DViews", &General::m_sync2DViews) ->Field("DefaultFOV", &General::m_defaultFOV) ->Field("DefaultAspectRatio", &General::m_defaultAspectRatio) - ->Field("EnableContextMenu", &General::m_enableContextMenu); + ->Field("EnableContextMenu", &General::m_contextMenuEnabled) + ->Field("StickySelect", &General::m_stickySelectEnabled); serialize.Class() ->Version(1) @@ -46,10 +48,12 @@ void CEditorPreferencesPage_ViewportGeneral::Reflect(AZ::SerializeContext& seria ->Field("ShowGridGuide", &Display::m_showGridGuide) ->Field("DisplayDimensions", &Display::m_displayDimension); + // clang-format off serialize.Class() ->Version(1) ->Field("SwapXY", &MapViewport::m_swapXY) ->Field("Resolution", &MapViewport::m_resolution); + // clang-format on serialize.Class() ->Version(1) @@ -80,31 +84,51 @@ void CEditorPreferencesPage_ViewportGeneral::Reflect(AZ::SerializeContext& seria editContext->Class("General Viewport Settings", "") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &General::m_sync2DViews, "Synchronize 2D Viewports", "Synchronize 2D Viewports") ->DataElement(AZ::Edit::UIHandlers::SpinBox, &General::m_defaultFOV, "Perspective View FOV", "Perspective View FOV") - ->Attribute("Multiplier", RAD2DEG(1)) - ->Attribute(AZ::Edit::Attributes::Min, 1.0f) - ->Attribute(AZ::Edit::Attributes::Max, 120.0f) - ->DataElement(AZ::Edit::UIHandlers::SpinBox, &General::m_defaultAspectRatio, "Perspective View Aspect Ratio", "Perspective View Aspect Ratio") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &General::m_enableContextMenu, "Enable Right-Click Context Menu", "Enable Right-Click Context Menu"); + ->Attribute("Multiplier", RAD2DEG(1)) + ->Attribute(AZ::Edit::Attributes::Min, 1.0f) + ->Attribute(AZ::Edit::Attributes::Max, 120.0f) + ->DataElement( + AZ::Edit::UIHandlers::SpinBox, &General::m_defaultAspectRatio, "Perspective View Aspect Ratio", + "Perspective View Aspect Ratio") + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &General::m_contextMenuEnabled, "Enable Right-Click Context Menu", + "Enable Right-Click Context Menu") + ->DataElement(AZ::Edit::UIHandlers::CheckBox, &General::m_stickySelectEnabled, "Enable Sticky Select", "Enable Sticky Select"); editContext->Class("Viewport Display Settings", "") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_showSafeFrame, "Show 4:3 Aspect Ratio Frame", "Show 4:3 Aspect Ratio Frame") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_highlightSelGeom, "Highlight Selected Geometry", "Highlight Selected Geometry") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_highlightSelVegetation, "Highlight Selected Vegetation", "Highlight Selected Vegetation") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_highlightOnMouseOver, "Highlight Geometry On Mouse Over", "Highlight Geometry On Mouse Over") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_hideMouseCursorWhenCaptured, "Hide Cursor When Captured", "Hide Mouse Cursor When Captured") + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &Display::m_showSafeFrame, "Show 4:3 Aspect Ratio Frame", "Show 4:3 Aspect Ratio Frame") + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &Display::m_highlightSelGeom, "Highlight Selected Geometry", "Highlight Selected Geometry") + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &Display::m_highlightSelVegetation, "Highlight Selected Vegetation", + "Highlight Selected Vegetation") + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &Display::m_highlightOnMouseOver, "Highlight Geometry On Mouse Over", + "Highlight Geometry On Mouse Over") + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &Display::m_hideMouseCursorWhenCaptured, "Hide Cursor When Captured", + "Hide Mouse Cursor When Captured") ->DataElement(AZ::Edit::UIHandlers::SpinBox, &Display::m_dragSquareSize, "Drag Square Size", "Drag Square Size") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_displayLinks, "Display Object Links", "Display Object Links") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_displayTracks, "Display Animation Tracks", "Display Animation Tracks") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_alwaysShowRadii, "Always Show Radii", "Always Show Radii") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_showBBoxes, "Show Bounding Boxes", "Show Bounding Boxes") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_drawEntityLabels, "Always Draw Entity Labels", "Always Draw Entity Labels") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_showTriggerBounds, "Always Show Trigger Bounds", "Always Show Trigger Bounds") + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &Display::m_drawEntityLabels, "Always Draw Entity Labels", "Always Draw Entity Labels") + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &Display::m_showTriggerBounds, "Always Show Trigger Bounds", "Always Show Trigger Bounds") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_showIcons, "Show Object Icons", "Show Object Icons") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_distanceScaleIcons, "Scale Object Icons with Distance", "Scale Object Icons with Distance") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_showFrozenHelpers, "Show Helpers of Frozen Objects", "Show Helpers of Frozen Objects") + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &Display::m_distanceScaleIcons, "Scale Object Icons with Distance", + "Scale Object Icons with Distance") + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &Display::m_showFrozenHelpers, "Show Helpers of Frozen Objects", + "Show Helpers of Frozen Objects") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_fillSelectedShapes, "Fill Selected Shapes", "Fill Selected Shapes") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_showGridGuide, "Show Snapping Grid Guide", "Show Snapping Grid Guide") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Display::m_displayDimension, "Display Dimension Figures", "Display Dimension Figures"); + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &Display::m_displayDimension, "Display Dimension Figures", "Display Dimension Figures"); editContext->Class("Map Viewport Settings", "") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &MapViewport::m_swapXY, "Swap X/Y Axis", "Swap X/Y Axis") @@ -113,42 +137,64 @@ void CEditorPreferencesPage_ViewportGeneral::Reflect(AZ::SerializeContext& seria editContext->Class("Text Label Settings", "") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &TextLabels::m_labelsOn, "Enabled", "Enabled") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &TextLabels::m_labelsDistance, "Distance", "Distance") - ->Attribute(AZ::Edit::Attributes::Min, 0.f) - ->Attribute(AZ::Edit::Attributes::Max, 100000.f); + ->Attribute(AZ::Edit::Attributes::Min, 0.f) + ->Attribute(AZ::Edit::Attributes::Max, 100000.f); editContext->Class("Selection Preview Color Settings", "") ->DataElement(AZ::Edit::UIHandlers::Color, &SelectionPreviewColor::m_colorGroupBBox, "Group Bounding Box", "Group Bounding Box") - ->DataElement(AZ::Edit::UIHandlers::Color, &SelectionPreviewColor::m_colorEntityBBox, "Entity Bounding Box", "Entity Bounding Box") - ->DataElement(AZ::Edit::UIHandlers::SpinBox, &SelectionPreviewColor::m_fBBoxAlpha, "Bounding Box Highlight Alpha", "Bounding Box Highlight Alpha") - ->Attribute(AZ::Edit::Attributes::Min, 0.0f) - ->Attribute(AZ::Edit::Attributes::Max, 1.0f) + ->DataElement( + AZ::Edit::UIHandlers::Color, &SelectionPreviewColor::m_colorEntityBBox, "Entity Bounding Box", "Entity Bounding Box") + ->DataElement( + AZ::Edit::UIHandlers::SpinBox, &SelectionPreviewColor::m_fBBoxAlpha, "Bounding Box Highlight Alpha", + "Bounding Box Highlight Alpha") + ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->Attribute(AZ::Edit::Attributes::Max, 1.0f) ->DataElement(AZ::Edit::UIHandlers::Color, &SelectionPreviewColor::m_geometryHighlightColor, "Geometry Color", "Geometry Color") - ->DataElement(AZ::Edit::UIHandlers::Color, &SelectionPreviewColor::m_solidBrushGeometryColor, "Solid Brush Geometry Color", "Solid Brush Geometry Color") - ->DataElement(AZ::Edit::UIHandlers::SpinBox, &SelectionPreviewColor::m_fgeomAlpha, "Geometry Highlight Alpha", "Geometry Highlight Alpha") - ->Attribute(AZ::Edit::Attributes::Min, 0.0f) - ->Attribute(AZ::Edit::Attributes::Max, 1.0f) - ->DataElement(AZ::Edit::UIHandlers::SpinBox, &SelectionPreviewColor::m_childObjectGeomAlpha, "Child Geometry Highlight Alpha", "Child Geometry Highlight Alpha") - ->Attribute(AZ::Edit::Attributes::Min, 0.0f) - ->Attribute(AZ::Edit::Attributes::Max, 1.0f); + ->DataElement( + AZ::Edit::UIHandlers::Color, &SelectionPreviewColor::m_solidBrushGeometryColor, "Solid Brush Geometry Color", + "Solid Brush Geometry Color") + ->DataElement( + AZ::Edit::UIHandlers::SpinBox, &SelectionPreviewColor::m_fgeomAlpha, "Geometry Highlight Alpha", "Geometry Highlight Alpha") + ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->Attribute(AZ::Edit::Attributes::Max, 1.0f) + ->DataElement( + AZ::Edit::UIHandlers::SpinBox, &SelectionPreviewColor::m_childObjectGeomAlpha, "Child Geometry Highlight Alpha", + "Child Geometry Highlight Alpha") + ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->Attribute(AZ::Edit::Attributes::Max, 1.0f); editContext->Class("General Viewport Preferences", "General Viewport Preferences") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20)) - ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportGeneral::m_general, "General Viewport Settings", "General Viewport Settings") - ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportGeneral::m_display, "Viewport Display Settings", "Viewport Display Settings") - ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportGeneral::m_map, "Map Viewport Settings", "Map Viewport Settings") - ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportGeneral::m_textLabels, "Text Label Settings", "Text Label Settings") - ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportGeneral::m_selectionPreviewColor, "Selection Preview Color Settings", "Selection Preview Color Settings"); + ->DataElement( + AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportGeneral::m_general, "General Viewport Settings", + "General Viewport Settings") + ->DataElement( + AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportGeneral::m_display, "Viewport Display Settings", + "Viewport Display Settings") + ->DataElement( + AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportGeneral::m_map, "Map Viewport Settings", + "Map Viewport Settings") + ->DataElement( + AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportGeneral::m_textLabels, "Text Label Settings", + "Text Label Settings") + ->DataElement( + AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportGeneral::m_selectionPreviewColor, + "Selection Preview Color Settings", "Selection Preview Color Settings"); } } - CEditorPreferencesPage_ViewportGeneral::CEditorPreferencesPage_ViewportGeneral() { InitializeSettings(); m_icon = QIcon(":/res/Viewport.svg"); } +const char* CEditorPreferencesPage_ViewportGeneral::GetCategory() +{ + return "Viewports"; +} + const char* CEditorPreferencesPage_ViewportGeneral::GetTitle() { return "Viewport"; @@ -159,14 +205,25 @@ QIcon& CEditorPreferencesPage_ViewportGeneral::GetIcon() return m_icon; } +void CEditorPreferencesPage_ViewportGeneral::OnCancel() +{ + // noop +} + +bool CEditorPreferencesPage_ViewportGeneral::OnQueryCancel() +{ + return true; +} + void CEditorPreferencesPage_ViewportGeneral::OnApply() { CDisplaySettings* ds = GetIEditor()->GetDisplaySettings(); gSettings.viewports.fDefaultAspectRatio = m_general.m_defaultAspectRatio; gSettings.viewports.fDefaultFov = m_general.m_defaultFOV; - gSettings.viewports.bEnableContextMenu = m_general.m_enableContextMenu; + gSettings.viewports.bEnableContextMenu = m_general.m_contextMenuEnabled; gSettings.viewports.bSync2DViews = m_general.m_sync2DViews; + SandboxEditor::SetStickySelectEnabled(m_general.m_stickySelectEnabled); gSettings.viewports.bShowSafeFrame = m_display.m_showSafeFrame; gSettings.viewports.bHighlightSelectedGeometry = m_display.m_highlightSelGeom; @@ -202,19 +259,19 @@ void CEditorPreferencesPage_ViewportGeneral::OnApply() gSettings.objectColorSettings.fChildGeomAlpha = m_selectionPreviewColor.m_childObjectGeomAlpha; gSettings.objectColorSettings.entityHighlight = QColor( - static_cast(m_selectionPreviewColor.m_colorEntityBBox.GetR() * 255.0f), - static_cast(m_selectionPreviewColor.m_colorEntityBBox.GetG() * 255.0f), - static_cast(m_selectionPreviewColor.m_colorEntityBBox.GetB() * 255.0f)); + static_cast(m_selectionPreviewColor.m_colorEntityBBox.GetR() * 255.0f), + static_cast(m_selectionPreviewColor.m_colorEntityBBox.GetG() * 255.0f), + static_cast(m_selectionPreviewColor.m_colorEntityBBox.GetB() * 255.0f)); gSettings.objectColorSettings.groupHighlight = QColor( - static_cast(m_selectionPreviewColor.m_colorGroupBBox.GetR() * 255.0f), - static_cast(m_selectionPreviewColor.m_colorGroupBBox.GetG() * 255.0f), - static_cast(m_selectionPreviewColor.m_colorGroupBBox.GetB() * 255.0f)); + static_cast(m_selectionPreviewColor.m_colorGroupBBox.GetR() * 255.0f), + static_cast(m_selectionPreviewColor.m_colorGroupBBox.GetG() * 255.0f), + static_cast(m_selectionPreviewColor.m_colorGroupBBox.GetB() * 255.0f)); gSettings.objectColorSettings.fBBoxAlpha = m_selectionPreviewColor.m_fBBoxAlpha; gSettings.objectColorSettings.fGeomAlpha = m_selectionPreviewColor.m_fgeomAlpha; gSettings.objectColorSettings.geometryHighlightColor = QColor( - static_cast(m_selectionPreviewColor.m_geometryHighlightColor.GetR() * 255.0f), - static_cast(m_selectionPreviewColor.m_geometryHighlightColor.GetG() * 255.0f), - static_cast(m_selectionPreviewColor.m_geometryHighlightColor.GetB() * 255.0f)); + static_cast(m_selectionPreviewColor.m_geometryHighlightColor.GetR() * 255.0f), + static_cast(m_selectionPreviewColor.m_geometryHighlightColor.GetG() * 255.0f), + static_cast(m_selectionPreviewColor.m_geometryHighlightColor.GetB() * 255.0f)); gSettings.objectColorSettings.solidBrushGeometryColor = QColor( static_cast(m_selectionPreviewColor.m_solidBrushGeometryColor.GetR() * 255.0f), static_cast(m_selectionPreviewColor.m_solidBrushGeometryColor.GetG() * 255.0f), @@ -227,8 +284,9 @@ void CEditorPreferencesPage_ViewportGeneral::InitializeSettings() m_general.m_defaultAspectRatio = gSettings.viewports.fDefaultAspectRatio; m_general.m_defaultFOV = gSettings.viewports.fDefaultFov; - m_general.m_enableContextMenu = gSettings.viewports.bEnableContextMenu; + m_general.m_contextMenuEnabled = gSettings.viewports.bEnableContextMenu; m_general.m_sync2DViews = gSettings.viewports.bSync2DViews; + m_general.m_stickySelectEnabled = SandboxEditor::StickySelectEnabled(); m_display.m_showSafeFrame = gSettings.viewports.bShowSafeFrame; m_display.m_highlightSelGeom = gSettings.viewports.bHighlightSelectedGeometry; @@ -256,10 +314,22 @@ void CEditorPreferencesPage_ViewportGeneral::InitializeSettings() m_textLabels.m_labelsDistance = ds->GetLabelsDistance(); m_selectionPreviewColor.m_childObjectGeomAlpha = gSettings.objectColorSettings.fChildGeomAlpha; - m_selectionPreviewColor.m_colorEntityBBox.Set(static_cast(gSettings.objectColorSettings.entityHighlight.redF()), static_cast(gSettings.objectColorSettings.entityHighlight.greenF()), static_cast(gSettings.objectColorSettings.entityHighlight.blueF()), 1.0f); - m_selectionPreviewColor.m_colorGroupBBox.Set(static_cast(gSettings.objectColorSettings.groupHighlight.redF()), static_cast(gSettings.objectColorSettings.groupHighlight.greenF()), static_cast(gSettings.objectColorSettings.groupHighlight.blueF()), 1.0f); + m_selectionPreviewColor.m_colorEntityBBox.Set( + static_cast(gSettings.objectColorSettings.entityHighlight.redF()), + static_cast(gSettings.objectColorSettings.entityHighlight.greenF()), + static_cast(gSettings.objectColorSettings.entityHighlight.blueF()), 1.0f); + m_selectionPreviewColor.m_colorGroupBBox.Set( + static_cast(gSettings.objectColorSettings.groupHighlight.redF()), + static_cast(gSettings.objectColorSettings.groupHighlight.greenF()), + static_cast(gSettings.objectColorSettings.groupHighlight.blueF()), 1.0f); m_selectionPreviewColor.m_fBBoxAlpha = gSettings.objectColorSettings.fBBoxAlpha; m_selectionPreviewColor.m_fgeomAlpha = gSettings.objectColorSettings.fGeomAlpha; - m_selectionPreviewColor.m_geometryHighlightColor.Set(static_cast(gSettings.objectColorSettings.geometryHighlightColor.redF()), static_cast(gSettings.objectColorSettings.geometryHighlightColor.greenF()), static_cast(gSettings.objectColorSettings.geometryHighlightColor.blueF()), 1.0f); - m_selectionPreviewColor.m_solidBrushGeometryColor.Set(static_cast(gSettings.objectColorSettings.solidBrushGeometryColor.redF()), static_cast(gSettings.objectColorSettings.solidBrushGeometryColor.greenF()), static_cast(gSettings.objectColorSettings.solidBrushGeometryColor.blueF()), 1.0f); + m_selectionPreviewColor.m_geometryHighlightColor.Set( + static_cast(gSettings.objectColorSettings.geometryHighlightColor.redF()), + static_cast(gSettings.objectColorSettings.geometryHighlightColor.greenF()), + static_cast(gSettings.objectColorSettings.geometryHighlightColor.blueF()), 1.0f); + m_selectionPreviewColor.m_solidBrushGeometryColor.Set( + static_cast(gSettings.objectColorSettings.solidBrushGeometryColor.redF()), + static_cast(gSettings.objectColorSettings.solidBrushGeometryColor.greenF()), + static_cast(gSettings.objectColorSettings.solidBrushGeometryColor.blueF()), 1.0f); } diff --git a/Code/Editor/EditorPreferencesPageViewportGeneral.h b/Code/Editor/EditorPreferencesPageViewportGeneral.h index a042bcf19b..be89cc6df4 100644 --- a/Code/Editor/EditorPreferencesPageViewportGeneral.h +++ b/Code/Editor/EditorPreferencesPageViewportGeneral.h @@ -5,18 +5,17 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once #include "Include/IPreferencesPage.h" -#include -#include -#include #include +#include +#include +#include #include - -class CEditorPreferencesPage_ViewportGeneral - : public IPreferencesPage +class CEditorPreferencesPage_ViewportGeneral : public IPreferencesPage { public: AZ_RTTI(CEditorPreferencesPage_ViewportGeneral, "{8511FF7F-F774-47E1-A99B-3DE3A867E403}", IPreferencesPage) @@ -26,12 +25,12 @@ public: CEditorPreferencesPage_ViewportGeneral(); virtual ~CEditorPreferencesPage_ViewportGeneral() = default; - virtual const char* GetCategory() override { return "Viewports"; } + virtual const char* GetCategory() override; virtual const char* GetTitle() override; virtual QIcon& GetIcon() override; virtual void OnApply() override; - virtual void OnCancel() override {} - virtual bool OnQueryCancel() override { return true; } + virtual void OnCancel() override; + virtual bool OnQueryCancel() override; private: void InitializeSettings(); @@ -43,7 +42,8 @@ private: bool m_sync2DViews; float m_defaultFOV; float m_defaultAspectRatio; - bool m_enableContextMenu; + bool m_contextMenuEnabled; + bool m_stickySelectEnabled; }; struct Display @@ -106,5 +106,3 @@ private: SelectionPreviewColor m_selectionPreviewColor; QIcon m_icon; }; - - diff --git a/Code/Editor/EditorToolsApplication.cpp b/Code/Editor/EditorToolsApplication.cpp index 1e5d747e4a..26e608f657 100644 --- a/Code/Editor/EditorToolsApplication.cpp +++ b/Code/Editor/EditorToolsApplication.cpp @@ -34,10 +34,14 @@ namespace EditorInternal : ToolsApplication(argc, argv) { EditorToolsApplicationRequests::Bus::Handler::BusConnect(); + AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusConnect(); + AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusConnect(); } EditorToolsApplication::~EditorToolsApplication() { + AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusDisconnect(); + AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusDisconnect(); EditorToolsApplicationRequests::Bus::Handler::BusDisconnect(); Stop(); } @@ -48,7 +52,6 @@ namespace EditorInternal return m_StartupAborted; } - void EditorToolsApplication::RegisterCoreComponents() { AzToolsFramework::ToolsApplication::RegisterCoreComponents(); @@ -274,5 +277,14 @@ namespace EditorInternal Exit(); } -} + AzToolsFramework::ViewportInteraction::KeyboardModifiers EditorToolsApplication::QueryKeyboardModifiers() + { + return AzToolsFramework::ViewportInteraction::BuildKeyboardModifiers(QGuiApplication::queryKeyboardModifiers()); + } + AZStd::chrono::milliseconds EditorToolsApplication::EditorViewportInputTimeNow() + { + const auto now = AZStd::chrono::high_resolution_clock::now(); + return AZStd::chrono::time_point_cast(now).time_since_epoch(); + } +} // namespace EditorInternal diff --git a/Code/Editor/EditorToolsApplication.h b/Code/Editor/EditorToolsApplication.h index 93916cfc8c..d4e6223445 100644 --- a/Code/Editor/EditorToolsApplication.h +++ b/Code/Editor/EditorToolsApplication.h @@ -7,7 +7,9 @@ */ #pragma once + #include +#include #include "Core/EditorMetricsPlainTextNameRegistration.h" #include "EditorToolsApplicationAPI.h" @@ -19,6 +21,8 @@ namespace EditorInternal class EditorToolsApplication : public AzToolsFramework::ToolsApplication , public EditorToolsApplicationRequests::Bus::Handler + , public AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler + , public AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler { public: EditorToolsApplication(int* argc, char*** argv); @@ -28,7 +32,7 @@ namespace EditorInternal void RegisterCoreComponents() override; - AZ::ComponentTypeList GetRequiredSystemComponents() const; + AZ::ComponentTypeList GetRequiredSystemComponents() const override; void StartCommon(AZ::Entity* systemEntity) override; @@ -44,6 +48,12 @@ namespace EditorInternal void CreateReflectionManager() override; void Reflect(AZ::ReflectContext* context) override; + // EditorModifierKeyRequestBus overrides ... + AzToolsFramework::ViewportInteraction::KeyboardModifiers QueryKeyboardModifiers() override; + + // EditorViewportInputTimeNowRequestBus overrides ... + AZStd::chrono::milliseconds EditorViewportInputTimeNow() override; + protected: // From EditorToolsApplicationRequests bool OpenLevel(AZStd::string_view levelName) override; diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index c1bd85a174..063ec125ba 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -20,6 +20,7 @@ namespace SandboxEditor constexpr AZStd::string_view AngleSnappingSetting = "/Amazon/Preferences/Editor/AngleSnapping"; constexpr AZStd::string_view AngleSizeSetting = "/Amazon/Preferences/Editor/AngleSize"; constexpr AZStd::string_view ShowGridSetting = "/Amazon/Preferences/Editor/ShowGrid"; + constexpr AZStd::string_view StickySelectSetting = "/Amazon/Preferences/Editor/StickySelect"; constexpr AZStd::string_view ManipulatorLineBoundWidthSetting = "/Amazon/Preferences/Editor/Manipulator/LineBoundWidth"; constexpr AZStd::string_view ManipulatorCircleBoundWidthSetting = "/Amazon/Preferences/Editor/Manipulator/CircleBoundWidth"; constexpr AZStd::string_view CameraTranslateSpeedSetting = "/Amazon/Preferences/Editor/Camera/TranslateSpeed"; @@ -158,6 +159,16 @@ namespace SandboxEditor SetRegistry(ShowGridSetting, showing); } + bool StickySelectEnabled() + { + return GetRegistry(StickySelectSetting, false); + } + + void SetStickySelectEnabled(const bool enabled) + { + SetRegistry(StickySelectSetting, enabled); + } + float ManipulatorLineBoundWidth() { return aznumeric_cast(GetRegistry(ManipulatorLineBoundWidthSetting, 0.1)); diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h index 8aeeee1384..20d397a29e 100644 --- a/Code/Editor/EditorViewportSettings.h +++ b/Code/Editor/EditorViewportSettings.h @@ -47,6 +47,9 @@ namespace SandboxEditor SANDBOX_API bool ShowingGrid(); SANDBOX_API void SetShowingGrid(bool showing); + SANDBOX_API bool StickySelectEnabled(); + SANDBOX_API void SetStickySelectEnabled(bool enabled); + SANDBOX_API float ManipulatorLineBoundWidth(); SANDBOX_API void SetManipulatorLineBoundWidth(float lineBoundWidth); diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp index f009fe7602..dad6734428 100644 --- a/Code/Editor/EditorViewportWidget.cpp +++ b/Code/Editor/EditorViewportWidget.cpp @@ -744,11 +744,15 @@ void EditorViewportWidget::RenderAll() { namespace AztfVi = AzToolsFramework::ViewportInteraction; + AztfVi::KeyboardModifiers keyboardModifiers; + AztfVi::EditorModifierKeyRequestBus::BroadcastResult( + keyboardModifiers, &AztfVi::EditorModifierKeyRequestBus::Events::QueryKeyboardModifiers); + m_debugDisplay->DepthTestOff(); m_manipulatorManager->DrawManipulators( *m_debugDisplay, GetCameraState(), BuildMouseInteractionInternal( - AztfVi::MouseButtons(AztfVi::TranslateMouseButtons(QGuiApplication::mouseButtons())), QueryKeyboardModifiers(), + AztfVi::MouseButtons(AztfVi::TranslateMouseButtons(QGuiApplication::mouseButtons())), keyboardModifiers, BuildMousePick(WidgetToViewport(mapFromGlobal(QCursor::pos()))))); m_debugDisplay->DepthTestOn(); } @@ -959,12 +963,13 @@ QWidget* EditorViewportWidget::GetWidgetForViewportContextMenu() bool EditorViewportWidget::ShowingWorldSpace() { - return QueryKeyboardModifiers().Shift(); -} + namespace AztfVi = AzToolsFramework::ViewportInteraction; -AzToolsFramework::ViewportInteraction::KeyboardModifiers EditorViewportWidget::QueryKeyboardModifiers() -{ - return AzToolsFramework::ViewportInteraction::BuildKeyboardModifiers(QGuiApplication::queryKeyboardModifiers()); + AztfVi::KeyboardModifiers keyboardModifiers; + AztfVi::EditorModifierKeyRequestBus::BroadcastResult( + keyboardModifiers, &AztfVi::EditorModifierKeyRequestBus::Events::QueryKeyboardModifiers); + + return keyboardModifiers.Shift(); } void EditorViewportWidget::SetViewportId(int id) @@ -1039,7 +1044,6 @@ void EditorViewportWidget::ConnectViewportInteractionRequestBus() { AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusConnect(GetViewportId()); AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusConnect(GetViewportId()); - AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusConnect(); m_viewportUi.ConnectViewportUiBus(GetViewportId()); AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusConnect(); @@ -1050,7 +1054,6 @@ void EditorViewportWidget::DisconnectViewportInteractionRequestBus() AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusDisconnect(); m_viewportUi.DisconnectViewportUiBus(); - AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusDisconnect(); AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusDisconnect(); AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusDisconnect(); } @@ -2425,7 +2428,7 @@ void EditorViewportWidget::RestoreViewportAfterGameMode() } else { - AZ_Error("CryLegacy", false, "Not restoring the editor viewport camera is currently unsupported"); + AZ_Warning("CryLegacy", false, "Not restoring the editor viewport camera is currently unsupported"); SetViewTM(preGameModeViewTM); } } @@ -2522,6 +2525,11 @@ float EditorViewportSettings::ManipulatorCircleBoundWidth() const return SandboxEditor::ManipulatorCircleBoundWidth(); } +bool EditorViewportSettings::StickySelectEnabled() const +{ + return SandboxEditor::StickySelectEnabled(); +} + AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once); bool EditorViewportWidget::ShouldPreviewFullscreen() const diff --git a/Code/Editor/EditorViewportWidget.h b/Code/Editor/EditorViewportWidget.h index c4a8e9fcca..49930a2a13 100644 --- a/Code/Editor/EditorViewportWidget.h +++ b/Code/Editor/EditorViewportWidget.h @@ -77,6 +77,7 @@ struct EditorViewportSettings : public AzToolsFramework::ViewportInteraction::Vi float AngleStep() const override; float ManipulatorLineBoundWidth() const override; float ManipulatorCircleBoundWidth() const override; + bool StickySelectEnabled() const override; }; // EditorViewportWidget window @@ -91,7 +92,6 @@ class SANDBOX_API EditorViewportWidget final , private AzFramework::InputSystemCursorConstraintRequestBus::Handler , private AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler , private AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler - , private AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler , private AzFramework::AssetCatalogEventBus::Handler , private AZ::RPI::SceneNotificationBus::Handler { @@ -211,9 +211,6 @@ private: // EditorEntityViewportInteractionRequestBus overrides ... void FindVisibleEntities(AZStd::vector& visibleEntities) override; - // EditorModifierKeyRequestBus overrides ... - AzToolsFramework::ViewportInteraction::KeyboardModifiers QueryKeyboardModifiers() override; - // Camera::EditorCameraRequestBus overrides ... void SetViewFromEntityPerspective(const AZ::EntityId& entityId) override; void SetViewAndMovementLockFromEntityPerspective(const AZ::EntityId& entityId, bool lockCameraMovement) override; diff --git a/Code/Editor/Export/ExportManager.h b/Code/Editor/Export/ExportManager.h index be81591e56..14318be957 100644 --- a/Code/Editor/Export/ExportManager.h +++ b/Code/Editor/Export/ExportManager.h @@ -36,8 +36,8 @@ namespace Export public: CMesh(); - virtual int GetFaceCount() const { return static_cast(m_faces.size()); } - virtual const Face* GetFaceBuffer() const { return m_faces.size() ? &m_faces[0] : 0; } + int GetFaceCount() const override { return static_cast(m_faces.size()); } + const Face* GetFaceBuffer() const override { return !m_faces.empty() ? &m_faces[0] : nullptr; } private: std::vector m_faces; @@ -54,13 +54,13 @@ namespace Export CObject(const char* pName); int GetVertexCount() const override { return static_cast(m_vertices.size()); } - const Vector3D* GetVertexBuffer() const override { return m_vertices.size() ? &m_vertices[0] : nullptr; } + const Vector3D* GetVertexBuffer() const override { return !m_vertices.empty() ? &m_vertices[0] : nullptr; } int GetNormalCount() const override { return static_cast(m_normals.size()); } - const Vector3D* GetNormalBuffer() const override { return m_normals.size() ? &m_normals[0] : nullptr; } + const Vector3D* GetNormalBuffer() const override { return !m_normals.empty() ? &m_normals[0] : nullptr; } int GetTexCoordCount() const override { return static_cast(m_texCoords.size()); } - const UV* GetTexCoordBuffer() const override { return m_texCoords.size() ? &m_texCoords[0] : nullptr; } + const UV* GetTexCoordBuffer() const override { return !m_texCoords.empty() ? &m_texCoords[0] : nullptr; } int GetMeshCount() const override { return static_cast(m_meshes.size()); } Mesh* GetMesh(int index) const override { return m_meshes[index]; } @@ -68,9 +68,9 @@ namespace Export size_t MeshHash() const override{return m_MeshHash; } void SetMaterialName(const char* pName); - virtual int GetEntityAnimationDataCount() const {return static_cast(m_entityAnimData.size()); } - virtual const EntityAnimData* GetEntityAnimationData(int index) const {return &m_entityAnimData[index]; } - virtual void SetEntityAnimationData(EntityAnimData entityData){ m_entityAnimData.push_back(entityData); }; + int GetEntityAnimationDataCount() const override {return static_cast(m_entityAnimData.size()); } + const EntityAnimData* GetEntityAnimationData(int index) const override {return &m_entityAnimData[index]; } + void SetEntityAnimationData(EntityAnimData entityData) override{ m_entityAnimData.push_back(entityData); }; void SetLastPtr(CBaseObject* pObject){m_pLastObject = pObject; }; CBaseObject* GetLastObjectPtr(){return m_pLastObject; }; @@ -92,9 +92,11 @@ namespace Export : public IData { public: - virtual int GetObjectCount() const { return static_cast(m_objects.size()); } - virtual Object* GetObject(int index) const { return m_objects[index]; } - virtual Object* AddObject(const char* objectName); + virtual ~CData() = default; + + int GetObjectCount() const override { return static_cast(m_objects.size()); } + Object* GetObject(int index) const override { return m_objects[index]; } + Object* AddObject(const char* objectName) override; void Clear(); private: @@ -117,7 +119,7 @@ public: //! Register exporter //! return true if succeed, otherwise false - virtual bool RegisterExporter(IExporter* pExporter); + bool RegisterExporter(IExporter* pExporter) override; //! Export specified geometry //! return true if succeed, otherwise false @@ -139,15 +141,15 @@ public: //! Exports the stat obj to the obj file specified //! returns true if succeeded, otherwise false - virtual bool ExportSingleStatObj(IStatObj* pStatObj, const char* filename); + bool ExportSingleStatObj(IStatObj* pStatObj, const char* filename) override; void SetBakedKeysSequenceExport(bool bBaked){m_bBakedKeysSequenceExport = bBaked; }; void SaveNodeKeysTimeToXML(); private: - void AddMesh(Export::CObject* pObj, const IIndexedMesh* pIndMesh, Matrix34A* pTm = 0); - bool AddStatObj(Export::CObject* pObj, IStatObj* pStatObj, Matrix34A* pTm = 0); + void AddMesh(Export::CObject* pObj, const IIndexedMesh* pIndMesh, Matrix34A* pTm = nullptr); + bool AddStatObj(Export::CObject* pObj, IStatObj* pStatObj, Matrix34A* pTm = nullptr); bool AddMeshes(Export::CObject* pObj); bool AddObject(CBaseObject* pBaseObj); void SolveHierarchy(); diff --git a/Code/Editor/GameExporter.cpp b/Code/Editor/GameExporter.cpp index 00dc3d8de1..9315505e08 100644 --- a/Code/Editor/GameExporter.cpp +++ b/Code/Editor/GameExporter.cpp @@ -432,25 +432,6 @@ void CGameExporter::ExportFileList(const QString& path, const QString& levelName newFileNode->setAttr("src", handle.m_filename.data()); newFileNode->setAttr("dest", handle.m_filename.data()); newFileNode->setAttr("size", handle.m_fileDesc.nSize); - - unsigned char md5[16]; - AZStd::string filenameToHash = GetIEditor()->GetGameEngine()->GetLevelPath().toUtf8().data(); - filenameToHash += "/"; - filenameToHash += AZStd::string{ handle.m_filename.data(), handle.m_filename.size() }; - if (gEnv->pCryPak->ComputeMD5(filenameToHash.data(), md5)) - { - char md5string[33]; - sprintf_s(md5string, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - md5[0], md5[1], md5[2], md5[3], - md5[4], md5[5], md5[6], md5[7], - md5[8], md5[9], md5[10], md5[11], - md5[12], md5[13], md5[14], md5[15]); - newFileNode->setAttr("md5", md5string); - } - else - { - newFileNode->setAttr("md5", ""); - } } } } while (handle = gEnv->pCryPak->FindNext(handle)); diff --git a/Code/Editor/IEditorImpl.h b/Code/Editor/IEditorImpl.h index 762dd1db11..26701edec2 100644 --- a/Code/Editor/IEditorImpl.h +++ b/Code/Editor/IEditorImpl.h @@ -79,70 +79,70 @@ public: void SetGameEngine(CGameEngine* ge); - void DeleteThis() { delete this; }; - IEditorClassFactory* GetClassFactory(); - CEditorCommandManager* GetCommandManager() { return m_pCommandManager; }; - ICommandManager* GetICommandManager() { return m_pCommandManager; } - void ExecuteCommand(const char* sCommand, ...); - void ExecuteCommand(const QString& command); - void SetDocument(CCryEditDoc* pDoc); - CCryEditDoc* GetDocument() const; + void DeleteThis() override { delete this; }; + IEditorClassFactory* GetClassFactory() override; + CEditorCommandManager* GetCommandManager() override { return m_pCommandManager; }; + ICommandManager* GetICommandManager() override { return m_pCommandManager; } + void ExecuteCommand(const char* sCommand, ...) override; + void ExecuteCommand(const QString& command) override; + void SetDocument(CCryEditDoc* pDoc) override; + CCryEditDoc* GetDocument() const override; bool IsLevelLoaded() const override; - void SetModifiedFlag(bool modified = true); - void SetModifiedModule(EModifiedModule eModifiedModule, bool boSet = true); - bool IsLevelExported() const; - bool SetLevelExported(bool boExported = true); + void SetModifiedFlag(bool modified = true) override; + void SetModifiedModule(EModifiedModule eModifiedModule, bool boSet = true) override; + bool IsLevelExported() const override; + bool SetLevelExported(bool boExported = true) override; void InitFinished(); - bool IsModified(); - bool IsInitialized() const{ return m_bInitialized; } - bool SaveDocument(); - ISystem* GetSystem(); - void WriteToConsole(const char* string) { CLogFile::WriteLine(string); }; - void WriteToConsole(const QString& string) { CLogFile::WriteLine(string); }; + bool IsModified() override; + bool IsInitialized() const override{ return m_bInitialized; } + bool SaveDocument() override; + ISystem* GetSystem() override; + void WriteToConsole(const char* string) override { CLogFile::WriteLine(string); }; + void WriteToConsole(const QString& string) override { CLogFile::WriteLine(string); }; // Change the message in the status bar - void SetStatusText(const QString& pszString); - virtual IMainStatusBar* GetMainStatusBar() override; - bool ShowConsole([[maybe_unused]] bool show) + void SetStatusText(const QString& pszString) override; + IMainStatusBar* GetMainStatusBar() override; + bool ShowConsole([[maybe_unused]] bool show) override { //if (AfxGetMainWnd())return ((CMainFrame *) (AfxGetMainWnd()))->ShowConsole(show); return false; } - void SetConsoleVar(const char* var, float value); - float GetConsoleVar(const char* var); + void SetConsoleVar(const char* var, float value) override; + float GetConsoleVar(const char* var) override; //! Query main window of the editor QMainWindow* GetEditorMainWindow() const override { return MainWindow::instance(); }; - QString GetPrimaryCDFolder(); + QString GetPrimaryCDFolder() override; QString GetLevelName() override; - QString GetLevelFolder(); - QString GetLevelDataFolder(); - QString GetSearchPath(EEditorPathName path); - QString GetResolvedUserFolder(); - bool ExecuteConsoleApp(const QString& CommandLine, QString& OutputText, bool bNoTimeOut = false, bool bShowWindow = false); - virtual bool IsInGameMode() override; - virtual void SetInGameMode(bool inGame) override; - virtual bool IsInSimulationMode() override; - virtual bool IsInTestMode() override; - virtual bool IsInPreviewMode() override; - virtual bool IsInConsolewMode() override; - virtual bool IsInLevelLoadTestMode() override; - virtual bool IsInMatEditMode() override { return m_bMatEditMode; } + QString GetLevelFolder() override; + QString GetLevelDataFolder() override; + QString GetSearchPath(EEditorPathName path) override; + QString GetResolvedUserFolder() override; + bool ExecuteConsoleApp(const QString& CommandLine, QString& OutputText, bool bNoTimeOut = false, bool bShowWindow = false) override; + bool IsInGameMode() override; + void SetInGameMode(bool inGame) override; + bool IsInSimulationMode() override; + bool IsInTestMode() override; + bool IsInPreviewMode() override; + bool IsInConsolewMode() override; + bool IsInLevelLoadTestMode() override; + bool IsInMatEditMode() override { return m_bMatEditMode; } //! Enables/Disable updates of editor. - void EnableUpdate(bool enable) { m_bUpdates = enable; }; + void EnableUpdate(bool enable) override { m_bUpdates = enable; }; //! Enable/Disable accelerator table, (Enabled by default). - void EnableAcceleratos(bool bEnable); - CGameEngine* GetGameEngine() { return m_pGameEngine; }; - CDisplaySettings* GetDisplaySettings() { return m_pDisplaySettings; }; - const SGizmoParameters& GetGlobalGizmoParameters(); - CBaseObject* NewObject(const char* typeName, const char* fileName = "", const char* name = "", float x = 0.0f, float y = 0.0f, float z = 0.0f, bool modifyDoc = true); - void DeleteObject(CBaseObject* obj); - CBaseObject* CloneObject(CBaseObject* obj); - IObjectManager* GetObjectManager(); + void EnableAcceleratos(bool bEnable) override; + CGameEngine* GetGameEngine() override { return m_pGameEngine; }; + CDisplaySettings* GetDisplaySettings() override { return m_pDisplaySettings; }; + const SGizmoParameters& GetGlobalGizmoParameters() override; + CBaseObject* NewObject(const char* typeName, const char* fileName = "", const char* name = "", float x = 0.0f, float y = 0.0f, float z = 0.0f, bool modifyDoc = true) override; + void DeleteObject(CBaseObject* obj) override; + CBaseObject* CloneObject(CBaseObject* obj) override; + IObjectManager* GetObjectManager() override; // This will return a null pointer if CrySystem is not loaded before // Global Sandbox Settings are loaded from the registry before CrySystem // At that stage GetSettingsManager will return null and xml node in @@ -150,27 +150,27 @@ public: // After m_IEditor is created and CrySystem loaded, it is possible // to feed memory node with all necessary data needed for export // (gSettings.Load() and CXTPDockingPaneManager/CXTPDockingPaneLayout Sandbox layout management) - CSettingsManager* GetSettingsManager(); - CSelectionGroup* GetSelection(); - int ClearSelection(); - CBaseObject* GetSelectedObject(); - void SelectObject(CBaseObject* obj); - void LockSelection(bool bLock); - bool IsSelectionLocked(); + CSettingsManager* GetSettingsManager() override; + CSelectionGroup* GetSelection() override; + int ClearSelection() override; + CBaseObject* GetSelectedObject() override; + void SelectObject(CBaseObject* obj) override; + void LockSelection(bool bLock) override; + bool IsSelectionLocked() override; - IDataBaseManager* GetDBItemManager(EDataBaseItemType itemType); - CMusicManager* GetMusicManager() { return m_pMusicManager; }; + IDataBaseManager* GetDBItemManager(EDataBaseItemType itemType) override; + CMusicManager* GetMusicManager() override { return m_pMusicManager; }; IEditorFileMonitor* GetFileMonitor() override; void RegisterEventLoopHook(IEventLoopHook* pHook) override; void UnregisterEventLoopHook(IEventLoopHook* pHook) override; - IIconManager* GetIconManager(); - float GetTerrainElevation(float x, float y); - Editor::EditorQtApplication* GetEditorQtApplication() { return m_QtApplication; } + IIconManager* GetIconManager() override; + float GetTerrainElevation(float x, float y) override; + Editor::EditorQtApplication* GetEditorQtApplication() override { return m_QtApplication; } const QColor& GetColorByName(const QString& name) override; ////////////////////////////////////////////////////////////////////////// - IMovieSystem* GetMovieSystem() + IMovieSystem* GetMovieSystem() override { if (m_pSystem) { @@ -179,37 +179,37 @@ public: return nullptr; }; - CPluginManager* GetPluginManager() { return m_pPluginManager; } - CViewManager* GetViewManager(); - CViewport* GetActiveView(); - void SetActiveView(CViewport* viewport); + CPluginManager* GetPluginManager() override { return m_pPluginManager; } + CViewManager* GetViewManager() override; + CViewport* GetActiveView() override; + void SetActiveView(CViewport* viewport) override; - CLevelIndependentFileMan* GetLevelIndependentFileMan() { return m_pLevelIndependentFileMan; } + CLevelIndependentFileMan* GetLevelIndependentFileMan() override { return m_pLevelIndependentFileMan; } - void UpdateViews(int flags, const AABB* updateRegion); - void ResetViews(); - void ReloadTrackView(); - Vec3 GetMarkerPosition() { return m_marker; }; - void SetMarkerPosition(const Vec3& pos) { m_marker = pos; }; - void SetSelectedRegion(const AABB& box); - void GetSelectedRegion(AABB& box); + void UpdateViews(int flags, const AABB* updateRegion) override; + void ResetViews() override; + void ReloadTrackView() override; + Vec3 GetMarkerPosition() override { return m_marker; }; + void SetMarkerPosition(const Vec3& pos) override { m_marker = pos; }; + void SetSelectedRegion(const AABB& box) override; + void GetSelectedRegion(AABB& box) override; bool AddToolbarItem(uint8 iId, IUIEvent* pIHandler); - void SetDataModified(); - void SetOperationMode(EOperationMode mode); - EOperationMode GetOperationMode(); + void SetDataModified() override; + void SetOperationMode(EOperationMode mode) override; + EOperationMode GetOperationMode() override; - ITransformManipulator* ShowTransformManipulator(bool bShow); - ITransformManipulator* GetTransformManipulator(); - void SetAxisConstraints(AxisConstrains axis); - AxisConstrains GetAxisConstrains(); - void SetAxisVectorLock(bool bAxisVectorLock) { m_bAxisVectorLock = bAxisVectorLock; } - bool IsAxisVectorLocked() { return m_bAxisVectorLock; } - void SetTerrainAxisIgnoreObjects(bool bIgnore); - bool IsTerrainAxisIgnoreObjects(); - void SetReferenceCoordSys(RefCoordSys refCoords); - RefCoordSys GetReferenceCoordSys(); - XmlNodeRef FindTemplate(const QString& templateName); - void AddTemplate(const QString& templateName, XmlNodeRef& tmpl); + ITransformManipulator* ShowTransformManipulator(bool bShow) override; + ITransformManipulator* GetTransformManipulator() override; + void SetAxisConstraints(AxisConstrains axis) override; + AxisConstrains GetAxisConstrains() override; + void SetAxisVectorLock(bool bAxisVectorLock) override { m_bAxisVectorLock = bAxisVectorLock; } + bool IsAxisVectorLocked() override { return m_bAxisVectorLock; } + void SetTerrainAxisIgnoreObjects(bool bIgnore) override; + bool IsTerrainAxisIgnoreObjects() override; + void SetReferenceCoordSys(RefCoordSys refCoords) override; + RefCoordSys GetReferenceCoordSys() override; + XmlNodeRef FindTemplate(const QString& templateName) override; + void AddTemplate(const QString& templateName, XmlNodeRef& tmpl) override; const QtViewPane* OpenView(QString sViewClassName, bool reuseOpened = true) override; @@ -220,87 +220,87 @@ public: */ QWidget* FindView(QString viewClassName) override; - bool CloseView(const char* sViewClassName); - bool SetViewFocus(const char* sViewClassName); + bool CloseView(const char* sViewClassName) override; + bool SetViewFocus(const char* sViewClassName) override; - virtual QWidget* OpenWinWidget(WinWidgetId openId) override; - virtual WinWidget::WinWidgetManager* GetWinWidgetManager() const override; + QWidget* OpenWinWidget(WinWidgetId openId) override; + WinWidget::WinWidgetManager* GetWinWidgetManager() const override; // close ALL panels related to classId, used when unloading plugins. - void CloseView(const GUID& classId); + void CloseView(const GUID& classId) override; bool SelectColor(QColor &color, QWidget *parent = 0) override; void Update(); - SFileVersion GetFileVersion() { return m_fileVersion; }; - SFileVersion GetProductVersion() { return m_productVersion; }; + SFileVersion GetFileVersion() override { return m_fileVersion; }; + SFileVersion GetProductVersion() override { return m_productVersion; }; //! Get shader enumerator. - CUndoManager* GetUndoManager() { return m_pUndoManager; }; - void BeginUndo(); - void RestoreUndo(bool undo); - void AcceptUndo(const QString& name); - void CancelUndo(); - void SuperBeginUndo(); - void SuperAcceptUndo(const QString& name); - void SuperCancelUndo(); - void SuspendUndo(); - void ResumeUndo(); - void Undo(); - void Redo(); - bool IsUndoRecording(); - bool IsUndoSuspended(); - void RecordUndo(IUndoObject* obj); - bool FlushUndo(bool isShowMessage = false); - bool ClearLastUndoSteps(int steps); - bool ClearRedoStack(); + CUndoManager* GetUndoManager() override { return m_pUndoManager; }; + void BeginUndo() override; + void RestoreUndo(bool undo) override; + void AcceptUndo(const QString& name) override; + void CancelUndo() override; + void SuperBeginUndo() override; + void SuperAcceptUndo(const QString& name) override; + void SuperCancelUndo() override; + void SuspendUndo() override; + void ResumeUndo() override; + void Undo() override; + void Redo() override; + bool IsUndoRecording() override; + bool IsUndoSuspended() override; + void RecordUndo(IUndoObject* obj) override; + bool FlushUndo(bool isShowMessage = false) override; + bool ClearLastUndoSteps(int steps) override; + bool ClearRedoStack() override; //! Retrieve current animation context. - CAnimationContext* GetAnimation(); + CAnimationContext* GetAnimation() override; CTrackViewSequenceManager* GetSequenceManager() override; ITrackViewSequenceManager* GetSequenceManagerInterface() override; - CToolBoxManager* GetToolBoxManager() { return m_pToolBoxManager; }; - IErrorReport* GetErrorReport() { return m_pErrorReport; } - IErrorReport* GetLastLoadedLevelErrorReport() { return m_pLasLoadedLevelErrorReport; } + CToolBoxManager* GetToolBoxManager() override { return m_pToolBoxManager; }; + IErrorReport* GetErrorReport() override { return m_pErrorReport; } + IErrorReport* GetLastLoadedLevelErrorReport() override { return m_pLasLoadedLevelErrorReport; } void StartLevelErrorReportRecording() override; - void CommitLevelErrorReport() {SAFE_DELETE(m_pLasLoadedLevelErrorReport); m_pLasLoadedLevelErrorReport = new CErrorReport(*m_pErrorReport); } - virtual IFileUtil* GetFileUtil() override { return m_pFileUtil; } - void Notify(EEditorNotifyEvent event); - void NotifyExcept(EEditorNotifyEvent event, IEditorNotifyListener* listener); - void RegisterNotifyListener(IEditorNotifyListener* listener); - void UnregisterNotifyListener(IEditorNotifyListener* listener); + void CommitLevelErrorReport() override {SAFE_DELETE(m_pLasLoadedLevelErrorReport); m_pLasLoadedLevelErrorReport = new CErrorReport(*m_pErrorReport); } + IFileUtil* GetFileUtil() override { return m_pFileUtil; } + void Notify(EEditorNotifyEvent event) override; + void NotifyExcept(EEditorNotifyEvent event, IEditorNotifyListener* listener) override; + void RegisterNotifyListener(IEditorNotifyListener* listener) override; + void UnregisterNotifyListener(IEditorNotifyListener* listener) override; //! Register document notifications listener. - void RegisterDocListener(IDocListener* listener); + void RegisterDocListener(IDocListener* listener) override; //! Unregister document notifications listener. - void UnregisterDocListener(IDocListener* listener); + void UnregisterDocListener(IDocListener* listener) override; //! Retrieve interface to the source control. - ISourceControl* GetSourceControl(); + ISourceControl* GetSourceControl() override; //! Retrieve true if source control is provided and enabled in settings bool IsSourceControlAvailable() override; //! Only returns true if source control is both available AND currently connected and functioning bool IsSourceControlConnected() override; //! Setup Material Editor mode void SetMatEditMode(bool bIsMatEditMode); - CUIEnumsDatabase* GetUIEnumsDatabase() { return m_pUIEnumsDatabase; }; - void AddUIEnums(); - void ReduceMemory(); + CUIEnumsDatabase* GetUIEnumsDatabase() override { return m_pUIEnumsDatabase; }; + void AddUIEnums() override; + void ReduceMemory() override; // Get Export manager - IExportManager* GetExportManager(); + IExportManager* GetExportManager() override; // Set current configuration spec of the editor. - void SetEditorConfigSpec(ESystemConfigSpec spec, ESystemConfigPlatform platform); - ESystemConfigSpec GetEditorConfigSpec() const; - ESystemConfigPlatform GetEditorConfigPlatform() const; - void ReloadTemplates(); + void SetEditorConfigSpec(ESystemConfigSpec spec, ESystemConfigPlatform platform) override; + ESystemConfigSpec GetEditorConfigSpec() const override; + ESystemConfigPlatform GetEditorConfigPlatform() const override; + void ReloadTemplates() override; void AddErrorMessage(const QString& text, const QString& caption); - virtual void ShowStatusText(bool bEnable); + void ShowStatusText(bool bEnable) override; void OnObjectContextMenuOpened(QMenu* pMenu, const CBaseObject* pObject); - virtual void RegisterObjectContextMenuExtension(TContextMenuExtensionFunc func) override; + void RegisterObjectContextMenuExtension(TContextMenuExtensionFunc func) override; - virtual SSystemGlobalEnvironment* GetEnv() override; - virtual IBaseLibraryManager* GetMaterialManagerLibrary() override; // Vladimir@Conffx - virtual IEditorMaterialManager* GetIEditorMaterialManager() override; // Vladimir@Conffx - virtual IImageUtil* GetImageUtil() override; // Vladimir@conffx - virtual SEditorSettings* GetEditorSettings() override; - virtual IEditorPanelUtils* GetEditorPanelUtils() override; - virtual ILogFile* GetLogFile() override { return m_pLogFile; } + SSystemGlobalEnvironment* GetEnv() override; + IBaseLibraryManager* GetMaterialManagerLibrary() override; // Vladimir@Conffx + IEditorMaterialManager* GetIEditorMaterialManager() override; // Vladimir@Conffx + IImageUtil* GetImageUtil() override; // Vladimir@conffx + SEditorSettings* GetEditorSettings() override; + IEditorPanelUtils* GetEditorPanelUtils() override; + ILogFile* GetLogFile() override { return m_pLogFile; } void UnloadPlugins() override; void LoadPlugins() override; diff --git a/Code/Editor/Include/IEditorClassFactory.h b/Code/Editor/Include/IEditorClassFactory.h index 0827dc96dc..dd47f803e2 100644 --- a/Code/Editor/Include/IEditorClassFactory.h +++ b/Code/Editor/Include/IEditorClassFactory.h @@ -14,8 +14,10 @@ #define CRYINCLUDE_EDITOR_INCLUDE_IEDITORCLASSFACTORY_H #pragma once +#include #include #include +#include #define DEFINE_UUID(l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ static const GUID uuid() { return { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }; } @@ -34,7 +36,7 @@ struct IUnknown #endif #define __uuidof(T) T::uuid() -#if defined(AZ_PLATFORM_LINUX) +#if defined(AZ_PLATFORM_LINUX) || defined(AZ_PLATFORM_MAC) # ifndef _REFGUID_DEFINED # define _REFGUID_DEFINED @@ -65,7 +67,7 @@ enum }; #endif -#endif // defined(AZ_PLATFORM_LINUX) +#endif // defined(AZ_PLATFORM_LINUX) || defined(AZ_PLATFORM_MAC) #include "SandboxAPI.h" diff --git a/Code/Editor/LevelTreeModel.h b/Code/Editor/LevelTreeModel.h index 4f0e36a311..7cc5cf5496 100644 --- a/Code/Editor/LevelTreeModel.h +++ b/Code/Editor/LevelTreeModel.h @@ -23,7 +23,7 @@ class LevelTreeModelFilter Q_OBJECT public: explicit LevelTreeModelFilter(QObject* parent = nullptr); - bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const; + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; void setFilterText(const QString&); QVariant data(const QModelIndex& index, int role) const override; private: diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Android/tool_dependencies_android.cmake b/Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake similarity index 85% rename from Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Android/tool_dependencies_android.cmake rename to Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake index 5bf4d7cb7e..69d3e37f2d 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Android/tool_dependencies_android.cmake +++ b/Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake @@ -6,5 +6,6 @@ # # -set(GEM_DEPENDENCIES +set(FILES + test_EditorCamera.cpp ) diff --git a/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp b/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp new file mode 100644 index 0000000000..affe5794cc --- /dev/null +++ b/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class EditorCameraTestEnvironment : public AZ::Test::GemTestEnvironment + { + // AZ::Test::GemTestEnvironment overrides ... + void AddGemsAndComponents() override; + }; + + void EditorCameraTestEnvironment::AddGemsAndComponents() + { + AddDynamicModulePaths({ CAMERA_EDITOR_MODULE }); + AddComponentDescriptors({ AzToolsFramework::Components::TransformComponent::CreateDescriptor() }); + } + + class EditorCameraFixture : public ::testing::Test + { + public: + AtomToolsFramework::ModularCameraViewportContext* m_cameraViewportContextView = nullptr; + AZStd::unique_ptr m_editorModularViewportCameraComposer; + AZStd::unique_ptr m_editorLibHandle; + AzFramework::ViewportControllerListPtr m_controllerList; + AZStd::unique_ptr m_entity; + + static const AzFramework::ViewportId TestViewportId; + + void SetUp() override + { + m_editorLibHandle = AZ::DynamicModuleHandle::Create("EditorLib"); + [[maybe_unused]] const bool loaded = m_editorLibHandle->Load(true); + AZ_Assert(loaded, "EditorLib could not be loaded"); + + m_controllerList = AZStd::make_shared(); + m_controllerList->RegisterViewportContext(TestViewportId); + + m_entity = AZStd::make_unique(); + m_entity->Init(); + m_entity->CreateComponent(); + m_entity->Activate(); + + m_editorModularViewportCameraComposer = AZStd::make_unique(TestViewportId); + + auto controller = m_editorModularViewportCameraComposer->CreateModularViewportCameraController(); + // set some overrides for the test + controller->SetCameraViewportContextBuilderCallback( + [this](AZStd::unique_ptr& cameraViewportContext) mutable + { + cameraViewportContext = AZStd::make_unique(); + m_cameraViewportContextView = cameraViewportContext.get(); + }); + + m_controllerList->Add(controller); + } + + void TearDown() override + { + m_editorModularViewportCameraComposer.reset(); + m_cameraViewportContextView = nullptr; + m_entity.reset(); + m_editorLibHandle = {}; + } + }; + + const AzFramework::ViewportId EditorCameraFixture::TestViewportId = AzFramework::ViewportId(1337); + + TEST_F(EditorCameraFixture, ModularViewportCameraControllerReferenceFrameUpdatedWhenViewportEntityisChanged) + { + // Given + const auto entityTransform = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(10.0f, 5.0f, -2.0f)); + AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformBus::Events::SetWorldTM, entityTransform); + + // When + // imitate viewport entity changing + Camera::EditorCameraNotificationBus::Broadcast( + &Camera::EditorCameraNotificationBus::Events::OnViewportViewEntityChanged, m_entity->GetId()); + + // ensure the viewport updates after the viewport view entity change + const float deltaTime = 1.0f / 60.0f; + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() }); + + // retrieve updated camera transform + const AZ::Transform cameraTransform = m_cameraViewportContextView->GetCameraTransform(); + + // Then + // camera transform matches that of the entity + EXPECT_THAT(cameraTransform, IsClose(entityTransform)); + } + + TEST_F(EditorCameraFixture, ReferenceFrameRemainsIdentityAfterExternalCameraTransformChangeWhenNotSet) + { + // Given + m_cameraViewportContextView->SetCameraTransform(AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f))); + + // When + AZ::Transform referenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + referenceFrame, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + + // Then + // reference frame is still the identity + EXPECT_THAT(referenceFrame, IsClose(AZ::Transform::CreateIdentity())); + } + + TEST_F(EditorCameraFixture, ExternalCameraTransformChangeWhenReferenceFrameIsSetUpdatesReferenceFrame) + { + // Given + const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame); + + const AZ::Transform nextTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f)); + m_cameraViewportContextView->SetCameraTransform(nextTransform); + + // When + AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + currentReferenceFrame, TestViewportId, + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + + // Then + EXPECT_THAT(currentReferenceFrame, IsClose(nextTransform)); + } + + TEST_F(EditorCameraFixture, ReferenceFrameReturnedToIdentityAfterClear) + { + // Given + const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame); + + // When + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame); + + AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + currentReferenceFrame, TestViewportId, + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + + // Then + EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity())); + } + + TEST_F(EditorCameraFixture, InterpolateToTransform) + { + // When + AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, + transformToInterpolateTo, 0.0f); + + // simulate interpolation + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); + + const auto finalTransform = m_cameraViewportContextView->GetCameraTransform(); + + // Then + EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo)); + } + + TEST_F(EditorCameraFixture, InterpolateToTransformWithReferenceSpaceSet) + { + // Given + const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame); + + AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f)); + + // When + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, + transformToInterpolateTo, 0.0f); + + // simulate interpolation + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); + + AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + currentReferenceFrame, TestViewportId, + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + + const auto finalTransform = m_cameraViewportContextView->GetCameraTransform(); + + // Then + EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo)); + EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity())); + } +} // namespace UnitTest + +// required to support running integration tests with the Camera Gem +AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv) +{ + ::testing::InitGoogleMock(&argc, argv); + AZ::Test::printUnusedParametersWarning(argc, argv); + AZ::Test::addTestEnvironments({ new UnitTest::EditorCameraTestEnvironment() }); + int result = RUN_ALL_TESTS(); + return result; +} + +IMPLEMENT_TEST_EXECUTABLE_MAIN(); diff --git a/Code/Editor/Lib/Tests/IEditorMock.h b/Code/Editor/Lib/Tests/IEditorMock.h index cb01757b9c..390ffebe79 100644 --- a/Code/Editor/Lib/Tests/IEditorMock.h +++ b/Code/Editor/Lib/Tests/IEditorMock.h @@ -25,6 +25,8 @@ public: } public: + virtual ~CEditorMock() = default; + MOCK_METHOD0(DeleteThis, void()); MOCK_METHOD0(GetSystem, ISystem*()); MOCK_METHOD0(GetClassFactory, IEditorClassFactory* ()); diff --git a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp index ce5564c7f6..157291cfc2 100644 --- a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp +++ b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp @@ -58,28 +58,6 @@ namespace UnitTest return true; } - class TestModularCameraViewportContextImpl : public AtomToolsFramework::ModularCameraViewportContext - { - public: - AZ::Transform GetCameraTransform() const override - { - return m_cameraTransform; - } - - void SetCameraTransform(const AZ::Transform& transform) override - { - m_cameraTransform = transform; - } - - void ConnectViewMatrixChangedHandler(AZ::RPI::ViewportContext::MatrixChangedEvent::Handler&) override - { - // noop - } - - private: - AZ::Transform m_cameraTransform = AZ::Transform::CreateIdentity(); - }; - class ModularViewportCameraControllerFixture : public AllocatorsTestFixture { public: @@ -146,7 +124,7 @@ namespace UnitTest controller->SetCameraViewportContextBuilderCallback( [this](AZStd::unique_ptr& cameraViewportContext) { - cameraViewportContext = AZStd::make_unique(); + cameraViewportContext = AZStd::make_unique(); m_cameraViewportContextView = cameraViewportContext.get(); }); diff --git a/Code/Editor/Objects/AxisGizmo.h b/Code/Editor/Objects/AxisGizmo.h index dfd35388b6..bab667a629 100644 --- a/Code/Editor/Objects/AxisGizmo.h +++ b/Code/Editor/Objects/AxisGizmo.h @@ -35,21 +35,21 @@ public: ////////////////////////////////////////////////////////////////////////// // Ovverides from CGizmo ////////////////////////////////////////////////////////////////////////// - virtual void GetWorldBounds(AABB& bbox); - virtual void Display(DisplayContext& dc); - virtual bool HitTest(HitContext& hc); - virtual const Matrix34& GetMatrix() const; + void GetWorldBounds(AABB& bbox) override; + void Display(DisplayContext& dc) override; + bool HitTest(HitContext& hc) override; + const Matrix34& GetMatrix() const override; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // ITransformManipulator implementation. ////////////////////////////////////////////////////////////////////////// - virtual Matrix34 GetTransformation(RefCoordSys coordSys, IDisplayViewport* view = nullptr) const; - virtual void SetTransformation(RefCoordSys coordSys, const Matrix34& tm); - virtual bool HitTestManipulator(HitContext& hc); - virtual bool MouseCallback(CViewport* view, EMouseEvent event, QPoint& point, int nFlags); - virtual void SetAlwaysUseLocal(bool on) + Matrix34 GetTransformation(RefCoordSys coordSys, IDisplayViewport* view = nullptr) const override; + void SetTransformation(RefCoordSys coordSys, const Matrix34& tm) override; + bool HitTestManipulator(HitContext& hc) override; + bool MouseCallback(CViewport* view, EMouseEvent event, QPoint& point, int nFlags) override; + void SetAlwaysUseLocal(bool on) override { m_bAlwaysUseLocal = on; } ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Editor/Objects/BaseObject.h b/Code/Editor/Objects/BaseObject.h index ea58b4e78d..3865696ecb 100644 --- a/Code/Editor/Objects/BaseObject.h +++ b/Code/Editor/Objects/BaseObject.h @@ -712,7 +712,7 @@ protected: // May be overridden in derived classes to handle helpers scaling. ////////////////////////////////////////////////////////////////////////// virtual void SetHelperScale([[maybe_unused]] float scale) {}; - virtual float GetHelperScale() { return 1; }; + virtual float GetHelperScale() { return 1.0f; }; void SetNameInternal(const QString& name) { m_name = name; } @@ -743,9 +743,6 @@ private: //! Only called once after creation by ObjectManager. void SetClassDesc(CObjectClassDesc* classDesc); - // From CObject, (not implemented) - virtual void Serialize([[maybe_unused]] CArchive& ar) {}; - EScaleWarningLevel GetScaleWarningLevel() const; ERotationWarningLevel GetRotationWarningLevel() const; diff --git a/Code/Editor/Objects/EntityObject.h b/Code/Editor/Objects/EntityObject.h index d5600d15b7..dcc6ff7b22 100644 --- a/Code/Editor/Objects/EntityObject.h +++ b/Code/Editor/Objects/EntityObject.h @@ -82,14 +82,14 @@ public: // Overrides from CBaseObject. ////////////////////////////////////////////////////////////////////////// //! Return type name of Entity. - QString GetTypeDescription() const { return GetEntityClass(); }; + QString GetTypeDescription() const override { return GetEntityClass(); }; ////////////////////////////////////////////////////////////////////////// - bool IsSameClass(CBaseObject* obj); + bool IsSameClass(CBaseObject* obj) override; - virtual bool Init(IEditor* ie, CBaseObject* prev, const QString& file); - virtual void InitVariables(); - virtual void Done(); + bool Init(IEditor* ie, CBaseObject* prev, const QString& file) override; + void InitVariables() override; + void Done() override; void DrawExtraLightInfo (DisplayContext& disp); @@ -102,29 +102,30 @@ public: void SetEntityPropertyFloat(const char* name, float value); void SetEntityPropertyString(const char* name, const QString& value); - virtual int MouseCreateCallback(CViewport* view, EMouseEvent event, QPoint& point, int flags); - virtual void OnContextMenu(QMenu* menu); + int MouseCreateCallback(CViewport* view, EMouseEvent event, QPoint& point, int flags) override; + void OnContextMenu(QMenu* menu) override; - void SetName(const QString& name); - void SetSelected(bool bSelect); + void SetName(const QString& name) override; + void SetSelected(bool bSelect) override; - virtual void GetLocalBounds(AABB& box); + void GetLocalBounds(AABB& box) override; - virtual bool HitTest(HitContext& hc); - virtual bool HitHelperTest(HitContext& hc); - virtual bool HitTestRect(HitContext& hc); - void UpdateVisibility(bool bVisible); - bool ConvertFromObject(CBaseObject* object); + bool HitTest(HitContext& hc) override; + bool HitHelperTest(HitContext& hc) override; + bool HitTestRect(HitContext& hc) override; + void UpdateVisibility(bool bVisible) override; + bool ConvertFromObject(CBaseObject* object) override; - virtual void Serialize(CObjectArchive& ar); - virtual void PostLoad(CObjectArchive& ar); + using CBaseObject::Serialize; + void Serialize(CObjectArchive& ar) override; + void PostLoad(CObjectArchive& ar) override; - XmlNodeRef Export(const QString& levelPath, XmlNodeRef& xmlNode); + XmlNodeRef Export(const QString& levelPath, XmlNodeRef& xmlNode) override; ////////////////////////////////////////////////////////////////////////// - void OnEvent(ObjectEvent event); + void OnEvent(ObjectEvent event) override; - virtual void SetTransformDelegate(ITransformDelegate* pTransformDelegate) override; + void SetTransformDelegate(ITransformDelegate* pTransformDelegate) override; // Set attach flags and target enum EAttachmentType @@ -139,15 +140,15 @@ public: EAttachmentType GetAttachType() const { return m_attachmentType; } QString GetAttachTarget() const { return m_attachmentTarget; } - virtual void SetHelperScale(float scale); - virtual float GetHelperScale(); + void SetHelperScale(float scale) override; + float GetHelperScale() override; - virtual void GatherUsedResources(CUsedResources& resources); - virtual bool IsSimilarObject(CBaseObject* pObject); + void GatherUsedResources(CUsedResources& resources) override; + bool IsSimilarObject(CBaseObject* pObject) override; - virtual bool HasMeasurementAxis() const { return false; } + bool HasMeasurementAxis() const override { return false; } - virtual bool IsIsolated() const { return false; } + bool IsIsolated() const override { return false; } ////////////////////////////////////////////////////////////////////////// // END CBaseObject @@ -232,7 +233,7 @@ protected: ////////////////////////////////////////////////////////////////////////// //! Must be called after cloning the object on clone of object. //! This will make sure object references are cloned correctly. - virtual void PostClone(CBaseObject* pFromObject, CObjectCloneContext& ctx); + void PostClone(CBaseObject* pFromObject, CObjectCloneContext& ctx) override; //! Draw default object items. void DrawProjectorPyramid(DisplayContext& dc, float dist); @@ -264,7 +265,7 @@ public: } protected: - void DeleteThis() { delete this; }; + void DeleteThis() override { delete this; }; ////////////////////////////////////////////////////////////////////////// // Radius callbacks. diff --git a/Code/Editor/Objects/GizmoManager.h b/Code/Editor/Objects/GizmoManager.h index c3a71ad81b..efc3e99205 100644 --- a/Code/Editor/Objects/GizmoManager.h +++ b/Code/Editor/Objects/GizmoManager.h @@ -23,14 +23,14 @@ class CGizmoManager : public IGizmoManager { public: - void AddGizmo(CGizmo* gizmo); - void RemoveGizmo(CGizmo* gizmo); + void AddGizmo(CGizmo* gizmo) override; + void RemoveGizmo(CGizmo* gizmo) override; int GetGizmoCount() const override; CGizmo* GetGizmoByIndex(int nIndex) const override; - void Display(DisplayContext& dc); - bool HitTest(HitContext& hc); + void Display(DisplayContext& dc) override; + bool HitTest(HitContext& hc) override; void DeleteAllTransformManipulators(); diff --git a/Code/Editor/Objects/LineGizmo.h b/Code/Editor/Objects/LineGizmo.h index af3b38b199..e50f044f3c 100644 --- a/Code/Editor/Objects/LineGizmo.h +++ b/Code/Editor/Objects/LineGizmo.h @@ -30,10 +30,10 @@ public: ////////////////////////////////////////////////////////////////////////// // Ovverides from CGizmo ////////////////////////////////////////////////////////////////////////// - virtual void SetName(const char* sName); - virtual void GetWorldBounds(AABB& bbox); - virtual void Display(DisplayContext& dc); - virtual bool HitTest(HitContext& hc); + void SetName(const char* sName) override; + void GetWorldBounds(AABB& bbox) override; + void Display(DisplayContext& dc) override; + bool HitTest(HitContext& hc) override; ////////////////////////////////////////////////////////////////////////// void SetObjects(CBaseObject* pObject1, CBaseObject* pObject2, const QString& boneName = ""); diff --git a/Code/Editor/Objects/ObjectManager.cpp b/Code/Editor/Objects/ObjectManager.cpp index e053d10fd1..06aa8866b3 100644 --- a/Code/Editor/Objects/ObjectManager.cpp +++ b/Code/Editor/Objects/ObjectManager.cpp @@ -52,6 +52,7 @@ public: GUID guid; public: + virtual ~CXMLObjectClassDesc() = default; REFGUID ClassID() override { return guid; diff --git a/Code/Editor/Objects/ObjectManager.h b/Code/Editor/Objects/ObjectManager.h index de0a4ce849..4ffa5e9a07 100644 --- a/Code/Editor/Objects/ObjectManager.h +++ b/Code/Editor/Objects/ObjectManager.h @@ -103,142 +103,142 @@ public: void RegisterObjectClasses(); - CBaseObject* NewObject(CObjectClassDesc* cls, CBaseObject* prev = 0, const QString& file = "", const char* newObjectName = nullptr); - CBaseObject* NewObject(const QString& typeName, CBaseObject* prev = 0, const QString& file = "", const char* newEntityName = nullptr); + CBaseObject* NewObject(CObjectClassDesc* cls, CBaseObject* prev = 0, const QString& file = "", const char* newObjectName = nullptr) override; + CBaseObject* NewObject(const QString& typeName, CBaseObject* prev = 0, const QString& file = "", const char* newEntityName = nullptr) override; - void DeleteObject(CBaseObject* obj); - void DeleteSelection(CSelectionGroup* pSelection); - void DeleteAllObjects(); - CBaseObject* CloneObject(CBaseObject* obj); + void DeleteObject(CBaseObject* obj) override; + void DeleteSelection(CSelectionGroup* pSelection) override; + void DeleteAllObjects() override; + CBaseObject* CloneObject(CBaseObject* obj) override; - void BeginEditParams(CBaseObject* obj, int flags); - void EndEditParams(int flags = 0); + void BeginEditParams(CBaseObject* obj, int flags) override; + void EndEditParams(int flags = 0) override; // Hides all transform manipulators. void HideTransformManipulators(); //! Get number of objects manager by ObjectManager (not contain sub objects of groups). - int GetObjectCount() const; + int GetObjectCount() const override; //! Get array of objects, managed by manager (not contain sub objects of groups). //! @param layer if 0 get objects for all layers, or layer to get objects from. - void GetObjects(CBaseObjectsArray& objects) const; + void GetObjects(CBaseObjectsArray& objects) const override; //! Get array of objects that pass the filter. //! @param filter The filter functor, return true if you want to get the certain obj, return false if want to skip it. - void GetObjects(CBaseObjectsArray& objects, BaseObjectFilterFunctor const& filter) const; + void GetObjects(CBaseObjectsArray& objects, BaseObjectFilterFunctor const& filter) const override; //! Update objects. void Update(); //! Display objects on display context. - void Display(DisplayContext& dc); + void Display(DisplayContext& dc) override; //! Called when selecting without selection helpers - this is needed since //! the visible object cache is normally not updated when not displaying helpers. - void ForceUpdateVisibleObjectCache(DisplayContext& dc); + void ForceUpdateVisibleObjectCache(DisplayContext& dc) override; //! Check intersection with objects. //! Find intersection with nearest to ray origin object hit by ray. //! If distance tollerance is specified certain relaxation applied on collision test. //! @return true if hit any object, and fills hitInfo structure. - bool HitTest(HitContext& hitInfo); + bool HitTest(HitContext& hitInfo) override; //! Check intersection with an object. //! @return true if hit, and fills hitInfo structure. - bool HitTestObject(CBaseObject* obj, HitContext& hc); + bool HitTestObject(CBaseObject* obj, HitContext& hc) override; //! Send event to all objects. //! Will cause OnEvent handler to be called on all objects. - void SendEvent(ObjectEvent event); + void SendEvent(ObjectEvent event) override; //! Send event to all objects within given bounding box. //! Will cause OnEvent handler to be called on objects within bounding box. - void SendEvent(ObjectEvent event, const AABB& bounds); + void SendEvent(ObjectEvent event, const AABB& bounds) override; ////////////////////////////////////////////////////////////////////////// //! Find object by ID. - CBaseObject* FindObject(REFGUID guid) const; + CBaseObject* FindObject(REFGUID guid) const override; ////////////////////////////////////////////////////////////////////////// //! Find object by name. - CBaseObject* FindObject(const QString& sName) const; + CBaseObject* FindObject(const QString& sName) const override; ////////////////////////////////////////////////////////////////////////// //! Find objects of given type. void FindObjectsOfType(const QMetaObject* pClass, std::vector& result) override; void FindObjectsOfType(ObjectType type, std::vector& result) override; ////////////////////////////////////////////////////////////////////////// //! Find objects which intersect with a given AABB. - virtual void FindObjectsInAABB(const AABB& aabb, std::vector& result) const; + void FindObjectsInAABB(const AABB& aabb, std::vector& result) const override; ////////////////////////////////////////////////////////////////////////// // Operations on objects. ////////////////////////////////////////////////////////////////////////// //! Makes object visible or invisible. - void HideObject(CBaseObject* obj, bool hide); + void HideObject(CBaseObject* obj, bool hide) override; //! Shows the last hidden object based on hidden ID - void ShowLastHiddenObject(); + void ShowLastHiddenObject() override; //! Freeze object, making it unselectable. - void FreezeObject(CBaseObject* obj, bool freeze); + void FreezeObject(CBaseObject* obj, bool freeze) override; //! Unhide all hidden objects. - void UnhideAll(); + void UnhideAll() override; //! Unfreeze all frozen objects. - void UnfreezeAll(); + void UnfreezeAll() override; ////////////////////////////////////////////////////////////////////////// // Object Selection. ////////////////////////////////////////////////////////////////////////// - bool SelectObject(CBaseObject* obj, bool bUseMask = true); - void UnselectObject(CBaseObject* obj); + bool SelectObject(CBaseObject* obj, bool bUseMask = true) override; + void UnselectObject(CBaseObject* obj) override; //! Select objects within specified distance from given position. //! Return number of selected objects. - int SelectObjects(const AABB& box, bool bUnselect = false); + int SelectObjects(const AABB& box, bool bUnselect = false) override; - virtual void SelectEntities(std::set& s); + void SelectEntities(std::set& s) override; - int MoveObjects(const AABB& box, const Vec3& offset, ImageRotationDegrees rotation, bool bIsCopy = false); + int MoveObjects(const AABB& box, const Vec3& offset, ImageRotationDegrees rotation, bool bIsCopy = false) override; //! Selects/Unselects all objects within 2d rectangle in given viewport. - void SelectObjectsInRect(CViewport* view, const QRect& rect, bool bSelect); - void FindObjectsInRect(CViewport* view, const QRect& rect, std::vector& guids); + void SelectObjectsInRect(CViewport* view, const QRect& rect, bool bSelect) override; + void FindObjectsInRect(CViewport* view, const QRect& rect, std::vector& guids) override; //! Clear default selection set. //! @Return number of objects removed from selection. - int ClearSelection(); + int ClearSelection() override; //! Deselect all current selected objects and selects object that were unselected. //! @Return number of selected objects. - int InvertSelection(); + int InvertSelection() override; //! Get current selection. - CSelectionGroup* GetSelection() const { return m_currSelection; }; + CSelectionGroup* GetSelection() const override { return m_currSelection; }; //! Get named selection. - CSelectionGroup* GetSelection(const QString& name) const; + CSelectionGroup* GetSelection(const QString& name) const override; // Get selection group names - void GetNameSelectionStrings(QStringList& names); + void GetNameSelectionStrings(QStringList& names) override; //! Change name of current selection group. //! And store it in list. - void NameSelection(const QString& name); + void NameSelection(const QString& name) override; //! Set one of name selections as current selection. - void SetSelection(const QString& name); - void RemoveSelection(const QString& name); + void SetSelection(const QString& name) override; + void RemoveSelection(const QString& name) override; bool IsObjectDeletionAllowed(CBaseObject* pObject); //! Delete all objects in selection group. - void DeleteSelection(); + void DeleteSelection() override; - uint32 ForceID() const{return m_ForceID; } - void ForceID(uint32 FID){m_ForceID = FID; } + uint32 ForceID() const override{return m_ForceID; } + void ForceID(uint32 FID) override{m_ForceID = FID; } //! Generates uniq name base on type name of object. - QString GenerateUniqueObjectName(const QString& typeName); + QString GenerateUniqueObjectName(const QString& typeName) override; //! Register object name in object manager, needed for generating uniq names. - void RegisterObjectName(const QString& name); + void RegisterObjectName(const QString& name) override; //! Decrease name number and remove if it was last in object manager, needed for generating uniq names. void UpdateRegisterObjectName(const QString& name); //! Enable/Disable generating of unique object names (Enabled by default). //! Return previous value. - bool EnableUniqObjectNames(bool bEnable); + bool EnableUniqObjectNames(bool bEnable) override; //! Register XML template of runtime class. void RegisterClassTemplate(const XmlNodeRef& templ); @@ -249,25 +249,25 @@ public: void RegisterCVars(); //! Find object class by name. - CObjectClassDesc* FindClass(const QString& className); - void GetClassCategories(QStringList& categories); + CObjectClassDesc* FindClass(const QString& className) override; + void GetClassCategories(QStringList& categories) override; void GetClassCategoryToolClassNamePairs(std::vector< std::pair >& categoryToolClassNamePairs) override; - void GetClassTypes(const QString& category, QStringList& types); + void GetClassTypes(const QString& category, QStringList& types) override; //! Export objects to xml. //! When onlyShared is true ony objects with shared flags exported, overwise only not shared object exported. - void Export(const QString& levelPath, XmlNodeRef& rootNode, bool onlyShared); - void ExportEntities(XmlNodeRef& rootNode); + void Export(const QString& levelPath, XmlNodeRef& rootNode, bool onlyShared) override; + void ExportEntities(XmlNodeRef& rootNode) override; //! Serialize Objects in manager to specified XML Node. //! @param flags Can be one of SerializeFlags. - void Serialize(XmlNodeRef& rootNode, bool bLoading, int flags = SERIALIZE_ALL); + void Serialize(XmlNodeRef& rootNode, bool bLoading, int flags = SERIALIZE_ALL) override; - void SerializeNameSelection(XmlNodeRef& rootNode, bool bLoading); + void SerializeNameSelection(XmlNodeRef& rootNode, bool bLoading) override; //! Load objects from object archive. //! @param bSelect if set newly loaded object will be selected. - void LoadObjects(CObjectArchive& ar, bool bSelect); + void LoadObjects(CObjectArchive& ar, bool bSelect) override; //! Delete from Object manager all objects without SHARED flag. void DeleteNotSharedObjects(); @@ -276,57 +276,57 @@ public: bool AddObject(CBaseObject* obj); void RemoveObject(CBaseObject* obj); - void ChangeObjectId(REFGUID oldId, REFGUID newId); - bool IsDuplicateObjectName(const QString& newName) const + void ChangeObjectId(REFGUID oldId, REFGUID newId) override; + bool IsDuplicateObjectName(const QString& newName) const override { return FindObject(newName) ? true : false; } - void ShowDuplicationMsgWarning(CBaseObject* obj, const QString& newName, bool bShowMsgBox) const; - void ChangeObjectName(CBaseObject* obj, const QString& newName); + void ShowDuplicationMsgWarning(CBaseObject* obj, const QString& newName, bool bShowMsgBox) const override; + void ChangeObjectName(CBaseObject* obj, const QString& newName) override; //! Convert object of one type to object of another type. //! Original object is deleted. - bool ConvertToType(CBaseObject* pObject, const QString& typeName); + bool ConvertToType(CBaseObject* pObject, const QString& typeName) override; //! Set new selection callback. //! @return previous selection callback. - IObjectSelectCallback* SetSelectCallback(IObjectSelectCallback* callback); + IObjectSelectCallback* SetSelectCallback(IObjectSelectCallback* callback) override; // Enables/Disables creating of game objects. - void SetCreateGameObject(bool enable) { m_createGameObjects = enable; }; + void SetCreateGameObject(bool enable) override { m_createGameObjects = enable; }; //! Return true if objects loaded from xml should immidiatly create game objects associated with them. - bool IsCreateGameObjects() const { return m_createGameObjects; }; + bool IsCreateGameObjects() const override { return m_createGameObjects; }; ////////////////////////////////////////////////////////////////////////// //! Get access to gizmo manager. - IGizmoManager* GetGizmoManager(); + IGizmoManager* GetGizmoManager() override; ////////////////////////////////////////////////////////////////////////// //! Invalidate visibily settings of objects. - void InvalidateVisibleList(); + void InvalidateVisibleList() override; ////////////////////////////////////////////////////////////////////////// // ObjectManager notification Callbacks. ////////////////////////////////////////////////////////////////////////// - void AddObjectEventListener(EventListener* listener); - void RemoveObjectEventListener(EventListener* listener); + void AddObjectEventListener(EventListener* listener) override; + void RemoveObjectEventListener(EventListener* listener) override; ////////////////////////////////////////////////////////////////////////// // Used to indicate starting and ending of objects loading. ////////////////////////////////////////////////////////////////////////// - void StartObjectsLoading(int numObjects); - void EndObjectsLoading(); + void StartObjectsLoading(int numObjects) override; + void EndObjectsLoading() override; ////////////////////////////////////////////////////////////////////////// // Gathers all resources used by all objects. - void GatherUsedResources(CUsedResources& resources); + void GatherUsedResources(CUsedResources& resources) override; - virtual bool IsLightClass(CBaseObject* pObject); + bool IsLightClass(CBaseObject* pObject) override; - virtual void FindAndRenameProperty2(const char* property2Name, const QString& oldValue, const QString& newValue); - virtual void FindAndRenameProperty2If(const char* property2Name, const QString& oldValue, const QString& newValue, const char* otherProperty2Name, const QString& otherValue); + virtual void FindAndRenameProperty2(const char* property2Name, const QString& oldValue, const QString& newValue) override; + virtual void FindAndRenameProperty2If(const char* property2Name, const QString& oldValue, const QString& newValue, const char* otherProperty2Name, const QString& otherValue) override; - bool IsReloading() const { return m_bInReloading; } + bool IsReloading() const override { return m_bInReloading; } void SetSkipUpdate(bool bSkipUpdate) override { m_bSkipObjectUpdate = bSkipUpdate; } void SetExportingLevel(bool bExporting) override { m_bLevelExporting = bExporting; } @@ -341,7 +341,7 @@ private: @param objectNode Xml node to serialize object info from. @param pUndoObject Pointer to deleted object for undo. */ - CBaseObject* NewObject(CObjectArchive& archive, CBaseObject* pUndoObject, bool bMakeNewId); + CBaseObject* NewObject(CObjectArchive& archive, CBaseObject* pUndoObject, bool bMakeNewId) override; //! Update visibility of all objects. void UpdateVisibilityList(); diff --git a/Code/Editor/Platform/Mac/EditorEntitlements.plist b/Code/Editor/Platform/Mac/EditorEntitlements.plist new file mode 100644 index 0000000000..cefa2bf93b --- /dev/null +++ b/Code/Editor/Platform/Mac/EditorEntitlements.plist @@ -0,0 +1,10 @@ + + + + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.disable-library-validation + + + diff --git a/Code/Editor/Platform/Mac/editor_mac.cmake b/Code/Editor/Platform/Mac/editor_mac.cmake index fdccfafb46..eed955f2e4 100644 --- a/Code/Editor/Platform/Mac/editor_mac.cmake +++ b/Code/Editor/Platform/Mac/editor_mac.cmake @@ -12,28 +12,5 @@ set_target_properties(Editor PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_LIST_DIR}/gui_info.plist RESOURCE ${CMAKE_CURRENT_LIST_DIR}/Images.xcassets XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME EditorAppIcon + ENTITLEMENT_FILE_PATH ${CMAKE_CURRENT_LIST_DIR}/EditorEntitlements.plist ) - -# We cannot use ly_add_target here because we're already including this file from inside ly_add_target -# So we need to setup target, dependencies and install logic manually. -add_executable(EditorDummy Platform/Mac/main_dummy.cpp) -add_executable(AZ::EditorDummy ALIAS EditorDummy) - -ly_target_link_libraries(EditorDummy - PRIVATE - AZ::AzCore - AZ::AzFramework) - -ly_add_dependencies(Editor EditorDummy) - -# Store the aliased target into a DIRECTORY property -set_property(DIRECTORY APPEND PROPERTY LY_DIRECTORY_TARGETS AZ::EditorDummy) - -# Store the directory path in a GLOBAL property so that it can be accessed -# in the layout install logic. Skip if the directory has already been added -get_property(ly_all_target_directories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES) -if(NOT CMAKE_CURRENT_SOURCE_DIR IN_LIST ly_all_target_directories) - set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGET_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}) -endif() - -ly_install_add_install_path_setreg(Editor) \ No newline at end of file diff --git a/Code/Editor/Platform/Mac/gui_info.plist b/Code/Editor/Platform/Mac/gui_info.plist index cc87cbbb47..5b5f94e977 100644 --- a/Code/Editor/Platform/Mac/gui_info.plist +++ b/Code/Editor/Platform/Mac/gui_info.plist @@ -3,7 +3,7 @@ CFBundleExecutable - EditorDummy + Editor CFBundleIdentifier org.O3DE.Editor CFBundlePackageType diff --git a/Code/Editor/Platform/Mac/main_dummy.cpp b/Code/Editor/Platform/Mac/main_dummy.cpp deleted file mode 100644 index 348a32ab47..0000000000 --- a/Code/Editor/Platform/Mac/main_dummy.cpp +++ /dev/null @@ -1,75 +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 - -int main(int argc, char* argv[]) -{ - // Create a ComponentApplication to initialize the AZ::SystemAllocator and initialize the SettingsRegistry - AZ::ComponentApplication::Descriptor desc; - AZ::ComponentApplication application; - application.Create(desc); - - AZStd::vector envVars; - - const char* homePath = std::getenv("HOME"); - envVars.push_back(AZStd::string::format("HOME=%s", homePath)); - - if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) - { - const char* dyldLibPathOrig = std::getenv("DYLD_LIBRARY_PATH"); - AZStd::string dyldSearchPath = AZStd::string::format("DYLD_LIBRARY_PATH=%s", dyldLibPathOrig); - if (AZ::IO::FixedMaxPath projectModulePath; - settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath)) - { - dyldSearchPath.append(":"); - dyldSearchPath.append(projectModulePath.c_str()); - } - - if (AZ::IO::FixedMaxPath installedBinariesFolder; - settingsRegistry->Get(installedBinariesFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) - { - if (AZ::IO::FixedMaxPath engineRootFolder; - settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder)) - { - installedBinariesFolder = engineRootFolder / installedBinariesFolder; - dyldSearchPath.append(":"); - dyldSearchPath.append(installedBinariesFolder.c_str()); - } - } - envVars.push_back(dyldSearchPath); - } - - AZStd::string commandArgs; - for (int i = 1; i < argc; i++) - { - commandArgs.append(argv[i]); - commandArgs.append(" "); - } - - AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - AZ::IO::Path processPath{ AZ::IO::PathView(AZ::Utils::GetExecutableDirectory()) }; - processPath /= "Editor"; - processLaunchInfo.m_processExecutableString = AZStd::move(processPath.Native()); - processLaunchInfo.m_commandlineParameters = commandArgs; - processLaunchInfo.m_environmentVariables = &envVars; - processLaunchInfo.m_showWindow = true; - - AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); - - application.Destroy(); - - return 0; -} - diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/Objects/ComponentEntityObject.h b/Code/Editor/Plugins/ComponentEntityEditorPlugin/Objects/ComponentEntityObject.h index 23151eb05c..bb19f4d77e 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/Objects/ComponentEntityObject.h +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/Objects/ComponentEntityObject.h @@ -86,7 +86,7 @@ public: // Always returns false as Component entity highlighting (accenting) is taken care of elsewhere bool IsHighlighted() { return false; } // Component entity highlighting (accenting) is taken care of elsewhere - void DrawHighlight(DisplayContext& /*dc*/) {}; + void DrawHighlight(DisplayContext& /*dc*/) override {}; // Don't auto-clone children. Cloning happens in groups with reference fixups, // and individually selected objercts should be cloned as individuals. @@ -164,7 +164,7 @@ protected: float GetRadius(); - void DeleteThis() { delete this; }; + void DeleteThis() override { delete this; }; bool IsNonLayerAncestorSelected() const; bool IsLayer() const; diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h index f2764681b2..617c5cb2c8 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h @@ -182,7 +182,7 @@ private: ////////////////////////////////////////////////////////////////////////// // AzToolsFramework::EditorContextMenu::Bus::Handler overrides void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override; - int GetMenuPosition() const; + int GetMenuPosition() const override; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/ComponentPalette/FavoriteComponentList.h b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/ComponentPalette/FavoriteComponentList.h index 5f704237ab..2bd2d83e04 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/ComponentPalette/FavoriteComponentList.h +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/ComponentPalette/FavoriteComponentList.h @@ -102,7 +102,7 @@ protected: void AddFavorites(const AZStd::vector& classDataContainer) override; ////////////////////////////////////////////////////////////////////////// - void rowsInserted(const QModelIndex& parent, int start, int end); + void rowsInserted(const QModelIndex& parent, int start, int end) override; // Context menu handlers void ShowContextMenu(const QPoint&); diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerListModel.hxx b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerListModel.hxx index 346a5e938a..8acb2f1a72 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerListModel.hxx +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerListModel.hxx @@ -255,7 +255,7 @@ protected: bool DropMimeDataAssets(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); bool CanDropMimeDataAssets(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const; - QMap itemData(const QModelIndex &index) const; + QMap itemData(const QModelIndex &index) const override; QVariant dataForAll(const QModelIndex& index, int role) const; QVariant dataForName(const QModelIndex& index, int role) const; QVariant dataForVisibility(const QModelIndex& index, int role) const; diff --git a/Code/Editor/Plugins/EditorCommon/CMakeLists.txt b/Code/Editor/Plugins/EditorCommon/CMakeLists.txt index 2aff57cfb1..cd9f2e79c7 100644 --- a/Code/Editor/Plugins/EditorCommon/CMakeLists.txt +++ b/Code/Editor/Plugins/EditorCommon/CMakeLists.txt @@ -51,4 +51,5 @@ ly_add_target( AZ::AzCore AZ::AzToolsFramework AZ::AzQtComponents + Legacy::EditorCore ) diff --git a/Code/Editor/Plugins/PerforcePlugin/PerforceSourceControl.cpp b/Code/Editor/Plugins/PerforcePlugin/PerforceSourceControl.cpp index 10c43c3d1b..dd9ccaa832 100644 --- a/Code/Editor/Plugins/PerforcePlugin/PerforceSourceControl.cpp +++ b/Code/Editor/Plugins/PerforcePlugin/PerforceSourceControl.cpp @@ -6,6 +6,7 @@ * */ +#include #include "CryFile.h" #include "PerforceSourceControl.h" #include "PasswordDlg.h" diff --git a/Code/Editor/PreferencesStdPages.h b/Code/Editor/PreferencesStdPages.h index f4a6e0b783..8a182bde55 100644 --- a/Code/Editor/PreferencesStdPages.h +++ b/Code/Editor/PreferencesStdPages.h @@ -28,16 +28,16 @@ public: ////////////////////////////////////////////////////////////////////////// // IUnkown implementation. - virtual HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObj); - virtual ULONG STDMETHODCALLTYPE AddRef(); - virtual ULONG STDMETHODCALLTYPE Release(); + HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObj) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; ////////////////////////////////////////////////////////////////////////// - virtual REFGUID ClassID(); + REFGUID ClassID() override; ////////////////////////////////////////////////////////////////////////// - virtual int GetPagesCount(); - virtual IPreferencesPage* CreateEditorPreferencesPage(int index) override; + int GetPagesCount() override; + IPreferencesPage* CreateEditorPreferencesPage(int index) override; }; #endif // CRYINCLUDE_EDITOR_PREFERENCESSTDPAGES_H diff --git a/Code/Editor/PythonEditorEventsBus.h b/Code/Editor/PythonEditorEventsBus.h index 5107a1c9cc..ffc357e64d 100644 --- a/Code/Editor/PythonEditorEventsBus.h +++ b/Code/Editor/PythonEditorEventsBus.h @@ -8,6 +8,7 @@ */ #pragma once +#include #include namespace AzToolsFramework @@ -138,7 +139,7 @@ namespace AzToolsFramework /* * Finds a pak file name for a given file. */ - virtual const char* GetPakFromFile(const char* filename) = 0; + virtual AZ::IO::Path GetPakFromFile(const char* filename) = 0; /* * Prints the message to the editor console window. diff --git a/Code/Editor/PythonEditorFuncs.cpp b/Code/Editor/PythonEditorFuncs.cpp index 200fd28f87..455cdfa17d 100644 --- a/Code/Editor/PythonEditorFuncs.cpp +++ b/Code/Editor/PythonEditorFuncs.cpp @@ -625,7 +625,7 @@ namespace } ////////////////////////////////////////////////////////////////////////// - const char* PyGetPakFromFile(const char* filename) + AZ::IO::Path PyGetPakFromFile(const char* filename) { auto pIPak = GetIEditor()->GetSystem()->GetIPak(); AZ::IO::HandleType fileHandle = pIPak->FOpen(filename, "rb"); @@ -633,8 +633,9 @@ namespace { throw std::logic_error("Invalid file name."); } - const char* pArchPath = pIPak->GetFileArchivePath(fileHandle); + AZ::IO::Path pArchPath = pIPak->GetFileArchivePath(fileHandle); pIPak->FClose(fileHandle); + return pArchPath; } @@ -1040,7 +1041,7 @@ namespace AzToolsFramework return PySetAxisConstraint(pConstrain); } - const char* PythonEditorComponent::GetPakFromFile(const char* filename) + AZ::IO::Path PythonEditorComponent::GetPakFromFile(const char* filename) { return PyGetPakFromFile(filename); } @@ -1114,7 +1115,7 @@ namespace AzToolsFramework addLegacyGeneral(behaviorContext->Method("get_axis_constraint", PyGetAxisConstraint, nullptr, "Gets axis.")); addLegacyGeneral(behaviorContext->Method("set_axis_constraint", PySetAxisConstraint, nullptr, "Sets axis.")); - addLegacyGeneral(behaviorContext->Method("get_pak_from_file", PyGetPakFromFile, nullptr, "Finds a pak file name for a given file.")); + addLegacyGeneral(behaviorContext->Method("get_pak_from_file", [](const char* filename) -> AZStd::string { return PyGetPakFromFile(filename).Native(); }, nullptr, "Finds a pak file name for a given file.")); addLegacyGeneral(behaviorContext->Method("log", PyLog, nullptr, "Prints the message to the editor console window.")); diff --git a/Code/Editor/PythonEditorFuncs.h b/Code/Editor/PythonEditorFuncs.h index 97ad8829ba..ef0c1327fa 100644 --- a/Code/Editor/PythonEditorFuncs.h +++ b/Code/Editor/PythonEditorFuncs.h @@ -91,7 +91,7 @@ namespace AzToolsFramework void SetAxisConstraint(AZStd::string_view pConstrain) override; - const char* GetPakFromFile(const char* filename) override; + AZ::IO::Path GetPakFromFile(const char* filename) override; void Log(const char* pMessage) override; diff --git a/Code/Editor/QtViewPane.h b/Code/Editor/QtViewPane.h index 09fc215833..194dd8919a 100644 --- a/Code/Editor/QtViewPane.h +++ b/Code/Editor/QtViewPane.h @@ -123,18 +123,18 @@ public: { } - virtual ESystemClassID SystemClassID() { return m_classId; }; + ESystemClassID SystemClassID() override { return m_classId; }; static const GUID& GetClassID() { return TWidget::GetClassID(); } - virtual const GUID& ClassID() + const GUID& ClassID() override { return GetClassID(); } - virtual QString ClassName() { return m_name; }; - virtual QString Category() { return m_category; }; + QString ClassName() override { return m_name; }; + QString Category() override { return m_category; }; QObject* CreateQObject() const override { return new TWidget(); }; QString GetPaneTitle() override { return m_name; }; diff --git a/Code/Editor/SelectSequenceDialog.h b/Code/Editor/SelectSequenceDialog.h index 5531949c1a..960da7ebe3 100644 --- a/Code/Editor/SelectSequenceDialog.h +++ b/Code/Editor/SelectSequenceDialog.h @@ -30,7 +30,7 @@ protected: void OnInitDialog() override; // Derived Dialogs should override this - virtual void GetItems(std::vector& outItems); + void GetItems(std::vector& outItems) override; }; #endif // CRYINCLUDE_EDITOR_SELECTSEQUENCEDIALOG_H diff --git a/Code/Editor/Settings.cpp b/Code/Editor/Settings.cpp index 80d248e1bf..05a6960695 100644 --- a/Code/Editor/Settings.cpp +++ b/Code/Editor/Settings.cpp @@ -171,7 +171,6 @@ SEditorSettings::SEditorSettings() bBackupOnSave = true; backupOnSaveMaxCount = 3; bApplyConfigSpecInEditor = true; - useLowercasePaths = 0; showErrorDialogOnLoad = 1; consoleBackgroundColorTheme = AzToolsFramework::ConsoleColorTheme::Dark; @@ -887,6 +886,7 @@ void SEditorSettings::Load() ////////////////////////////////////////////////////////////////////////// AZ_CVAR(bool, ed_previewGameInFullscreen_once, false, nullptr, AZ::ConsoleFunctorFlags::IsInvisible, "Preview the game (Ctrl+G, \"Play Game\", etc.) in fullscreen once"); +AZ_CVAR(bool, ed_lowercasepaths, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Convert CCryFile paths to lowercase on Open"); void SEditorSettings::PostInitApply() { @@ -898,7 +898,6 @@ void SEditorSettings::PostInitApply() // Create CVars. REGISTER_CVAR2("ed_highlightGeometry", &viewports.bHighlightMouseOverGeometry, viewports.bHighlightMouseOverGeometry, 0, "Highlight geometry when mouse over it"); REGISTER_CVAR2("ed_showFrozenHelpers", &viewports.nShowFrozenHelpers, viewports.nShowFrozenHelpers, 0, "Show helpers of frozen objects"); - REGISTER_CVAR2("ed_lowercasepaths", &useLowercasePaths, useLowercasePaths, 0, "generate paths in lowercase"); gEnv->pConsole->RegisterInt("fe_fbx_savetempfile", 0, 0, "When importing an FBX file into Facial Editor, this will save out a conversion FSQ to the Animations/temp folder for trouble shooting"); REGISTER_CVAR2_CB("ed_toolbarIconSize", &gui.nToolbarIconSize, gui.nToolbarIconSize, VF_NULL, "Override size of the toolbar icons 0-default, 16,32,...", ToolbarIconSizeChanged); diff --git a/Code/Editor/Settings.h b/Code/Editor/Settings.h index a408822129..8bf22b43e5 100644 --- a/Code/Editor/Settings.h +++ b/Code/Editor/Settings.h @@ -340,8 +340,6 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING //! how many save backups to keep int backupOnSaveMaxCount; - int useLowercasePaths; - ////////////////////////////////////////////////////////////////////////// // Autobackup. ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Editor/ToolbarCustomizationDialog.h b/Code/Editor/ToolbarCustomizationDialog.h index 7063c2b792..0e64bfbb5b 100644 --- a/Code/Editor/ToolbarCustomizationDialog.h +++ b/Code/Editor/ToolbarCustomizationDialog.h @@ -39,7 +39,7 @@ public: protected: void dragMoveEvent(QDragMoveEvent* ev) override; void dragEnterEvent(QDragEnterEvent* ev) override; - void dropEvent(QDropEvent* ev); + void dropEvent(QDropEvent* ev) override; private: void OnTabChanged(int index); diff --git a/Code/Editor/TopRendererWnd.h b/Code/Editor/TopRendererWnd.h index 3a5c1026bc..c5bc7a31f2 100644 --- a/Code/Editor/TopRendererWnd.h +++ b/Code/Editor/TopRendererWnd.h @@ -35,11 +35,11 @@ public: /** Get type of this viewport. */ - virtual EViewportType GetType() const { return ET_ViewportMap; } - virtual void SetType(EViewportType type); + EViewportType GetType() const override { return ET_ViewportMap; } + void SetType(EViewportType type) override; - virtual void ResetContent(); - virtual void UpdateContent(int flags); + void ResetContent() override; + void UpdateContent(int flags) override; //! Map viewport position to world space position. virtual Vec3 ViewToWorld(const QPoint& vp, bool* collideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const override; @@ -52,7 +52,7 @@ public: protected: // Draw everything. - virtual void Draw(DisplayContext& dc); + void Draw(DisplayContext& dc) override; private: bool m_bContentsUpdated; diff --git a/Code/Editor/TrackView/SequenceBatchRenderDialog.h b/Code/Editor/TrackView/SequenceBatchRenderDialog.h index 5d8934f783..9be10c22af 100644 --- a/Code/Editor/TrackView/SequenceBatchRenderDialog.h +++ b/Code/Editor/TrackView/SequenceBatchRenderDialog.h @@ -187,7 +187,7 @@ protected: int m_customFPS; void InitializeContext(); - virtual void OnMovieEvent(IMovieListener::EMovieEvent event, IAnimSequence* pSequence); + void OnMovieEvent(IMovieListener::EMovieEvent event, IAnimSequence* pSequence) override; void CaptureItemStart(); diff --git a/Code/Editor/TrackView/TrackViewCurveEditor.h b/Code/Editor/TrackView/TrackViewCurveEditor.h index b2e019899b..1af55a9e0c 100644 --- a/Code/Editor/TrackView/TrackViewCurveEditor.h +++ b/Code/Editor/TrackView/TrackViewCurveEditor.h @@ -50,8 +50,8 @@ public: void SetPlayCallback(const std::function& callback); // IAnimationContextListener - virtual void OnSequenceChanged(CTrackViewSequence* pNewSequence); - virtual void OnTimeChanged(float newTime); + void OnSequenceChanged(CTrackViewSequence* pNewSequence) override; + void OnTimeChanged(float newTime) override; protected: void showEvent(QShowEvent* event) override; @@ -65,7 +65,7 @@ private: void OnSplineTimeMarkerChange(); // IEditorNotifyListener - virtual void OnEditorNotifyEvent(EEditorNotifyEvent event) override; + void OnEditorNotifyEvent(EEditorNotifyEvent event) override; //ITrackViewSequenceListener void OnKeysChanged(CTrackViewSequence* pSequence) override; @@ -109,8 +109,8 @@ public: float GetFPS() const { return m_widget->GetFPS(); } void SetTickDisplayMode(ETVTickMode mode) { m_widget->SetTickDisplayMode(mode); } - virtual void OnSequenceChanged(CTrackViewSequence* pNewSequence) { m_widget->OnSequenceChanged(pNewSequence); } - virtual void OnTimeChanged(float newTime) { m_widget->OnTimeChanged(newTime); } + void OnSequenceChanged(CTrackViewSequence* pNewSequence) override { m_widget->OnSequenceChanged(pNewSequence); } + void OnTimeChanged(float newTime) override { m_widget->OnTimeChanged(newTime); } // ITrackViewSequenceListener delegation to m_widget void OnKeysChanged(CTrackViewSequence* pSequence) override { m_widget->OnKeysChanged(pSequence); } diff --git a/Code/Editor/TrackView/TrackViewDialog.h b/Code/Editor/TrackView/TrackViewDialog.h index f6c1126713..c66f31e1f7 100644 --- a/Code/Editor/TrackView/TrackViewDialog.h +++ b/Code/Editor/TrackView/TrackViewDialog.h @@ -69,10 +69,10 @@ public: void UpdateSequenceLockStatus(); // IAnimationContextListener - virtual void OnSequenceChanged(CTrackViewSequence* pNewSequence) override; + void OnSequenceChanged(CTrackViewSequence* pNewSequence) override; // ITrackViewSequenceListener - virtual void OnSequenceSettingsChanged(CTrackViewSequence* pSequence) override; + void OnSequenceSettingsChanged(CTrackViewSequence* pSequence) override; void UpdateDopeSheetTime(CTrackViewSequence* pSequence); @@ -197,8 +197,8 @@ private: bool processRawInput(MSG* pMsg); #endif - virtual void OnNodeSelectionChanged(CTrackViewSequence* pSequence) override; - virtual void OnNodeRenamed(CTrackViewNode* pNode, const char* pOldName) override; + void OnNodeSelectionChanged(CTrackViewSequence* pSequence) override; + void OnNodeRenamed(CTrackViewNode* pNode, const char* pOldName) override; void OnSequenceAdded(CTrackViewSequence* pSequence) override; void OnSequenceRemoved(CTrackViewSequence* pSequence) override; @@ -209,8 +209,8 @@ private: void AddDialogListeners(); void RemoveDialogListeners(); - virtual void BeginUndoTransaction(); - virtual void EndUndoTransaction(); + void BeginUndoTransaction() override; + void EndUndoTransaction() override; void SaveCurrentSequenceToFBX(); void SaveSequenceTimingToXML(); diff --git a/Code/Editor/TrackView/TrackViewNode.h b/Code/Editor/TrackView/TrackViewNode.h index 59df06750b..80ca7b9d49 100644 --- a/Code/Editor/TrackView/TrackViewNode.h +++ b/Code/Editor/TrackView/TrackViewNode.h @@ -117,13 +117,14 @@ class CTrackViewKeyBundle public: CTrackViewKeyBundle() : m_bAllOfSameType(true) {} + virtual ~CTrackViewKeyBundle() = default; - virtual bool AreAllKeysOfSameType() const override { return m_bAllOfSameType; } + bool AreAllKeysOfSameType() const override { return m_bAllOfSameType; } - virtual unsigned int GetKeyCount() const override { return static_cast(m_keys.size()); } - virtual CTrackViewKeyHandle GetKey(unsigned int index) override { return m_keys[index]; } + unsigned int GetKeyCount() const override { return static_cast(m_keys.size()); } + CTrackViewKeyHandle GetKey(unsigned int index) override { return m_keys[index]; } - virtual void SelectKeys(const bool bSelected) override; + void SelectKeys(const bool bSelected) override; CTrackViewKeyHandle GetSingleSelectedKey(); diff --git a/Code/Editor/TrackView/TrackViewSequence.h b/Code/Editor/TrackView/TrackViewSequence.h index 69858adf8f..a392ad00f9 100644 --- a/Code/Editor/TrackView/TrackViewSequence.h +++ b/Code/Editor/TrackView/TrackViewSequence.h @@ -100,16 +100,16 @@ public: void Load() override; // ITrackViewNode - virtual ETrackViewNodeType GetNodeType() const override { return eTVNT_Sequence; } + ETrackViewNodeType GetNodeType() const override { return eTVNT_Sequence; } - virtual AZStd::string GetName() const override { return m_pAnimSequence->GetName(); } - virtual bool SetName(const char* pName) override; - virtual bool CanBeRenamed() const override { return true; } + AZStd::string GetName() const override { return m_pAnimSequence->GetName(); } + bool SetName(const char* pName) override; + bool CanBeRenamed() const override { return true; } // Binding/Unbinding - virtual void BindToEditorObjects() override; - virtual void UnBindFromEditorObjects() override; - virtual bool IsBoundToEditorObjects() const override; + void BindToEditorObjects() override; + void UnBindFromEditorObjects() override; + bool IsBoundToEditorObjects() const override; // Time range void SetTimeRange(Range timeRange); @@ -136,10 +136,10 @@ public: uint32 GetCryMovieId() const { return m_pAnimSequence->GetId(); } // Rendering - virtual void Render(const SAnimContext& animContext) override; + void Render(const SAnimContext& animContext) override; // Playback control - virtual void Animate(const SAnimContext& animContext) override; + void Animate(const SAnimContext& animContext) override; void Resume() { m_pAnimSequence->Resume(); } void Pause() { m_pAnimSequence->Pause(); } void StillUpdate() { m_pAnimSequence->StillUpdate(); } @@ -162,7 +162,7 @@ public: void TimeChanged(float newTime) { m_pAnimSequence->TimeChanged(newTime); } // Check if it's a group node - virtual bool IsGroupNode() const override { return true; } + bool IsGroupNode() const override { return true; } // Track Events (TODO: Undo?) int GetTrackEventsCount() const { return m_pAnimSequence->GetTrackEventsCount(); } @@ -195,7 +195,7 @@ public: bool IsActiveSequence() const; // The root sequence node is always an active director - virtual bool IsActiveDirector() const override { return true; } + bool IsActiveDirector() const override { return true; } // Copy keys to clipboard (in XML form) void CopyKeysToClipboard(const bool bOnlySelectedKeys, const bool bOnlyFromSelectedTracks); @@ -306,15 +306,15 @@ private: // Called when an animation updates needs to be schedules void ForceAnimation(); - virtual void CopyKeysToClipboard(XmlNodeRef& xmlNode, const bool bOnlySelectedKeys, const bool bOnlyFromSelectedTracks) override; + void CopyKeysToClipboard(XmlNodeRef& xmlNode, const bool bOnlySelectedKeys, const bool bOnlyFromSelectedTracks) override; std::deque GetMatchingTracks(CTrackViewAnimNode* pAnimNode, XmlNodeRef trackNode); void GetMatchedPasteLocationsRec(std::vector& locations, CTrackViewNode* pCurrentNode, XmlNodeRef clipboardNode); - virtual void BeginUndoTransaction(); - virtual void EndUndoTransaction(); - virtual void BeginRestoreTransaction(); - virtual void EndRestoreTransaction(); + void BeginUndoTransaction() override; + void EndUndoTransaction() override; + void BeginRestoreTransaction() override; + void EndRestoreTransaction() override; // For record mode on AZ::Entities - connect (or disconnect) to buses for notification of property changes void ConnectToBusesForRecording(const AZ::EntityId& entityIdForBus, bool enableConnection); diff --git a/Code/Editor/TrackView/TrackViewSequenceManager.h b/Code/Editor/TrackView/TrackViewSequenceManager.h index 21c10f009a..1474323dc6 100644 --- a/Code/Editor/TrackView/TrackViewSequenceManager.h +++ b/Code/Editor/TrackView/TrackViewSequenceManager.h @@ -27,7 +27,7 @@ public: CTrackViewSequenceManager(); ~CTrackViewSequenceManager(); - virtual void OnEditorNotifyEvent(EEditorNotifyEvent event); + void OnEditorNotifyEvent(EEditorNotifyEvent event) override; unsigned int GetCount() const { return static_cast(m_sequences.size()); } @@ -65,7 +65,7 @@ private: void OnSequenceAdded(CTrackViewSequence* pSequence); void OnSequenceRemoved(CTrackViewSequence* pSequence); - virtual void OnDataBaseItemEvent(IDataBaseItem* pItem, EDataBaseItemEvent event); + void OnDataBaseItemEvent(IDataBaseItem* pItem, EDataBaseItemEvent event) override; // AZ::EntitySystemBus void OnEntityNameChanged(const AZ::EntityId& entityId, const AZStd::string& name) override; diff --git a/Code/Editor/TrackView/TrackViewSplineCtrl.h b/Code/Editor/TrackView/TrackViewSplineCtrl.h index 2f4c790627..7cf12fb750 100644 --- a/Code/Editor/TrackView/TrackViewSplineCtrl.h +++ b/Code/Editor/TrackView/TrackViewSplineCtrl.h @@ -28,7 +28,7 @@ public: CTrackViewSplineCtrl(QWidget* parent); virtual ~CTrackViewSplineCtrl(); - virtual void ClearSelection(); + void ClearSelection() override; void AddSpline(ISplineInterpolator* pSpline, CTrackViewTrack* pTrack, const QColor& color); void AddSpline(ISplineInterpolator * pSpline, CTrackViewTrack * pTrack, QColor anColorArray[4]); @@ -53,12 +53,12 @@ protected: void wheelEvent(QWheelEvent* event) override; private: - virtual void SelectKey(ISplineInterpolator* pSpline, int nKey, int nDimension, bool bSelect) override; - virtual void SelectRectangle(const QRect& rc, bool bSelect) override; + void SelectKey(ISplineInterpolator* pSpline, int nKey, int nDimension, bool bSelect) override; + void SelectRectangle(const QRect& rc, bool bSelect) override; std::vector m_tracks; - virtual bool GetTangentHandlePts(QPoint& inTangentPt, QPoint& pt, QPoint& outTangentPt, + bool GetTangentHandlePts(QPoint& inTangentPt, QPoint& pt, QPoint& outTangentPt, int nSpline, int nKey, int nDimension) override; void ComputeIncomingTangentAndEaseTo(float& ds, float& easeTo, QPoint inTangentPt, int nSpline, int nKey, int nDimension); @@ -67,7 +67,7 @@ private: void AdjustTCB(float d_tension, float d_continuity, float d_bias); void MoveSelectedTangentHandleTo(const QPoint& point); - virtual ISplineCtrlUndo* CreateSplineCtrlUndoObject(std::vector& splineContainer); + ISplineCtrlUndo* CreateSplineCtrlUndoObject(std::vector& splineContainer) override; bool m_bKeysFreeze; bool m_bTangentsFreeze; diff --git a/Code/Editor/Util/ColumnGroupTreeView.h b/Code/Editor/Util/ColumnGroupTreeView.h index 3c7dea91c3..eda5f8e9c9 100644 --- a/Code/Editor/Util/ColumnGroupTreeView.h +++ b/Code/Editor/Util/ColumnGroupTreeView.h @@ -44,7 +44,7 @@ public slots: QVector Groups() const; protected: - void paintEvent(QPaintEvent* event) + void paintEvent(QPaintEvent* event) override { if (model() && model()->rowCount() > 0) { diff --git a/Code/Editor/Util/FileUtil.cpp b/Code/Editor/Util/FileUtil.cpp index 610a9c6e16..eeb6912acf 100644 --- a/Code/Editor/Util/FileUtil.cpp +++ b/Code/Editor/Util/FileUtil.cpp @@ -149,12 +149,13 @@ bool CFileUtil::ExtractFile(QString& file, bool bMsgBoxAskForExtraction, const c // Check if in pack. if (cryfile.IsInPak()) { - const char* sPakName = cryfile.GetPakPath(); - if (bMsgBoxAskForExtraction) { + AZ::IO::FixedMaxPath sPakName{ cryfile.GetPakPath() }; // Cannot edit file in pack, suggest to extract it for editing. - if (QMessageBox::critical(QApplication::activeWindow(), QString(), QObject::tr("File %1 is inside a PAK file %2\r\nDo you want it to be extracted for editing ?").arg(file, sPakName), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + if (QMessageBox::critical(QApplication::activeWindow(), QString(), + QObject::tr("File %1 is inside a PAK file %2\r\nDo you want it to be extracted for editing ?").arg(file, sPakName.c_str()), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { return false; } @@ -173,10 +174,9 @@ bool CFileUtil::ExtractFile(QString& file, bool bMsgBoxAskForExtraction, const c if (diskFile.open(QFile::WriteOnly)) { // Copy data from packed file to disk file. - char* data = new char[cryfile.GetLength()]; - cryfile.ReadRaw(data, cryfile.GetLength()); - diskFile.write(data, cryfile.GetLength()); - delete []data; + auto data = AZStd::make_unique(cryfile.GetLength()); + cryfile.ReadRaw(data.get(), cryfile.GetLength()); + diskFile.write(data.get(), cryfile.GetLength()); } else { @@ -185,7 +185,14 @@ bool CFileUtil::ExtractFile(QString& file, bool bMsgBoxAskForExtraction, const c } else { - file = cryfile.GetAdjustedFilename(); + + if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr) + { + if (AZ::IO::FixedMaxPath resolvedFilePath; fileIoBase->ResolvePath(resolvedFilePath, cryfile.GetFilename())) + { + file = QString::fromUtf8(resolvedFilePath.c_str(), static_cast(resolvedFilePath.Native().size())); + } + } } return true; @@ -2157,13 +2164,13 @@ uint32 CFileUtil::GetAttributes(const char* filename, bool bUseSourceControl /*= return SCC_FILE_ATTRIBUTE_READONLY | SCC_FILE_ATTRIBUTE_INPAK; } - const char* adjustedFile = file.GetAdjustedFilename(); - if (!AZ::IO::SystemFile::Exists(adjustedFile)) + auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); + if (!fileIoBase->Exists(file.GetFilename())) { return SCC_FILE_ATTRIBUTE_INVALID; } - if (!AZ::IO::SystemFile::IsWritable(adjustedFile)) + if (fileIoBase->IsReadOnly(file.GetFilename())) { return SCC_FILE_ATTRIBUTE_NORMAL | SCC_FILE_ATTRIBUTE_READONLY; } diff --git a/Code/Editor/Util/PakFile.cpp b/Code/Editor/Util/PakFile.cpp index fc8431ef24..b629b45f74 100644 --- a/Code/Editor/Util/PakFile.cpp +++ b/Code/Editor/Util/PakFile.cpp @@ -68,7 +68,7 @@ bool CPakFile::Open(const char* filename, bool bAbsolutePath) if (bAbsolutePath) { - m_pArchive = pCryPak->OpenArchive(filename, nullptr, AZ::IO::INestedArchive::FLAGS_ABSOLUTE_PATHS); + m_pArchive = pCryPak->OpenArchive(filename, {}, AZ::IO::INestedArchive::FLAGS_ABSOLUTE_PATHS); } else { @@ -93,7 +93,7 @@ bool CPakFile::OpenForRead(const char* filename) { return false; } - m_pArchive = pCryPak->OpenArchive(filename, nullptr, AZ::IO::INestedArchive::FLAGS_OPTIMIZED_READ_ONLY | AZ::IO::INestedArchive::FLAGS_ABSOLUTE_PATHS); + m_pArchive = pCryPak->OpenArchive(filename, {}, AZ::IO::INestedArchive::FLAGS_OPTIMIZED_READ_ONLY | AZ::IO::INestedArchive::FLAGS_ABSOLUTE_PATHS); if (m_pArchive) { return true; diff --git a/Code/Editor/Util/Variable.h b/Code/Editor/Util/Variable.h index 9c3f96a3f6..639161775c 100644 --- a/Code/Editor/Util/Variable.h +++ b/Code/Editor/Util/Variable.h @@ -379,11 +379,11 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING public: virtual ~CVariableBase() {} - void SetName(const QString& name) { m_name = name; }; + void SetName(const QString& name) override { m_name = name; }; //! Get name of parameter. - QString GetName() const { return m_name; }; + QString GetName() const override { return m_name; }; - QString GetHumanName() const + QString GetHumanName() const override { if (!m_humanName.isEmpty()) { @@ -391,82 +391,82 @@ public: } return m_name; } - void SetHumanName(const QString& name) { m_humanName = name; } + void SetHumanName(const QString& name) override { m_humanName = name; } - void SetDescription(const char* desc) { m_description = desc; }; - void SetDescription(const QString& desc) { m_description = desc; }; + void SetDescription(const char* desc) override { m_description = desc; }; + void SetDescription(const QString& desc) override { m_description = desc; }; //! Get name of parameter. - QString GetDescription() const { return m_description; }; + QString GetDescription() const override { return m_description; }; - EType GetType() const { return IVariable::UNKNOWN; }; - int GetSize() const { return sizeof(*this); }; + EType GetType() const override { return IVariable::UNKNOWN; }; + int GetSize() const override { return sizeof(*this); }; - unsigned char GetDataType() const { return m_dataType; }; - void SetDataType(unsigned char dataType) { m_dataType = dataType; } + unsigned char GetDataType() const override { return m_dataType; }; + void SetDataType(unsigned char dataType) override { m_dataType = dataType; } - void SetFlags(int flags) { m_flags = static_cast(flags); } - int GetFlags() const { return m_flags; } - void SetFlagRecursive(EFlags flag) { m_flags |= flag; } + void SetFlags(int flags) override { m_flags = static_cast(flags); } + int GetFlags() const override { return m_flags; } + void SetFlagRecursive(EFlags flag) override { m_flags |= flag; } - void SetUserData(const QVariant &data){ m_userData = data; }; - QVariant GetUserData() const { return m_userData; } + void SetUserData(const QVariant &data) override { m_userData = data; }; + QVariant GetUserData() const override { return m_userData; } ////////////////////////////////////////////////////////////////////////// // Set methods. ////////////////////////////////////////////////////////////////////////// - void Set([[maybe_unused]] int value) { assert(0); } - void Set([[maybe_unused]] bool value) { assert(0); } - void Set([[maybe_unused]] float value) { assert(0); } - void Set([[maybe_unused]] double value) { assert(0); } - void Set([[maybe_unused]] const Vec2& value) { assert(0); } - void Set([[maybe_unused]] const Vec3& value) { assert(0); } - void Set([[maybe_unused]] const Vec4& value) { assert(0); } - void Set([[maybe_unused]] const Ang3& value) { assert(0); } - void Set([[maybe_unused]] const Quat& value) { assert(0); } - void Set([[maybe_unused]] const QString& value) { assert(0); } - void Set([[maybe_unused]] const char* value) { assert(0); } - void SetDisplayValue(const QString& value) { Set(value); } + void Set([[maybe_unused]] int value) override { assert(0); } + void Set([[maybe_unused]] bool value) override { assert(0); } + void Set([[maybe_unused]] float value) override { assert(0); } + void Set([[maybe_unused]] double value) override { assert(0); } + void Set([[maybe_unused]] const Vec2& value) override { assert(0); } + void Set([[maybe_unused]] const Vec3& value) override { assert(0); } + void Set([[maybe_unused]] const Vec4& value) override { assert(0); } + void Set([[maybe_unused]] const Ang3& value) override { assert(0); } + void Set([[maybe_unused]] const Quat& value) override { assert(0); } + void Set([[maybe_unused]] const QString& value) override { assert(0); } + void Set([[maybe_unused]] const char* value) override { assert(0); } + void SetDisplayValue(const QString& value) override { Set(value); } ////////////////////////////////////////////////////////////////////////// // Get methods. ////////////////////////////////////////////////////////////////////////// - void Get([[maybe_unused]] int& value) const { assert(0); } - void Get([[maybe_unused]] bool& value) const { assert(0); } - void Get([[maybe_unused]] float& value) const { assert(0); } - void Get([[maybe_unused]] double& value) const { assert(0); } - void Get([[maybe_unused]] Vec2& value) const { assert(0); } - void Get([[maybe_unused]] Vec3& value) const { assert(0); } - void Get([[maybe_unused]] Vec4& value) const { assert(0); } - void Get([[maybe_unused]] Ang3& value) const { assert(0); } - void Get([[maybe_unused]] Quat& value) const { assert(0); } - void Get([[maybe_unused]] QString& value) const { assert(0); } - QString GetDisplayValue() const { QString val; Get(val); return val; } + void Get([[maybe_unused]] int& value) const override { assert(0); } + void Get([[maybe_unused]] bool& value) const override { assert(0); } + void Get([[maybe_unused]] float& value) const override { assert(0); } + void Get([[maybe_unused]] double& value) const override { assert(0); } + void Get([[maybe_unused]] Vec2& value) const override { assert(0); } + void Get([[maybe_unused]] Vec3& value) const override { assert(0); } + void Get([[maybe_unused]] Vec4& value) const override { assert(0); } + void Get([[maybe_unused]] Ang3& value) const override { assert(0); } + void Get([[maybe_unused]] Quat& value) const override { assert(0); } + void Get([[maybe_unused]] QString& value) const override { assert(0); } + QString GetDisplayValue() const override { QString val; Get(val); return val; } ////////////////////////////////////////////////////////////////////////// // IVariableContainer functions ////////////////////////////////////////////////////////////////////////// - virtual void AddVariable([[maybe_unused]] IVariable* var) { assert(0); } + void AddVariable([[maybe_unused]] IVariable* var) override { assert(0); } - virtual bool DeleteVariable([[maybe_unused]] IVariable* var, [[maybe_unused]] bool recursive = false) { return false; } - virtual void DeleteAllVariables() {} + bool DeleteVariable([[maybe_unused]] IVariable* var, [[maybe_unused]] bool recursive = false) override { return false; } + void DeleteAllVariables() override {} - virtual int GetNumVariables() const { return 0; } - virtual IVariable* GetVariable([[maybe_unused]] int index) const { return nullptr; } + int GetNumVariables() const override { return 0; } + IVariable* GetVariable([[maybe_unused]] int index) const override { return nullptr; } - virtual bool IsContainsVariable([[maybe_unused]] IVariable* pVar, [[maybe_unused]] bool bRecursive = false) const { return false; } + bool IsContainsVariable([[maybe_unused]] IVariable* pVar, [[maybe_unused]] bool bRecursive = false) const override { return false; } - virtual IVariable* FindVariable([[maybe_unused]] const char* name, [[maybe_unused]] bool bRecursive = false, [[maybe_unused]] bool bHumanName = false) const { return nullptr; } + IVariable* FindVariable([[maybe_unused]] const char* name, [[maybe_unused]] bool bRecursive = false, [[maybe_unused]] bool bHumanName = false) const override { return nullptr; } - virtual bool IsEmpty() const { return true; } + bool IsEmpty() const override { return true; } ////////////////////////////////////////////////////////////////////////// - void Wire(IVariable* var) + void Wire(IVariable* var) override { m_wiredVars.push_back(var); } ////////////////////////////////////////////////////////////////////////// - void Unwire(IVariable* var) + void Unwire(IVariable* var) override { if (!var) { @@ -480,7 +480,7 @@ public: } ////////////////////////////////////////////////////////////////////////// - void AddOnSetCallback(OnSetCallback* func) + void AddOnSetCallback(OnSetCallback* func) override { if (!stl::find(m_onSetFuncs, func)) { @@ -489,13 +489,13 @@ public: } ////////////////////////////////////////////////////////////////////////// - void RemoveOnSetCallback(OnSetCallback* func) + void RemoveOnSetCallback(OnSetCallback* func) override { stl::find_and_erase(m_onSetFuncs, func); } ////////////////////////////////////////////////////////////////////////// - void ClearOnSetCallbacks() + void ClearOnSetCallbacks() override { m_onSetFuncs.clear(); } @@ -509,7 +509,7 @@ public: } ////////////////////////////////////////////////////////////////////////// - void RemoveOnSetEnumCallback(OnSetCallback* func) + void RemoveOnSetEnumCallback(OnSetCallback* func) override { stl::find_and_erase(m_onSetEnumFuncs, func); } @@ -520,7 +520,7 @@ public: } - virtual void OnSetValue([[maybe_unused]] bool bRecursive) + void OnSetValue([[maybe_unused]] bool bRecursive) override { // If have wired variables or OnSet callback, process them. // Send value to wired variable. @@ -549,7 +549,8 @@ public: } ////////////////////////////////////////////////////////////////////////// - void Serialize(XmlNodeRef node, bool load) + using IVariable::Serialize; + void Serialize(XmlNodeRef node, bool load) override { if (load) { @@ -567,8 +568,8 @@ public: } } - virtual void EnableUpdateCallbacks(bool boEnable){m_boUpdateCallbacksEnabled = boEnable; }; - virtual void SetForceModified(bool bForceModified) { m_bForceModified = bForceModified; } + void EnableUpdateCallbacks(bool boEnable) override{m_boUpdateCallbacksEnabled = boEnable; }; + void SetForceModified(bool bForceModified) override { m_bForceModified = bForceModified; } protected: // Constructor. CVariableBase() @@ -641,13 +642,13 @@ public: CVariableArray(){} //! Get name of parameter. - virtual EType GetType() const { return IVariable::ARRAY; }; - virtual int GetSize() const { return sizeof(CVariableArray); }; + EType GetType() const override { return IVariable::ARRAY; }; + int GetSize() const override { return sizeof(CVariableArray); }; ////////////////////////////////////////////////////////////////////////// // Set methods. ////////////////////////////////////////////////////////////////////////// - virtual void Set(const QString& value) + void Set(const QString& value) override { if (m_strValue != value) { @@ -655,7 +656,7 @@ public: OnSetValue(false); } } - void OnSetValue(bool bRecursive) + void OnSetValue(bool bRecursive) override { CVariableBase::OnSetValue(bRecursive); if (bRecursive) @@ -666,7 +667,7 @@ public: } } } - void SetFlagRecursive(EFlags flag) + void SetFlagRecursive(EFlags flag) override { CVariableBase::SetFlagRecursive(flag); for (Variables::iterator it = m_vars.begin(); it != m_vars.end(); ++it) @@ -677,9 +678,9 @@ public: ////////////////////////////////////////////////////////////////////////// // Get methods. ////////////////////////////////////////////////////////////////////////// - virtual void Get(QString& value) const { value = m_strValue; } + void Get(QString& value) const override { value = m_strValue; } - virtual bool HasDefaultValue() const + bool HasDefaultValue() const override { for (Variables::const_iterator it = m_vars.begin(); it != m_vars.end(); ++it) { @@ -691,7 +692,7 @@ public: return true; } - virtual void ResetToDefault() + void ResetToDefault() override { for (Variables::const_iterator it = m_vars.begin(); it != m_vars.end(); ++it) { @@ -700,7 +701,7 @@ public: } ////////////////////////////////////////////////////////////////////////// - IVariable* Clone(bool bRecursive) const + IVariable* Clone(bool bRecursive) const override { CVariableArray* var = new CVariableArray(*this); @@ -713,7 +714,7 @@ public: } ////////////////////////////////////////////////////////////////////////// - void CopyValue(IVariable* fromVar) + void CopyValue(IVariable* fromVar) override { assert(fromVar); if (fromVar->GetType() != IVariable::ARRAY) @@ -733,20 +734,20 @@ public: } ////////////////////////////////////////////////////////////////////////// - virtual int GetNumVariables() const { return static_cast(m_vars.size()); } + int GetNumVariables() const override { return static_cast(m_vars.size()); } - virtual IVariable* GetVariable(int index) const + IVariable* GetVariable(int index) const override { assert(index >= 0 && index < (int)m_vars.size()); return m_vars[index]; } - virtual void AddVariable(IVariable* var) + void AddVariable(IVariable* var) override { m_vars.push_back(var); } - virtual bool DeleteVariable(IVariable* var, bool recursive /*=false*/) + bool DeleteVariable(IVariable* var, bool recursive /*=false*/) override { bool found = stl::find_and_erase(m_vars, var); if (!found && recursive) @@ -762,12 +763,12 @@ public: return found; } - virtual void DeleteAllVariables() + void DeleteAllVariables() override { m_vars.clear(); } - virtual bool IsContainsVariable(IVariable* pVar, bool bRecursive) const + bool IsContainsVariable(IVariable* pVar, bool bRecursive) const override { for (Variables::const_iterator it = m_vars.begin(); it != m_vars.end(); ++it) { @@ -793,14 +794,15 @@ public: return false; } - virtual IVariable* FindVariable(const char* name, bool bRecursive, bool bHumanName) const; + IVariable* FindVariable(const char* name, bool bRecursive, bool bHumanName) const override; - virtual bool IsEmpty() const + bool IsEmpty() const override { return m_vars.empty(); } - void Serialize(XmlNodeRef node, bool load) + using IVariable::Serialize; + void Serialize(XmlNodeRef node, bool load) override { if (load) { @@ -1074,11 +1076,11 @@ class CVariableVoid { public: CVariableVoid(){}; - virtual EType GetType() const { return IVariable::UNKNOWN; }; - virtual IVariable* Clone([[maybe_unused]] bool bRecursive) const { return new CVariableVoid(*this); } - virtual void CopyValue([[maybe_unused]] IVariable* fromVar) {}; - virtual bool HasDefaultValue() const { return true; } - virtual void ResetToDefault() {}; + EType GetType() const override { return IVariable::UNKNOWN; }; + IVariable* Clone([[maybe_unused]] bool bRecursive) const override { return new CVariableVoid(*this); } + void CopyValue([[maybe_unused]] IVariable* fromVar) override {}; + bool HasDefaultValue() const override { return true; } + void ResetToDefault() override {}; protected: CVariableVoid(const CVariableVoid& v) : CVariableBase(v) {}; @@ -1112,44 +1114,44 @@ public: } //! Get name of parameter. - virtual EType GetType() const { return (EType)var_type::type_traits::type(); }; - virtual int GetSize() const { return sizeof(T); }; + EType GetType() const override { return (EType)var_type::type_traits::type(); }; + int GetSize() const override { return sizeof(T); }; ////////////////////////////////////////////////////////////////////////// // Set methods. ////////////////////////////////////////////////////////////////////////// - virtual void Set(int value) { SetValue(value); } - virtual void Set(bool value) { SetValue(value); } - virtual void Set(float value) { SetValue(value); } - virtual void Set(double value) { SetValue(value); } - virtual void Set(const Vec2& value) { SetValue(value); } - virtual void Set(const Vec3& value) { SetValue(value); } - virtual void Set(const Vec4& value) { SetValue(value); } - virtual void Set(const Ang3& value) { SetValue(value); } - virtual void Set(const Quat& value) { SetValue(value); } - virtual void Set(const QString& value) { SetValue(value); } - virtual void Set(const char* value) { SetValue(QString(value)); } + void Set(int value) override { SetValue(value); } + void Set(bool value) override { SetValue(value); } + void Set(float value) override { SetValue(value); } + void Set(double value) override { SetValue(value); } + void Set(const Vec2& value) override { SetValue(value); } + void Set(const Vec3& value) override { SetValue(value); } + void Set(const Vec4& value) override { SetValue(value); } + void Set(const Ang3& value) override { SetValue(value); } + void Set(const Quat& value) override { SetValue(value); } + void Set(const QString& value) override { SetValue(value); } + void Set(const char* value) override { SetValue(QString(value)); } ////////////////////////////////////////////////////////////////////////// // Get methods. ////////////////////////////////////////////////////////////////////////// - virtual void Get(int& value) const { GetValue(value); } - virtual void Get(bool& value) const { GetValue(value); } - virtual void Get(float& value) const { GetValue(value); } - virtual void Get(double& value) const { GetValue(value); } - virtual void Get(Vec2& value) const { GetValue(value); } - virtual void Get(Vec3& value) const { GetValue(value); } - virtual void Get(Vec4& value) const { GetValue(value); } - virtual void Get(Quat& value) const { GetValue(value); } - virtual void Get(QString& value) const { GetValue(value); } - virtual bool HasDefaultValue() const + void Get(int& value) const override { GetValue(value); } + void Get(bool& value) const override { GetValue(value); } + void Get(float& value) const override { GetValue(value); } + void Get(double& value) const override { GetValue(value); } + void Get(Vec2& value) const override { GetValue(value); } + void Get(Vec3& value) const override { GetValue(value); } + void Get(Vec4& value) const override { GetValue(value); } + void Get(Quat& value) const override { GetValue(value); } + void Get(QString& value) const override { GetValue(value); } + bool HasDefaultValue() const override { T defval; var_type::init(defval); return m_valueDef == defval; } - virtual void ResetToDefault() + void ResetToDefault() override { T defval; var_type::init(defval); @@ -1159,7 +1161,7 @@ public: ////////////////////////////////////////////////////////////////////////// // Limits. ////////////////////////////////////////////////////////////////////////// - virtual void SetLimits(float fMin, float fMax, float fStep = 0.f, bool bHardMin = true, bool bHardMax = true) + void SetLimits(float fMin, float fMax, float fStep = 0.f, bool bHardMin = true, bool bHardMax = true) override { m_valueMin = fMin; m_valueMax = fMax; @@ -1171,7 +1173,7 @@ public: m_customLimits = true; } - virtual void GetLimits(float& fMin, float& fMax, float& fStep, bool& bHardMin, bool& bHardMax) + void GetLimits(float& fMin, float& fMax, float& fStep, bool& bHardMin, bool& bHardMax) override { if (!m_customLimits && var_type::type_traits::supports_range()) { @@ -1199,7 +1201,7 @@ public: m_customLimits = false; } - virtual bool HasCustomLimits() + bool HasCustomLimits() override { return m_customLimits; } @@ -1217,14 +1219,14 @@ public: void operator=(const T& value) { SetValue(value); } ////////////////////////////////////////////////////////////////////////// - IVariable* Clone([[maybe_unused]] bool bRecursive) const + IVariable* Clone([[maybe_unused]] bool bRecursive) const override { Self* var = new Self(*this); return var; } ////////////////////////////////////////////////////////////////////////// - void CopyValue(IVariable* fromVar) + void CopyValue(IVariable* fromVar) override { assert(fromVar); T val; @@ -1668,7 +1670,7 @@ struct CSmartVariableBase return *pV; } // Cast to CVariableBase& VarType& operator*() const { return *pVar; } - VarType* operator->(void) const { return pVar; } + VarType* operator->() const { return pVar; } VarType* GetVar() const { return pVar; }; @@ -1730,7 +1732,7 @@ struct CSmartVariableArray } VarType& operator*() const { return *pVar; } - VarType* operator->(void) const { return pVar; } + VarType* operator->() const { return pVar; } VarType* GetVar() const { return pVar; }; @@ -1752,35 +1754,35 @@ public: // Dtor. virtual ~CVarBlock() {} //! Add variable to block. - virtual void AddVariable(IVariable* var); + void AddVariable(IVariable* var) override; //! Remove variable from block - virtual bool DeleteVariable(IVariable* var, bool bRecursive = false); + bool DeleteVariable(IVariable* var, bool bRecursive = false) override; void AddVariable(IVariable* pVar, const char* varName, unsigned char dataType = IVariable::DT_SIMPLE); // This used from smart variable pointer. void AddVariable(CVariableBase& var, const char* varName, unsigned char dataType = IVariable::DT_SIMPLE); //! Returns number of variables in block. - virtual int GetNumVariables() const { return static_cast(m_vars.size()); } + int GetNumVariables() const override { return static_cast(m_vars.size()); } //! Get pointer to stored variable by index. - virtual IVariable* GetVariable(int index) const + IVariable* GetVariable(int index) const override { assert(index >= 0 && index < m_vars.size()); return m_vars[index]; } // Clear all vars from VarBlock. - virtual void DeleteAllVariables() { m_vars.clear(); }; + void DeleteAllVariables() override { m_vars.clear(); }; //! Return true if variable block is empty (Does not have any vars). - virtual bool IsEmpty() const { return m_vars.empty(); } + bool IsEmpty() const override { return m_vars.empty(); } // Returns true if var block contains specified variable. - virtual bool IsContainsVariable(IVariable* pVar, bool bRecursive = true) const; + bool IsContainsVariable(IVariable* pVar, bool bRecursive = true) const override; //! Find variable by name. - virtual IVariable* FindVariable(const char* name, bool bRecursive = true, bool bHumanName = false) const; + IVariable* FindVariable(const char* name, bool bRecursive = true, bool bHumanName = false) const override; ////////////////////////////////////////////////////////////////////////// //! Clone var block. diff --git a/Code/Editor/Util/XmlArchive.cpp b/Code/Editor/Util/XmlArchive.cpp index e6bc93fdf4..18c3fc8e63 100644 --- a/Code/Editor/Util/XmlArchive.cpp +++ b/Code/Editor/Util/XmlArchive.cpp @@ -124,7 +124,7 @@ bool CXmlArchive::SaveToPak([[maybe_unused]] const QString& levelPath, CPakFile& if (pakFile.GetArchive()) { - CLogFile::FormatLine("Saving pak file %s", (const char*)pakFile.GetArchive()->GetFullPath()); + CLogFile::FormatLine("Saving pak file %.*s", AZ_STRING_ARG(pakFile.GetArchive()->GetFullPath().Native())); } pNamedData->Save(pakFile); diff --git a/Code/Editor/Viewport.h b/Code/Editor/Viewport.h index 74897508f0..60c1306420 100644 --- a/Code/Editor/Viewport.h +++ b/Code/Editor/Viewport.h @@ -172,7 +172,7 @@ public: //! Get current view matrix. //! This is a matrix that transforms from world space to view space. - virtual const Matrix34& GetViewTM() const + const Matrix34& GetViewTM() const override { AZ_Error("CryLegacy", false, "QtViewport::GetViewTM not implemented"); static const Matrix34 m; @@ -182,7 +182,7 @@ public: ////////////////////////////////////////////////////////////////////////// //! Get current screen matrix. //! Screen matrix transform from World space to Screen space. - virtual const Matrix34& GetScreenTM() const + const Matrix34& GetScreenTM() const override { return m_screenTM; } @@ -190,9 +190,9 @@ public: virtual Vec3 MapViewToCP(const QPoint& point) = 0; //! Map viewport position to world space position. - virtual Vec3 ViewToWorld(const QPoint& vp, bool* pCollideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const = 0; + Vec3 ViewToWorld(const QPoint& vp, bool* pCollideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const override = 0; //! Convert point on screen to world ray. - virtual void ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3& rayDir) const = 0; + void ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3& rayDir) const override = 0; //! Get normal for viewport position virtual Vec3 ViewToWorldNormal(const QPoint& vp, bool onlyTerrain, bool bTestRenderMesh = false) = 0; @@ -261,7 +261,7 @@ public: virtual void SetCursorString(const QString& str) = 0; virtual void SetFocus() = 0; - virtual void Invalidate(bool bErase = 1) = 0; + virtual void Invalidate(bool bErase = true) = 0; // Is overridden by RenderViewport virtual void SetFOV([[maybe_unused]] float fov) {} @@ -274,7 +274,7 @@ public: void SetViewPane(CLayoutViewPane* viewPane) { m_viewPane = viewPane; } - virtual CViewport *asCViewport() { return this; } + CViewport *asCViewport() override { return this; } protected: CLayoutViewPane* m_viewPane = nullptr; @@ -336,7 +336,7 @@ public: void SetActiveWindow() override { activateWindow(); } //! Called while window is idle. - virtual void Update(); + void Update() override; /** Set name of this viewport. */ @@ -344,24 +344,24 @@ public: /** Get name of viewport */ - QString GetName() const; + QString GetName() const override; - virtual void SetFocus() { setFocus(); } - virtual void Invalidate([[maybe_unused]] bool bErase = 1) { update(); } + void SetFocus() override { setFocus(); } + void Invalidate([[maybe_unused]] bool bErase = 1) override { update(); } // Is overridden by RenderViewport - virtual void SetFOV([[maybe_unused]] float fov) {} - virtual float GetFOV() const; + void SetFOV([[maybe_unused]] float fov) override {} + float GetFOV() const override; // Must be overridden in derived classes. // Returns: // e.g. 4.0/3.0 - virtual float GetAspectRatio() const = 0; - virtual void GetDimensions(int* pWidth, int* pHeight) const; - virtual void ScreenToClient(QPoint& pPoint) const override; + float GetAspectRatio() const override = 0; + void GetDimensions(int* pWidth, int* pHeight) const override; + void ScreenToClient(QPoint& pPoint) const override; - virtual void ResetContent(); - virtual void UpdateContent(int flags); + void ResetContent() override; + void UpdateContent(int flags) override; //! Set current zoom factor for this viewport. virtual void SetZoomFactor(float fZoomFactor); @@ -373,10 +373,10 @@ public: virtual void OnDeactivate(); //! Map world space position to viewport position. - virtual QPoint WorldToView(const Vec3& wp) const override; + QPoint WorldToView(const Vec3& wp) const override; //! Map world space position to 3D viewport position. - virtual Vec3 WorldToView3D(const Vec3& wp, int nFlags = 0) const; + Vec3 WorldToView3D(const Vec3& wp, int nFlags = 0) const override; //! Map viewport position to world space position. virtual Vec3 ViewToWorld(const QPoint& vp, bool* pCollideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const override; @@ -391,17 +391,18 @@ public: //! This method return a vector (p2-p1) in world space alligned to construction plane and restriction axises. //! p1 and p2 must be given in world space and lie on construction plane. - virtual Vec3 GetCPVector(const Vec3& p1, const Vec3& p2, int axis); + using CViewport::GetCPVector; + Vec3 GetCPVector(const Vec3& p1, const Vec3& p2, int axis) override; //! Snap any given 3D world position to grid lines if snap is enabled. Vec3 SnapToGrid(const Vec3& vec) override; - virtual float GetGridStep() const; + float GetGridStep() const override; //! Returns the screen scale factor for a point given in world coordinates. //! This factor gives the width in world-space units at the point's distance of the viewport. - virtual float GetScreenScaleFactor([[maybe_unused]] const Vec3& worldPoint) const { return 1; }; + float GetScreenScaleFactor([[maybe_unused]] const Vec3& worldPoint) const override { return 1; }; - void SetAxisConstrain(int axis); + void SetAxisConstrain(int axis) override; /// Take raw input and create a final mouse interaction. /// @attention Do not map **point** from widget to viewport explicitly, @@ -413,7 +414,7 @@ public: // Selection. ////////////////////////////////////////////////////////////////////////// //! Resets current selection region. - virtual void ResetSelectionRegion(); + void ResetSelectionRegion() override; //! Set 2D selection rectangle. void SetSelectionRectangle(const QRect& rect) override; @@ -422,12 +423,12 @@ public: //! Called when dragging selection rectangle. void OnDragSelectRectangle(const QRect& rect, bool bNormalizeRect = false) override; //! Get selection precision tolerance. - float GetSelectionTolerance() const { return m_selectionTolerance; } + float GetSelectionTolerance() const override { return m_selectionTolerance; } //! Center viewport on selection. void CenterOnSelection() override {} void CenterOnAABB([[maybe_unused]] const AABB& aabb) override {} - virtual void CenterOnSliceInstance() {} + void CenterOnSliceInstance() override {} //! Performs hit testing of 2d point in view to find which object hit. bool HitTest(const QPoint& point, HitContext& hitInfo) override; @@ -440,10 +441,10 @@ public: float GetDistanceToLine(const Vec3& lineP1, const Vec3& lineP2, const QPoint& point) const override; // Access to the member m_bAdvancedSelectMode so interested modules can know its value. - bool GetAdvancedSelectModeFlag(); + bool GetAdvancedSelectModeFlag() override; - virtual void GetPerpendicularAxis(EAxis* pAxis, bool* pIs2D) const; - virtual const ::Plane* GetConstructionPlane() const { return &m_constructionPlane; } + void GetPerpendicularAxis(EAxis* pAxis, bool* pIs2D) const override; + const ::Plane* GetConstructionPlane() const override { return &m_constructionPlane; } ////////////////////////////////////////////////////////////////////////// @@ -451,7 +452,7 @@ public: //! Set construction plane from given position construction matrix refrence coord system and axis settings. ////////////////////////////////////////////////////////////////////////// void MakeConstructionPlane(int axis) override; - virtual void SetConstructionMatrix(RefCoordSys coordSys, const Matrix34& xform); + void SetConstructionMatrix(RefCoordSys coordSys, const Matrix34& xform) override; virtual const Matrix34& GetConstructionMatrix(RefCoordSys coordSys); // Set simple construction plane origin. void SetConstructionOrigin(const Vec3& worldPos); @@ -461,11 +462,11 @@ public: ////////////////////////////////////////////////////////////////////////// // Undo for viewpot operations. - void BeginUndo(); - void AcceptUndo(const QString& undoDescription); - void CancelUndo(); - void RestoreUndo(); - bool IsUndoRecording() const; + void BeginUndo() override; + void AcceptUndo(const QString& undoDescription) override; + void CancelUndo() override; + void RestoreUndo() override; + bool IsUndoRecording() const override; ////////////////////////////////////////////////////////////////////////// //! Get prefered original size for this viewport. @@ -473,39 +474,39 @@ public: virtual QSize GetIdealSize() const; //! Check if world space bounding box is visible in this view. - virtual bool IsBoundsVisible(const AABB& box) const; + bool IsBoundsVisible(const AABB& box) const override; ////////////////////////////////////////////////////////////////////////// - void SetCursor(const QCursor& cursor) + void SetCursor(const QCursor& cursor) override { setCursor(cursor); } // Set`s current cursor string. void SetCurrentCursor(const QCursor& hCursor, const QString& cursorString); - virtual void SetCurrentCursor(EStdCursor stdCursor, const QString& cursorString); - void SetCurrentCursor(EStdCursor stdCursor); - virtual void SetCursorString(const QString& cursorString); - void ResetCursor(); - void SetSupplementaryCursorStr(const QString& str); + void SetCurrentCursor(EStdCursor stdCursor, const QString& cursorString) override; + void SetCurrentCursor(EStdCursor stdCursor) override; + void SetCursorString(const QString& cursorString) override; + void ResetCursor() override; + void SetSupplementaryCursorStr(const QString& str) override; ////////////////////////////////////////////////////////////////////////// // Return visble objects cache. - CBaseObjectsCache* GetVisibleObjectsCache() { return m_pVisibleObjectsCache; }; + CBaseObjectsCache* GetVisibleObjectsCache() override { return m_pVisibleObjectsCache; }; - void RegisterRenderListener(IRenderListener* piListener); - bool UnregisterRenderListener(IRenderListener* piListener); - bool IsRenderListenerRegistered(IRenderListener* piListener); + void RegisterRenderListener(IRenderListener* piListener) override; + bool UnregisterRenderListener(IRenderListener* piListener) override; + bool IsRenderListenerRegistered(IRenderListener* piListener) override; - void AddPostRenderer(IPostRenderer* pPostRenderer); - bool RemovePostRenderer(IPostRenderer* pPostRenderer); + void AddPostRenderer(IPostRenderer* pPostRenderer) override; + bool RemovePostRenderer(IPostRenderer* pPostRenderer) override; void CaptureMouse() override { m_mouseCaptured = true; QWidget::grabMouse(); } void ReleaseMouse() override { m_mouseCaptured = false; QWidget::releaseMouse(); } - virtual void setRay(QPoint& vp, Vec3& raySrc, Vec3& rayDir); - virtual void setHitcontext(QPoint& vp, Vec3& raySrc, Vec3& rayDir); + void setRay(QPoint& vp, Vec3& raySrc, Vec3& rayDir) override; + void setHitcontext(QPoint& vp, Vec3& raySrc, Vec3& rayDir) override; QPoint m_vp; AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING Vec3 m_raySrc; diff --git a/Code/Editor/ViewportTitleDlg.cpp b/Code/Editor/ViewportTitleDlg.cpp index 1f04f71712..65d6de8944 100644 --- a/Code/Editor/ViewportTitleDlg.cpp +++ b/Code/Editor/ViewportTitleDlg.cpp @@ -154,6 +154,8 @@ void CViewportTitleDlg::SetupCameraDropdownMenu() cameraMenu->addMenu(GetFovMenu()); m_ui->m_cameraMenu->setMenu(cameraMenu); m_ui->m_cameraMenu->setPopupMode(QToolButton::InstantPopup); + QObject::connect(cameraMenu, &QMenu::aboutToShow, this, &CViewportTitleDlg::CheckForCameraSpeedUpdate); + QAction* gotoPositionAction = new QAction("Go to position", cameraMenu); connect(gotoPositionAction, &QAction::triggered, this, &CViewportTitleDlg::OnBnClickedGotoPosition); cameraMenu->addAction(gotoPositionAction); diff --git a/Code/Editor/ViewportTitleDlg.h b/Code/Editor/ViewportTitleDlg.h index 5acb04ca99..4a2a454907 100644 --- a/Code/Editor/ViewportTitleDlg.h +++ b/Code/Editor/ViewportTitleDlg.h @@ -77,7 +77,7 @@ Q_SIGNALS: protected: virtual void OnInitDialog(); - virtual void OnEditorNotifyEvent(EEditorNotifyEvent event); + void OnEditorNotifyEvent(EEditorNotifyEvent event) override; void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam) override; void OnMaximize(); diff --git a/Code/Framework/AtomCore/AtomCore/Utils/ScopedValue.h b/Code/Framework/AtomCore/AtomCore/Utils/ScopedValue.h new file mode 100644 index 0000000000..f6ed6d6df4 --- /dev/null +++ b/Code/Framework/AtomCore/AtomCore/Utils/ScopedValue.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include + +namespace AZ +{ + //! Sets a variable upon construction and again when the object goes out of scope. + template + class ScopedValue + { + private: + T* m_ptr; + T m_finalValue; + + public: + ScopedValue(T* ptr, T initialValue, T finalValue) : + m_ptr(ptr), m_finalValue(finalValue) + { + AZ_Assert(m_ptr, "ScopedValue::m_ptr is null"); + *m_ptr = initialValue; + } + + ~ScopedValue() + { + *m_ptr = m_finalValue; + } + }; + +} // namespace AZ diff --git a/Code/Framework/AtomCore/AtomCore/atomcore_files.cmake b/Code/Framework/AtomCore/AtomCore/atomcore_files.cmake index c90263468f..9167c1e645 100644 --- a/Code/Framework/AtomCore/AtomCore/atomcore_files.cmake +++ b/Code/Framework/AtomCore/AtomCore/atomcore_files.cmake @@ -19,4 +19,5 @@ set(FILES std/containers/vector_set.h std/containers/vector_set_base.h std/parallel/concurrency_checker.h + Utils/ScopedValue.h ) diff --git a/Code/Framework/AtomCore/Tests/ScopedValueTest.cpp b/Code/Framework/AtomCore/Tests/ScopedValueTest.cpp new file mode 100644 index 0000000000..9578cb2329 --- /dev/null +++ b/Code/Framework/AtomCore/Tests/ScopedValueTest.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 + +namespace UnitTest +{ + TEST(ScopedValueTest, TestBoolValue) + { + bool localValue = false; + + { + AZ::ScopedValue scopedValue(&localValue, true, false); + EXPECT_EQ(true, localValue); + } + + EXPECT_EQ(false, localValue); + } + + TEST(ScopedValueTest, TestIntValue) + { + int localValue = 0; + + { + AZ::ScopedValue scopedValue(&localValue, 1, 2); + EXPECT_EQ(1, localValue); + } + + EXPECT_EQ(2, localValue); + } +} diff --git a/Code/Framework/AtomCore/Tests/atomcore_tests_files.cmake b/Code/Framework/AtomCore/Tests/atomcore_tests_files.cmake index 0f5fcb441d..4522a4b7b6 100644 --- a/Code/Framework/AtomCore/Tests/atomcore_tests_files.cmake +++ b/Code/Framework/AtomCore/Tests/atomcore_tests_files.cmake @@ -12,5 +12,6 @@ set(FILES InstanceDatabase.cpp lru_cache.cpp Main.cpp + ScopedValueTest.cpp vector_set.cpp ) diff --git a/Code/Framework/AzCore/AzCore/Component/Component.h b/Code/Framework/AzCore/AzCore/Component/Component.h index 9b4ae3f55f..3cbb9b5a86 100644 --- a/Code/Framework/AzCore/AzCore/Component/Component.h +++ b/Code/Framework/AzCore/AzCore/Component/Component.h @@ -266,7 +266,7 @@ namespace AZ _ComponentClass::RTTI_Type().ToString().c_str(), descriptor->GetName(), _ComponentClass::RTTI_TypeName()); \ return nullptr; \ } \ - else if (descriptor->GetName() != _ComponentClass::RTTI_TypeName()) \ + if (descriptor->GetName() != _ComponentClass::RTTI_TypeName()) \ { \ AZ_Error("Component", false, "The same component UUID (%s) / name (%s) was registered twice. This isn't allowed, " \ "it can cause lifetime management issues / crashes.\nThis situation can happen by declaring a component " \ diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp index 04a76b0f8e..a13f11c007 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp @@ -1251,6 +1251,8 @@ namespace AZ return AZ::SettingsRegistryInterface::VisitResponse::Continue; } + + using SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value) override { // By default the auto load option is true diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h index 3768a75d83..bfc541ca09 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h @@ -196,14 +196,14 @@ namespace AZ ////////////////////////////////////////////////////////////////////////// // ComponentApplicationRequests - void RegisterComponentDescriptor(const ComponentDescriptor* descriptor) override final; - void UnregisterComponentDescriptor(const ComponentDescriptor* descriptor) override final; - void RegisterEntityAddedEventHandler(EntityAddedEvent::Handler& handler) override final; - void RegisterEntityRemovedEventHandler(EntityRemovedEvent::Handler& handler) override final; - void RegisterEntityActivatedEventHandler(EntityActivatedEvent::Handler& handler) override final; - void RegisterEntityDeactivatedEventHandler(EntityDeactivatedEvent::Handler& handler) override final; - void SignalEntityActivated(Entity* entity) override final; - void SignalEntityDeactivated(Entity* entity) override final; + void RegisterComponentDescriptor(const ComponentDescriptor* descriptor) final; + void UnregisterComponentDescriptor(const ComponentDescriptor* descriptor) final; + void RegisterEntityAddedEventHandler(EntityAddedEvent::Handler& handler) final; + void RegisterEntityRemovedEventHandler(EntityRemovedEvent::Handler& handler) final; + void RegisterEntityActivatedEventHandler(EntityActivatedEvent::Handler& handler) final; + void RegisterEntityDeactivatedEventHandler(EntityDeactivatedEvent::Handler& handler) final; + void SignalEntityActivated(Entity* entity) final; + void SignalEntityDeactivated(Entity* entity) final; bool AddEntity(Entity* entity) override; bool RemoveEntity(Entity* entity) override; bool DeleteEntity(const EntityId& id) override; diff --git a/Code/Framework/AzCore/AzCore/Component/Entity.cpp b/Code/Framework/AzCore/AzCore/Component/Entity.cpp index 4febd893b0..0fe201d448 100644 --- a/Code/Framework/AzCore/AzCore/Component/Entity.cpp +++ b/Code/Framework/AzCore/AzCore/Component/Entity.cpp @@ -207,12 +207,6 @@ namespace AZ ActivateComponent(**it); } - // Cache the transform interface to the transform interface - // Generally this pattern is not recommended unless for component event buses - // As we have a guarantee (by design) that components can't change during active state) - // Even though technically they can connect disconnect from the bus. - m_transform = TransformBus::FindFirstHandler(m_id); - SetState(State::Active); EBUS_EVENT_ID(m_id, EntityBus, OnEntityActivated, m_id); @@ -1330,6 +1324,19 @@ namespace AZ return *processSignature; } + AZ::TransformInterface* Entity::GetTransform() const + { + // Lazy evaluation of the cached entity transform. + if(!m_transform) + { + // Generally this pattern is not recommended unless for component event buses + // As we have a guarantee (by design) that components can't change during active state) + // Even though technically they can connect disconnect from the bus. + m_transform = TransformBus::FindFirstHandler(m_id); + } + return m_transform; + } + //========================================================================= // MakeId // Ids must be unique across a project at authoring time. Runtime doesn't matter diff --git a/Code/Framework/AzCore/AzCore/Component/Entity.h b/Code/Framework/AzCore/AzCore/Component/Entity.h index 3887b2e03e..c2bde375bb 100644 --- a/Code/Framework/AzCore/AzCore/Component/Entity.h +++ b/Code/Framework/AzCore/AzCore/Component/Entity.h @@ -362,10 +362,9 @@ namespace AZ //! @return The Process Signature of the local machine. static AZ::u32 GetProcessSignature(); - /// @cond EXCLUDE_DOCS - //! @deprecated Use the TransformBus to communicate with the TransformInterface. - inline TransformInterface* GetTransform() const { return m_transform; } - /// @endcond + //! Gets the TransformInterface for the entity. + //! @return The TransformInterface for the entity. + TransformInterface* GetTransform() const; //! Sorts an entity's components based on the dependencies between components. //! If all dependencies are met, the required services can be activated @@ -414,7 +413,7 @@ namespace AZ //! A cached pointer to the transform interface. //! We recommend using AZ::TransformBus and caching locally instead of accessing //! the transform interface directly through this pointer. - TransformInterface* m_transform; + mutable TransformInterface* m_transform; //! A user-friendly name for the entity. This makes error messages easier to read. AZStd::string m_name; diff --git a/Code/Framework/AzCore/AzCore/Debug/AssetTrackingTypes.h b/Code/Framework/AzCore/AzCore/Debug/AssetTrackingTypes.h index c46edbb80e..f538c516c3 100644 --- a/Code/Framework/AzCore/AzCore/Debug/AssetTrackingTypes.h +++ b/Code/Framework/AzCore/AzCore/Debug/AssetTrackingTypes.h @@ -86,6 +86,7 @@ namespace AZ class AssetTreeNodeBase { public: + virtual ~AssetTreeNodeBase() = default; virtual const AssetPrimaryInfo* GetAssetPrimaryInfo() const = 0; virtual AssetTreeNodeBase* FindOrAddChild(const AssetTrackingId& id, const AssetPrimaryInfo* info) = 0; }; @@ -94,6 +95,7 @@ namespace AZ class AssetTreeBase { public: + virtual ~AssetTreeBase() = default; virtual AssetTreeNodeBase& GetRoot() = 0; }; @@ -101,6 +103,7 @@ namespace AZ class AssetAllocationTableBase { public: + virtual ~AssetAllocationTableBase() = default; virtual AssetTreeNodeBase* FindAllocation(void* ptr) const = 0; }; } diff --git a/Code/Framework/AzCore/AzCore/Debug/AssetTrackingTypesImpl.h b/Code/Framework/AzCore/AzCore/Debug/AssetTrackingTypesImpl.h index 7916a442b5..eac809c406 100644 --- a/Code/Framework/AzCore/AzCore/Debug/AssetTrackingTypesImpl.h +++ b/Code/Framework/AzCore/AzCore/Debug/AssetTrackingTypesImpl.h @@ -31,6 +31,8 @@ namespace AZ { } + ~AssetTreeNode() override = default; + const AssetPrimaryInfo* GetAssetPrimaryInfo() const override { return m_primaryinfo; @@ -67,6 +69,8 @@ namespace AZ class AssetTree : public AssetTreeBase { public: + ~AssetTree() override = default; + AssetTreeNodeBase& GetRoot() override { return m_rootAssets; @@ -99,6 +103,7 @@ namespace AZ AllocationTable(mutex_type& mutex) : m_mutex(mutex) { } + ~AllocationTable() override = default; AssetTreeNodeBase* FindAllocation(void* ptr) const override { diff --git a/Code/Framework/AzCore/AzCore/Debug/BudgetTracker.h b/Code/Framework/AzCore/AzCore/Debug/BudgetTracker.h index 1357bb5870..34d8510349 100644 --- a/Code/Framework/AzCore/AzCore/Debug/BudgetTracker.h +++ b/Code/Framework/AzCore/AzCore/Debug/BudgetTracker.h @@ -19,7 +19,7 @@ namespace AZ::Debug class BudgetTracker { public: - AZ_RTTI(BudgetTracker, "{E14A746D-BFFE-4C02-90FB-4699B79864A5}"); + AZ_TYPE_INFO(BudgetTracker, "{E14A746D-BFFE-4C02-90FB-4699B79864A5}"); static Budget* GetBudgetFromEnvironment(const char* budgetName, uint32_t crc); ~BudgetTracker(); diff --git a/Code/Framework/AzCore/AzCore/Debug/Profiler.h b/Code/Framework/AzCore/AzCore/Debug/Profiler.h index 8af48e47f6..56103e8314 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Profiler.h +++ b/Code/Framework/AzCore/AzCore/Debug/Profiler.h @@ -59,6 +59,20 @@ namespace AZStd namespace AZ::Debug { + // interface for externally defined profiler systems + class Profiler + { + public: + AZ_RTTI(Profiler, "{3E5D6329-72D1-41BA-9158-68A349D1A4D5}"); + + Profiler() = default; + virtual ~Profiler() = default; + + // support for the extra macro args (e.g. format strings) will come in a later PR + virtual void BeginRegion(const Budget* budget, const char* eventName) = 0; + virtual void EndRegion(const Budget* budget) = 0; + }; + class ProfileScope { public: diff --git a/Code/Framework/AzCore/AzCore/Debug/Profiler.inl b/Code/Framework/AzCore/AzCore/Debug/Profiler.inl index 8ca8368ce1..74c0f553c4 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Profiler.inl +++ b/Code/Framework/AzCore/AzCore/Debug/Profiler.inl @@ -6,6 +6,8 @@ * */ +#include + namespace AZ::Debug { template @@ -22,9 +24,11 @@ namespace AZ::Debug PIXBeginEvent(PIX_COLOR_INDEX(budget->Crc() & 0xff), eventName, args...); #endif budget->BeginProfileRegion(); -// TODO: injecting instrumentation for other profilers -// NOTE: external profiler registration won't occur inline in a header necessarily in this manner, but the exact mechanism -// will be introduced in a future PR + + if (auto profiler = AZ::Interface::Get(); profiler) + { + profiler->BeginRegion(budget, eventName); + } #endif } @@ -39,6 +43,10 @@ namespace AZ::Debug #if defined(USE_PIX) PIXEndEvent(); #endif + if (auto profiler = AZ::Interface::Get(); profiler) + { + profiler->EndRegion(budget); + } #endif } diff --git a/Code/Framework/AzCore/AzCore/Debug/TraceMessagesDriller.h b/Code/Framework/AzCore/AzCore/Debug/TraceMessagesDriller.h index 16d8f4fba3..32726931fc 100644 --- a/Code/Framework/AzCore/AzCore/Debug/TraceMessagesDriller.h +++ b/Code/Framework/AzCore/AzCore/Debug/TraceMessagesDriller.h @@ -28,21 +28,21 @@ namespace AZ protected: ////////////////////////////////////////////////////////////////////////// // Driller - virtual const char* GroupName() const { return "SystemDrillers"; } - virtual const char* GetName() const { return "TraceMessagesDriller"; } - virtual const char* GetDescription() const { return "Handles all system messages like Assert, Exception, Error, Warning, Printf, etc."; } - virtual void Start(const Param* params = NULL, int numParams = 0); - virtual void Stop(); + const char* GroupName() const override { return "SystemDrillers"; } + const char* GetName() const override { return "TraceMessagesDriller"; } + const char* GetDescription() const override { return "Handles all system messages like Assert, Exception, Error, Warning, Printf, etc."; } + void Start(const Param* params = NULL, int numParams = 0) override; + void Stop() override; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // TraceMessagesDrillerBus /// Triggered when a AZ_Assert failed. This is terminating event! (the code will break, crash). - virtual void OnAssert(const char* message); - virtual void OnException(const char* message); - virtual void OnError(const char* window, const char* message); - virtual void OnWarning(const char* window, const char* message); - virtual void OnPrintf(const char* window, const char* message); + void OnAssert(const char* message) override; + void OnException(const char* message) override; + void OnError(const char* window, const char* message) override; + void OnWarning(const char* window, const char* message) override; + void OnPrintf(const char* window, const char* message) override; ////////////////////////////////////////////////////////////////////////// }; } // namespace Debug diff --git a/Code/Framework/AzCore/AzCore/Driller/Stream.h b/Code/Framework/AzCore/AzCore/Driller/Stream.h index f883984b11..5efa416ef4 100644 --- a/Code/Framework/AzCore/AzCore/Driller/Stream.h +++ b/Code/Framework/AzCore/AzCore/Driller/Stream.h @@ -443,7 +443,7 @@ namespace AZ const unsigned char* GetData() const { return m_data.data(); } unsigned int GetDataSize() const { return static_cast(m_data.size()); } inline void Reset() { m_data.clear(); } - virtual void WriteBinary(const void* data, unsigned int dataSize) + void WriteBinary(const void* data, unsigned int dataSize) override { m_data.insert(m_data.end(), reinterpret_cast(data), reinterpret_cast(data) + dataSize); } @@ -489,7 +489,7 @@ namespace AZ } unsigned int GetDataLeft() const { return static_cast(m_dataEnd - m_data); } - virtual unsigned int ReadBinary(void* data, unsigned int maxDataSize) + unsigned int ReadBinary(void* data, unsigned int maxDataSize) override { AZ_Assert(m_data != nullptr, "You must call SetData function, before you can read data!"); AZ_Assert(data != nullptr && maxDataSize > 0, "We must have a valid pointer and max data size!"); @@ -523,7 +523,7 @@ namespace AZ bool Open(const char* fileName, int mode, int platformFlags = 0); void Close(); - virtual void WriteBinary(const void* data, unsigned int dataSize); + void WriteBinary(const void* data, unsigned int dataSize) override; }; /** @@ -540,7 +540,7 @@ namespace AZ DrillerInputFileStream(); ~DrillerInputFileStream(); bool Open(const char* fileName, int mode, int platformFlags = 0); - virtual unsigned int ReadBinary(void* data, unsigned int maxDataSize); + unsigned int ReadBinary(void* data, unsigned int maxDataSize) override; void Close(); }; diff --git a/Code/Framework/AzCore/AzCore/EBus/EBus.h b/Code/Framework/AzCore/AzCore/EBus/EBus.h index 1bff4ff297..58754ff9b8 100644 --- a/Code/Framework/AzCore/AzCore/EBus/EBus.h +++ b/Code/Framework/AzCore/AzCore/EBus/EBus.h @@ -1717,6 +1717,7 @@ AZ_POP_DISABLE_WARNING { EBusRouterNode m_routerNode; public: + virtual ~EBusNestedVersionRouter() = default; template void BusRouterConnect(Container& container, int order = 0); diff --git a/Code/Framework/AzCore/AzCore/IO/CompressorZLib.h b/Code/Framework/AzCore/AzCore/IO/CompressorZLib.h index abd21e1450..dd1fb226c3 100644 --- a/Code/Framework/AzCore/AzCore/IO/CompressorZLib.h +++ b/Code/Framework/AzCore/AzCore/IO/CompressorZLib.h @@ -98,21 +98,21 @@ namespace AZ /// Return compressor type id. static AZ::u32 TypeId(); - virtual AZ::u32 GetTypeId() const { return TypeId(); } + AZ::u32 GetTypeId() const override { return TypeId(); } /// Called when we open a stream to Read for the first time. Data contains the first. dataSize <= m_maxHeaderSize. - virtual bool ReadHeaderAndData(CompressorStream* stream, AZ::u8* data, unsigned int dataSize); + bool ReadHeaderAndData(CompressorStream* stream, AZ::u8* data, unsigned int dataSize) override; /// Called when we are about to start writing to a compressed stream. - virtual bool WriteHeaderAndData(CompressorStream* stream); + bool WriteHeaderAndData(CompressorStream* stream) override; /// Forwarded function from the Device when we from a compressed stream. - virtual SizeType Read(CompressorStream* stream, SizeType byteSize, SizeType offset, void* buffer); + SizeType Read(CompressorStream* stream, SizeType byteSize, SizeType offset, void* buffer) override; /// Forwarded function from the Device when we write to a compressed stream. - virtual SizeType Write(CompressorStream* stream, SizeType byteSize, const void* data, SizeType offset = SizeType(-1)); + SizeType Write(CompressorStream* stream, SizeType byteSize, const void* data, SizeType offset = SizeType(-1)) override; /// Write a seek point. - virtual bool WriteSeekPoint(CompressorStream* stream); + bool WriteSeekPoint(CompressorStream* stream) override; /// Set auto seek point even dataSize bytes. - virtual bool StartCompressor(CompressorStream* stream, int compressionLevel, SizeType autoSeekDataSize); + bool StartCompressor(CompressorStream* stream, int compressionLevel, SizeType autoSeekDataSize) override; /// Called just before we close the stream. All compression data will be flushed and finalized. (You can't add data afterwards). - virtual bool Close(CompressorStream* stream); + bool Close(CompressorStream* stream) override; protected: diff --git a/Code/Framework/AzCore/AzCore/IO/FileReader.cpp b/Code/Framework/AzCore/AzCore/IO/FileReader.cpp new file mode 100644 index 0000000000..94118cdfe4 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/IO/FileReader.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace AZ::IO +{ + FileReader::FileReader() = default; + + FileReader::FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath) + { + Open(fileIoBase, filePath); + } + + FileReader::~FileReader() + { + Close(); + } + + FileReader::FileReader(FileReader&& other) + { + AZStd::swap(m_file, other.m_file); + AZStd::swap(m_fileIoBase, other.m_fileIoBase); + } + + FileReader& FileReader::operator=(FileReader&& other) + { + // Close the current file and take over other file + Close(); + m_file = AZStd::move(other.m_file); + m_fileIoBase = AZStd::move(other.m_fileIoBase); + other.m_file = AZStd::monostate{}; + other.m_fileIoBase = {}; + + return *this; + } + + bool FileReader::Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath) + { + // Close file if the FileReader has an instance open + Close(); + + if (fileIoBase != nullptr) + { + AZ::IO::HandleType fileHandle; + if (fileIoBase->Open(filePath, IO::OpenMode::ModeRead, fileHandle)) + { + m_file = fileHandle; + m_fileIoBase = fileIoBase; + return true; + } + } + else + { + AZ::IO::SystemFile file; + if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY)) + { + m_file = AZStd::move(file); + return true; + } + } + + return false; + } + + bool FileReader::IsOpen() const + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + return *fileHandle != AZ::IO::InvalidHandle; + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + return systemFile->IsOpen(); + } + + return false; + } + + void FileReader::Close() + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + if (AZ::IO::FileIOBase* fileIo = m_fileIoBase; fileIo != nullptr) + { + fileIo->Close(*fileHandle); + } + } + + m_file = AZStd::monostate{}; + m_fileIoBase = {}; + } + + auto FileReader::Length() const -> SizeType + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + if (SizeType fileSize{}; m_fileIoBase->Size(*fileHandle, fileSize)) + { + return fileSize; + } + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + return systemFile->Length(); + } + + return 0; + } + + auto FileReader::Read(SizeType byteSize, void* buffer) -> SizeType + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + if (SizeType bytesRead{}; m_fileIoBase->Read(*fileHandle, buffer, byteSize, false, &bytesRead)) + { + return bytesRead; + } + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + return systemFile->Read(byteSize, buffer); + } + + return 0; + } + + auto FileReader::Tell() const -> SizeType + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + if (SizeType fileOffset{}; m_fileIoBase->Tell(*fileHandle, fileOffset)) + { + return fileOffset; + } + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + return systemFile->Tell(); + } + + return 0; + } + + bool FileReader::Seek(AZ::s64 offset, SeekType type) + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + return m_fileIoBase->Seek(*fileHandle, offset, type); + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + systemFile->Seek(offset, static_cast(type)); + return true; + } + + return false; + } + + bool FileReader::Eof() const + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + return m_fileIoBase->Eof(*fileHandle); + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + return systemFile->Eof(); + } + + return false; + } + + bool FileReader::GetFilePath(AZ::IO::FixedMaxPath& filePath) const + { + if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr) + { + AZ::IO::FixedMaxPathString& pathStringRef = filePath.Native(); + if (m_fileIoBase->GetFilename(*fileHandle, pathStringRef.data(), pathStringRef.capacity())) + { + pathStringRef.resize_no_construct(AZStd::char_traits::length(pathStringRef.data())); + return true; + } + } + else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr) + { + filePath = systemFile->Name(); + return true; + } + + return false; + } +} diff --git a/Code/Framework/AzCore/AzCore/IO/FileReader.h b/Code/Framework/AzCore/AzCore/IO/FileReader.h new file mode 100644 index 0000000000..4fdb18b2b2 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/IO/FileReader.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include + +namespace AZ::IO +{ + class FileIOBase; + enum class SeekType : AZ::u32; + + //! Structure which encapsulates delegates File Read operations + //! to either the FileIOBase or SystemFile classes based if a FileIOBase* instance has been supplied + //! to the FileSystemReader class + //! the SettingsRegistry option to use FileIO + class FileReader + { + using HandleType = AZ::u32; + using FileHandleType = AZStd::variant; + public: + using SizeType = AZ::u64; + + //! Creates FileReader instance in the default state with no file opend + FileReader(); + ~FileReader(); + + //! Creates a new FileReader instance and attempts to open the file at the supplied path + //! Uses the FileIOBase instance if supplied + //! @param fileIOBase pointer to fileIOBase instance + //! @param null-terminated filePath to open + FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath); + + //! Takes ownership of the supplied FileReader handle + FileReader(FileReader&& other); + + //! Moves ownership of FileReader handle to this instance + FileReader& operator=(FileReader&& other); + + //! Opens a File using the FileIOBase instance if non-nullptr + //! Otherwise fall back to use SystemFile + //! @param fileIOBase pointer to fileIOBase instance + //! @param null-terminated filePath to open + //! @return true if the File is opened successfully + bool Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath); + + //! Returns true if a file is currently open + //! @return true if the file is open + bool IsOpen() const; + + //! Closes the File + void Close(); + + //! Retrieve the length of the OpenFile + SizeType Length() const; + + //! Attempts to read up to byte size bytes into the supplied buffer + //! @param byteSize - Maximum number of bytes to read + //! @param buffer - Buffer to read bytes into + //! @returns the number of bytes read if the file is open, otherwise 0 + SizeType Read(SizeType byteSize, void* buffer); + + //! Returns the current file offset + //! @returns file offset if the file is open, otherwise 0 + SizeType Tell() const; + + //! Seeks within the open file to the offset supplied + //! @param offset File offset to seek to + //! @param type parameter to indicate the reference point to start the seek from + //! @returns true if the file is open and the seek succeeded + bool Seek(AZ::s64 offset, SeekType type); + + //! Returns true if the file is open and in the EOF state + bool Eof() const; + + //! Store the file path of the open file into the output file path parameter + //! The filePath reference is left unmodified, if the path was not stored + //! @return true if the filePath was stored + bool GetFilePath(AZ::IO::FixedMaxPath& filePath) const; + + private: + + FileHandleType m_file; + AZ::IO::FileIOBase* m_fileIoBase{}; + }; +} diff --git a/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp b/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp index 5bff79b422..651abb89fe 100644 --- a/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp +++ b/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp @@ -160,12 +160,12 @@ void SystemFile::Seek(SeekSizeType offset, SeekMode mode) Platform::Seek(m_handle, this, offset, mode); } -SystemFile::SizeType SystemFile::Tell() +SystemFile::SizeType SystemFile::Tell() const { return Platform::Tell(m_handle, this); } -bool SystemFile::Eof() +bool SystemFile::Eof() const { return Platform::Eof(m_handle, this); } diff --git a/Code/Framework/AzCore/AzCore/IO/SystemFile.h b/Code/Framework/AzCore/AzCore/IO/SystemFile.h index 8a5b2b2521..551ce89ce7 100644 --- a/Code/Framework/AzCore/AzCore/IO/SystemFile.h +++ b/Code/Framework/AzCore/AzCore/IO/SystemFile.h @@ -72,9 +72,9 @@ namespace AZ /// Seek in current file. void Seek(SeekSizeType offset, SeekMode mode); /// Get the cursor position in the current file. - SizeType Tell(); + SizeType Tell() const; /// Is the cursor at the end of the file? - bool Eof(); + bool Eof() const; /// Get the time the file was last modified. AZ::u64 ModificationTime(); /// Read data from a file synchronous. Return number of bytes actually read in the buffer. diff --git a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobNotify.h b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobNotify.h index 9b6a39af4f..1c15b7105e 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobNotify.h +++ b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobNotify.h @@ -31,7 +31,7 @@ namespace AZ { } protected: - virtual void Process() + void Process() override { m_notifyFlag->store(true, AZStd::memory_order_release); } diff --git a/Code/Framework/AzCore/AzCore/Math/InterpolationSample.h b/Code/Framework/AzCore/AzCore/Math/InterpolationSample.h index 12b18f86b8..942f02727e 100644 --- a/Code/Framework/AzCore/AzCore/Math/InterpolationSample.h +++ b/Code/Framework/AzCore/AzCore/Math/InterpolationSample.h @@ -77,7 +77,7 @@ namespace AZ : public Sample { public: - Vector3 GetInterpolatedValue(TimeType time) override final + Vector3 GetInterpolatedValue(TimeType time) final { Vector3 interpolatedValue = m_previousValue; if (m_targetTimestamp != 0) @@ -108,7 +108,7 @@ namespace AZ : public Sample { public: - Quaternion GetInterpolatedValue(TimeType time) override final + Quaternion GetInterpolatedValue(TimeType time) final { Quaternion interpolatedValue = m_previousValue; if (m_targetTimestamp != 0) @@ -144,7 +144,7 @@ namespace AZ : public Sample { public: - Vector3 GetInterpolatedValue(TimeType /*time*/) override final + Vector3 GetInterpolatedValue(TimeType /*time*/) final { return GetTargetValue(); } @@ -155,7 +155,7 @@ namespace AZ : public Sample { public: - Quaternion GetInterpolatedValue(TimeType /*time*/) override final + Quaternion GetInterpolatedValue(TimeType /*time*/) final { return GetTargetValue(); } diff --git a/Code/Framework/AzCore/AzCore/Math/Transform.cpp b/Code/Framework/AzCore/AzCore/Math/Transform.cpp index 5cb09fe9a8..1701820bae 100644 --- a/Code/Framework/AzCore/AzCore/Math/Transform.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Transform.cpp @@ -353,6 +353,7 @@ namespace AZ Method("CreateFromMatrix3x3AndTranslation", &Transform::CreateFromMatrix3x3AndTranslation)-> Method("CreateUniformScale", &Transform::CreateUniformScale)-> Method("CreateTranslation", &Transform::CreateTranslation)-> + Method("CreateLookAt", &Transform::CreateLookAt)-> Method("ConstructFromValuesNumeric", &Internal::ConstructTransformFromValues); } } diff --git a/Code/Framework/AzCore/AzCore/Memory/HeapSchema.h b/Code/Framework/AzCore/AzCore/Memory/HeapSchema.h index f6c6f98315..f72ae31057 100644 --- a/Code/Framework/AzCore/AzCore/Memory/HeapSchema.h +++ b/Code/Framework/AzCore/AzCore/Memory/HeapSchema.h @@ -48,18 +48,18 @@ namespace AZ HeapSchema(const Descriptor& desc); virtual ~HeapSchema(); - virtual pointer_type Allocate(size_type byteSize, size_type alignment, int flags, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0); - virtual void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0); - virtual pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment) { (void)ptr; (void)newSize; (void)newAlignment; return NULL; } - virtual size_type Resize(pointer_type ptr, size_type newSize) { (void)ptr; (void)newSize; return 0; } - virtual size_type AllocationSize(pointer_type ptr); + pointer_type Allocate(size_type byteSize, size_type alignment, int flags, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) override; + void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override; + pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment) override { (void)ptr; (void)newSize; (void)newAlignment; return NULL; } + size_type Resize(pointer_type ptr, size_type newSize) override { (void)ptr; (void)newSize; return 0; } + size_type AllocationSize(pointer_type ptr) override; - virtual size_type NumAllocatedBytes() const { return m_used; } - virtual size_type Capacity() const { return m_capacity; } - virtual size_type GetMaxAllocationSize() const; - size_type GetMaxContiguousAllocationSize() const override; - virtual IAllocatorAllocate* GetSubAllocator() { return m_subAllocator; } - virtual void GarbageCollect() {} + size_type NumAllocatedBytes() const override { return m_used; } + size_type Capacity() const override { return m_capacity; } + size_type GetMaxAllocationSize() const override; + size_type GetMaxContiguousAllocationSize() const override; + IAllocatorAllocate* GetSubAllocator() override { return m_subAllocator; } + void GarbageCollect() override {} private: AZ_FORCE_INLINE size_type ChunckSize(pointer_type ptr); diff --git a/Code/Framework/AzCore/AzCore/Memory/HphaSchema.h b/Code/Framework/AzCore/AzCore/Memory/HphaSchema.h index 0f84ca1e68..5ee0205196 100644 --- a/Code/Framework/AzCore/AzCore/Memory/HphaSchema.h +++ b/Code/Framework/AzCore/AzCore/Memory/HphaSchema.h @@ -56,22 +56,22 @@ namespace AZ HphaSchema(const Descriptor& desc); virtual ~HphaSchema(); - virtual pointer_type Allocate(size_type byteSize, size_type alignment, int flags = 0, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0); - virtual void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0); - virtual pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment); + pointer_type Allocate(size_type byteSize, size_type alignment, int flags = 0, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) override; + void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override; + pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment) override; /// Resizes allocated memory block to the size possible and returns that size. - virtual size_type Resize(pointer_type ptr, size_type newSize); - virtual size_type AllocationSize(pointer_type ptr); + size_type Resize(pointer_type ptr, size_type newSize) override; + size_type AllocationSize(pointer_type ptr) override; - virtual size_type NumAllocatedBytes() const; - virtual size_type Capacity() const; - virtual size_type GetMaxAllocationSize() const; - size_type GetMaxContiguousAllocationSize() const override; - virtual size_type GetUnAllocatedMemory(bool isPrint = false) const; - virtual IAllocatorAllocate* GetSubAllocator() { return m_desc.m_subAllocator; } + size_type NumAllocatedBytes() const override; + size_type Capacity() const override; + size_type GetMaxAllocationSize() const override; + size_type GetMaxContiguousAllocationSize() const override; + size_type GetUnAllocatedMemory(bool isPrint = false) const override; + IAllocatorAllocate* GetSubAllocator() override { return m_desc.m_subAllocator; } /// Return unused memory to the OS (if we don't use fixed block). Don't call this unless you really need free memory, it is slow. - virtual void GarbageCollect(); + void GarbageCollect() override; private: // [LY-84974][sconel@][2018-08-10] SliceStrike integration up to CL 671758 diff --git a/Code/Framework/AzCore/AzCore/Memory/MallocSchema.h b/Code/Framework/AzCore/AzCore/Memory/MallocSchema.h index cbf928e189..7a8c4a0366 100644 --- a/Code/Framework/AzCore/AzCore/Memory/MallocSchema.h +++ b/Code/Framework/AzCore/AzCore/Memory/MallocSchema.h @@ -41,18 +41,18 @@ namespace AZ //--------------------------------------------------------------------- // IAllocatorAllocate //--------------------------------------------------------------------- - virtual pointer_type Allocate(size_type byteSize, size_type alignment, int flags, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) override; - virtual void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override; - virtual pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment) override; - virtual size_type Resize(pointer_type ptr, size_type newSize) override; - virtual size_type AllocationSize(pointer_type ptr) override; + pointer_type Allocate(size_type byteSize, size_type alignment, int flags, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) override; + void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override; + pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment) override; + size_type Resize(pointer_type ptr, size_type newSize) override; + size_type AllocationSize(pointer_type ptr) override; - virtual size_type NumAllocatedBytes() const override; - virtual size_type Capacity() const override; - virtual size_type GetMaxAllocationSize() const override; - virtual size_type GetMaxContiguousAllocationSize() const override; - virtual IAllocatorAllocate* GetSubAllocator() override; - virtual void GarbageCollect() override; + size_type NumAllocatedBytes() const override; + size_type Capacity() const override; + size_type GetMaxAllocationSize() const override; + size_type GetMaxContiguousAllocationSize() const override; + IAllocatorAllocate* GetSubAllocator() override; + void GarbageCollect() override; private: typedef void* (*MallocFn)(size_t); diff --git a/Code/Framework/AzCore/AzCore/Memory/Memory.h b/Code/Framework/AzCore/AzCore/Memory/Memory.h index d09003f3f0..2e08ec1b15 100644 --- a/Code/Framework/AzCore/AzCore/Memory/Memory.h +++ b/Code/Framework/AzCore/AzCore/Memory/Memory.h @@ -849,7 +849,7 @@ namespace AZ return AZ::AllocatorInstance::Get().GetUnAllocatedMemory(isPrint); } - virtual IAllocatorAllocate* GetSubAllocator() override + IAllocatorAllocate* GetSubAllocator() override { return AZ::AllocatorInstance::Get().GetSubAllocator(); } diff --git a/Code/Framework/AzCore/AzCore/Memory/MemoryDriller.h b/Code/Framework/AzCore/AzCore/Memory/MemoryDriller.h index 94c364c719..9bcbc85217 100644 --- a/Code/Framework/AzCore/AzCore/Memory/MemoryDriller.h +++ b/Code/Framework/AzCore/AzCore/Memory/MemoryDriller.h @@ -38,24 +38,24 @@ namespace AZ protected: ////////////////////////////////////////////////////////////////////////// // Driller - virtual const char* GroupName() const { return "SystemDrillers"; } - virtual const char* GetName() const { return "MemoryDriller"; } - virtual const char* GetDescription() const { return "Reports all allocators and memory allocations."; } - virtual void Start(const Param* params = NULL, int numParams = 0); - virtual void Stop(); + const char* GroupName() const override { return "SystemDrillers"; } + const char* GetName() const override { return "MemoryDriller"; } + const char* GetDescription() const override { return "Reports all allocators and memory allocations."; } + void Start(const Param* params = NULL, int numParams = 0) override; + void Stop() override; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // MemoryDrillerBus - virtual void RegisterAllocator(IAllocator* allocator); - virtual void UnregisterAllocator(IAllocator* allocator); + void RegisterAllocator(IAllocator* allocator) override; + void UnregisterAllocator(IAllocator* allocator) override; - virtual void RegisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, unsigned int stackSuppressCount); - virtual void UnregisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, AllocationInfo* info); - virtual void ReallocateAllocation(IAllocator* allocator, void* prevAddress, void* newAddress, size_t newByteSize, size_t newAlignment); - virtual void ResizeAllocation(IAllocator* allocator, void* address, size_t newSize); + void RegisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, unsigned int stackSuppressCount) override; + void UnregisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, AllocationInfo* info) override; + void ReallocateAllocation(IAllocator* allocator, void* prevAddress, void* newAddress, size_t newByteSize, size_t newAlignment) override; + void ResizeAllocation(IAllocator* allocator, void* address, size_t newSize) override; - virtual void DumpAllAllocations(); + void DumpAllAllocations() override; ////////////////////////////////////////////////////////////////////////// void RegisterAllocatorOutput(IAllocator* allocator); diff --git a/Code/Framework/AzCore/AzCore/Memory/OverrunDetectionAllocator.h b/Code/Framework/AzCore/AzCore/Memory/OverrunDetectionAllocator.h index 3dffdfad56..9895a5f84f 100644 --- a/Code/Framework/AzCore/AzCore/Memory/OverrunDetectionAllocator.h +++ b/Code/Framework/AzCore/AzCore/Memory/OverrunDetectionAllocator.h @@ -77,18 +77,18 @@ namespace AZ //--------------------------------------------------------------------- // IAllocatorAllocate //--------------------------------------------------------------------- - virtual pointer_type Allocate(size_type byteSize, size_type alignment, int flags, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) override; - virtual void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override; - virtual pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment) override; - virtual size_type Resize(pointer_type ptr, size_type newSize) override; - virtual size_type AllocationSize(pointer_type ptr) override; + pointer_type Allocate(size_type byteSize, size_type alignment, int flags, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) override; + void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override; + pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment) override; + size_type Resize(pointer_type ptr, size_type newSize) override; + size_type AllocationSize(pointer_type ptr) override; - virtual size_type NumAllocatedBytes() const override; - virtual size_type Capacity() const override; - virtual size_type GetMaxAllocationSize() const override; + size_type NumAllocatedBytes() const override; + size_type Capacity() const override; + size_type GetMaxAllocationSize() const override; size_type GetMaxContiguousAllocationSize() const override; - virtual IAllocatorAllocate* GetSubAllocator() override; - virtual void GarbageCollect() override; + IAllocatorAllocate* GetSubAllocator() override; + void GarbageCollect() override; private: OverrunDetectionSchemaImpl* m_impl; diff --git a/Code/Framework/AzCore/AzCore/Module/Module.h b/Code/Framework/AzCore/AzCore/Module/Module.h index a4843f002c..3809bd1bb4 100644 --- a/Code/Framework/AzCore/AzCore/Module/Module.h +++ b/Code/Framework/AzCore/AzCore/Module/Module.h @@ -62,7 +62,7 @@ namespace AZ * DO NOT OVERRIDE. This method will return in the future, but at this point things reflected here are not unreflected for all ReflectContexts (Serialize, Editor, Network, Script, etc.) * Place all calls to non-component reflect functions inside of a component reflect function to ensure that your types are unreflected. */ - virtual void Reflect(AZ::ReflectContext*) final { } + void Reflect(AZ::ReflectContext*) {} /** * Override to require specific components on the system entity. diff --git a/Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.h b/Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.h index b436f5adab..0a1af21213 100644 --- a/Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.h +++ b/Code/Framework/AzCore/AzCore/RTTI/BehaviorContext.h @@ -594,8 +594,8 @@ namespace AZ void SetArgumentName(size_t index, const AZStd::string& name) override; const AZStd::string* GetArgumentToolTip(size_t index) const override; void SetArgumentToolTip(size_t index, const AZStd::string& name) override; - virtual void SetDefaultValue(size_t index, BehaviorDefaultValuePtr defaultValue) override; - virtual BehaviorDefaultValuePtr GetDefaultValue(size_t index) const override; + void SetDefaultValue(size_t index, BehaviorDefaultValuePtr defaultValue) override; + BehaviorDefaultValuePtr GetDefaultValue(size_t index) const override; const BehaviorParameter* GetResult() const override; void OverrideParameterTraits(size_t index, AZ::u32 addTraits, AZ::u32 removeTraits) override; diff --git a/Code/Framework/AzCore/AzCore/RTTI/RTTI.h b/Code/Framework/AzCore/AzCore/RTTI/RTTI.h index fa081a9497..acf6f64f77 100644 --- a/Code/Framework/AzCore/AzCore/RTTI/RTTI.h +++ b/Code/Framework/AzCore/AzCore/RTTI/RTTI.h @@ -5,8 +5,8 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#ifndef AZCORE_RTTI_H -#define AZCORE_RTTI_H + +#pragma once #include #include @@ -44,21 +44,9 @@ namespace AZ /// RTTI typeId typedef void (* RTTI_EnumCallback)(const AZ::TypeId& /*typeId*/, void* /*userData*/); - // Disabling missing override warning because we intentionally want to allow for declaring RTTI base classes that don't impelment RTTI. -#if defined(AZ_COMPILER_CLANG) -# define AZ_PUSH_DISABLE_OVERRIDE_WARNING \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Winconsistent-missing-override\"") -# define AZ_POP_DISABLE_OVERRIDE_WARNING \ - _Pragma("clang diagnostic pop") -#else -# define AZ_PUSH_DISABLE_OVERRIDE_WARNING -# define AZ_POP_DISABLE_OVERRIDE_WARNING -#endif - // We require AZ_TYPE_INFO to be declared #define AZ_RTTI_COMMON() \ - AZ_PUSH_DISABLE_OVERRIDE_WARNING \ + AZ_PUSH_DISABLE_WARNING(26433, "-Winconsistent-missing-override") \ void RTTI_Enable(); \ virtual inline const AZ::TypeId& RTTI_GetType() const { return RTTI_Type(); } \ virtual inline const char* RTTI_GetTypeName() const { return RTTI_TypeName(); } \ @@ -66,7 +54,7 @@ namespace AZ virtual void RTTI_EnumTypes(AZ::RTTI_EnumCallback cb, void* userData) { RTTI_EnumHierarchy(cb, userData); } \ static inline const AZ::TypeId& RTTI_Type() { return TYPEINFO_Uuid(); } \ static inline const char* RTTI_TypeName() { return TYPEINFO_Name(); } \ - AZ_POP_DISABLE_OVERRIDE_WARNING + AZ_POP_DISABLE_WARNING //#define AZ_RTTI_1(_1) static_assert(false,"You must provide a valid classUuid!") @@ -74,8 +62,10 @@ namespace AZ #define AZ_RTTI_1() AZ_RTTI_COMMON() \ static bool RTTI_IsContainType(const AZ::TypeId& id) { return id == RTTI_Type(); } \ static void RTTI_EnumHierarchy(AZ::RTTI_EnumCallback cb, void* userData) { cb(RTTI_Type(), userData); } \ + AZ_PUSH_DISABLE_WARNING(26433, "-Winconsistent-missing-override") \ virtual inline const void* RTTI_AddressOf(const AZ::TypeId& id) const { return (id == RTTI_Type()) ? this : nullptr; } \ - virtual inline void* RTTI_AddressOf(const AZ::TypeId& id) { return (id == RTTI_Type()) ? this : nullptr; } + virtual inline void* RTTI_AddressOf(const AZ::TypeId& id) { return (id == RTTI_Type()) ? this : nullptr; } \ + AZ_POP_DISABLE_WARNING /// AZ_RTTI(BaseClass) #define AZ_RTTI_2(_1) AZ_RTTI_COMMON() \ @@ -85,14 +75,14 @@ namespace AZ static void RTTI_EnumHierarchy(AZ::RTTI_EnumCallback cb, void* userData) { \ cb(RTTI_Type(), userData); \ AZ::Internal::RttiCaller<_1>::RTTI_EnumHierarchy(cb, userData); } \ - AZ_PUSH_DISABLE_OVERRIDE_WARNING \ + AZ_PUSH_DISABLE_WARNING(26433, "-Winconsistent-missing-override") \ virtual inline const void* RTTI_AddressOf(const AZ::TypeId& id) const { \ if (id == RTTI_Type()) { return this; } \ return AZ::Internal::RttiCaller<_1>::RTTI_AddressOf(this, id); } \ virtual inline void* RTTI_AddressOf(const AZ::TypeId& id) { \ if (id == RTTI_Type()) { return this; } \ return AZ::Internal::RttiCaller<_1>::RTTI_AddressOf(this, id); } \ - AZ_POP_DISABLE_OVERRIDE_WARNING + AZ_POP_DISABLE_WARNING /// AZ_RTTI(BaseClass1,BaseClass2) #define AZ_RTTI_3(_1, _2) AZ_RTTI_COMMON() \ @@ -104,7 +94,7 @@ namespace AZ cb(RTTI_Type(), userData); \ AZ::Internal::RttiCaller<_1>::RTTI_EnumHierarchy(cb, userData); \ AZ::Internal::RttiCaller<_2>::RTTI_EnumHierarchy(cb, userData); } \ - AZ_PUSH_DISABLE_OVERRIDE_WARNING \ + AZ_PUSH_DISABLE_WARNING(26433, "-Winconsistent-missing-override") \ virtual inline const void* RTTI_AddressOf(const AZ::TypeId& id) const { \ if (id == RTTI_Type()) { return this; } \ const void* r = AZ::Internal::RttiCaller<_1>::RTTI_AddressOf(this, id); if (r) { return r; } \ @@ -113,7 +103,7 @@ namespace AZ if (id == RTTI_Type()) { return this; } \ void* r = AZ::Internal::RttiCaller<_1>::RTTI_AddressOf(this, id); if (r) { return r; } \ return AZ::Internal::RttiCaller<_2>::RTTI_AddressOf(this, id); } \ - AZ_POP_DISABLE_OVERRIDE_WARNING + AZ_POP_DISABLE_WARNING /// AZ_RTTI(BaseClass1,BaseClass2,BaseClass3) #define AZ_RTTI_4(_1, _2, _3) AZ_RTTI_COMMON() \ @@ -127,7 +117,7 @@ namespace AZ AZ::Internal::RttiCaller<_1>::RTTI_EnumHierarchy(cb, userData); \ AZ::Internal::RttiCaller<_2>::RTTI_EnumHierarchy(cb, userData); \ AZ::Internal::RttiCaller<_3>::RTTI_EnumHierarchy(cb, userData); } \ - AZ_PUSH_DISABLE_OVERRIDE_WARNING \ + AZ_PUSH_DISABLE_WARNING(26433, "-Winconsistent-missing-override") \ virtual inline const void* RTTI_AddressOf(const AZ::TypeId& id) const { \ if (id == RTTI_Type()) { return this; } \ const void* r = AZ::Internal::RttiCaller<_1>::RTTI_AddressOf(this, id); if (r) { return r; } \ @@ -138,7 +128,7 @@ namespace AZ void* r = AZ::Internal::RttiCaller<_1>::RTTI_AddressOf(this, id); if (r) { return r; } \ r = AZ::Internal::RttiCaller<_2>::RTTI_AddressOf(this, id); if (r) { return r; } \ return AZ::Internal::RttiCaller<_3>::RTTI_AddressOf(this, id); } \ - AZ_POP_DISABLE_OVERRIDE_WARNING + AZ_POP_DISABLE_WARNING /// AZ_RTTI(BaseClass1,BaseClass2,BaseClass3,BaseClass4) #define AZ_RTTI_5(_1, _2, _3, _4) AZ_RTTI_COMMON() \ @@ -154,7 +144,7 @@ namespace AZ AZ::Internal::RttiCaller<_2>::RTTI_EnumHierarchy(cb, userData); \ AZ::Internal::RttiCaller<_3>::RTTI_EnumHierarchy(cb, userData); \ AZ::Internal::RttiCaller<_4>::RTTI_EnumHierarchy(cb, userData); } \ - AZ_PUSH_DISABLE_OVERRIDE_WARNING \ + AZ_PUSH_DISABLE_WARNING(26433, "-Winconsistent-missing-override") \ virtual inline const void* RTTI_AddressOf(const AZ::TypeId& id) const { \ if (id == RTTI_Type()) { return this; } \ const void* r = AZ::Internal::RttiCaller<_1>::RTTI_AddressOf(this, id); if (r) { return r; } \ @@ -167,7 +157,7 @@ namespace AZ r = AZ::Internal::RttiCaller<_2>::RTTI_AddressOf(this, id); if (r) { return r; } \ r = AZ::Internal::RttiCaller<_3>::RTTI_AddressOf(this, id); if (r) { return r; } \ return AZ::Internal::RttiCaller<_4>::RTTI_AddressOf(this, id); } \ - AZ_POP_DISABLE_OVERRIDE_WARNING + AZ_POP_DISABLE_WARNING /// AZ_RTTI(BaseClass1,BaseClass2,BaseClass3,BaseClass4,BaseClass5) #define AZ_RTTI_6(_1, _2, _3, _4, _5) AZ_RTTI_COMMON() \ @@ -185,7 +175,7 @@ namespace AZ AZ::Internal::RttiCaller<_3>::RTTI_EnumHierarchy(cb, userData); \ AZ::Internal::RttiCaller<_4>::RTTI_EnumHierarchy(cb, userData); \ AZ::Internal::RttiCaller<_5>::RTTI_EnumHierarchy(cb, userData); } \ - AZ_PUSH_DISABLE_OVERRIDE_WARNING \ + AZ_PUSH_DISABLE_WARNING(26433, "-Winconsistent-missing-override") \ virtual inline const void* RTTI_AddressOf(const AZ::TypeId& id) const { \ if (id == RTTI_Type()) { return this; } \ const void* r = AZ::Internal::RttiCaller<_1>::RTTI_AddressOf(this, id); if (r) { return r; } \ @@ -200,7 +190,7 @@ namespace AZ r = AZ::Internal::RttiCaller<_3>::RTTI_AddressOf(this, id); if (r) { return r; } \ r = AZ::Internal::RttiCaller<_4>::RTTI_AddressOf(this, id); if (r) { return r; } \ return AZ::Internal::RttiCaller<_5>::RTTI_AddressOf(this, id); } \ - AZ_POP_DISABLE_OVERRIDE_WARNING + AZ_POP_DISABLE_WARNING ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // MACRO specialization to allow optional parameters for template version of AZ_RTTI @@ -951,10 +941,7 @@ namespace AZ { return AZStd::shared_ptr(ptr, castPtr); } - else - { - return AZStd::shared_ptr(); - } + return AZStd::shared_ptr(); } // RttiCast specialization for intrusive_ptr. @@ -1077,7 +1064,6 @@ namespace AZ { return AZ::Internal::RttiIsTypeOfIdHelper::Check(id, data, typename HasAZRtti>::kind_type()); } + } // namespace AZ -#endif // AZCORE_RTTI_H -#pragma once diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.h b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.h index c46ba32209..a691bb1bcb 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.h @@ -19,8 +19,8 @@ namespace AZ public: AZ_COMPONENT(JsonSystemComponent, "{3C2C7234-9512-4E24-86F0-C40865D7EECE}", Component); - void Activate(); - void Deactivate(); + void Activate() override; + void Deactivate() override; static void Reflect(ReflectContext* reflectContext); }; diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.h b/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.h index e0d2635fc3..937c8389ff 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.h @@ -46,7 +46,8 @@ namespace AZ public: AZ_RTTI(JsonUnorderedMapSerializer, "{EF4478D3-1820-4FDB-A7B7-C9711EB41602}", JsonMapSerializer); AZ_CLASS_ALLOCATOR_DECL; - + + using JsonMapSerializer::Store; JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) override; }; @@ -63,6 +64,7 @@ namespace AZ const SerializeContext::ClassElement* keyElement, const SerializeContext::ClassElement* valueElement, const rapidjson::Value& key, const rapidjson::Value& value, JsonDeserializerContext& context) override; + using JsonMapSerializer::Store; JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) override; }; diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistry.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistry.h index 106e232904..3dc1be87c5 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistry.h +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistry.h @@ -370,6 +370,11 @@ namespace AZ //! @param applyPatchSettings The ApplyPatchSettings which are using during JSON Merging virtual void SetApplyPatchSettings(const AZ::JsonApplyPatchSettings& applyPatchSettings) = 0; virtual void GetApplyPatchSettings(AZ::JsonApplyPatchSettings& applyPatchSettings) = 0; + + //! Stores option to indicate whether the FileIOBase instance should be used for file operations + //! @param useFileIo If true the FileIOBase instance will attempted to be used for FileIOBase + //! operations before falling back to use SystemFile + virtual void SetUseFileIO(bool useFileIo) = 0; }; inline SettingsRegistryInterface::Visitor::~Visitor() = default; diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp index 6864dcd1c8..92f546815e 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp @@ -9,11 +9,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#include #include #include @@ -131,6 +135,12 @@ namespace AZ pointer.Create(m_settings, m_settings.GetAllocator()).SetArray(); } + SettingsRegistryImpl::SettingsRegistryImpl(bool useFileIo) + : SettingsRegistryImpl() + { + m_useFileIo = useFileIo; + } + void SettingsRegistryImpl::SetContext(SerializeContext* context) { AZStd::scoped_lock lock(m_settingMutex); @@ -723,15 +733,10 @@ namespace AZ RegistryFileList fileList; scratchBuffer->clear(); - AZ::IO::FixedMaxPathString folderPath{ path }; - constexpr AZStd::string_view pathSeparators{ AZ_CORRECT_AND_WRONG_DATABASE_SEPARATOR }; - if (pathSeparators.find_first_of(folderPath.back()) == AZStd::string_view::npos) - { - folderPath.push_back(AZ_CORRECT_DATABASE_SEPARATOR); - } + AZ::IO::FixedMaxPath folderPath{ path }; - const size_t platformKeyOffset = folderPath.size(); - folderPath.push_back('*'); + const size_t platformKeyOffset = folderPath.Native().size(); + folderPath /= '*'; Value specialzationArray(kArrayType); size_t specializationCount = specializations.GetCount(); @@ -741,47 +746,13 @@ namespace AZ specialzationArray.PushBack(Value(name.data(), aznumeric_caster(name.length()), m_settings.GetAllocator()), m_settings.GetAllocator()); } pointer.Create(m_settings, m_settings.GetAllocator()).SetObject() - .AddMember(StringRef("Folder"), Value(folderPath.c_str(), aznumeric_caster(folderPath.size()), m_settings.GetAllocator()), m_settings.GetAllocator()) + .AddMember(StringRef("Folder"), Value(folderPath.c_str(), aznumeric_caster(folderPath.Native().size()), m_settings.GetAllocator()), m_settings.GetAllocator()) .AddMember(StringRef("Specializations"), AZStd::move(specialzationArray), m_settings.GetAllocator()); - auto callback = [this, &fileList, &specializations, &pointer, &folderPath](const char* filename, bool isFile) -> bool + + auto CreateSettingsFindCallback = [this, &fileList, &specializations, &pointer, &folderPath](bool isPlatformFile) { - if (isFile) - { - if (fileList.size() >= MaxRegistryFolderEntries) - { - AZ_Error("Settings Registry", false, "Too many files in registry folder."); - AZStd::scoped_lock lock(m_settingMutex); - pointer.Create(m_settings, m_settings.GetAllocator()).SetObject() - .AddMember(StringRef("Error"), StringRef("Too many files in registry folder."), m_settings.GetAllocator()) - .AddMember(StringRef("Path"), Value(folderPath.c_str(), aznumeric_caster(folderPath.size()), m_settings.GetAllocator()), m_settings.GetAllocator()) - .AddMember(StringRef("File"), Value(filename, m_settings.GetAllocator()), m_settings.GetAllocator()); - return false; - } - - fileList.push_back(); - RegistryFile& registryFile = fileList.back(); - if (!ExtractFileDescription(registryFile, filename, specializations)) - { - fileList.pop_back(); - } - } - return true; - }; - SystemFile::FindFiles(folderPath.c_str(), callback); - - - if (!platform.empty()) - { - // Move the folderPath prefix back to the supplied path before the wildcard - folderPath.erase(platformKeyOffset); - folderPath += PlatformFolder; - folderPath.push_back(AZ_CORRECT_DATABASE_SEPARATOR); - folderPath += platform; - folderPath.push_back(AZ_CORRECT_DATABASE_SEPARATOR); - folderPath.push_back('*'); - - auto platformCallback = [this, &fileList, &specializations, &pointer, &folderPath](const char* filename, bool isFile) -> bool + return [this, &fileList, &specializations, &pointer, &folderPath, isPlatformFile](AZStd::string_view filename, bool isFile) -> bool { if (isFile) { @@ -791,8 +762,8 @@ namespace AZ AZStd::scoped_lock lock(m_settingMutex); pointer.Create(m_settings, m_settings.GetAllocator()).SetObject() .AddMember(StringRef("Error"), StringRef("Too many files in registry folder."), m_settings.GetAllocator()) - .AddMember(StringRef("Path"), Value(folderPath.c_str(), aznumeric_caster(folderPath.size()), m_settings.GetAllocator()), m_settings.GetAllocator()) - .AddMember(StringRef("File"), Value(filename, m_settings.GetAllocator()), m_settings.GetAllocator()); + .AddMember(StringRef("Path"), Value(folderPath.c_str(), aznumeric_caster(folderPath.Native().size()), m_settings.GetAllocator()), m_settings.GetAllocator()) + .AddMember(StringRef("File"), Value(filename.data(), aznumeric_caster(filename.size()), m_settings.GetAllocator()), m_settings.GetAllocator()); return false; } @@ -800,7 +771,7 @@ namespace AZ RegistryFile& registryFile = fileList.back(); if (ExtractFileDescription(registryFile, filename, specializations)) { - registryFile.m_isPlatformFile = true; + registryFile.m_isPlatformFile = isPlatformFile; } else { @@ -809,7 +780,42 @@ namespace AZ } return true; }; - SystemFile::FindFiles(folderPath.c_str(), platformCallback); + }; + + struct FindFilesPayload + { + bool m_isPlatformFile{}; + AZStd::fixed_vector m_pathSegmentsToAppend; + }; + + AZStd::fixed_vector findFilesPayloads{ {false} }; + if (!platform.empty()) + { + findFilesPayloads.push_back(FindFilesPayload{ true, { PlatformFolder, platform } }); + } + + for (const FindFilesPayload& findFilesPayload : findFilesPayloads) + { + // Erase back to initial path + folderPath.Native().erase(platformKeyOffset); + for (AZStd::string_view pathSegmentToAppend : findFilesPayload.m_pathSegmentsToAppend) + { + folderPath /= pathSegmentToAppend; + } + + auto findFilesCallback = CreateSettingsFindCallback(findFilesPayload.m_isPlatformFile); + if (AZ::IO::FileIOBase* fileIo = m_useFileIo ? AZ::IO::FileIOBase::GetInstance() : nullptr; fileIo != nullptr) + { + auto FileIoToSystemFileFindFiles = [findFilesCallback = AZStd::move(findFilesCallback), fileIo](const char* filePath) -> bool + { + return findFilesCallback(AZ::IO::PathView(filePath).Filename().Native(), !fileIo->IsDirectory(filePath)); + }; + fileIo->FindFiles(folderPath.c_str(), "*", FileIoToSystemFileFindFiles); + } + else + { + SystemFile::FindFiles((folderPath / "*").c_str(), findFilesCallback); + } } if (!fileList.empty()) @@ -831,16 +837,14 @@ namespace AZ // Load the registry files in the sorted order. for (RegistryFile& registryFile : fileList) { - folderPath.erase(platformKeyOffset); // Erase all characters after the platformKeyOffset + folderPath.Native().erase(platformKeyOffset); // Erase all characters after the platformKeyOffset if (registryFile.m_isPlatformFile) { - folderPath += PlatformFolder; - folderPath.push_back(AZ_CORRECT_DATABASE_SEPARATOR); - folderPath += platform; - folderPath.push_back(AZ_CORRECT_DATABASE_SEPARATOR); + folderPath /= PlatformFolder; + folderPath /= platform; } - folderPath += registryFile.m_relativePath; + folderPath /= registryFile.m_relativePath; if (!registryFile.m_isPatch) { @@ -1027,39 +1031,44 @@ namespace AZ return false; } - bool SettingsRegistryImpl::ExtractFileDescription(RegistryFile& output, const char* filename, const Specializations& specializations) + bool SettingsRegistryImpl::ExtractFileDescription(RegistryFile& output, AZStd::string_view filename, const Specializations& specializations) { - if (!filename || filename[0] == 0) + static constexpr auto PatchExtensionWithDot = AZStd::fixed_string<32>(".") + PatchExtension; + static constexpr auto ExtensionWithDot = AZStd::fixed_string<32>(".") + Extension; + static constexpr AZ::IO::PathView PatchExtensionView(PatchExtensionWithDot); + static constexpr AZ::IO::PathView ExtensionView(ExtensionWithDot); + + if (filename.empty()) { AZ_Error("Settings Registry", false, "Settings file without name found"); return false; } - AZStd::string_view filePath{ filename }; - const size_t filePathSize = filePath.size(); + AZ::IO::PathView filePath{ filename }; + const size_t filePathSize = filePath.Native().size(); // The filePath.empty() check makes sure that the file extension after the final isn't added to the output.m_tags - AZStd::optional pathTag = AZ::StringFunc::TokenizeNext(filePath, '.'); - for (; pathTag && !filePath.empty(); pathTag = AZ::StringFunc::TokenizeNext(filePath, '.')) + auto AppendSpecTags = [&output](AZStd::string_view pathTag) { - output.m_tags.push_back(Specializations::Hash(*pathTag)); - } + output.m_tags.push_back(Specializations::Hash(pathTag)); + }; + AZ::StringFunc::TokenizeVisitor(filePath.Stem().Native(), AppendSpecTags, '.'); // If token is invalid, then the filename has no characters and therefore no extension - if (pathTag) + if (AZ::IO::PathView fileExtension = filePath.Extension(); !fileExtension.empty()) { - if (pathTag->size() >= AZStd::char_traits::length(PatchExtension) && azstrnicmp(pathTag->data(), PatchExtension, pathTag->size()) == 0) + if (fileExtension == PatchExtensionView) { output.m_isPatch = true; } - else if (pathTag->size() != AZStd::char_traits::length(Extension) || azstrnicmp(pathTag->data(), Extension, pathTag->size()) != 0) + else if (fileExtension != ExtensionView) { return false; } } else { - AZ_Error("Settings Registry", false, R"(Settings file without extension found: "%s")", filename); + AZ_Error("Settings Registry", false, R"(Settings file without extension found: "%.*s")", AZ_STRING_ARG(filename)); return false; } @@ -1074,7 +1083,7 @@ namespace AZ { if (*currentIt == *(currentIt - 1)) { - AZ_Error("Settings Registry", false, R"(One or more tags are duplicated in registry file "%s")", filename); + AZ_Error("Settings Registry", false, R"(One or more tags are duplicated in registry file "%.*s")", AZ_STRING_ARG(filename)); return false; } ++currentIt; @@ -1103,7 +1112,7 @@ namespace AZ } else { - AZ_Error("Settings Registry", false, R"(Found relative path to settings file "%s" is too long.)", filename); + AZ_Error("Settings Registry", false, R"(Found relative path to settings file "%.*s" is too long.)", AZ_STRING_ARG(filename)); return false; } } @@ -1116,8 +1125,8 @@ namespace AZ Pointer pointer(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/-"); - SystemFile file; - if (!file.Open(path, SystemFile::OpenMode::SF_OPEN_READ_ONLY)) + FileReader fileReader(m_useFileIo ? AZ::IO::FileIOBase::GetInstance(): nullptr, path); + if (!fileReader.IsOpen()) { AZ_Error("Settings Registry", false, R"(Unable to open registry file "%s".)", path); pointer.Create(m_settings, m_settings.GetAllocator()).SetObject() @@ -1126,7 +1135,7 @@ namespace AZ return false; } - u64 fileSize = file.Length(); + u64 fileSize = fileReader.Length(); if (fileSize == 0) { AZ_Warning("Settings Registry", false, R"(Registry file "%s" is 0 bytes in length. There is no nothing to merge)", path); @@ -1136,9 +1145,10 @@ namespace AZ .AddMember(StringRef("Path"), Value(path, m_settings.GetAllocator()), m_settings.GetAllocator()); return false; } + scratchBuffer.clear(); scratchBuffer.resize_no_construct(fileSize + 1); - if (file.Read(fileSize, scratchBuffer.data()) != fileSize) + if (fileReader.Read(fileSize, scratchBuffer.data()) != fileSize) { AZ_Error("Settings Registry", false, R"(Unable to read registry file "%s".)", path); pointer.Create(m_settings, m_settings.GetAllocator()).SetObject() @@ -1268,4 +1278,9 @@ namespace AZ { applyPatchSettings = m_applyPatchSettings; } + + void SettingsRegistryImpl::SetUseFileIO(bool useFileIo) + { + m_useFileIo = useFileIo; + } } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.h index 036f5c6596..ac214711b8 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.h +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.h @@ -35,6 +35,10 @@ namespace AZ static constexpr size_t MaxRegistryFolderEntries = 128; SettingsRegistryImpl(); + //! @param useFileIo - If true attempt to redirect + //! file read operations through the FileIOBase instance first before falling back to SystemFile + //! otherwise always use SystemFile + explicit SettingsRegistryImpl(bool useFileIo); AZ_DISABLE_COPY_MOVE(SettingsRegistryImpl); ~SettingsRegistryImpl() override = default; @@ -83,6 +87,8 @@ namespace AZ void SetApplyPatchSettings(const AZ::JsonApplyPatchSettings& applyPatchSettings) override; void GetApplyPatchSettings(AZ::JsonApplyPatchSettings& applyPatchSettings) override; + void SetUseFileIO(bool useFileIo) override; + private: using TagList = AZStd::fixed_vector; struct RegistryFile @@ -104,7 +110,7 @@ namespace AZ // Compares if lhs is less than rhs in terms of processing order. This can also detect and report conflicts. bool IsLessThan(bool& collisionFound, const RegistryFile& lhs, const RegistryFile& rhs, const Specializations& specializations, const rapidjson::Pointer& historyPointer, AZStd::string_view folderPath); - bool ExtractFileDescription(RegistryFile& output, const char* filename, const Specializations& specializations); + bool ExtractFileDescription(RegistryFile& output, AZStd::string_view filename, const Specializations& specializations); bool MergeSettingsFileInternal(const char* path, Format format, AZStd::string_view rootKey, AZStd::vector& scratchBuffer); void SignalNotifier(AZStd::string_view jsonPath, Type type); @@ -119,5 +125,7 @@ namespace AZ JsonSerializerSettings m_serializationSettings; JsonDeserializerSettings m_deserializationSettings; JsonApplyPatchSettings m_applyPatchSettings; + + bool m_useFileIo{}; }; } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index 71ffece892..da7110e36e 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -6,6 +6,8 @@ * */ +#include +#include #include #include #include @@ -78,6 +80,7 @@ namespace AZ::Internal struct EnginePathsVisitor : public AZ::SettingsRegistryInterface::Visitor { + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit( [[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, [[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override @@ -355,6 +358,7 @@ namespace AZ::SettingsRegistryMergeUtils : m_settingsSpecialization{ specializations } {} + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, [[maybe_unused]] AZ::SettingsRegistryInterface::Type type, bool value) override { @@ -386,8 +390,36 @@ namespace AZ::SettingsRegistryMergeUtils const ConfigParserSettings& configParserSettings) { auto configPath = FindEngineRoot(registry) / filePath; - IO::SystemFile configFile; - if (!configFile.Open(configPath.c_str(), IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY)) + IO::FileReader configFile; + bool configFileOpened{}; + switch (configParserSettings.m_fileReaderClass) + { + case ConfigParserSettings::FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile: + { + auto fileIo = AZ::IO::FileIOBase::GetInstance(); + configFileOpened = configFile.Open(fileIo, configPath.c_str()); + break; + } + case ConfigParserSettings::FileReaderClass::UseSystemFileOnly: + { + configFileOpened = configFile.Open(nullptr, configPath.c_str()); + break; + } + case ConfigParserSettings::FileReaderClass::UseFileIOOnly: + { + auto fileIo = AZ::IO::FileIOBase::GetInstance(); + if (fileIo == nullptr) + { + return false; + } + configFileOpened = configFile.Open(fileIo, configPath.c_str()); + break; + } + default: + AZ_Error("SettingsRegistryMergeUtils", false, "An Invalid FileReaderClass enum value has been supplied"); + return false; + } + if (!configFileOpened) { AZ_Warning("SettingsRegistryMergeUtils", false, R"(Unable to open file "%s")", configPath.c_str()); return false; @@ -478,7 +510,7 @@ namespace AZ::SettingsRegistryMergeUtils AZ_Error("SettingsRegistryMergeUtils", false, R"(The config file "%s" contains a line which is longer than the max line length of %zu.)" "\n" R"(Parsing will halt. The line content so far is:)" "\n" - R"("%.*s")" "\n", configFile.Name(), configBuffer.max_size(), + R"("%.*s")" "\n", configPath.c_str(), configBuffer.max_size(), aznumeric_cast(configBuffer.size()), configBuffer.data()); configFileParsed = false; break; @@ -761,6 +793,7 @@ namespace AZ::SettingsRegistryMergeUtils return SettingsRegistryInterface::VisitResponse::Continue; } + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view, [[maybe_unused]] AZStd::string_view valueName, SettingsRegistryInterface::Type, AZStd::string_view value) override { if (processingSourcePathKey) @@ -896,6 +929,7 @@ namespace AZ::SettingsRegistryMergeUtils struct CommandLineVisitor : AZ::SettingsRegistryInterface::Visitor { + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type , AZStd::string_view value) override { diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h index 02346c2ba1..daa64c0343 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h @@ -155,6 +155,15 @@ namespace AZ::SettingsRegistryMergeUtils //! structure which is forwarded to the SettingsRegistryInterface MergeCommandLineArgument function //! The structure contains a functor which returns true if a character is a valid delimiter SettingsRegistryInterface::CommandLineArgumentSettings m_commandLineSettings; + + //! enumeration to indicate if AZ::IO::FileIOBase should be used to open the config file over AZ::IO::SystemFile + enum class FileReaderClass + { + UseFileIOIfAvailableFallbackToSystemFile, + UseSystemFileOnly, + UseFileIOOnly + }; + FileReaderClass m_fileReaderClass = FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile; }; //! Loads basic configuration files which have structures similar to Windows INI files //! It is inspired by the Python configparser module: https://docs.python.org/3.10/library/configparser.html diff --git a/Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockSettingsRegistry.h b/Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockSettingsRegistry.h index 7e6bb3d7b4..91dc0924c8 100644 --- a/Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockSettingsRegistry.h +++ b/Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockSettingsRegistry.h @@ -57,6 +57,7 @@ namespace AZ MOCK_METHOD1(SetApplyPatchSettings, void(const JsonApplyPatchSettings&)); MOCK_METHOD1(GetApplyPatchSettings, void(JsonApplyPatchSettings&)); + MOCK_METHOD1(SetUseFileIO, void(bool)); }; } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/UnitTest/TestTypes.h b/Code/Framework/AzCore/AzCore/UnitTest/TestTypes.h index d9e3dfad1e..e5dcb32d9d 100644 --- a/Code/Framework/AzCore/AzCore/UnitTest/TestTypes.h +++ b/Code/Framework/AzCore/AzCore/UnitTest/TestTypes.h @@ -131,17 +131,23 @@ namespace UnitTest , public AllocatorsBase { public: - // Bring in both const and non-const SetUp and TearDown function into scope to resolve warning 4266 - // no override available for virtual member function from base 'benchmark::Fixture'; function is hidden - using ::benchmark::Fixture::SetUp, ::benchmark::Fixture::TearDown; - //Benchmark interface + void SetUp(const ::benchmark::State& st) override + { + AZ_UNUSED(st); + SetupAllocator(); + } void SetUp(::benchmark::State& st) override { AZ_UNUSED(st); SetupAllocator(); } + void TearDown(const ::benchmark::State& st) override + { + AZ_UNUSED(st); + TeardownAllocator(); + } void TearDown(::benchmark::State& st) override { AZ_UNUSED(st); diff --git a/Code/Framework/AzCore/AzCore/UserSettings/UserSettingsProvider.h b/Code/Framework/AzCore/AzCore/UserSettings/UserSettingsProvider.h index 4721d3baa8..6a807c915e 100644 --- a/Code/Framework/AzCore/AzCore/UserSettings/UserSettingsProvider.h +++ b/Code/Framework/AzCore/AzCore/UserSettings/UserSettingsProvider.h @@ -116,9 +116,9 @@ namespace AZ ////////////////////////////////////////////////////////////////////////// // UserSettingsBus - virtual AZStd::intrusive_ptr FindUserSettings(u32 id); - virtual void AddUserSettings(u32 id, UserSettings* settings); - virtual bool Save(const char* settingsPath, SerializeContext* sc); + AZStd::intrusive_ptr FindUserSettings(u32 id) override; + void AddUserSettings(u32 id, UserSettings* settings) override; + bool Save(const char* settingsPath, SerializeContext* sc) override; ////////////////////////////////////////////////////////////////////////// static void Reflect(ReflectContext* reflection); diff --git a/Code/Framework/AzCore/AzCore/XML/rapidxml.h b/Code/Framework/AzCore/AzCore/XML/rapidxml.h index 694e0a1bf6..9e6d648293 100644 --- a/Code/Framework/AzCore/AzCore/XML/rapidxml.h +++ b/Code/Framework/AzCore/AzCore/XML/rapidxml.h @@ -13,6 +13,7 @@ // the intention is that you only include the customized version of rapidXML through this header, so that // you can override behavior here. +#include #include #endif // AZCORE_RAPIDXML_RAPIDXML_H_INCLUDED diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 6c498c3335..aa07959997 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -166,6 +166,8 @@ set(FILES IO/FileIO.cpp IO/FileIO.h IO/FileIOEventBus.h + IO/FileReader.cpp + IO/FileReader.h IO/IOUtils.h IO/IOUtils.cpp IO/IStreamer.h diff --git a/Code/Framework/AzCore/AzCore/std/algorithm.h b/Code/Framework/AzCore/AzCore/std/algorithm.h index 3508eff2b4..e28d127062 100644 --- a/Code/Framework/AzCore/AzCore/std/algorithm.h +++ b/Code/Framework/AzCore/AzCore/std/algorithm.h @@ -831,7 +831,6 @@ namespace AZStd // find first element that value is before, using operator< typename iterator_traits::difference_type count = AZStd::distance(first, last); typename iterator_traits::difference_type step{}; - count = AZStd::distance(first, last); for (; 0 < count; ) { // divide and conquer, find half that contains answer step = count / 2; diff --git a/Code/Framework/AzCore/AzCore/std/parallel/thread.h b/Code/Framework/AzCore/AzCore/std/parallel/thread.h index 9f7830c91b..15d8c9dc8e 100644 --- a/Code/Framework/AzCore/AzCore/std/parallel/thread.h +++ b/Code/Framework/AzCore/AzCore/std/parallel/thread.h @@ -187,7 +187,7 @@ namespace AZStd : m_f(AZStd::move(f)) {} thread_info_impl(Internal::thread_move_t f) : m_f(f) {} - virtual void execute() { m_f(); } + void execute() override { m_f(); } private: F m_f; diff --git a/Code/Framework/AzCore/AzCore/std/smart_ptr/shared_count.h b/Code/Framework/AzCore/AzCore/std/smart_ptr/shared_count.h index c86adbd69e..ac49dc9ae5 100644 --- a/Code/Framework/AzCore/AzCore/std/smart_ptr/shared_count.h +++ b/Code/Framework/AzCore/AzCore/std/smart_ptr/shared_count.h @@ -129,16 +129,16 @@ namespace AZStd { } - virtual void dispose() // nothrow + void dispose() override // nothrow { AZStd::checked_delete(px_); } - virtual void destroy() // nothrow + void destroy() override // nothrow { this->~this_type(); a_.deallocate(this, sizeof(this_type), AZStd::alignment_of::value); } - virtual void* get_deleter(Internal::sp_typeinfo const&) + void* get_deleter(Internal::sp_typeinfo const&) override { return 0; } @@ -176,18 +176,18 @@ namespace AZStd { } - virtual void dispose() // nothrow + void dispose() override // nothrow { d_(p_); } - virtual void destroy() // nothrow + void destroy() override // nothrow { this->~this_type(); a_.deallocate(this, sizeof(this_type), AZStd::alignment_of::value); } - virtual void* get_deleter(Internal::sp_typeinfo const& ti) + void* get_deleter(Internal::sp_typeinfo const& ti) override { return ti == aztypeid(D) ? &reinterpret_cast(d_) : 0; } diff --git a/Code/Framework/AzCore/AzCore/std/string/fixed_string.h b/Code/Framework/AzCore/AzCore/std/string/fixed_string.h index 079fe40cda..b68f21784b 100644 --- a/Code/Framework/AzCore/AzCore/std/string/fixed_string.h +++ b/Code/Framework/AzCore/AzCore/std/string/fixed_string.h @@ -96,6 +96,10 @@ namespace AZStd && !is_convertible_v>> constexpr basic_fixed_string(const T& convertibleToView, size_type rhsOffset, size_type count); + + // #12 + constexpr basic_fixed_string(AZStd::nullptr_t) = delete; + constexpr operator AZStd::basic_string_view() const; constexpr auto begin() -> iterator; @@ -120,6 +124,7 @@ namespace AZStd constexpr auto operator=(const T& convertible_to_view) -> AZStd::enable_if_t> && !is_convertible_v, basic_fixed_string&>; + constexpr auto operator=(AZStd::nullptr_t) -> basic_fixed_string& = delete; constexpr auto operator+=(const basic_fixed_string& rhs) -> basic_fixed_string&; constexpr auto operator+=(const_pointer ptr) -> basic_fixed_string&; diff --git a/Code/Framework/AzCore/AzCore/std/string/regex.h b/Code/Framework/AzCore/AzCore/std/string/regex.h index 2ca223937b..3d4a2a0b38 100644 --- a/Code/Framework/AzCore/AzCore/std/string/regex.h +++ b/Code/Framework/AzCore/AzCore/std/string/regex.h @@ -215,6 +215,7 @@ namespace AZStd struct ErrorSink { + virtual ~ErrorSink() = default; virtual void RegexError(regex_constants::error_type code) = 0; }; } @@ -1079,7 +1080,7 @@ namespace AZStd NodeBase* m_next; NodeBase* m_previous; - virtual ~NodeBase() { } + virtual ~NodeBase() = default; }; inline void DestroyNode(NodeBase* node, NodeBase* end = nullptr) @@ -1758,7 +1759,7 @@ namespace AZStd return (*this); } - ~basic_regex() + ~basic_regex() override { // destroy the object Clear(); } @@ -2916,7 +2917,7 @@ namespace AZStd } template - inline NodeBase* Builder::BeginGroup(void) + inline NodeBase* Builder::BeginGroup() { // add group node return (NewNode(NT_group)); } @@ -3026,7 +3027,7 @@ namespace AZStd } template - inline RootNode* Builder::EndPattern(void) + inline RootNode* Builder::EndPattern() { // wrap up NewNode(NT_end); return m_root; diff --git a/Code/Framework/AzCore/AzCore/std/string/string.h b/Code/Framework/AzCore/AzCore/std/string/string.h index c02c6805a1..9ef7ea3086 100644 --- a/Code/Framework/AzCore/AzCore/std/string/string.h +++ b/Code/Framework/AzCore/AzCore/std/string/string.h @@ -168,6 +168,9 @@ namespace AZStd { } + // C++23 overload to prevent initializing a string_view via a nullptr or integer type + constexpr basic_string(AZStd::nullptr_t) = delete; + inline ~basic_string() { // destroy the string @@ -197,6 +200,7 @@ namespace AZStd inline this_type& operator=(AZStd::basic_string_view view) { return assign(view); } inline this_type& operator=(const_pointer ptr) { return assign(ptr); } inline this_type& operator=(Element ch) { return assign(1, ch); } + inline this_type& operator=(AZStd::nullptr_t) = delete; inline this_type& operator+=(const this_type& rhs) { return append(rhs); } inline this_type& operator+=(const_pointer ptr) { return append(ptr); } inline this_type& operator+=(Element ch) { return append(1, ch); } diff --git a/Code/Framework/AzCore/AzCore/std/string/string_view.h b/Code/Framework/AzCore/AzCore/std/string/string_view.h index b2390c293a..30e61f95ce 100644 --- a/Code/Framework/AzCore/AzCore/std/string/string_view.h +++ b/Code/Framework/AzCore/AzCore/std/string/string_view.h @@ -502,6 +502,9 @@ namespace AZStd swap(other); } + // C++23 overload to prevent initializing a string_view via a nullptr or integer type + constexpr basic_string_view(AZStd::nullptr_t) = delete; + constexpr const_reference operator[](size_type index) const { return data()[index]; } /// Returns value, not reference. If index is out of bounds, 0 is returned (can't be reference). constexpr value_type at(size_type index) const diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Module/DynamicModuleHandle_UnixLike.cpp b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Module/DynamicModuleHandle_UnixLike.cpp index 5cf854d49f..1ca6a7c4c7 100644 --- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Module/DynamicModuleHandle_UnixLike.cpp +++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Module/DynamicModuleHandle_UnixLike.cpp @@ -72,6 +72,7 @@ namespace AZ { if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { + bool fileFound = false; if (AZ::IO::FixedMaxPath projectModulePath; settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath)) { @@ -79,6 +80,23 @@ namespace AZ if (AZ::IO::SystemFile::Exists(projectModulePath.c_str())) { m_fileName.assign(projectModulePath.c_str(), projectModulePath.Native().size()); + fileFound = true; + } + } + if (!fileFound) + { + if (AZ::IO::FixedMaxPath installedBinariesPath; + settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) + { + if (AZ::IO::FixedMaxPath engineRootFolder; + settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder)) + { + installedBinariesPath = engineRootFolder / installedBinariesPath / fullFilePath; + if (AZ::IO::SystemFile::Exists(installedBinariesPath.c_str())) + { + m_fileName.assign(installedBinariesPath.c_str(), installedBinariesPath.Native().size()); + } + } } } } diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp index 9aff67a00f..2e31936057 100644 --- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp +++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp @@ -9,6 +9,7 @@ #include #include +#include namespace AZ { @@ -39,6 +40,14 @@ namespace AZ AZ::IO::FixedMaxPath path{homePath}; return path.Native(); } + + struct passwd* pass = getpwuid(getuid()); + if (pass) + { + AZ::IO::FixedMaxPath path{pass->pw_dir}; + return path.Native(); + } + return {}; } diff --git a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Memory/OverrunDetectionAllocator_WinAPI.h b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Memory/OverrunDetectionAllocator_WinAPI.h index 19f83374e7..b5cdc11d5d 100644 --- a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Memory/OverrunDetectionAllocator_WinAPI.h +++ b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Memory/OverrunDetectionAllocator_WinAPI.h @@ -21,7 +21,7 @@ namespace AZ class WinAPIOverrunDetectionSchema : public OverrunDetectionSchema::PlatformAllocator { public: - virtual SystemInformation GetSystemInformation() override + SystemInformation GetSystemInformation() override { SystemInformation result; SYSTEM_INFO info; @@ -32,7 +32,7 @@ namespace AZ return result; } - virtual void* ReserveBytes(size_t amount) override + void* ReserveBytes(size_t amount) override { void* result = VirtualAlloc(0, amount, MEM_RESERVE, PAGE_NOACCESS); @@ -45,12 +45,12 @@ namespace AZ return result; } - virtual void ReleaseReservedBytes(void* base) override + void ReleaseReservedBytes(void* base) override { VirtualFree(base, 0, MEM_RELEASE); } - virtual void* CommitBytes(void* base, size_t amount) override + void* CommitBytes(void* base, size_t amount) override { void* result = VirtualAlloc(base, amount, MEM_COMMIT, PAGE_READWRITE); @@ -63,7 +63,7 @@ namespace AZ return result; } - virtual void DecommitBytes(void* base, size_t amount) override + void DecommitBytes(void* base, size_t amount) override { VirtualFree(base, amount, MEM_DECOMMIT); } diff --git a/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StreamerConfiguration_Windows.cpp b/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StreamerConfiguration_Windows.cpp index fe6d83b7a4..0a73da5a92 100644 --- a/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StreamerConfiguration_Windows.cpp +++ b/Code/Framework/AzCore/Platform/Windows/AzCore/IO/Streamer/StreamerConfiguration_Windows.cpp @@ -267,6 +267,7 @@ namespace AZ::IO SettingsRegistryInterface::VisitResponse::Continue : SettingsRegistryInterface::VisitResponse::Skip; } + using SettingsRegistryInterface::Visitor::Visit; void Visit([[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName, [[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override { diff --git a/Code/Framework/AzCore/Tests/AZStd/String.cpp b/Code/Framework/AzCore/Tests/AZStd/String.cpp index 91d2237ca2..68bdd311d5 100644 --- a/Code/Framework/AzCore/Tests/AZStd/String.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/String.cpp @@ -1210,9 +1210,6 @@ namespace UnitTest AZStd::string findStr("Hay"); string_view view3(findStr); - string_view nullptrView4(nullptr); - - EXPECT_EQ(emptyView1, nullptrView4); // copy const size_t destBufferSize = 32; @@ -1264,9 +1261,6 @@ namespace UnitTest AZStd::size_t rfindResult = view3.rfind('a', 2); EXPECT_EQ(1, rfindResult); - rfindResult = nullptrView4.rfind(""); - EXPECT_EQ(string_view::npos, rfindResult); - rfindResult = emptyView1.rfind(""); EXPECT_EQ(string_view::npos, rfindResult); @@ -1373,17 +1367,11 @@ namespace UnitTest { string_view view1("The quick brown fox jumped over the lazy dog"); string_view view2("Needle in Haystack"); - string_view nullBeaverView(nullptr); string_view emptyBeaverView; string_view superEmptyBeaverView(""); - EXPECT_EQ(nullBeaverView, emptyBeaverView); - EXPECT_EQ(superEmptyBeaverView, nullBeaverView); - EXPECT_EQ(emptyBeaverView, superEmptyBeaverView); - EXPECT_EQ(nullBeaverView, ""); - EXPECT_EQ(nullBeaverView, nullptr); EXPECT_EQ("", emptyBeaverView); - EXPECT_EQ(nullptr, superEmptyBeaverView); + EXPECT_EQ("", superEmptyBeaverView); EXPECT_EQ("The quick brown fox jumped over the lazy dog", view1); EXPECT_NE("The slow brown fox jumped over the lazy dog", view1); @@ -1421,8 +1409,6 @@ namespace UnitTest EXPECT_LE(beaverView, "Busy Beaver"); EXPECT_LE("Likable Beaver", notBeaverView); EXPECT_LE("Busy Beaver", beaverView); - EXPECT_LE(nullBeaverView, nullBeaverView); - EXPECT_LE(nullBeaverView, lowerBeaverStr); EXPECT_LE(microBeaverStr, view1); EXPECT_LE(compareStr, beaverView); diff --git a/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp b/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp index 2ca564681c..e968ee28fc 100644 --- a/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp @@ -100,6 +100,7 @@ namespace JsonSerializationTests AZ::AllocatorInstance::Destroy(); } + using JsonSerializerConformityTestDescriptor>::Reflect; void Reflect(AZStd::unique_ptr& context) override { context->RegisterGenericType(); diff --git a/Code/Framework/AzCore/Tests/IO/FileReaderTests.cpp b/Code/Framework/AzCore/Tests/IO/FileReaderTests.cpp new file mode 100644 index 0000000000..691b3f2821 --- /dev/null +++ b/Code/Framework/AzCore/Tests/IO/FileReaderTests.cpp @@ -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 + * + */ +#include +#include +#include + +namespace UnitTest +{ + template + class FileReaderTestFixture + : public ScopedAllocatorSetupFixture + { + public: + void SetUp() override + { + if constexpr (AZStd::is_same_v) + { + m_fileIo = AZStd::make_unique(); + } + } + + void TearDown() override + { + m_fileIo.reset(); + } + + protected: + AZStd::unique_ptr m_fileIo{}; + }; + + using FileIOTypes = ::testing::Types; + + TYPED_TEST_CASE(FileReaderTestFixture, FileIOTypes); + + TYPED_TEST(FileReaderTestFixture, ConstructorWithFilePath_OpensFileSuccessfully) + { + AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename()); + EXPECT_TRUE(fileReader.IsOpen()); + } + + TYPED_TEST(FileReaderTestFixture, Open_OpensFileSucessfully) + { + AZ::IO::FileReader fileReader; + fileReader.Open(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename()); + EXPECT_TRUE(fileReader.IsOpen()); + } + + TYPED_TEST(FileReaderTestFixture, Eof_OnNULDeviceFile_Succeeds) + { + AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename()); + EXPECT_TRUE(fileReader.Eof()); + } + + TYPED_TEST(FileReaderTestFixture, GetFilePath_ReturnsNULDeviceFilename_Succeeds) + { + AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename()); + AZ::IO::FixedMaxPath filePath; + EXPECT_TRUE(fileReader.GetFilePath(filePath)); + AZ::IO::FixedMaxPath nulFilename{ AZ::IO::SystemFile::GetNullFilename() }; + if (this->m_fileIo) + { + EXPECT_TRUE(this->m_fileIo->ResolvePath(nulFilename, nulFilename)); + } + EXPECT_EQ(nulFilename, filePath); + } + +} // namespace UnitTest diff --git a/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp b/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp index cf212aa34c..34076a10f6 100644 --- a/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp +++ b/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp @@ -956,18 +956,8 @@ AZ_POP_DISABLE_WARNING namespace Benchmark { class PathBenchmarkFixture - : public ::benchmark::Fixture - , public ::UnitTest::AllocatorsBase + : public ::UnitTest::AllocatorsBenchmarkFixture { - public: - void SetUp([[maybe_unused]] const ::benchmark::State& state) override - { - ::UnitTest::AllocatorsBase::SetupAllocator(); - } - void TearDown([[maybe_unused]] const ::benchmark::State& state) override - { - ::UnitTest::AllocatorsBase::TeardownAllocator(); - } protected: AZStd::fixed_vector m_appendPaths{ "foo", "bar", "baz", "bazzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", "boo/bar/base", "C:\\path\\to\\O3DE", "C", "\\\\", "/", R"(test\\path/with\mixed\separators)" }; diff --git a/Code/Framework/AzCore/Tests/Jobs.cpp b/Code/Framework/AzCore/Tests/Jobs.cpp index b63e139dd0..041f4970d1 100644 --- a/Code/Framework/AzCore/Tests/Jobs.cpp +++ b/Code/Framework/AzCore/Tests/Jobs.cpp @@ -1704,7 +1704,7 @@ namespace Benchmark static const AZ::u32 MEDIUM_NUMBER_OF_JOBS = 1024; static const AZ::u32 LARGE_NUMBER_OF_JOBS = 16384; - void SetUp([[maybe_unused]] ::benchmark::State& state) override + void internalSetUp() { AllocatorInstance::Create(); AllocatorInstance::Create(); @@ -1749,8 +1749,16 @@ namespace Benchmark return randomDepthDistribution(randomDepthGenerator); }); } + void SetUp(::benchmark::State&) override + { + internalSetUp(); + } + void SetUp(const ::benchmark::State&) override + { + internalSetUp(); + } - void TearDown([[maybe_unused]] ::benchmark::State& state) override + void internalTearDown() { JobContext::SetGlobalContext(nullptr); @@ -1763,6 +1771,14 @@ namespace Benchmark AllocatorInstance::Destroy(); AllocatorInstance::Destroy(); } + void TearDown(::benchmark::State&) override + { + internalTearDown(); + } + void TearDown(const ::benchmark::State&) override + { + internalTearDown(); + } protected: inline void RunCalculatePiJob(AZ::s32 depth, AZ::s8 priority) diff --git a/Code/Framework/AzCore/Tests/Math/FrustumPerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/FrustumPerformanceTests.cpp index b132c9f726..e26d896e5a 100644 --- a/Code/Framework/AzCore/Tests/Math/FrustumPerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/FrustumPerformanceTests.cpp @@ -19,8 +19,7 @@ namespace Benchmark class BM_MathFrustum : public benchmark::Fixture { - public: - void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { m_testFrustum = AZ::Frustum(AZ::ViewFrustumAttributes(AZ::Transform::CreateIdentity(), 1.0f, 2.0f * atanf(0.5f), 10.0f, 90.0f)); @@ -40,6 +39,15 @@ namespace Benchmark return data; }); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } struct Data { diff --git a/Code/Framework/AzCore/Tests/Math/Matrix3x3PerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/Matrix3x3PerformanceTests.cpp index f345f26d06..918673d475 100644 --- a/Code/Framework/AzCore/Tests/Math/Matrix3x3PerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Matrix3x3PerformanceTests.cpp @@ -23,8 +23,7 @@ namespace Benchmark class BM_MathMatrix3x3 : public benchmark::Fixture { - public: - void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { m_testDataArray.resize(1000); @@ -44,6 +43,15 @@ namespace Benchmark return testData; }); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } struct TestData { diff --git a/Code/Framework/AzCore/Tests/Math/Matrix3x4PerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/Matrix3x4PerformanceTests.cpp index 9aed29005d..63ddefdd2c 100644 --- a/Code/Framework/AzCore/Tests/Math/Matrix3x4PerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Matrix3x4PerformanceTests.cpp @@ -21,8 +21,7 @@ namespace Benchmark class BM_MathMatrix3x4 : public benchmark::Fixture { - public: - void SetUp([[maybe_unused]] const::benchmark::State& state) override + void internalSetUp() { m_testDataArray.resize(1000); @@ -58,6 +57,15 @@ namespace Benchmark return testData; }); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } struct TestData { diff --git a/Code/Framework/AzCore/Tests/Math/Matrix4x4PerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/Matrix4x4PerformanceTests.cpp index 90865064c8..21f440c3a1 100644 --- a/Code/Framework/AzCore/Tests/Math/Matrix4x4PerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Matrix4x4PerformanceTests.cpp @@ -20,8 +20,7 @@ namespace Benchmark class BM_MathMatrix4x4 : public benchmark::Fixture { - public: - void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { m_testDataArray.resize(1000); @@ -41,6 +40,15 @@ namespace Benchmark return testData; }); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } struct TestData { diff --git a/Code/Framework/AzCore/Tests/Math/ObbPerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/ObbPerformanceTests.cpp index 8463758fa5..d1e8ac2225 100644 --- a/Code/Framework/AzCore/Tests/Math/ObbPerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/ObbPerformanceTests.cpp @@ -19,8 +19,7 @@ namespace Benchmark class BM_MathObb : public benchmark::Fixture { - public: - void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { m_position.Set(1.0f, 2.0f, 3.0f); m_rotation = AZ::Quaternion::CreateRotationZ(AZ::Constants::QuarterPi); @@ -28,6 +27,16 @@ namespace Benchmark m_obb = AZ::Obb::CreateFromPositionRotationAndHalfLengths(m_position, m_rotation, m_halfLengths); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } + AZ::Obb m_obb; AZ::Vector3 m_position; AZ::Quaternion m_rotation; diff --git a/Code/Framework/AzCore/Tests/Math/PlanePerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/PlanePerformanceTests.cpp index c23ded582c..e066207635 100644 --- a/Code/Framework/AzCore/Tests/Math/PlanePerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/PlanePerformanceTests.cpp @@ -18,14 +18,7 @@ namespace Benchmark class BM_MathPlane : public benchmark::Fixture { - public: - BM_MathPlane() - { - const unsigned int seed = 1; - rng = std::mt19937_64(seed); - } - - void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { for (int i = 0; i < m_numIters; ++i) { @@ -39,7 +32,7 @@ namespace Benchmark m_distance = unif(rng); m_dists.push_back(m_distance); - //set these differently so they don't overlap with same values as other vectors + // set these differently so they don't overlap with same values as other vectors m_normal = AZ::Vector3(unif(rng), unif(rng), unif(rng)); m_normal.Normalize(); m_distance = unif(rng); @@ -47,6 +40,21 @@ namespace Benchmark m_planes.push_back(m_plane); } } + public: + BM_MathPlane() + { + const unsigned int seed = 1; + rng = std::mt19937_64(seed); + } + + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } AZ::Plane m_plane; AZ::Vector3 m_normal; diff --git a/Code/Framework/AzCore/Tests/Math/QuaternionPerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/QuaternionPerformanceTests.cpp index 6f5a23d027..486a33ba67 100644 --- a/Code/Framework/AzCore/Tests/Math/QuaternionPerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/QuaternionPerformanceTests.cpp @@ -17,8 +17,7 @@ namespace Benchmark class BM_MathQuaternion : public benchmark::Fixture { - public: - void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { m_quatDataArray.resize(1000); @@ -42,6 +41,15 @@ namespace Benchmark return quatData; }); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } struct QuatData { diff --git a/Code/Framework/AzCore/Tests/Math/ShapeIntersectionPerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/ShapeIntersectionPerformanceTests.cpp index b088330302..ecab1717b2 100644 --- a/Code/Framework/AzCore/Tests/Math/ShapeIntersectionPerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/ShapeIntersectionPerformanceTests.cpp @@ -35,8 +35,7 @@ namespace Benchmark class BM_MathShapeIntersection : public benchmark::Fixture { - public: - void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { m_testDataArray.resize(1000); @@ -58,6 +57,15 @@ namespace Benchmark return testData; }); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } struct TestData { diff --git a/Code/Framework/AzCore/Tests/Math/TransformPerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/TransformPerformanceTests.cpp index 193535c020..dde0192ef9 100644 --- a/Code/Framework/AzCore/Tests/Math/TransformPerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/TransformPerformanceTests.cpp @@ -22,8 +22,7 @@ namespace Benchmark class BM_MathTransform : public benchmark::Fixture { - public: - void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { m_testDataArray.resize(1000); @@ -51,6 +50,15 @@ namespace Benchmark return testData; }); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } struct TestData { diff --git a/Code/Framework/AzCore/Tests/Math/Vector2PerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/Vector2PerformanceTests.cpp index e8506890aa..3ab306ff66 100644 --- a/Code/Framework/AzCore/Tests/Math/Vector2PerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Vector2PerformanceTests.cpp @@ -19,8 +19,7 @@ namespace Benchmark class BM_MathVector2 : public benchmark::Fixture { - public: - void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { m_vecDataArray.resize(1000); @@ -37,6 +36,15 @@ namespace Benchmark return vecData; }); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } struct VecData { diff --git a/Code/Framework/AzCore/Tests/Math/Vector3PerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/Vector3PerformanceTests.cpp index 27fa01ae95..5f33730bca 100644 --- a/Code/Framework/AzCore/Tests/Math/Vector3PerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Vector3PerformanceTests.cpp @@ -19,8 +19,7 @@ namespace Benchmark class BM_MathVector3 : public benchmark::Fixture { - public: - void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { m_vecDataArray.resize(1000); @@ -37,6 +36,15 @@ namespace Benchmark return vecData; }); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } struct VecData { diff --git a/Code/Framework/AzCore/Tests/Math/Vector4PerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/Vector4PerformanceTests.cpp index 4a0bcb49d2..f12851b1ed 100644 --- a/Code/Framework/AzCore/Tests/Math/Vector4PerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Vector4PerformanceTests.cpp @@ -19,8 +19,7 @@ namespace Benchmark class BM_MathVector4 : public benchmark::Fixture { - public: - void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { m_vecDataArray.resize(1000); @@ -38,6 +37,15 @@ namespace Benchmark return vecData; }); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } struct VecData { diff --git a/Code/Framework/AzCore/Tests/Memory/HphaSchema.cpp b/Code/Framework/AzCore/Tests/Memory/HphaSchema.cpp index aaf4ce1811..85dd79931d 100644 --- a/Code/Framework/AzCore/Tests/Memory/HphaSchema.cpp +++ b/Code/Framework/AzCore/Tests/Memory/HphaSchema.cpp @@ -120,19 +120,34 @@ namespace Benchmark class HphaSchemaBenchmarkFixture : public ::benchmark::Fixture { - public: - void SetUp(const ::benchmark::State& state) override + void internalSetUp() { - AZ_UNUSED(state); AZ::AllocatorInstance::Create(); } - void TearDown(const ::benchmark::State& state) override + void internalTearDown() { - AZ_UNUSED(state); AZ::AllocatorInstance::Destroy(); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } + void TearDown(const benchmark::State&) override + { + internalTearDown(); + } + void TearDown(benchmark::State&) override + { + internalTearDown(); + } + static void BM_Allocations(benchmark::State& state, const AllocationSizeArray& allocationArray) { AZStd::vector allocations; diff --git a/Code/Framework/AzCore/Tests/Name/NameTests.cpp b/Code/Framework/AzCore/Tests/Name/NameTests.cpp index 5417d36051..eb0a048e2f 100644 --- a/Code/Framework/AzCore/Tests/Name/NameTests.cpp +++ b/Code/Framework/AzCore/Tests/Name/NameTests.cpp @@ -362,7 +362,7 @@ namespace UnitTest // Test specific construction case that was failing. // The constructor calls Name::SetName() which does a move assignment // Name& Name::operator=(Name&& rhs) was leaving m_view pointing to the m_data in a temporary Name object. - AZ::Name emptyName(AZStd::string_view(nullptr)); + AZ::Name emptyName(AZStd::string_view{}); EXPECT_TRUE(emptyName.IsEmpty()); EXPECT_EQ(0, emptyName.GetStringView().data()[0]); } 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 6d572a02a3..22fb6379d2 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 @@ -1155,6 +1155,23 @@ namespace Benchmark { class StorageDriveWindowsFixture : public benchmark::Fixture { + void internalTearDown() + { + using namespace AZ::IO; + + AZStd::string temp; + m_absolutePath.swap(temp); + + delete m_streamer; + m_streamer = nullptr; + + SystemFile::Delete(TestFileName); + + AZ::IO::FileIOBase::SetInstance(nullptr); + AZ::IO::FileIOBase::SetInstance(m_previousFileIO); + delete m_fileIO; + m_fileIO = nullptr; + } public: constexpr static const char* TestFileName = "StreamerBenchmark.bin"; constexpr static size_t FileSize = 64_mib; @@ -1197,20 +1214,13 @@ namespace Benchmark } } - void TearDown([[maybe_unused]] const ::benchmark::State& state) override + void TearDown(const benchmark::State&) override { - using namespace AZ::IO; - - AZStd::string temp; - m_absolutePath.swap(temp); - - delete m_streamer; - - SystemFile::Delete(TestFileName); - - AZ::IO::FileIOBase::SetInstance(nullptr); - AZ::IO::FileIOBase::SetInstance(m_previousFileIO); - delete m_fileIO; + internalTearDown(); + } + void TearDown(benchmark::State&) override + { + internalTearDown(); } void RepeatedlyReadFile(benchmark::State& state) diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/ArraySerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/ArraySerializerTests.cpp index e410ba9ca4..8582453683 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/ArraySerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/ArraySerializerTests.cpp @@ -35,6 +35,7 @@ namespace JsonSerializationTests features.m_fixedSizeArray = true; } + using JsonSerializerConformityTestDescriptor::Reflect; void Reflect(AZStd::unique_ptr& context) override { context->RegisterGenericType(); @@ -243,6 +244,7 @@ namespace JsonSerializationTests ])"; } + using ArraySerializerTestDescriptionBase>::Reflect; void Reflect(AZStd::unique_ptr& context) override { Base::Reflect(context); @@ -299,6 +301,7 @@ namespace JsonSerializationTests AZ::JsonArraySerializer m_serializer; public: + using BaseJsonSerializerFixture::RegisterAdditional; void RegisterAdditional(AZStd::unique_ptr& context) override { context->RegisterGenericType(); diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/BasicContainerSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/BasicContainerSerializerTests.cpp index 3bc574c2ce..4a6a8e9e8a 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/BasicContainerSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/BasicContainerSerializerTests.cpp @@ -60,6 +60,7 @@ namespace JsonSerializationTests return "[188, 288, 388]"; } + using BasicContainerConformityTestDescriptor::Reflect; void Reflect(AZStd::unique_ptr& context) override { context->RegisterGenericType(); @@ -133,6 +134,7 @@ namespace JsonSerializationTests return "[188, 288, 388]"; } + using BasicContainerConformityTestDescriptor::Reflect; void Reflect(AZStd::unique_ptr& context) override { context->RegisterGenericType(); @@ -225,6 +227,7 @@ namespace JsonSerializationTests features.m_supportsPartialInitialization = true; } + using BasicContainerConformityTestDescriptor::Reflect; void Reflect(AZStd::unique_ptr& context) override { SimpleClass::Reflect(context, true); @@ -291,6 +294,7 @@ namespace JsonSerializationTests using Container = AZStd::vector; using BaseClassContainer = AZStd::vector>; + using JsonBasicContainerSerializerTests::RegisterAdditional; void RegisterAdditional(AZStd::unique_ptr& serializeContext) override { SimpleClass::Reflect(serializeContext, true); @@ -352,6 +356,7 @@ namespace JsonSerializationTests static constexpr size_t ContainerSize = 4; using Container = AZStd::fixed_vector; + using JsonBasicContainerSerializerTests::RegisterAdditional; void RegisterAdditional(AZStd::unique_ptr& serializeContext) override { serializeContext->RegisterGenericType(); @@ -387,6 +392,7 @@ namespace JsonSerializationTests public: using Set = AZStd::set; + using JsonBasicContainerSerializerTests::RegisterAdditional; void RegisterAdditional(AZStd::unique_ptr& serializeContext) override { serializeContext->RegisterGenericType(); diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/BoolSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/BoolSerializerTests.cpp index a670a2a6e1..e9bb404ba8 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/BoolSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/BoolSerializerTests.cpp @@ -83,6 +83,7 @@ namespace JsonSerializationTests BaseJsonSerializerFixture::TearDown(); } + using BaseJsonSerializerFixture::RegisterAdditional; void RegisterAdditional(AZStd::unique_ptr& serializeContext) override { serializeContext->Class() diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/DoubleSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/DoubleSerializerTests.cpp index 76aaae393d..53eb855a63 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/DoubleSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/DoubleSerializerTests.cpp @@ -95,6 +95,7 @@ namespace JsonSerializationTests BaseJsonSerializerFixture::TearDown(); } + using BaseJsonSerializerFixture::RegisterAdditional; void RegisterAdditional(AZStd::unique_ptr& serializeContext) override { serializeContext->Class() diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/MapSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/MapSerializerTests.cpp index 4979df04be..7ffe3ffee0 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/MapSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/MapSerializerTests.cpp @@ -44,6 +44,7 @@ namespace JsonSerializationTests features.m_supportsPartialInitialization = false; } + using JsonSerializerConformityTestDescriptor::Reflect; void Reflect(AZStd::unique_ptr& context) override { context->RegisterGenericType(); @@ -247,6 +248,7 @@ namespace JsonSerializationTests features.m_supportsPartialInitialization = true; } + using MapBaseTestDescription, Serializer>::Reflect; void Reflect(AZStd::unique_ptr& context) override { SimpleClass::Reflect(context, true); diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/SmartPointerSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/SmartPointerSerializerTests.cpp index 43dc1a85d9..d89d9bfb33 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/SmartPointerSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/SmartPointerSerializerTests.cpp @@ -33,6 +33,7 @@ namespace JsonSerializationTests return AZStd::make_shared(); } + using JsonSerializerConformityTestDescriptor::Reflect; void Reflect(AZStd::unique_ptr& context) override { context->RegisterGenericType(); @@ -102,6 +103,7 @@ namespace JsonSerializationTests return *lhs == *rhs; } + using Base::Reflect; void Reflect(AZStd::unique_ptr& context) override { SimpleClass::Reflect(context, true); @@ -176,6 +178,7 @@ namespace JsonSerializationTests features.m_supportsPartialInitialization = true; } + using SmartPointerBaseTestDescription>::Reflect; void Reflect(AZStd::unique_ptr& context) override { SimpleInheritence::Reflect(context, true); @@ -340,6 +343,7 @@ namespace JsonSerializationTests features.m_supportsPartialInitialization = true; } + using SmartPointerBaseTestDescription>::Reflect; void Reflect(AZStd::unique_ptr& context) override { MultipleInheritence::Reflect(context, true); @@ -513,7 +517,8 @@ namespace JsonSerializationTests public: using SmartPointer = typename SmartPointerSimpleDerivedClassTestDescription::SmartPointer; using InstanceSmartPointer = AZStd::shared_ptr; - + + using BaseJsonSerializerFixture::RegisterAdditional; void RegisterAdditional(AZStd::unique_ptr& context) override { m_description.Reflect(context); diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/TupleSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/TupleSerializerTests.cpp index ff0fbcc5a6..cfd844f3f5 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/TupleSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/TupleSerializerTests.cpp @@ -72,6 +72,7 @@ namespace JsonSerializationTests TupleSerializerTestsInternal::ConfigureFeatures(features); } + using JsonSerializerConformityTestDescriptor>::Reflect; void Reflect(AZStd::unique_ptr& context) override { context->Class()->Field("pair", &PairPlaceholder::m_pair); @@ -126,6 +127,7 @@ namespace JsonSerializationTests TupleSerializerTestsInternal::ConfigureFeatures(features); } + using JsonSerializerConformityTestDescriptor::Reflect; void Reflect(AZStd::unique_ptr& context) override { context->RegisterGenericType(); @@ -344,6 +346,7 @@ namespace JsonSerializationTests features.m_enableNewInstanceTests = false; } + using JsonSerializerConformityTestDescriptor::Reflect; void Reflect(AZStd::unique_ptr& context) override { context->Class() @@ -477,6 +480,7 @@ namespace JsonSerializationTests features.m_typeToInject = rapidjson::kNullType; } + using JsonSerializerConformityTestDescriptor::Reflect; void Reflect(AZStd::unique_ptr& context) override { context->RegisterGenericType(); @@ -535,6 +539,7 @@ namespace JsonSerializationTests BaseJsonSerializerFixture::TearDown(); } + using BaseJsonSerializerFixture::RegisterAdditional; void RegisterAdditional(AZStd::unique_ptr& serializeContext) override { SimpleClass::Reflect(serializeContext, true); diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/UnorderedSetSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/UnorderedSetSerializerTests.cpp index 17902b6900..f4a1f48dc5 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/UnorderedSetSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/UnorderedSetSerializerTests.cpp @@ -54,6 +54,7 @@ namespace JsonSerializationTests features.m_supportsPartialInitialization = false; } + using JsonSerializerConformityTestDescriptor>::Reflect; void Reflect(AZStd::unique_ptr& context) override { context->RegisterGenericType(); @@ -108,6 +109,7 @@ namespace JsonSerializationTests context->RegisterGenericType(); } + using JsonSerializerConformityTestDescriptor::Reflect; bool AreEqual(const MultiSet& lhs, const MultiSet& rhs) override { return @@ -139,6 +141,7 @@ namespace JsonSerializationTests BaseJsonSerializerFixture::TearDown(); } + using BaseJsonSerializerFixture::RegisterAdditional; void RegisterAdditional(AZStd::unique_ptr& serializeContext) override { serializeContext->RegisterGenericType(); diff --git a/Code/Framework/AzCore/Tests/SettingsRegistryTests.cpp b/Code/Framework/AzCore/Tests/SettingsRegistryTests.cpp index 61f93fb860..2da5701207 100644 --- a/Code/Framework/AzCore/Tests/SettingsRegistryTests.cpp +++ b/Code/Framework/AzCore/Tests/SettingsRegistryTests.cpp @@ -423,6 +423,8 @@ namespace SettingsRegistryTests struct : public AZ::SettingsRegistryInterface::Visitor { + using AZ::SettingsRegistryInterface::Visitor::Visit; + using ValueType [[maybe_unused]] = typename SettingsType::ValueType; void Visit([[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type type, ValueType value) override { @@ -452,6 +454,8 @@ namespace SettingsRegistryTests struct : public AZ::SettingsRegistryInterface::Visitor { + using AZ::SettingsRegistryInterface::Visitor::Visit; + using ValueType [[maybe_unused]] = typename SettingsType::ValueType; void Visit([[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type type, ValueType value) override { @@ -482,6 +486,7 @@ namespace SettingsRegistryTests struct : public AZ::SettingsRegistryInterface::Visitor { + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit([[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type type, AZ::s64 value) override { EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Integer, type); @@ -517,6 +522,8 @@ namespace SettingsRegistryTests EXPECT_TRUE(path.ends_with(valueName)); return AZ::SettingsRegistryInterface::VisitResponse::Continue; } + + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type , AZStd::string_view)override { EXPECT_TRUE(path.ends_with(valueName)); @@ -1510,7 +1517,7 @@ namespace SettingsRegistryTests m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR); *m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder; - bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, nullptr); + bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}); EXPECT_TRUE(result); EXPECT_EQ(4, counter); @@ -1552,7 +1559,7 @@ namespace SettingsRegistryTests m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR); *m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder; - bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, "Special", nullptr); + bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, "Special"); EXPECT_TRUE(result); EXPECT_EQ(6, counter); @@ -1591,7 +1598,7 @@ namespace SettingsRegistryTests m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR); *m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder; - bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, nullptr); + bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}); EXPECT_TRUE(result); EXPECT_EQ(4, counter); @@ -1632,7 +1639,7 @@ namespace SettingsRegistryTests m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR); *m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder; - bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, nullptr); + bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}); EXPECT_TRUE(result); EXPECT_EQ(4, counter); @@ -1665,7 +1672,7 @@ namespace SettingsRegistryTests m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR); *m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder; - bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, "Special", nullptr); + bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, "Special"); EXPECT_TRUE(result); EXPECT_EQ(1, counter); @@ -1715,7 +1722,7 @@ namespace SettingsRegistryTests TEST_F(SettingsRegistryTest, MergeSettingsFolder_EmptyFolder_ReportsSuccessButNothingAdded) { - bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, nullptr); + bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}); EXPECT_TRUE(result); EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0")); // Folder and specialization settings. @@ -1727,7 +1734,7 @@ namespace SettingsRegistryTests constexpr AZStd::fixed_string path(AZ::IO::MaxPathLength + 1, 'a'); AZ_TEST_START_TRACE_SUPPRESSION; - bool result = m_registry->MergeSettingsFolder(path, { "editor", "test" }, {}, nullptr); + bool result = m_registry->MergeSettingsFolder(path, { "editor", "test" }, {}); AZ_TEST_STOP_TRACE_SUPPRESSION(1); EXPECT_FALSE(result); @@ -1744,7 +1751,7 @@ namespace SettingsRegistryTests AZ_TEST_START_TRACE_SUPPRESSION; m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR); *m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder; - bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, nullptr); + bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}); EXPECT_GT(::UnitTest::TestRunner::Instance().StopAssertTests(), 0); EXPECT_FALSE(result); diff --git a/Code/Framework/AzCore/Tests/TaskTests.cpp b/Code/Framework/AzCore/Tests/TaskTests.cpp index f65dffcd99..e743ab6643 100644 --- a/Code/Framework/AzCore/Tests/TaskTests.cpp +++ b/Code/Framework/AzCore/Tests/TaskTests.cpp @@ -551,19 +551,37 @@ namespace Benchmark { class TaskGraphBenchmarkFixture : public ::benchmark::Fixture { - public: - void SetUp(benchmark::State&) override + void internalSetUp() { executor = new TaskExecutor; graph = new TaskGraph; } - void TearDown(benchmark::State&) override + void internalTearDown() { delete graph; delete executor; } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } + + void TearDown(const benchmark::State&) override + { + internalTearDown(); + } + void TearDown(benchmark::State&) override + { + internalTearDown(); + } + TaskDescriptor descriptors[4] = { { "critical", "benchmark", TaskPriority::CRITICAL }, { "high", "benchmark", TaskPriority::HIGH }, { "medium", "benchmark", TaskPriority::MEDIUM }, diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index d4d107f094..c36d37d874 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -37,6 +37,7 @@ set(FILES FileIOBaseTestTypes.h Geometry2DUtils.cpp Interface.cpp + IO/FileReaderTests.cpp IO/Path/PathTests.cpp IPC.cpp Jobs.cpp diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp index 958dba2cc9..12974d03cf 100644 --- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp +++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp @@ -81,71 +81,6 @@ namespace AzFramework static constexpr const char s_prefabSystemKey[] = "/Amazon/Preferences/EnablePrefabSystem"; static constexpr const char s_prefabWipSystemKey[] = "/Amazon/Preferences/EnablePrefabSystemWipFeatures"; static constexpr const char s_legacySlicesAssertKey[] = "/Amazon/Preferences/ShouldAssertForLegacySlicesUsage"; - - // A Helper function that can load an app descriptor from file. - AZ::Outcome, AZStd::string> LoadDescriptorFromFilePath(const char* appDescriptorFilePath, AZ::SerializeContext& serializeContext) - { - AZStd::unique_ptr loadedDescriptor; - - AZ::IO::SystemFile appDescriptorFile; - if (!appDescriptorFile.Open(appDescriptorFilePath, AZ::IO::SystemFile::SF_OPEN_READ_ONLY)) - { - return AZ::Failure(AZStd::string::format("Failed to open file: %s", appDescriptorFilePath)); - } - - AZ::IO::SystemFileStream appDescriptorFileStream(&appDescriptorFile, true); - if (!appDescriptorFileStream.IsOpen()) - { - return AZ::Failure(AZStd::string::format("Failed to stream file: %s", appDescriptorFilePath)); - } - - // Callback function for allocating the root elements in the file. - AZ::ObjectStream::InplaceLoadRootInfoCB inplaceLoadCb = - [](void** rootAddress, const AZ::SerializeContext::ClassData**, const AZ::Uuid& classId, AZ::SerializeContext*) - { - if (rootAddress && classId == azrtti_typeid()) - { - // ComponentApplication::Descriptor is normally a singleton. - // Force a unique instance to be created. - *rootAddress = aznew AZ::ComponentApplication::Descriptor(); - } - }; - - // Callback function for saving the root elements in the file. - AZ::ObjectStream::ClassReadyCB classReadyCb = - [&loadedDescriptor](void* classPtr, const AZ::Uuid& classId, AZ::SerializeContext* context) - { - // Save descriptor, delete anything else loaded from file. - if (classId == azrtti_typeid()) - { - loadedDescriptor.reset(static_cast(classPtr)); - } - else if (const AZ::SerializeContext::ClassData* classData = context->FindClassData(classId)) - { - classData->m_factory->Destroy(classPtr); - } - else - { - AZ_Error("Application", false, "Unexpected type %s found in application descriptor file. This memory will leak.", - classId.ToString().c_str()); - } - }; - - // There's other stuff in the file we may not recognize (system components), but we're not interested in that stuff. - AZ::ObjectStream::FilterDescriptor loadFilter(&AZ::Data::AssetFilterNoAssetLoading, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES); - - if (!AZ::ObjectStream::LoadBlocking(&appDescriptorFileStream, serializeContext, classReadyCb, loadFilter, inplaceLoadCb)) - { - return AZ::Failure(AZStd::string::format("Failed to load objects from file: %s", appDescriptorFilePath)); - } - - if (!loadedDescriptor) - { - return AZ::Failure(AZStd::string::format("Failed to find descriptor object in file: %s", appDescriptorFilePath)); - } - - return AZ::Success(AZStd::move(loadedDescriptor)); - } } Application::Application() diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp index 1891c50d10..d2a81102dc 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp @@ -40,8 +40,6 @@ #include -#include - namespace AZ::IO { AZ_CVAR(int, sys_PakPriority, aznumeric_cast(ArchiveVars{}.nPriority), nullptr, AZ::ConsoleFunctorFlags::Null, @@ -64,40 +62,6 @@ namespace AZ::IO::ArchiveInternal // to the actual index , this offset is added to get the valid handle static constexpr size_t PseudoFileIdxOffset = 1; - // Explanation of this function: it is like a 'find and replace' for paths - // if the source path starts with 'aliasToLookFor' it will replace it with 'aliasToReplaceWith' - // else it will leave it untouched. - // the only caveat here is that it will perform this replacement if the source path either begins - // with the literal alias to look for, or begins with the actual absolute path that the alias to - // look for represents. It is a way of redirecting all @devassets@ to @assets@ regardless of whether - // you input a string that literally starts with @devassets@ or one that starts with the absolute path to the - // folder that @devassets@ aliases. - AZStd::optional ConvertAbsolutePathToAliasedPath(AZStd::string_view sourcePath, - AZStd::string_view aliasToLookFor, AZStd::string_view aliasToReplaceWith) - { - if (auto fileIo = AZ::IO::FileIOBase::GetDirectInstance(); !aliasToLookFor.empty() && !aliasToReplaceWith.empty() && !sourcePath.empty() && fileIo) - { - auto convertedPath = fileIo->ConvertToAlias(sourcePath); - if (!convertedPath) - { - return AZStd::nullopt; - } - - if (convertedPath->Native().starts_with(aliasToLookFor)) - { - convertedPath->Native().replace(0, aliasToLookFor.size(), aliasToReplaceWith); - } - // lowercase path if it starts with either the @assets@ or @root@ alias - if (convertedPath->Native().starts_with("@assets@") || convertedPath->Native().starts_with("@root@") - || convertedPath->Native().starts_with("@projectplatformcache@")) - { - AZStd::to_lower(convertedPath->Native().begin(), convertedPath->Native().end()); - } - return convertedPath; - } - return AZStd::make_optional(sourcePath); - } - struct CCachedFileRawData { void* m_pCachedData; @@ -146,15 +110,12 @@ namespace AZ::IO::ArchiveInternal uint32_t GetFileSize() { return GetFile() ? GetFile()->GetFileEntry()->desc.lSizeUncompressed : 0; } int FSeek(uint64_t nOffset, int nMode); - size_t FRead(void* pDest, size_t nSize, size_t nCount, AZ::IO::HandleType fileHandle); - size_t FReadAll(void* pDest, size_t nFileSize, AZ::IO::HandleType fileHandle); + size_t FRead(void* pDest, size_t bytesToRead, AZ::IO::HandleType fileHandle); void* GetFileData(size_t& nFileSize, AZ::IO::HandleType fileHandle); int FEof(); - char* FGets(char* pBuf, int n); - int Getc(); uint64_t GetModificationTime() { return m_pFileData->GetFileEntry()->GetModificationTime(); } - const char* GetArchivePath() { return m_pFileData->GetZip()->GetFilePath(); } + AZ::IO::PathView GetArchivePath() { return m_pFileData->GetZip()->GetFilePath(); } protected: uint64_t m_nCurSeek; CCachedFileDataPtr m_pFileData; @@ -205,7 +166,7 @@ namespace AZ::IO::ArchiveInternal } ////////////////////////////////////////////////////////////////////////// - size_t ArchiveInternal::CZipPseudoFile::FRead(void* pDest, size_t nSize, size_t nCount, [[maybe_unused]] AZ::IO::HandleType fileHandle) + size_t ArchiveInternal::CZipPseudoFile::FRead(void* pDest, size_t bytesToRead, [[maybe_unused]] AZ::IO::HandleType fileHandle) { AZ_PROFILE_FUNCTION(AzCore); @@ -214,21 +175,13 @@ namespace AZ::IO::ArchiveInternal return 0; } - size_t nTotal = nSize * nCount; + size_t nTotal = bytesToRead; if (!nTotal || (uint32_t)m_nCurSeek >= GetFileSize()) { return 0; } - if (nTotal > GetFileSize() - m_nCurSeek) - { - nTotal = GetFileSize() - m_nCurSeek; - if (nTotal < nSize) - { - return 0; - } - nTotal -= nTotal % nSize; - } + nTotal = AZStd::min(nTotal, GetFileSize() - m_nCurSeek); int64_t nReadBytes = GetFile()->ReadData(pDest, m_nCurSeek, nTotal); if (nReadBytes == -1) @@ -242,32 +195,9 @@ namespace AZ::IO::ArchiveInternal nTotal = (size_t)nReadBytes; } m_nCurSeek += nTotal; - return nTotal / nSize; + return nTotal; } - ////////////////////////////////////////////////////////////////////////// - size_t ArchiveInternal::CZipPseudoFile::FReadAll(void* pDest, size_t nFileSize, [[maybe_unused]] AZ::IO::HandleType fileHandle) - { - if (!GetFile()) - { - return 0; - } - - if (nFileSize != GetFileSize()) - { - AZ_Assert(false, "File size parameter of nFileSize does not match the file size of the zip file"); // Bad call - return 0; - } - - if (!GetFile()->ReadData(pDest, 0, nFileSize)) - { - return 0; - } - - m_nCurSeek = nFileSize; - - return nFileSize; - } ////////////////////////////////////////////////////////////////////////// void* ArchiveInternal::CZipPseudoFile::GetFileData(size_t& nFileSize, [[maybe_unused]] AZ::IO::HandleType fileHandle) @@ -292,70 +222,6 @@ namespace AZ::IO::ArchiveInternal return (uint32_t)m_nCurSeek >= GetFileSize(); } - char* ArchiveInternal::CZipPseudoFile::FGets(char* pBuf, int n) - { - if (!GetFile()) - { - return nullptr; - } - - char* pData = (char*)GetFile()->GetData(); - if (!pData) - { - return nullptr; - } - int nn = 0; - int i; - for (i = 0; i < n; i++) - { - if (i + m_nCurSeek == GetFileSize()) - { - break; - } - char c = pData[i + m_nCurSeek]; - if (c == 0xa || c == 0) - { - pBuf[nn++] = c; - i++; - break; - } - else - if (c == 0xd) - { - continue; - } - pBuf[nn++] = c; - } - pBuf[nn] = 0; - m_nCurSeek += i; - - if (m_nCurSeek == GetFileSize()) - { - return nullptr; - } - return pBuf; - } - - int ArchiveInternal::CZipPseudoFile::Getc() - { - if (!GetFile()) - { - return EOF; - } - char* pData = (char*)GetFile()->GetData(); - if (!pData) - { - return EOF; - } - int c = EOF; - if (m_nCurSeek == GetFileSize()) - { - return c; - } - c = pData[m_nCurSeek]; - m_nCurSeek += 1; - return c; - } } namespace AZ::IO @@ -379,16 +245,17 @@ namespace AZ::IO void Add(AZStd::string_view sResourceFile) override { - auto filename = ArchiveInternal::ConvertAbsolutePathToAliasedPath(sResourceFile); - if (!filename) + if (sResourceFile.empty()) { - AZ_Error("Archive", false, "Path %s cannot be converted to @alias@ form. It is longer than MaxPathLength %zu", aznumeric_cast(sResourceFile.size()), - sResourceFile.data(), AZ::IO::MaxPathLength); return; } - AZ::IO::FixedMaxPathString& convertedFilename = filename->Native(); - AZStd::replace(convertedFilename.begin(), convertedFilename.end(), AZ_WRONG_DATABASE_SEPARATOR, AZ_CORRECT_DATABASE_SEPARATOR); - AZStd::to_lower(convertedFilename.begin(), convertedFilename.end()); + AZ::IO::FixedMaxPath convertedFilename; + if (!AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(convertedFilename, sResourceFile)) + { + AZ_Error("Archive", false, "Path %.*s cannot be resolved. It is longer than MaxPathLength %zu", + AZ_STRING_ARG(sResourceFile), AZ::IO::MaxPathLength); + return; + } AZStd::scoped_lock lock(m_lock); m_set.emplace(convertedFilename); @@ -397,23 +264,20 @@ namespace AZ::IO { AZStd::scoped_lock lock(m_lock); m_set.clear(); - m_iter = m_set.begin(); + m_iter = m_set.end(); } bool IsExist(AZStd::string_view sResourceFile) override { - auto filename = ArchiveInternal::ConvertAbsolutePathToAliasedPath(sResourceFile); - if (!filename) + AZ::IO::FixedMaxPath convertedFilename; + if (!AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(convertedFilename, sResourceFile)) { - AZ_Error("Archive", false, "Path %.*s cannot be converted to @alias@ form. It is longer than MaxPathLength %zu", aznumeric_cast(sResourceFile.size()), - sResourceFile.data(), AZ::IO::MaxPathLength); + AZ_Error("Archive", false, "Path %.*s cannot be resolved. It is longer than MaxPathLength %zu", + AZ_STRING_ARG(sResourceFile), AZ::IO::MaxPathLength); return false; } - AZ::IO::FixedMaxPathString& convertedFilename = filename->Native(); - AZStd::replace(convertedFilename.begin(), convertedFilename.end(), AZ_WRONG_DATABASE_SEPARATOR, AZ_CORRECT_DATABASE_SEPARATOR); - AZStd::to_lower(convertedFilename.begin(), convertedFilename.end()); AZStd::scoped_lock lock(m_lock); - return m_set.contains(AZStd::string_view{ convertedFilename }); + return m_set.contains(AZ::IO::PathView{ convertedFilename }); } bool Load(AZStd::string_view sResourceListFilename) override { @@ -425,9 +289,8 @@ namespace AZ::IO AZ::IO::SizeType nLen = file.Length(); AZStd::string pMemBlock; - pMemBlock.resize_no_construct(nLen); - char* buf = pMemBlock.data(); - file.Read(nLen, buf); + pMemBlock.resize_no_construct(nLen);; + file.Read(pMemBlock.size(), pMemBlock.data()); // Parse file, every line in a file represents a resource filename. AZ::StringFunc::TokenizeVisitor(pMemBlock, @@ -464,7 +327,7 @@ namespace AZ::IO } private: - using ResourceSet = AZStd::set; + using ResourceSet = AZStd::set>; AZStd::recursive_mutex m_lock; ResourceSet m_set; ResourceSet::iterator m_iter; @@ -499,12 +362,13 @@ namespace AZ::IO , m_pNextLevelResourceList{ new CResourceList{} } , m_mainThreadId{ AZStd::this_thread::get_id() } { + CompressionBus::Handler::BusConnect(); } ////////////////////////////////////////////////////////////////////////// Archive::~Archive() { - Release(); + CompressionBus::Handler::BusDisconnect(); m_arrZips = {}; @@ -530,51 +394,13 @@ namespace AZ::IO AZ_Assert(m_cachedFileRawDataSet.empty(), "All Archive file cached raw data instances not closed"); } - bool Archive::CheckFileAccessDisabled([[maybe_unused]] AZStd::string_view name, [[maybe_unused]] const char* mode) - { - return false; - } - void Archive::LogFileAccessCallStack([[maybe_unused]] AZStd::string_view name, [[maybe_unused]] AZStd::string_view nameFull, [[maybe_unused]] const char* mode) { // Print call stack for each find. - AZ_TracePrintf("Archive", "LogFileAccessCallStack() - name=%.*s; nameFull=%.*s; mode=%s\n", aznumeric_cast(name.size()), name.data(), aznumeric_cast(nameFull.size()), nameFull.data(), mode); + AZ_TracePrintf("Archive", "LogFileAccessCallStack() - name=%.*s; nameFull=%.*s; mode=%s\n", AZ_STRING_ARG(name), AZ_STRING_ARG(nameFull), mode); AZ::Debug::Trace::PrintCallstack("Archive", 32); } - ////////////////////////////////////////////////////////////////////////// - - bool Archive::IsInstalledToHDD(AZStd::string_view) const - { - return true; - } - - ////////////////////////////////////////////////////////////////////////// - void Archive::ParseAliases(AZStd::string_view szCommandLine) - { - // this is a list of pairs separated by commas, i.e. Folder1,FolderNew,Textures,TestBuildTextures etc. - AZStd::optional aliasKey = AZ::StringFunc::TokenizeNext(szCommandLine, ','); - AZStd::optional aliasPath = AZ::StringFunc::TokenizeNext(szCommandLine, ','); - for ( ;aliasKey && aliasPath; aliasKey = AZ::StringFunc::TokenizeNext(szCommandLine,','), AZ::StringFunc::TokenizeNext(szCommandLine,',')) - { - // inform the Archive system - SetAlias(*aliasKey, *aliasPath, true); - AZ_TracePrintf("Archive", "Archive ALIAS:%.*s = %.*s\n", aznumeric_cast(aliasKey->size()), aliasKey->data(), - aznumeric_cast(aliasPath->size()), aliasPath->data()); - - } - } - - ////////////////////////////////////////////////////////////////////////// - //! if bReturnSame==true, it will return the input name if an alias doesn't exist. Otherwise returns nullptr - const char* Archive::GetAlias(AZStd::string_view szName, bool bReturnSame) - { - constexpr size_t MaxAliasLength = 32; - AZStd::fixed_string aliasKey{ szName }; - const char* dest = AZ::IO::FileIOBase::GetDirectInstance()->GetAlias(aliasKey.c_str()); - return (bReturnSame && !dest) ? szName.data() : dest; - } - ////////////////////////////////////////////////////////////////////////// void Archive::SetLocalizationFolder(AZStd::string_view sLocalizationFolder) { @@ -591,28 +417,6 @@ namespace AZ::IO m_sLocalizationFolder += AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING; } - ////////////////////////////////////////////////////////////////////////// - void Archive::SetAlias(AZStd::string_view szName, AZStd::string_view szAlias, bool bAdd) - { - constexpr size_t MaxAliasLength = 32; - AZStd::fixed_string aliasKey{ szName }; - if (bAdd) - { - AZ::IO::PathString aliasPath{ szAlias }; - AZ::IO::FileIOBase::GetDirectInstance()->SetAlias(aliasKey.c_str(), aliasPath.c_str()); - } - else - { - AZ::IO::FileIOBase::GetDirectInstance()->ClearAlias(aliasKey.c_str()); - } - } - - - const char* Archive::AdjustFileName(AZStd::string_view src, char* dst, size_t dstSize, uint32_t, bool) - { - AZ::IO::FixedMaxPathString srcPath{ src }; - return AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(srcPath.c_str(), dst, dstSize) ? dst : nullptr; - } ////////////////////////////////////////////////////////////////////////// bool Archive::IsFileExist(AZStd::string_view sFilename, EFileSearchLocation fileLocation) @@ -679,52 +483,50 @@ namespace AZ::IO } ////////////////////////////////////////////////////////////////////////// - AZ::IO::HandleType Archive::FOpen(AZStd::string_view pName, const char* szMode, uint32_t nInputFlags) + AZ::IO::HandleType Archive::FOpen(AZStd::string_view pName, const char* szMode) { AZ_PROFILE_FUNCTION(AzCore); const size_t pathLen = pName.size(); - if (pathLen == 0 || pathLen >= MaxPath) + if (pathLen == 0 || pathLen >= AZ::IO::MaxPathLength) { return AZ::IO::InvalidHandle; } SAutoCollectFileAccessTime accessTime(this); - AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; - - const bool bFileCanBeOnDisk = 0 != (nInputFlags & FOPEN_ONDISK); - // get the priority into local variable to avoid it changing in the course of // this function execution (?) const ArchiveLocationPriority nVarPakPriority = GetPakPriority(); AZ::IO::OpenMode nOSFlags = AZ::IO::GetOpenModeFromStringMode(szMode); - auto szFullPath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pName); - if (!szFullPath) + AZ::IO::FixedMaxPath szFullPath; + if (!AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(szFullPath, pName)) { - AZ_Assert(szFullPath, "Unable to resolve path for filepath %.*s", aznumeric_cast(pName.size()), pName.data()); + AZ_Assert(false, "Unable to resolve path for filepath %.*s", aznumeric_cast(pName.size()), pName.data()); return false; } const bool fileWritable = (nOSFlags & (AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeAppend | AZ::IO::OpenMode::ModeUpdate)) != AZ::IO::OpenMode::Invalid; - AZ_PROFILE_SCOPE(Game, "File: %s Archive: %p", szFullPath->c_str(), this); + AZ_PROFILE_SCOPE(Game, "File: %s Archive: %p", szFullPath.c_str(), this); if (fileWritable) { // we need to open the file for writing, but we failed to do so. // the only reason that can be is that there are no directories for that file. // now create those dirs - if (!MakeDir(szFullPath->ParentPath().Native())) + if (AZ::IO::FixedMaxPath parentPath = szFullPath.ParentPath(); + !AZ::IO::FileIOBase::GetDirectInstance()->CreatePath(parentPath.c_str())) { return AZ::IO::InvalidHandle; } - if (AZ::IO::FileIOBase::GetDirectInstance()->Open(szFullPath->c_str(), nOSFlags, fileHandle)) + if (AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; + AZ::IO::FileIOBase::GetDirectInstance()->Open(szFullPath.c_str(), nOSFlags, fileHandle)) { if (az_archive_verbosity) { - AZ_TracePrintf("Archive", " Archive::FOpen() has directly opened requested file %s for writing", szFullPath->c_str()); + AZ_TracePrintf("Archive", " Archive::FOpen() has directly opened requested file %s for writing", szFullPath.c_str()); } return fileHandle; } @@ -732,35 +534,41 @@ namespace AZ::IO return AZ::IO::InvalidHandle; } - if (nVarPakPriority == ArchiveLocationPriority::ePakPriorityFileFirst) // if the file system files have priority now.. + auto OpenFromFileSystem = [this, &szFullPath, pName, nOSFlags]() -> HandleType { - if (AZ::IO::FileIOBase::GetDirectInstance()->Open(szFullPath->c_str(), nOSFlags, fileHandle)) + if (AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; + AZ::IO::FileIOBase::GetDirectInstance()->Open(szFullPath.c_str(), nOSFlags, fileHandle)) { if (az_archive_verbosity) { - AZ_TracePrintf("Archive", " Archive::FOpen() has directly opened requested file %s with FileFirst priority", szFullPath->c_str()); + AZ_TracePrintf("Archive", " Archive::FOpen() has directly opened requested file %s on for reading", szFullPath.c_str()); } RecordFile(fileHandle, pName); return fileHandle; } - } - uint32_t archiveFlags = 0; - CCachedFileDataPtr pFileData = GetFileData(szFullPath->Native(), archiveFlags); - if (pFileData) + return AZ::IO::InvalidHandle; + }; + auto OpenFromArchive = [this, &szFullPath, pName]() -> HandleType { - bool logged = false; - ZipDir::Cache* pZip = pFileData->GetZip(); - if (pZip) + uint32_t archiveFlags = 0; + CCachedFileDataPtr pFileData = GetFileData(szFullPath.Native(), archiveFlags); + if (pFileData == nullptr) { - const char* pZipFilePath = pZip->GetFilePath(); - if (pZipFilePath && pZipFilePath[0]) + return AZ::IO::InvalidHandle; + } + + bool logged = false; + if (ZipDir::Cache* pZip = pFileData->GetZip(); pZip != nullptr) + { + AZ::IO::PathView pZipFilePath = pZip->GetFilePath(); + if (!pZipFilePath.empty()) { if (az_archive_verbosity) { - AZ_TracePrintf("Archive", " Archive::FOpen() has opened requested file %s from archive %s, disk offset %u", - szFullPath->c_str(), pZipFilePath, pFileData->GetFileEntry()->nFileDataOffset); + AZ_TracePrintf("Archive", " Archive::FOpen() has opened requested file %s from archive %.*s, disk offset %u", + szFullPath.c_str(), AZ_STRING_ARG(pZipFilePath.Native()), pFileData->GetFileEntry()->nFileDataOffset); logged = true; } } @@ -771,57 +579,54 @@ namespace AZ::IO if (az_archive_verbosity) { AZ_TracePrintf("Archive", " Archive::FOpen() has opened requested file %s from an archive file who's path isn't known", - szFullPath->c_str()); + szFullPath.c_str()); } } - } - else - { - if (nVarPakPriority != ArchiveLocationPriority::ePakPriorityPakOnly || bFileCanBeOnDisk) // if the archive files had more priority, we didn't attempt fopen before- try it now + + size_t nFile; + // find the empty slot and open the file there; return the handle { - if (AZ::IO::FileIOBase::GetDirectInstance()->Open(szFullPath->c_str(), nOSFlags, fileHandle)) + // try to open the pseudofile from one of the zips, make sure there is no user alias + AZStd::unique_lock lock(m_csOpenFiles); + for (nFile = 0; nFile < m_arrOpenFiles.size() && m_arrOpenFiles[nFile]->GetFile(); ++nFile) { - if (az_archive_verbosity) - { - AZ_TracePrintf("Archive", " Archive::FOpen() has directly opened requested file %s after failing to open from archives", - szFullPath->c_str()); - } - - RecordFile(fileHandle, pName); - return fileHandle; + continue; } + if (nFile == m_arrOpenFiles.size()) + { + m_arrOpenFiles.emplace_back(AZStd::make_unique()); + } + AZStd::unique_ptr& rZipFile = m_arrOpenFiles[nFile]; + rZipFile->Construct(pFileData.get()); } - return AZ::IO::InvalidHandle; // we can't find such file in the pack files - } - // try to open the pseudofile from one of the zips, make sure there is no user alias - AZStd::unique_lock lock(m_csOpenFiles); + AZ::IO::HandleType handle = (AZ::IO::HandleType)(nFile + ArchiveInternal::PseudoFileIdxOffset); - size_t nFile; - // find the empty slot and open the file there; return the handle + RecordFile(handle, pName); + + return handle; // the handle to the file + }; + + switch (nVarPakPriority) { - for (nFile = 0; nFile < m_arrOpenFiles.size() && m_arrOpenFiles[nFile]->GetFile(); ++nFile) - { - continue; - } - if (nFile == m_arrOpenFiles.size()) - { - m_arrOpenFiles.emplace_back(AZStd::make_unique()); - } - AZStd::unique_ptr& rZipFile = m_arrOpenFiles[nFile]; - rZipFile->Construct(pFileData.get()); - } - - AZ::IO::HandleType ret = (AZ::IO::HandleType)(nFile + ArchiveInternal::PseudoFileIdxOffset); - - if (az_archive_verbosity) + case ArchiveLocationPriority::ePakPriorityFileFirst: { - AZ_TracePrintf("Archive", " Archive::FOpen() has opened psuedo zip file %.*s", aznumeric_cast(pName.size()), pName.data()); + AZ::IO::HandleType fileHandle = OpenFromFileSystem(); + return fileHandle != AZ::IO::InvalidHandle ? fileHandle : OpenFromArchive(); + } + case ArchiveLocationPriority::ePakPriorityPakFirst: + { + AZ::IO::HandleType fileHandle = OpenFromArchive(); + return fileHandle != AZ::IO::InvalidHandle ? fileHandle : OpenFromFileSystem(); + } + case ArchiveLocationPriority::ePakPriorityPakOnly: + { + return OpenFromArchive(); + } + default: + return AZ::IO::InvalidHandle; } - RecordFile(ret, pName); - - return ret; // the handle to the file } ////////////////////////////////////////////////////////////////////////// @@ -872,19 +677,14 @@ namespace AZ::IO ////////////////////////////////////////////////////////////////////////// // tests if the given file path refers to an existing file inside registered (opened) packs // the path must be absolute normalized lower-case with forward-slashes - ZipDir::FileEntry* Archive::FindPakFileEntry(AZStd::string_view szPath, uint32_t& nArchiveFlags, ZipDir::CachePtr* pZip, bool bSkipInMemoryArchives) const + ZipDir::FileEntry* Archive::FindPakFileEntry(AZStd::string_view szPath, uint32_t& nArchiveFlags, ZipDir::CachePtr* pZip) const { - AZ::IO::FixedMaxPath unaliasedPath; + AZ::IO::FixedMaxPath resolvedPath; + if (!AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(resolvedPath, szPath)) { - auto convertedPath = ArchiveInternal::ConvertAbsolutePathToAliasedPath(szPath); - - if (!convertedPath) - { - AZ_Error("Archive", false, "Path %s cannot be converted to @alias@ form. It is longer than MaxPathLength %zu", aznumeric_cast(szPath.size()), - szPath.data(), AZ::IO::MaxPathLength); - return nullptr; - } - unaliasedPath = AZStd::move(*convertedPath); + AZ_Error("Archive", false, "Path %s cannot be converted to @alias@ form. It is longer than MaxPathLength %zu", aznumeric_cast(szPath.size()), + szPath.data(), AZ::IO::MaxPathLength); + return nullptr; } @@ -892,28 +692,17 @@ namespace AZ::IO // scan through registered archive files and try to find this file for (auto itZip = m_arrZips.rbegin(); itZip != m_arrZips.rend(); ++itZip) { - if (bSkipInMemoryArchives && itZip->pArchive->GetFlags() & INestedArchive::FLAGS_IN_MEMORY_MASK) - { - continue; - } - if (itZip->pArchive->GetFlags() & INestedArchive::FLAGS_DISABLE_PAK) { continue; } - auto [bindRootIter, unaliasedIter] = AZStd::mismatch(itZip->m_pathBindRoot.begin(), itZip->m_pathBindRoot.end(), - unaliasedPath.begin(), unaliasedPath.end()); // If the bindRootIter is at the end then it is a prefix of the source path - if (bindRootIter == itZip->m_pathBindRoot.end()) + if (resolvedPath.IsRelativeTo(itZip->m_pathBindRoot)) { // unaliasedIter is past the bind root, so append the rest of it to a new relative path object - AZ::IO::FixedMaxPath relativePathInZip; - for (; unaliasedIter != unaliasedPath.end(); ++unaliasedIter) - { - relativePathInZip /= *unaliasedIter; - } + AZ::IO::FixedMaxPath relativePathInZip = resolvedPath.LexicallyRelative(itZip->m_pathBindRoot); ZipDir::FileEntry* pFileEntry = itZip->pZip->FindFile(relativePathInZip.Native()); if (pFileEntry) @@ -955,7 +744,7 @@ namespace AZ::IO } // returns the path to the archive in which the file was opened - const char* Archive::GetFileArchivePath(AZ::IO::HandleType fileHandle) + AZ::IO::PathView Archive::GetFileArchivePath(AZ::IO::HandleType fileHandle) { ArchiveInternal::CZipPseudoFile* pseudoFile = GetPseudoFile(fileHandle); if (pseudoFile) @@ -964,7 +753,7 @@ namespace AZ::IO } else { - return nullptr; + return {}; } } @@ -1066,7 +855,7 @@ namespace AZ::IO return 1; } - size_t Archive::FWrite(const void* data, size_t length, size_t elems, AZ::IO::HandleType fileHandle) + size_t Archive::FWrite(const void* data, size_t bytesToWrite, AZ::IO::HandleType fileHandle) { SAutoCollectFileAccessTime accessTime(this); @@ -1077,47 +866,28 @@ namespace AZ::IO } AZ_Assert(fileHandle != AZ::IO::InvalidHandle, "Invalid file has been passed to FWrite"); - if (AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, data, length * elems)) + if (AZ::u64 bytesWritten{}; AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, data, bytesToWrite, &bytesWritten)) { - return elems; + return bytesWritten; } return 0; } ////////////////////////////////////////////////////////////////////////// - size_t Archive::FReadRaw(void* pData, size_t nSize, size_t nCount, AZ::IO::HandleType fileHandle) + size_t Archive::FRead(void* pData, size_t bytesToRead, AZ::IO::HandleType fileHandle) { AZ_PROFILE_FUNCTION(AzCore); - AZ_PROFILE_SCOPE(Game, "Size: %d Archive: %p", nSize, this); SAutoCollectFileAccessTime accessTime(this); ArchiveInternal::CZipPseudoFile* pseudoFile = GetPseudoFile(fileHandle); if (pseudoFile) { - return pseudoFile->FRead(pData, nSize, nCount, fileHandle); + return pseudoFile->FRead(pData, bytesToRead, fileHandle); } AZ::u64 bytesRead = 0; - AZ::IO::FileIOBase::GetDirectInstance()->Read(fileHandle, pData, nSize * nCount, false, &bytesRead); - return static_cast(bytesRead / nSize); - } - - ////////////////////////////////////////////////////////////////////////// - size_t Archive::FReadRawAll(void* pData, size_t nFileSize, AZ::IO::HandleType fileHandle) - { - AZ_PROFILE_FUNCTION(AzCore); - - SAutoCollectFileAccessTime accessTime(this); - ArchiveInternal::CZipPseudoFile* pseudoFile = GetPseudoFile(fileHandle); - if (pseudoFile) - { - return pseudoFile->FReadAll(pData, nFileSize, fileHandle); - } - - AZ::IO::FileIOBase::GetDirectInstance()->Seek(fileHandle, 0, AZ::IO::SeekType::SeekFromStart); - AZ::u64 bytesRead = 0; - AZ::IO::FileIOBase::GetDirectInstance()->Read(fileHandle, pData, nFileSize, false, &bytesRead); - return static_cast(bytesRead); + AZ::IO::FileIOBase::GetDirectInstance()->Read(fileHandle, pData, bytesToRead, false, &bytesRead); + return bytesRead; } ////////////////////////////////////////////////////////////////////////// @@ -1234,48 +1004,6 @@ namespace AZ::IO } - int Archive::FPrintf(AZ::IO::HandleType fileHandle, const char* szFormat, ...) - { - SAutoCollectFileAccessTime accessTime(this); - ArchiveInternal::CZipPseudoFile* pseudoFile = GetPseudoFile(fileHandle); - if (pseudoFile) - { - return 0; // we don't support it now - } - - va_list arglist; - int rv; - va_start(arglist, szFormat); - rv = static_cast(AZ::IO::PrintV(fileHandle, szFormat, arglist)); - va_end(arglist); - return rv; - } - - char* Archive::FGets(char* str, int n, AZ::IO::HandleType fileHandle) - { - SAutoCollectFileAccessTime accessTime(this); - ArchiveInternal::CZipPseudoFile* pseudoFile = GetPseudoFile(fileHandle); - if (pseudoFile) - { - return pseudoFile->FGets(str, n); - } - - return AZ::IO::FGetS(str, n, fileHandle); - } - - int Archive::Getc(AZ::IO::HandleType fileHandle) - { - SAutoCollectFileAccessTime accessTime(this); - ArchiveInternal::CZipPseudoFile* pseudoFile = GetPseudoFile(fileHandle); - if (pseudoFile) - { - return pseudoFile->Getc(); - } - - return AZ::IO::GetC(fileHandle); - } - - ////////////////////////////////////////////////////////////////////////// AZ::IO::ArchiveFileIterator Archive::FindFirst(AZStd::string_view pDir, EFileSearchType searchType) { @@ -1304,7 +1032,7 @@ namespace AZ::IO break; } - AZStd::intrusive_ptr pFindData = new AZ::IO::FindData(); + AZStd::intrusive_ptr pFindData = aznew AZ::IO::FindData(); pFindData->Scan(this, szFullPath->Native(), bAllowUseFileSystem, bScanZips); return pFindData->Fetch(); @@ -1322,18 +1050,6 @@ namespace AZ::IO return true; } - ////////////////////////////////////////////////////////////////////////// - bool Archive::LoadPakToMemory([[maybe_unused]] AZStd::string_view pName, [[maybe_unused]] IArchive::EInMemoryArchiveLocation nLoadPakToMemory, - [[maybe_unused]] AZStd::intrusive_ptr pMemoryBlock) - { - return true; - } - - ////////////////////////////////////////////////////////////////////////// - void Archive::LoadPaksToMemory([[maybe_unused]] int nMaxArchiveSize, [[maybe_unused]] bool bLoadToMemory) - { - } - auto Archive::GetLevelPackOpenEvent() -> LevelPackOpenEvent* { return &m_levelOpenEvent; @@ -1344,7 +1060,7 @@ namespace AZ::IO return &m_levelCloseEvent; } //====================================================================== - bool Archive::OpenPack(AZStd::string_view szBindRootIn, AZStd::string_view szPath, uint32_t nFlags, + bool Archive::OpenPack(AZStd::string_view szBindRootIn, AZStd::string_view szPath, AZStd::intrusive_ptr pData, AZ::IO::FixedMaxPathString* pFullPath, bool addLevels) { AZ_Assert(!szBindRootIn.empty(), "Bind Root should not be empty"); @@ -1363,7 +1079,7 @@ namespace AZ::IO return false; } - bool result = OpenPackCommon(szBindRoot->Native(), szFullPath->Native(), nFlags, pData, addLevels); + bool result = OpenPackCommon(szBindRoot->Native(), szFullPath->Native(), pData, addLevels); if (pFullPath) { @@ -1373,7 +1089,7 @@ namespace AZ::IO return result; } - bool Archive::OpenPack(AZStd::string_view szPath, uint32_t nFlags, AZStd::intrusive_ptr pData, + bool Archive::OpenPack(AZStd::string_view szPath, AZStd::intrusive_ptr pData, AZ::IO::FixedMaxPathString* pFullPath, bool addLevels) { auto szFullPath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(szPath); @@ -1385,7 +1101,7 @@ namespace AZ::IO AZStd::string_view bindRoot = szFullPath->ParentPath().Native(); - bool result = OpenPackCommon(bindRoot, szFullPath->Native(), nFlags, pData, addLevels); + bool result = OpenPackCommon(bindRoot, szFullPath->Native(), pData, addLevels); if (pFullPath) { @@ -1396,34 +1112,22 @@ namespace AZ::IO } - bool Archive::OpenPackCommon(AZStd::string_view szBindRoot, AZStd::string_view szFullPath, uint32_t nArchiveFlags, + bool Archive::OpenPackCommon(AZStd::string_view szBindRoot, AZStd::string_view szFullPath, AZStd::intrusive_ptr pData, bool addLevels) { - // Note this will replace @devassets@ with @assets@ to provide a proper bind root for the archives - auto conversionResult = ArchiveInternal::ConvertAbsolutePathToAliasedPath(szBindRoot); - if (!conversionResult) - { - AZ_Error("Archive", false, "Path %.*s cannot be converted to @alias@ form. It is longer than MaxPathLength %zu", - aznumeric_cast(szBindRoot.size()), szBindRoot.data(), AZ::IO::MaxPathLength); - return false; - } - // setup PackDesc before the duplicate test PackDesc desc; - desc.strFileName = szFullPath; + desc.m_strFileName = szFullPath; - if (!conversionResult || conversionResult->empty()) + if (AZ::IO::FixedMaxPath pathBindRoot; !AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, szBindRoot)) { - desc.m_pathBindRoot = "@assets@"; + AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, "@assets@"); + desc.m_pathBindRoot = pathBindRoot.LexicallyNormal().String(); } else { - // Create a bind root without any trailing slashes - desc.m_pathBindRoot = AZStd::move(*conversionResult); - if (desc.m_pathBindRoot.HasRelativePath() && !desc.m_pathBindRoot.HasFilename()) - { - desc.m_pathBindRoot = desc.m_pathBindRoot.ParentPath(); - } + // Create a bind root + desc.m_pathBindRoot = pathBindRoot.LexicallyNormal().String(); } // hold the lock from the point we query the zip array, @@ -1433,56 +1137,23 @@ namespace AZ::IO // try to find this - maybe the pack has already been opened for (auto it = m_arrZips.begin(); it != m_arrZips.end(); ++it) { - const char* pFilePath = it->pZip->GetFilePath(); - if (pFilePath == desc.strFileName && it->m_pathBindRoot == desc.m_pathBindRoot) + if (AZ::IO::PathView archiveFilePath = it->pZip->GetFilePath(); + archiveFilePath == desc.m_strFileName && it->m_pathBindRoot == desc.m_pathBindRoot) { return true; // already opened } } } - int flags = INestedArchive::FLAGS_OPTIMIZED_READ_ONLY | INestedArchive::FLAGS_ABSOLUTE_PATHS; - if ((nArchiveFlags & FLAGS_PAK_IN_MEMORY) != 0) - { - flags |= INestedArchive::FLAGS_IN_MEMORY; - } - if ((nArchiveFlags & FLAGS_PAK_IN_MEMORY_CPU) != 0) - { - flags |= INestedArchive::FLAGS_IN_MEMORY_CPU; - } - if ((nArchiveFlags & FLAGS_FILENAMES_AS_CRC32) != 0) - { - flags |= INestedArchive::FLAGS_FILENAMES_AS_CRC32; - } - if ((nArchiveFlags & FLAGS_REDIRECT_TO_DISC) != 0) - { - flags |= FLAGS_REDIRECT_TO_DISC; - } - if ((nArchiveFlags & INestedArchive::FLAGS_OVERRIDE_PAK) != 0) - { - flags |= INestedArchive::FLAGS_OVERRIDE_PAK; - } - if ((nArchiveFlags & FLAGS_LEVEL_PAK_INSIDE_PAK) != 0) - { - flags |= INestedArchive::FLAGS_INSIDE_PAK; - } + const int flags = INestedArchive::FLAGS_OPTIMIZED_READ_ONLY | INestedArchive::FLAGS_ABSOLUTE_PATHS; desc.pArchive = OpenArchive(szFullPath, szBindRoot, flags, pData); if (!desc.pArchive) { return false; // couldn't open the archive } - if (m_filesCachedOnHDD.size()) - { - uint32_t crc = AZ::Crc32(szFullPath); - if (m_filesCachedOnHDD.find(crc) != m_filesCachedOnHDD.end()) - { - uint32_t eFlags = desc.pArchive->GetFlags(); - desc.pArchive->SetFlags(eFlags | INestedArchive::FLAGS_ON_HDD); - } - } - AZ_TracePrintf("Archive", "Opening archive file %.*s\n", aznumeric_cast(szFullPath.size()), szFullPath.data()); + AZ_TracePrintf("Archive", "Opening archive file %.*s\n", AZ_STRING_ARG(szFullPath)); desc.pZip = static_cast(desc.pArchive.get())->GetCache(); AZStd::unique_lock lock(m_csZips); @@ -1493,20 +1164,14 @@ namespace AZ::IO // All we have to do is name the archive appropriately to make // sure later archives added to the current set of archives sort higher // and therefore get used instead of lower sorted archives - AZStd::string_view nextBundle; + AZ::IO::PathView nextBundle; ZipArray::reverse_iterator revItZip = m_arrZips.rbegin(); - if ((nArchiveFlags & INestedArchive::FLAGS_OVERRIDE_PAK) == 0) + for (; revItZip != m_arrZips.rend(); ++revItZip) { - for (; revItZip != m_arrZips.rend(); ++revItZip) + nextBundle = revItZip->GetFullPath(); + if (desc.GetFullPath() > revItZip->GetFullPath()) { - if ((revItZip->pArchive->GetFlags() & INestedArchive::FLAGS_OVERRIDE_PAK) == 0) - { - nextBundle = revItZip->GetFullPath(); - if (azstricmp(desc.GetFullPath(), revItZip->GetFullPath()) > 0) - { - break; - } - } + break; } } @@ -1555,17 +1220,17 @@ namespace AZ::IO } AZ::IO::ArchiveNotificationBus::Broadcast([](AZ::IO::ArchiveNotifications* archiveNotifications, const char* bundleName, - AZStd::shared_ptr bundleManifest, const char* nextBundle, AZStd::shared_ptr bundleCatalog) + AZStd::shared_ptr bundleManifest, const AZ::IO::FixedMaxPath& nextBundle, AZStd::shared_ptr bundleCatalog) { - archiveNotifications->BundleOpened(bundleName, bundleManifest, nextBundle, bundleCatalog); - }, desc.strFileName.c_str(), bundleManifest, nextBundle.data(), bundleCatalog); + archiveNotifications->BundleOpened(bundleName, bundleManifest, nextBundle.c_str(), bundleCatalog); + }, desc.m_strFileName.c_str(), bundleManifest, nextBundle, bundleCatalog); return true; } // after this call, the file will be unlocked and closed, and its contents won't be used to search for files - bool Archive::ClosePack(AZStd::string_view pName, [[maybe_unused]] uint32_t nFlags) + bool Archive::ClosePack(AZStd::string_view pName) { auto szZipPath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pName); if (!szZipPath) @@ -1581,16 +1246,16 @@ namespace AZ::IO AZStd::unique_lock lock(m_csZips); for (auto it = m_arrZips.begin(); it != m_arrZips.end();) { - if (azstricmp(szZipPath->c_str(), it->GetFullPath()) == 0) + if (szZipPath == it->GetFullPath()) { // this is the pack with the given name - remove it, and if possible it will be deleted // the zip is referenced from the archive and *it; the archive is referenced only from *it // // the pZip (cache) can be referenced from stream engine and pseudo-files. // the archive can be referenced from outside - AZ::IO::ArchiveNotificationBus::Broadcast([](AZ::IO::ArchiveNotifications* archiveNotifications, const char* bundleName) + AZ::IO::ArchiveNotificationBus::Broadcast([](AZ::IO::ArchiveNotifications* archiveNotifications, const AZ::IO::FixedMaxPath& bundleName) { - archiveNotifications->BundleClosed(bundleName); + archiveNotifications->BundleClosed(bundleName.c_str()); }, it->GetFullPath()); if (usePrefabSystemForLevels) @@ -1643,7 +1308,7 @@ namespace AZ::IO return foundMatchingPackFile; } - bool Archive::OpenPacks(AZStd::string_view pWildcardIn, uint32_t nFlags, AZStd::vector* pFullPaths) + bool Archive::OpenPacks(AZStd::string_view pWildcardIn, AZStd::vector* pFullPaths) { auto strBindRoot{ AZ::IO::PathView(pWildcardIn).ParentPath() }; AZ::IO::FixedMaxPath bindRoot; @@ -1651,10 +1316,10 @@ namespace AZ::IO { bindRoot = strBindRoot; } - return OpenPacksCommon(bindRoot.Native(), pWildcardIn, nFlags, pFullPaths); + return OpenPacksCommon(bindRoot.Native(), pWildcardIn, pFullPaths); } - bool Archive::OpenPacks(AZStd::string_view szBindRoot, AZStd::string_view pWildcardIn, uint32_t nFlags, AZStd::vector* pFullPaths) + bool Archive::OpenPacks(AZStd::string_view szBindRoot, AZStd::string_view pWildcardIn, AZStd::vector* pFullPaths) { auto bindRoot = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(szBindRoot); if (!bindRoot) @@ -1662,16 +1327,16 @@ namespace AZ::IO AZ_Assert(false, "Unable to resolve path for filepath %.*s", aznumeric_cast(szBindRoot.size()), szBindRoot.data()); return false; } - return OpenPacksCommon(bindRoot->Native(), pWildcardIn, nFlags, pFullPaths); + return OpenPacksCommon(bindRoot->Native(), pWildcardIn, pFullPaths); } - bool Archive::OpenPacksCommon(AZStd::string_view szDir, AZStd::string_view pWildcardIn, uint32_t nArchiveFlags, AZStd::vector* pFullPaths, bool addLevels) + bool Archive::OpenPacksCommon(AZStd::string_view szDir, AZStd::string_view pWildcardIn, AZStd::vector* pFullPaths, bool addLevels) { constexpr AZStd::string_view wildcards{ "*?" }; if (wildcards.find_first_of(pWildcardIn) == AZStd::string_view::npos) { // No wildcards, just open pack - if (OpenPackCommon(szDir, pWildcardIn, nArchiveFlags, nullptr, addLevels)) + if (OpenPackCommon(szDir, pWildcardIn, nullptr, addLevels)) { if (pFullPaths) { @@ -1683,25 +1348,24 @@ namespace AZ::IO if (AZ::IO::ArchiveFileIterator fileIterator = FindFirst(pWildcardIn, IArchive::eFileSearchType_AllowOnDiskOnly); fileIterator) { - AZStd::vector files; + AZStd::vector files; do { - AZStd::string foundFilename{ fileIterator.m_filename }; - AZStd::to_lower(foundFilename.begin(), foundFilename.end()); - files.emplace_back(AZStd::move(foundFilename)); + auto& foundFilename = files.emplace_back(fileIterator.m_filename); + AZStd::to_lower(foundFilename.Native().begin(), foundFilename.Native().end()); } while (fileIterator = FindNext(fileIterator)); - // Open files in alphabet order. + // Open files in alphabetical order. AZStd::sort(files.begin(), files.end()); bool bAllOk = true; - for (const AZStd::string& file : files) + for (const AZ::IO::FixedMaxPath& file : files) { - bAllOk = OpenPackCommon(szDir, file, nArchiveFlags, nullptr, addLevels) && bAllOk; + bAllOk = OpenPackCommon(szDir, file.Native(), nullptr, addLevels) && bAllOk; if (pFullPaths) { - pFullPaths->emplace_back(file.begin(), file.end()); + pFullPaths->emplace_back(AZStd::move(file.Native())); } } @@ -1713,7 +1377,7 @@ namespace AZ::IO } - bool Archive::ClosePacks(AZStd::string_view pWildcardIn, uint32_t nFlags) + bool Archive::ClosePacks(AZStd::string_view pWildcardIn) { auto path = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pWildcardIn); if (!path) @@ -1723,26 +1387,13 @@ namespace AZ::IO } return AZ::IO::FileIOBase::GetDirectInstance()->FindFiles(AZ::IO::FixedMaxPath(path->ParentPath()).c_str(), - AZ::IO::FixedMaxPath(path->Filename()).c_str(), [&](const char* filePath) -> bool + AZ::IO::FixedMaxPath(path->Filename()).c_str(), [this](const char* filePath) -> bool { - ClosePack(filePath, nFlags); + ClosePack(filePath); return true; }); } - - ///////////////////////////////////////////////////// - bool Archive::Init([[maybe_unused]] AZStd::string_view szBasePath) - { - BusConnect(); - return true; - } - - void Archive::Release() - { - BusDisconnect(); - } - ////////////////////////////////////////////////////////////////////////// ArchiveInternal::CZipPseudoFile* Archive::GetPseudoFile(AZ::IO::HandleType fileHandle) const { @@ -1912,36 +1563,6 @@ namespace AZ::IO return m_pFileEntry->nFileDataOffset; } - bool Archive::MakeDir(AZStd::string_view szPathIn) - { - AZ::IO::StackString pathStr{ szPathIn }; - // Determine if there is a period ('.') after the last slash to determine if the path contains a file. - // This used to be a strchr on the whole path which could contain a period in a path, such as network domain paths (domain.user). - size_t findDotFromPos = pathStr.rfind(AZ_CORRECT_FILESYSTEM_SEPARATOR); - if (findDotFromPos == AZ::IO::StackString::npos) - { - findDotFromPos = pathStr.rfind(AZ_WRONG_FILESYSTEM_SEPARATOR); - if (findDotFromPos == AZ::IO::StackString::npos) - { - findDotFromPos = 0; - } - } - size_t dotPos = pathStr.find('.', findDotFromPos); - if (dotPos != AZ::IO::StackString::npos) - { - AZStd::string fullPath; - AZ::StringFunc::Path::GetFullPath(pathStr.c_str(), fullPath); - pathStr = fullPath; - } - - if (pathStr.empty()) - { - return true; - } - - return AZ::IO::FileIOBase::GetDirectInstance()->CreatePath(pathStr.c_str()); - } - ////////////////////////////////////////////////////////////////////////// // open the physical archive file - creates if it doesn't exist // returns nullptr if it's invalid or can't open the file @@ -1962,15 +1583,6 @@ namespace AZ::IO uint32_t nFactoryFlags = 0; - if (nFlags & INestedArchive::FLAGS_IN_MEMORY) - { - nFactoryFlags |= ZipDir::CacheFactory::FLAGS_IN_MEMORY; - } - - if (nFlags & INestedArchive::FLAGS_IN_MEMORY_CPU) - { - nFactoryFlags |= ZipDir::CacheFactory::FLAGS_IN_MEMORY_CPU; - } if (nFlags & INestedArchive::FLAGS_DONT_COMPACT) { @@ -1982,10 +1594,6 @@ namespace AZ::IO nFactoryFlags |= ZipDir::CacheFactory::FLAGS_READ_ONLY; } - if (nFlags & INestedArchive::FLAGS_INSIDE_PAK) - { - nFactoryFlags |= ZipDir::CacheFactory::FLAGS_READ_INSIDE_PAK; - } INestedArchive* pArchive = FindArchive(szFullPath->Native()); if (pArchive) @@ -2016,7 +1624,10 @@ namespace AZ::IO if (!pakOnDisk && (nFactoryFlags & ZipDir::CacheFactory::FLAGS_READ_ONLY)) { // Archive file not found. - AZ_TracePrintf("Archive", "Archive file %s does not exist\n", szFullPath->c_str()); + if (az_archive_verbosity) + { + AZ_TracePrintf("Archive", "Archive file %s does not exist\n", szFullPath->c_str()); + } return nullptr; } @@ -2031,148 +1642,6 @@ namespace AZ::IO return nullptr; } - uint32_t Archive::ComputeCRC(AZStd::string_view szPath, [[maybe_unused]] uint32_t nFileOpenFlags) - { - AZ_Assert(!szPath.empty(), "Path to compute Crc cannot be empty"); - - AZ::Crc32 dwCRC = 0; - - // generate crc32 - { - // avoid heap allocation by working in 8k chunks - const uint32_t dwChunkSize = 1024 * 8; - - // note that the actual CRC algorithm can work on various sized words but operates on individual words - // so there's little difference between feeding it 8k and 8mb, except you might save yourself some io calls. - - uint8_t pMem[dwChunkSize]; - - - AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); - if (!fileIO) - { - return ZipDir::ZD_ERROR_INVALID_CALL; - } - - AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; - - if (AZ::IO::PathString filepath{ szPath }; !fileIO->Open(filepath.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary, fileHandle)) - { - return ZipDir::ZD_ERROR_INVALID_PATH; - } - // load whole file in chunks and compute CRC - while (true) - { - AZ::u64 bytesRead = 0; - fileIO->Read(fileHandle, pMem, dwChunkSize, false, &bytesRead); // read up to ChunkSize bytes and put the actual number of bytes read into bytesRead. - - if (bytesRead) - { - dwCRC.Add(pMem, aznumeric_caster(bytesRead)); - } - else - { - break; - } - } - - FClose(fileHandle); - } - - return dwCRC; - } - - bool Archive::ComputeMD5(AZStd::string_view szPath, uint8_t* md5, uint32_t nFileOpenFlags, bool useDirectFileAccess) - { - if (szPath.empty() || !md5) - { - return false; - } - - MD5Context context; - MD5Init(&context); - - // generate checksum - { - const AZ::u64 dwChunkSize = 1024 * 1024; // 1MB chunks - AZStd::unique_ptr pMem{ reinterpret_cast(AZ::AllocatorInstance::Get().Allocate(dwChunkSize, alignof(uint8_t))), - [](uint8_t* ptr) { AZ::AllocatorInstance::Get().DeAllocate(ptr); } - }; - - if (!pMem) - { - return false; - } - - AZ::u64 dwSize = 0; - - AZ::IO::PathString filepath{ szPath }; - if (useDirectFileAccess) - { - - AZ::IO::FileIOBase::GetDirectInstance()->Size(filepath.c_str(), dwSize); - } - else - { - AZ::IO::HandleType fileHandle = FOpen(filepath, "rb", nFileOpenFlags); - - if (fileHandle != AZ::IO::InvalidHandle) - { - dwSize = FGetSize(fileHandle); - FClose(fileHandle); - } - } - - // rbx open flags, x is a hint to not cache whole file in memory. - AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; - if (useDirectFileAccess) - { - AZ::IO::FileIOBase::GetDirectInstance()->Open(filepath.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary, fileHandle); - } - else - { - fileHandle = FOpen(filepath, "rbx", nFileOpenFlags); - } - - if (fileHandle == AZ::IO::InvalidHandle) - { - return false; - } - - // load whole file in chunks and compute Md5 - while (dwSize > 0) - { - uint64_t dwLocalSize = AZStd::min(dwSize, dwChunkSize); - - AZ::u64 read{ 0 }; - if (useDirectFileAccess) - { - AZ::IO::FileIOBase::GetDirectInstance()->Read(fileHandle, pMem.get(), dwLocalSize, false, &read); - } - else - { - read = FReadRaw(pMem.get(), 1, dwLocalSize, fileHandle); - } - AZ_Assert(read == dwLocalSize, "Failed to read dwLocalSize %" PRIu32 " bytes from file", dwLocalSize); - - MD5Update(&context, pMem.get(), aznumeric_cast(dwLocalSize)); - dwSize -= dwLocalSize; - } - - if (useDirectFileAccess) - { - AZ::IO::FileIOBase::GetDirectInstance()->Close(fileHandle); - } - else - { - FClose(fileHandle); - } - } - - MD5Final(md5, &context); - return true; - } - void Archive::Register(INestedArchive* pArchive) { AZStd::unique_lock lock(m_archiveMutex); @@ -2185,7 +1654,7 @@ namespace AZ::IO AZStd::unique_lock lock(m_archiveMutex); if (pArchive) { - AZ_TracePrintf("Archive", "Closing Archive file: %s\n", pArchive->GetFullPath()); + AZ_TracePrintf("Archive", "Closing Archive file: %.*s\n", AZ_STRING_ARG(pArchive->GetFullPath().Native())); } ArchiveArray::iterator it; if (m_arrArchives.size() < 16) @@ -2212,7 +1681,7 @@ namespace AZ::IO { AZStd::shared_lock lock(m_archiveMutex); auto it = AZStd::lower_bound(m_arrArchives.begin(), m_arrArchives.end(), szFullPath, NestedArchiveSortByName()); - if (it != m_arrArchives.end() && !azstrnicmp(szFullPath.data(), (*it)->GetFullPath(), szFullPath.size())) + if (it != m_arrArchives.end() && szFullPath == (*it)->GetFullPath()) { return *it; } @@ -2280,7 +1749,7 @@ namespace AZ::IO case RFOM_Disabled: default: - AZ_Assert(false, "File record option %d", aznumeric_cast(eList));; + AZ_Assert(false, "File record option %d", aznumeric_cast(eList)); } return nullptr; } @@ -2325,11 +1794,14 @@ namespace AZ::IO if (m_eRecordFileOpenList != IArchive::RFOM_Disabled) { // we only want to record ASSET access - // assets are identified as things which start with no alias, or with the @assets@ alias - auto assetPath = AZ::IO::FileIOBase::GetInstance()->ConvertToAlias(szFilename); - if (assetPath && (assetPath->Native().starts_with("@assets@") - || assetPath->Native().starts_with("@root@") - || assetPath->Native().starts_with("@projectplatformcache@"))) + // assets are identified as files that are relative to the resolved @assets@ alias path + auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); + const char* aliasValue = fileIoBase->GetAlias("@assets@"); + + if (AZ::IO::FixedMaxPath resolvedFilePath; + fileIoBase->ResolvePath(resolvedFilePath, szFilename) + && aliasValue != nullptr + && resolvedFilePath.IsRelativeTo(aliasValue)) { IResourceList* pList = GetResourceList(m_eRecordFileOpenList); @@ -2360,13 +1832,8 @@ namespace AZ::IO bool prev = false; if (threadId == m_mainThreadId) { - prev = m_disableRuntimeFileAccess[0]; - m_disableRuntimeFileAccess[0] = status; - } - else if (threadId == m_renderThreadId) - { - prev = m_disableRuntimeFileAccess[1]; - m_disableRuntimeFileAccess[1] = status; + prev = m_disableRuntimeFileAccess; + m_disableRuntimeFileAccess = status; } return prev; } @@ -2440,16 +1907,6 @@ namespace AZ::IO return AZ::AllocatorInstance::Get().DeAllocate(p); } - void Archive::Lock() - { - m_csMain.lock(); - } - - void Archive::Unlock() - { - m_csMain.unlock(); - } - // gets the current archive priority ArchiveLocationPriority Archive::GetPakPriority() const { @@ -2519,7 +1976,7 @@ namespace AZ::IO { found = true; - info.m_archiveFilename.InitFromRelativePath(archive->GetFilePath()); + info.m_archiveFilename.InitFromRelativePath(archive->GetFilePath().Native()); info.m_offset = pFileData->GetFileDataOffset(); info.m_compressedSize = entry->desc.lSizeCompressed; info.m_uncompressedSize = entry->desc.lSizeUncompressed; @@ -2561,7 +2018,7 @@ namespace AZ::IO ZipDir::CachePtr pZip; uint32_t nArchiveFlags; - ZipDir::FileEntry* pFileEntry = FindPakFileEntry(szFullPath->Native(), nArchiveFlags, &pZip, false); + ZipDir::FileEntry* pFileEntry = FindPakFileEntry(szFullPath->Native(), nArchiveFlags, &pZip); if (!pFileEntry) { return 0; @@ -2589,7 +2046,7 @@ namespace AZ::IO return static_cast(StreamMediaType::TypeHDD); } - bool Archive::SetPacksAccessible(bool bAccessible, AZStd::string_view pWildcard, uint32_t nFlags) + bool Archive::SetPacksAccessible(bool bAccessible, AZStd::string_view pWildcard) { auto filePath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pWildcard); if (!filePath) @@ -2601,24 +2058,24 @@ namespace AZ::IO return AZ::IO::FileIOBase::GetDirectInstance()->FindFiles(AZ::IO::FixedMaxPath(filePath->ParentPath()).c_str(), AZ::IO::FixedMaxPath(filePath->Filename()).c_str(), [&](const char* filePath) -> bool { - SetPackAccessible(bAccessible, filePath, nFlags); + SetPackAccessible(bAccessible, filePath); return true; }); } - bool Archive::SetPackAccessible(bool bAccessible, AZStd::string_view pName, [[maybe_unused]] uint32_t nFlags) + bool Archive::SetPackAccessible(bool bAccessible, AZStd::string_view pName) { auto szZipPath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pName); if (!szZipPath) { - AZ_Assert(false, "Unable to resolve path for filepath %.*s", aznumeric_cast(pName.size()), pName.data()); + AZ_Assert(false, "Unable to resolve path for filepath %.*s", AZ_STRING_ARG(pName)); return false; } AZStd::unique_lock lock(m_csZips); for (auto it = m_arrZips.begin(); it != m_arrZips.end(); ++it) { - if (!azstricmp(szZipPath->c_str(), it->GetFullPath())) + if (szZipPath == it->GetFullPath()) { return it->pArchive->SetPackAccessible(bAccessible); } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h index ec964f7fa3..f08d90a66e 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include @@ -115,12 +115,12 @@ namespace AZ::IO struct PackDesc { AZ::IO::Path m_pathBindRoot; // the zip binding root - AZStd::string strFileName; // the zip file name (with path) - very useful for debugging so please don't remove + AZ::IO::Path m_strFileName; // the zip file name (with path) - very useful for debugging so please don't remove // [LYN-2376] Remove once legacy slice support is removed bool m_containsLevelPak = false; // indicates whether this archive has level.pak inside it or not - const char* GetFullPath() const { return pZip->GetFilePath(); } + AZ::IO::PathView GetFullPath() const { return pZip->GetFilePath(); } AZStd::intrusive_ptr pArchive; ZipDir::CachePtr pZip; @@ -129,10 +129,7 @@ namespace AZ::IO // ArchiveFindDataSet entire purpose is to keep a reference to the intrusive_ptr of ArchiveFindData // so that it doesn't go out of scope - using ArchiveFindDataSet = AZStd::set, AZ::OSStdAllocator>; - - // given the source relative path, constructs the full path to the file according to the flags - const char* AdjustFileName(AZStd::string_view src, char* dst, size_t dstSize, uint32_t nFlags, bool skipMods = false) override; + using ArchiveFindDataSet = AZStd::set>; /** @@ -154,29 +151,17 @@ namespace AZ::IO //! CompressionBus Handler implementation. void FindCompressionInfo(bool& found, AZ::IO::CompressionInfo& info, const AZStd::string_view filename) override; - //! Processes an alias command line containing multiple aliases. - void ParseAliases(AZStd::string_view szCommandLine) override; - //! adds or removes an alias from the list - if bAdd set to false will remove it - void SetAlias(AZStd::string_view szName, AZStd::string_view szAlias, bool bAdd) override; - //! gets an alias from the list, if any exist. - //! if bReturnSame==true, it will return the input name if an alias doesn't exist. Otherwise returns nullptr - const char* GetAlias(AZStd::string_view szName, bool bReturnSame = true) override; - // Set the localization folder void SetLocalizationFolder(AZStd::string_view sLocalizationFolder) override; const char* GetLocalizationFolder() const override { return m_sLocalizationFolder.c_str(); } const char* GetLocalizationRoot() const override { return m_sLocalizationRoot.c_str(); } - // lock all the operations - void Lock() override; - void Unlock() override; - // open the physical archive file - creates if it doesn't exist // returns nullptr if it's invalid or can't open the file - AZStd::intrusive_ptr OpenArchive(AZStd::string_view szPath, AZStd::string_view bindRoot = {}, uint32_t nFlags = 0, AZStd::intrusive_ptr pData = nullptr) override; + AZStd::intrusive_ptr OpenArchive(AZStd::string_view szPath, AZStd::string_view bindRoot = {}, uint32_t nArchiveFlags = 0, AZStd::intrusive_ptr pData = nullptr) override; // returns the path to the archive in which the file was opened - const char* GetFileArchivePath(AZ::IO::HandleType fileHandle) override; + AZ::IO::PathView GetFileArchivePath(AZ::IO::HandleType fileHandle) override; ////////////////////////////////////////////////////////////////////////// @@ -192,40 +177,31 @@ namespace AZ::IO void RegisterFileAccessSink(IArchiveFileAccessSink* pSink) override; void UnregisterFileAccessSink(IArchiveFileAccessSink* pSink) override; - bool Init(AZStd::string_view szBasePath) override; - void Release() override; - - bool IsInstalledToHDD(AZStd::string_view acFilePath = 0) const override; - // [LYN-2376] Remove 'addLevels' parameter once legacy slice support is removed - bool OpenPack(AZStd::string_view pName, uint32_t nFlags = 0, AZStd::intrusive_ptr pData = nullptr, AZ::IO::FixedMaxPathString* pFullPath = nullptr, bool addLevels = true) override; - bool OpenPack(AZStd::string_view szBindRoot, AZStd::string_view pName, uint32_t nFlags = 0, AZStd::intrusive_ptr pData = nullptr, AZ::IO::FixedMaxPathString* pFullPath = nullptr, bool addLevels = true) override; + bool OpenPack(AZStd::string_view pName, AZStd::intrusive_ptr pData = nullptr, AZ::IO::FixedMaxPathString* pFullPath = nullptr, bool addLevels = true) override; + bool OpenPack(AZStd::string_view szBindRoot, AZStd::string_view pName, AZStd::intrusive_ptr pData = nullptr, AZ::IO::FixedMaxPathString* pFullPath = nullptr, bool addLevels = true) override; // after this call, the file will be unlocked and closed, and its contents won't be used to search for files - bool ClosePack(AZStd::string_view pName, uint32_t nFlags = 0) override; - bool OpenPacks(AZStd::string_view pWildcard, uint32_t nFlags = 0, AZStd::vector* pFullPaths = nullptr) override; - bool OpenPacks(AZStd::string_view szBindRoot, AZStd::string_view pWildcard, uint32_t nFlags = 0, AZStd::vector* pFullPaths = nullptr) override; + bool ClosePack(AZStd::string_view pName) override; + bool OpenPacks(AZStd::string_view pWildcard, AZStd::vector* pFullPaths = nullptr) override; + bool OpenPacks(AZStd::string_view szBindRoot, AZStd::string_view pWildcard, AZStd::vector* pFullPaths = nullptr) override; // closes pack files by the path and wildcard - bool ClosePacks(AZStd::string_view pWildcard, uint32_t nFlags = 0) override; + bool ClosePacks(AZStd::string_view pWildcard) override; //returns if a archive exists matching the wildcard bool FindPacks(AZStd::string_view pWildcardIn) override; // prevent access to specific archive files - bool SetPacksAccessible(bool bAccessible, AZStd::string_view pWildcard, uint32_t nFlags = 0) override; - bool SetPackAccessible(bool bAccessible, AZStd::string_view pName, uint32_t nFlags = 0) override; + bool SetPacksAccessible(bool bAccessible, AZStd::string_view pWildcard) override; + bool SetPackAccessible(bool bAccessible, AZStd::string_view pName) override; // returns the file modification time uint64_t GetModificationTime(AZ::IO::HandleType fileHandle) override; - bool LoadPakToMemory(AZStd::string_view pName, EInMemoryArchiveLocation nLoadArchiveToMemory, AZStd::intrusive_ptr pMemoryBlock = nullptr) override; - void LoadPaksToMemory(int nMaxArchiveSize, bool bLoadToMemory) override; - - AZ::IO::HandleType FOpen(AZStd::string_view pName, const char* mode, uint32_t nPathFlags = 0) override; - size_t FReadRaw(void* data, size_t length, size_t elems, AZ::IO::HandleType handle) override; - size_t FReadRawAll(void* data, size_t nFileSize, AZ::IO::HandleType handle) override; + AZ::IO::HandleType FOpen(AZStd::string_view pName, const char* mode) override; + size_t FRead(void* data, size_t bytesToRead, AZ::IO::HandleType handle) override; void* FGetCachedFileData(AZ::IO::HandleType handle, size_t& nFileSize) override; - size_t FWrite(const void* data, size_t length, size_t elems, AZ::IO::HandleType handle) override; + size_t FWrite(const void* data, size_t bytesToWrite, AZ::IO::HandleType handle) override; size_t FSeek(AZ::IO::HandleType handle, uint64_t seek, int mode) override; uint64_t FTell(AZ::IO::HandleType handle) override; int FFlush(AZ::IO::HandleType handle) override; @@ -234,9 +210,7 @@ namespace AZ::IO AZ::IO::ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator fileIterator) override; bool FindClose(AZ::IO::ArchiveFileIterator fileIterator) override; int FEof(AZ::IO::HandleType handle) override; - char* FGets(char*, int, AZ::IO::HandleType) override; - int Getc(AZ::IO::HandleType) override; - int FPrintf(AZ::IO::HandleType handle, const char* format, ...) override; + size_t FGetSize(AZ::IO::HandleType fileHandle) override; size_t FGetSize(AZStd::string_view sFilename, bool bAllowUseFileSystem = false) override; bool IsInPak(AZ::IO::HandleType handle) override; @@ -248,9 +222,6 @@ namespace AZ::IO bool IsFolder(AZStd::string_view sPath) override; IArchive::SignedFileSize GetFileSizeOnDisk(AZStd::string_view filename) override; - // creates a directory - bool MakeDir(AZStd::string_view szPath) override; - // compresses the raw data into raw data. The buffer for compressed data itself with the heap passed. Uses method 8 (deflate) // returns one of the Z_* errors (Z_OK upon success) // MT-safe @@ -275,22 +246,12 @@ namespace AZ::IO IResourceList* GetResourceList(ERecordFileOpenList eList) override; void SetResourceList(ERecordFileOpenList eList, IResourceList* pResourceList) override; - uint32_t ComputeCRC(AZStd::string_view szPath, uint32_t nFileOpenFlags = 0) override; - bool ComputeMD5(AZStd::string_view szPath, uint8_t* md5, uint32_t nFileOpenFlags = 0, bool useDirectFileAccess = false) override; - void DisableRuntimeFileAccess(bool status) override { - m_disableRuntimeFileAccess[0] = status; - m_disableRuntimeFileAccess[1] = status; + m_disableRuntimeFileAccess = status; } bool DisableRuntimeFileAccess(bool status, AZStd::thread_id threadId) override; - bool CheckFileAccessDisabled(AZStd::string_view name, const char* mode) override; - - void SetRenderThreadId(AZStd::thread_id renderThreadId) override - { - m_renderThreadId = renderThreadId; - } // gets the current archive priority ArchiveLocationPriority GetPakPriority() const override; @@ -307,11 +268,11 @@ namespace AZ::IO // Return cached file data for entries inside archive file. CCachedFileDataPtr GetOpenedFileDataInZip(AZ::IO::HandleType file); ZipDir::FileEntry* FindPakFileEntry(AZStd::string_view szPath, uint32_t& nArchiveFlags, - ZipDir::CachePtr* pZip = {}, bool bSkipInMemoryArchives = {}) const; + ZipDir::CachePtr* pZip = {}) const; private: - bool OpenPackCommon(AZStd::string_view szBindRoot, AZStd::string_view pName, uint32_t nArchiveFlags, AZStd::intrusive_ptr pData = nullptr, bool addLevels = true); - bool OpenPacksCommon(AZStd::string_view szDir, AZStd::string_view pWildcardIn, uint32_t nArchiveFlags, AZStd::vector* pFullPaths = nullptr, bool addLevels = true); + bool OpenPackCommon(AZStd::string_view szBindRoot, AZStd::string_view pName, AZStd::intrusive_ptr pData = nullptr, bool addLevels = true); + bool OpenPacksCommon(AZStd::string_view szDir, AZStd::string_view pWildcardIn, AZStd::vector* pFullPaths = nullptr, bool addLevels = true); ZipDir::FileEntry* FindPakFileEntry(AZStd::string_view szPath) const; @@ -346,9 +307,6 @@ namespace AZ::IO AZStd::mutex m_cachedFileRawDataMutex; // For m_pCachedFileRawDataSet using RawDataCacheLockGuard = AZStd::scoped_lock; - // The F* emulation functions critical section: protects all F* functions - // that don't have a chance to be called recursively (to avoid deadlocks) - AZStd::mutex m_csMain; mutable AZStd::shared_mutex m_archiveMutex; ArchiveArray m_arrArchives; @@ -360,8 +318,6 @@ namespace AZ::IO ////////////////////////////////////////////////////////////////////////// IArchive::ERecordFileOpenList m_eRecordFileOpenList = RFOM_Disabled; - using RecordedFilesSet = AZStd::set; - RecordedFilesSet m_recordedFilesSet; AZStd::intrusive_ptr m_pEngineStartupResourceList; @@ -372,28 +328,16 @@ namespace AZ::IO float m_fFileAccessTime{}; // Time used to perform file operations AZStd::vector m_FileAccessSinks; // useful for gathering file access statistics - bool m_disableRuntimeFileAccess[2]{}; + bool m_disableRuntimeFileAccess{}; //threads which we don't want to access files from during the game AZStd::thread_id m_mainThreadId{}; - AZStd::thread_id m_renderThreadId{}; AZStd::fixed_string<128> m_sLocalizationFolder; AZStd::fixed_string<128> m_sLocalizationRoot; - AZStd::set, AZ::OSStdAllocator> m_filesCachedOnHDD; - // [LYN-2376] Remove once legacy slice support is removed LevelPackOpenEvent m_levelOpenEvent; LevelPackCloseEvent m_levelCloseEvent; }; } - -namespace AZ::IO::ArchiveInternal -{ - // Utility function to de-alias archive file opening and file-within-archive opening - // if the file specified was an absolute path but it points at one of the aliases, de-alias it and replace it with that alias. - // this works around problems where the level editor is in control but still mounts asset packs (ie, level.pak mounted as @assets@) - AZStd::optional ConvertAbsolutePathToAliasedPath(AZStd::string_view sourcePath, - AZStd::string_view aliasToLookFor = "@devassets@", AZStd::string_view aliasToReplaceWith = "@assets@"); -} diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp index 55f7640785..85ce0b6f9a 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp @@ -5,10 +5,9 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include +#include #include #include // for function<> in the find files callback. -#include #include #include @@ -188,7 +187,7 @@ namespace AZ::IO return IO::ResultCode::Error; } - size_t result = m_archive->FReadRaw(buffer, 1, size, fileHandle); + size_t result = m_archive->FRead(buffer, size, fileHandle); if (bytesRead) { *bytesRead = static_cast(result); @@ -213,7 +212,7 @@ namespace AZ::IO return IO::ResultCode::Error; } - size_t result = m_archive->FWrite(buffer, 1, size, fileHandle); + size_t result = m_archive->FWrite(buffer, size, fileHandle); if (bytesWritten) { *bytesWritten = static_cast(result); @@ -357,14 +356,8 @@ namespace AZ::IO return IO::ResultCode::Error; } - // avoid using AZStd::string if possible - use OSString instead of StringFunc - AZ::OSString destPath(destinationFilePath); + IO::Path destPath(IO::PathView(destinationFilePath).ParentPath()); - AZ::OSString::size_type pos = destPath.find_last_of(AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR); - if (pos != AZ::OSString::npos) - { - destPath.resize(pos); - } CreatePath(destPath.c_str()); if (!Open(destinationFilePath, IO::OpenMode::ModeWrite | IO::OpenMode::ModeBinary, destinationFile)) @@ -466,31 +459,25 @@ namespace AZ::IO return IO::ResultCode::Error; } - AZStd::fixed_string total = filePath; + AZ::IO::FixedMaxPath total = filePath; if (total.empty()) { return IO::ResultCode::Error; } - if (!total.ends_with(AZ_CORRECT_FILESYSTEM_SEPARATOR) && !total.ends_with(AZ_WRONG_FILESYSTEM_SEPARATOR)) - { - total.append(AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); - } - - total.append(filter); + total /= filter; AZ::IO::ArchiveFileIterator fileIterator = m_archive->FindFirst(total.c_str()); if (!fileIterator) { return IO::ResultCode::Success; // its not an actual fatal error to not find anything. } - for (;fileIterator; fileIterator = m_archive->FindNext(fileIterator)) + for (; fileIterator; fileIterator = m_archive->FindNext(fileIterator)) { - total = AZStd::fixed_string::format("%s/%.*s", filePath, aznumeric_cast(fileIterator.m_filename.size()), fileIterator.m_filename.data()); - AZStd::optional resolvedAliasLength = ConvertToAlias(total.data(), total.capacity()); - if (resolvedAliasLength) + total = filePath; + total /= fileIterator.m_filename; + if (ConvertToAlias(total, total)) { - total.resize_no_construct(*resolvedAliasLength); if (!callback(total.c_str())) { break; @@ -510,8 +497,13 @@ namespace AZ::IO const auto fileIt = m_trackedFiles.find(fileHandle); if (fileIt != m_trackedFiles.end()) { - AZ_Assert(filenameSize >= fileIt->second.length(), "Filename size %" PRIu64 " is larger than the size of the tracked file %s:%zu", fileIt->second.c_str(), fileIt->second.size()); - azstrncpy(filename, filenameSize, fileIt->second.c_str(), fileIt->second.length()); + const AZStd::string_view trackedFileView = fileIt->second.Native(); + if (filenameSize <= trackedFileView.size()) + { + return false; + } + size_t trackedFileViewLength = trackedFileView.copy(filename, trackedFileView.size()); + filename[trackedFileViewLength] = '\0'; return true; } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h index 2cd8f37adc..21cef18a7a 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h @@ -13,7 +13,6 @@ #include #include #include -#include namespace AZ::IO @@ -65,7 +64,7 @@ namespace AZ::IO void SetAlias(const char* alias, const char* path) override; void ClearAlias(const char* alias) override; AZStd::optional ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override; - bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const; + bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override; using FileIOBase::ConvertToAlias; const char* GetAlias(const char* alias) const override; bool ResolvePath(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize) const override; @@ -78,7 +77,7 @@ namespace AZ::IO protected: // we keep a list of file names ever opened so that we can easily return it. mutable AZStd::recursive_mutex m_operationGuard; - AZStd::unordered_map, AZStd::equal_to, AZ::OSStdAllocator> m_trackedFiles; + AZStd::unordered_map m_trackedFiles; AZStd::fixed_vector m_copyBuffer; IArchive* m_archive; }; diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp index c4c845a832..d7a92efbf6 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp @@ -15,34 +15,6 @@ namespace AZ::IO { - size_t ArchiveFileIteratorHash::operator()(const AZ::IO::ArchiveFileIterator& iter) const - { - return iter.GetHash(); - } - - bool AZStdStringLessCaseInsensitive::operator()(AZStd::string_view left, AZStd::string_view right) const - { - // If one or both strings are 0-length, return true if the left side is smaller, false if they're equal or left is larger. - size_t compareLength = (AZStd::min)(left.size(), right.size()); - if (compareLength == 0) - { - return left.size() < right.size(); - } - - // They're both non-zero, so compare the strings up until the length of the shorter string. - int compareResult = azstrnicmp(left.data(), right.data(), compareLength); - - // If both strings are equal for the number of characters compared, return true if the left side is shorter, false if - // they're equal or left is longer. - if (compareResult == 0) - { - return left.size() < right.size(); - } - - // Return true if the left side should come first alphabetically, false if the right side should. - return compareResult < 0; - } - FileDesc::FileDesc(Attribute fileAttribute, uint64_t fileSize, time_t accessTime, time_t creationTime, time_t writeTime) : nAttrib{ fileAttribute } , nSize{ fileSize } @@ -52,10 +24,9 @@ namespace AZ::IO { } - ArchiveFileIterator::ArchiveFileIterator(FindData* findData, AZStd::string_view filename, const FileDesc& fileDesc) + // ArchiveFileIterator + ArchiveFileIterator::ArchiveFileIterator(FindData* findData) : m_findData{ findData } - , m_filename{ filename } - , m_fileDesc{ fileDesc } { } @@ -73,21 +44,36 @@ namespace AZ::IO return operator++(); } - bool ArchiveFileIterator::operator==(const AZ::IO::ArchiveFileIterator& rhs) const - { - return GetHash() == rhs.GetHash(); - } ArchiveFileIterator::operator bool() const { return m_findData && m_lastFetchValid; } - size_t ArchiveFileIterator::GetHash() const + // FindData::ArchiveFile + FindData::ArchiveFile::ArchiveFile() = default; + FindData::ArchiveFile::ArchiveFile(AZStd::string_view filename, const FileDesc& fileDesc) + : m_filename(filename) + , m_fileDesc(fileDesc) + { + } + size_t FindData::ArchiveFile::GetHash() const { return AZStd::hash{}(m_filename.c_str()); } + bool FindData::ArchiveFile::operator==(const ArchiveFile& rhs) const + { + return GetHash() == rhs.GetHash(); + } + + // FindData::ArchiveFilehash + size_t FindData::ArchiveFileHash::operator()(const ArchiveFile& archiveFile) const + { + return archiveFile.GetHash(); + } + + // FindData void FindData::Scan(IArchive* archive, AZStd::string_view szDir, bool bAllowUseFS, bool bScanZips) { // get the priority into local variable to avoid it changing in the course of @@ -119,40 +105,37 @@ namespace AZ::IO void FindData::ScanFS([[maybe_unused]] IArchive* archive, AZStd::string_view szDirIn) { - AZStd::string searchDirectory; - AZStd::string pattern; + AZ::IO::PathView directory{ szDirIn }; + AZ::IO::FixedMaxPath searchDirectory = directory.ParentPath(); + AZ::IO::FixedMaxPath pattern = directory.Filename(); + auto ScanFileSystem = [this](const char* filePath) -> bool { - AZ::IO::PathString directory{ szDirIn }; - AZ::StringFunc::Path::GetFullPath(directory.c_str(), searchDirectory); - AZ::StringFunc::Path::GetFullFileName(directory.c_str(), pattern); - } - AZ::IO::FileIOBase::GetDirectInstance()->FindFiles(searchDirectory.c_str(), pattern.c_str(), [&](const char* filePath) -> bool - { - AZ::IO::ArchiveFileIterator fileIterator{ nullptr, AZ::IO::PathView(filePath).Filename().Native(), {} }; + ArchiveFile archiveFile{ AZ::IO::PathView(filePath).Filename().Native(), {} }; if (AZ::IO::FileIOBase::GetDirectInstance()->IsDirectory(filePath)) { - fileIterator.m_fileDesc.nAttrib = fileIterator.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::Subdirectory; - m_fileSet.emplace(AZStd::move(fileIterator)); + archiveFile.m_fileDesc.nAttrib = archiveFile.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::Subdirectory; + m_fileSet.emplace(AZStd::move(archiveFile)); } else { if (AZ::IO::FileIOBase::GetDirectInstance()->IsReadOnly(filePath)) { - fileIterator.m_fileDesc.nAttrib = fileIterator.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::ReadOnly; + archiveFile.m_fileDesc.nAttrib = archiveFile.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::ReadOnly; } AZ::u64 fileSize = 0; AZ::IO::FileIOBase::GetDirectInstance()->Size(filePath, fileSize); - fileIterator.m_fileDesc.nSize = fileSize; - fileIterator.m_fileDesc.tWrite = AZ::IO::FileIOBase::GetDirectInstance()->ModificationTime(filePath); + archiveFile.m_fileDesc.nSize = fileSize; + archiveFile.m_fileDesc.tWrite = AZ::IO::FileIOBase::GetDirectInstance()->ModificationTime(filePath); // These times are not supported by our file interface - fileIterator.m_fileDesc.tAccess = fileIterator.m_fileDesc.tWrite; - fileIterator.m_fileDesc.tCreate = fileIterator.m_fileDesc.tWrite; - m_fileSet.emplace(AZStd::move(fileIterator)); + archiveFile.m_fileDesc.tAccess = archiveFile.m_fileDesc.tWrite; + archiveFile.m_fileDesc.tCreate = archiveFile.m_fileDesc.tWrite; + m_fileSet.emplace(AZStd::move(archiveFile)); } return true; - }); + }; + AZ::IO::FileIOBase::GetDirectInstance()->FindFiles(searchDirectory.c_str(), pattern.c_str(), ScanFileSystem); } ////////////////////////////////////////////////////////////////////////// @@ -180,7 +163,7 @@ namespace AZ::IO fileDesc.nAttrib = AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive; fileDesc.nSize = fileEntry->desc.lSizeUncompressed; fileDesc.tWrite = fileEntry->GetModificationTime(); - m_fileSet.emplace(AZ::IO::ArchiveFileIterator{ this, fname, fileDesc }); + m_fileSet.emplace(fname, fileDesc); } ZipDir::FindDir findDirectoryEntry(zipCache); @@ -193,7 +176,7 @@ namespace AZ::IO } AZ::IO::FileDesc fileDesc; fileDesc.nAttrib = AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive | AZ::IO::FileDesc::Attribute::Subdirectory; - m_fileSet.emplace(AZ::IO::ArchiveFileIterator{ this, fname, fileDesc }); + m_fileSet.emplace(fname, fileDesc); } }; @@ -208,30 +191,16 @@ namespace AZ::IO // so there's really no way to filter out opening the pack and looking at the files inside. // however, the bind root is not part of the inner zip entry name either // and the ZipDir::FindFile actually expects just the chopped off piece. - // we have to find whats in common between them and check that: + // we have to find the common path segments between them and check that: - auto resolvedBindRoot = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(it->m_pathBindRoot); - if (!resolvedBindRoot) + AZ::IO::FixedMaxPath bindRoot; + if (!AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(bindRoot, it->m_pathBindRoot)) { AZ_Assert(false, "Unable to resolve Path for archive %s bind root %s", it->GetFullPath(), it->m_pathBindRoot.c_str()); return; } - AZ::IO::FixedMaxPath bindRoot{ *resolvedBindRoot }; - auto [bindRootIter, sourcePathIter] = AZStd::mismatch(AZStd::begin(bindRoot), AZStd::end(bindRoot), - AZStd::begin(sourcePath), AZStd::end(sourcePath)); - if (sourcePathIter == AZStd::begin(sourcePath)) - { - // The path has no characters in common , early out the search as filepath is not part of the iterated zip - continue; - } - - AZ::IO::FixedMaxPath sourcePathRemainder; - for (; sourcePathIter != AZStd::end(sourcePath); ++sourcePathIter) - { - sourcePathRemainder /= *sourcePathIter; - } // Example: // "@assets@\\levels\\*" <--- szDir // "@assets@\\" <--- mount point @@ -256,18 +225,26 @@ namespace AZ::IO // then it means that the pack's mount point itself might be a return value, not the files inside the pack // in that case, we compare the mount point remainder itself with the search filter + auto [bindRootIter, sourcePathIter] = AZStd::mismatch(bindRoot.begin(), bindRoot.end(), + sourcePath.begin(), sourcePath.end()); if (bindRootIter != bindRoot.end()) { + AZ::IO::FixedMaxPath sourcePathRemainder; + for (; sourcePathIter != sourcePath.end(); ++sourcePathIter) + { + sourcePathRemainder /= *sourcePathIter; + } + // Retrieve next path component of the mount point remainder - if (!bindRootIter->empty() && AZStd::wildcard_match(sourcePathRemainder.Native(), bindRootIter->Native())) + if (!bindRootIter->empty() && bindRootIter->Match(sourcePathRemainder.Native())) { AZ::IO::FileDesc fileDesc{ AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive | AZ::IO::FileDesc::Attribute::Subdirectory }; - m_fileSet.emplace(AZ::IO::ArchiveFileIterator{ this, bindRootIter->Native(), fileDesc }); + m_fileSet.emplace(AZStd::move(bindRootIter->Native()), fileDesc); } } else { - + AZ::IO::FixedMaxPath sourcePathRemainder = sourcePath.LexicallyRelative(bindRoot); // if we get here, it means that the search pattern's root and the mount point for this pack are identical // which means we may search inside the pack. ScanInZip(it->pZip.get(), sourcePathRemainder.Native()); @@ -280,17 +257,17 @@ namespace AZ::IO { if (m_fileSet.empty()) { - AZ::IO::ArchiveFileIterator emptyFileIterator; - emptyFileIterator.m_lastFetchValid = false; - emptyFileIterator.m_findData = this; - return emptyFileIterator; + return {}; } // Remove Fetched item from the FindData map so that the iteration continues - AZ::IO::ArchiveFileIterator fileIterator{ *m_fileSet.begin() }; + AZ::IO::ArchiveFileIterator fileIterator; + auto archiveFileIt = m_fileSet.begin(); + fileIterator.m_filename = archiveFileIt->m_filename; + fileIterator.m_fileDesc = archiveFileIt->m_fileDesc; fileIterator.m_lastFetchValid = true; fileIterator.m_findData = this; - m_fileSet.erase(m_fileSet.begin()); + m_fileSet.erase(archiveFileIt); return fileIterator; } } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.h b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.h index 12544d124d..ebdbf45626 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.h @@ -36,56 +36,72 @@ namespace AZ::IO AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::IO::FileDesc::Attribute); + inline constexpr size_t ArchiveFilenameMaxLength = 256; + using ArchiveFileString = AZStd::fixed_string; + class FindData; + //! This is not really an iterator, but a handle + //! that extends ownership of any found filenames from an archive file or the file system struct ArchiveFileIterator { ArchiveFileIterator() = default; - ArchiveFileIterator(FindData* findData, AZStd::string_view filename, const FileDesc& fileDesc); + explicit ArchiveFileIterator(FindData* findData); ArchiveFileIterator operator++(); ArchiveFileIterator operator++(int); - bool operator==(const AZ::IO::ArchiveFileIterator& rhs) const; - explicit operator bool() const; - size_t GetHash() const; - - inline static constexpr size_t FilenameMaxLength = 256; - AZStd::fixed_string m_filename; + ArchiveFileString m_filename; FileDesc m_fileDesc; - AZStd::intrusive_ptr m_findData{}; private: friend class FindData; + friend class Archive; + AZStd::intrusive_ptr m_findData; bool m_lastFetchValid{}; }; - struct ArchiveFileIteratorHash - { - size_t operator()(const AZ::IO::ArchiveFileIterator& iter) const; - }; - struct AZStdStringLessCaseInsensitive - { - bool operator()(AZStd::string_view left, AZStd::string_view right) const; - - using is_transparent = void; - }; class FindData : public AZStd::intrusive_base { public: AZ_CLASS_ALLOCATOR(FindData, AZ::SystemAllocator, 0); FindData() = default; - AZ::IO::ArchiveFileIterator Fetch(); + ArchiveFileIterator Fetch(); void Scan(IArchive* archive, AZStd::string_view path, bool bAllowUseFS = false, bool bScanZips = true); protected: void ScanFS(IArchive* archive, AZStd::string_view path); + // Populates the FileSet with files within the that match the path pattern that is + // if it refers to a file within a bound archive root or returns the archive root + // path if the path pattern matches it. void ScanZips(IArchive* archive, AZStd::string_view path); - using FileSet = AZStd::unordered_set; + class ArchiveFile + { + public: + friend class FindData; + + ArchiveFile(); + ArchiveFile(AZStd::string_view filename, const FileDesc& fileDesc); + + size_t GetHash() const; + bool operator==(const ArchiveFile& rhs) const; + + private: + ArchiveFileString m_filename; + FileDesc m_fileDesc; + }; + + struct ArchiveFileHash + { + size_t operator()(const ArchiveFile& archiveFile) const; + }; + + using FileSet = AZStd::unordered_set; FileSet m_fileSet; }; + } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h b/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h index 5ac417564a..bd9615110a 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h @@ -12,12 +12,10 @@ #include #include #include -#include #include #include #include #include -#include #include @@ -106,66 +104,6 @@ namespace AZ::IO { AZ_RTTI(IArchive, "{764A2260-FF8A-4C86-B958-EBB0B69D9DFA}"); using FileTime = uint64_t; - // Flags used in file path resolution rules - enum EPathResolutionRules - { - // If used, the source path will be treated as the destination path - // and no transformations will be done. Pass this flag when the path is to be the actual - // path on the disk/in the packs and doesn't need adjustment (or after it has come through adjustments already) - // if this is set, AdjustFileName will not map the input path into the folder (Ex: Shaders will not be converted to Game\Shaders) - FLAGS_PATH_REAL = 1 << 16, - - // AdjustFileName will always copy the file path to the destination path: - // regardless of the returned value, szDestpath can be used - FLAGS_COPY_DEST_ALWAYS = 1 << 17, - - // Adds trailing slash to the path - FLAGS_ADD_TRAILING_SLASH = 1L << 18, - - // if this is set, AdjustFileName will not make relative paths into full paths - FLAGS_NO_FULL_PATH = 1 << 21, - - // if this is set, AdjustFileName will redirect path to disc - FLAGS_REDIRECT_TO_DISC = 1 << 22, - - // if this is set, AdjustFileName will not adjust path for writing files - FLAGS_FOR_WRITING = 1 << 23, - - // if this is set, the archive would be stored in memory (gpu) - FLAGS_PAK_IN_MEMORY = 1 << 25, - - // Store all file names as crc32 in a flat directory structure. - FLAGS_FILENAMES_AS_CRC32 = 1 << 26, - - // if this is set, AdjustFileName will try to find the file under any mod paths we know about - FLAGS_CHECK_MOD_PATHS = 1 << 27, - - // if this is set, AdjustFileName will always check the filesystem/disk and not check inside open archives - FLAGS_NEVER_IN_PAK = 1 << 28, - - // returns existing file name from the local data or existing cache file name - // used by the resource compiler to pass the real file name - FLAGS_RESOLVE_TO_CACHE = 1 << 29, - - // if this is set, the archive would be stored in memory (cpu) - FLAGS_PAK_IN_MEMORY_CPU = 1 << 30, - - // if this is set, the level pak is inside another archive - FLAGS_LEVEL_PAK_INSIDE_PAK = 1 << 31, - }; - - // Used for widening FOpen functionality. They're ignored for the regular File System files. - enum EFOpenFlags - { - // If possible, will prevent the file from being read from memory. - FOPEN_HINT_DIRECT_OPERATION = 1, - // Will prevent a "missing file" warnings to be created. - FOPEN_HINT_QUIET = 1 << 1, - // File should be on disk - FOPEN_ONDISK = 1 << 2, - // Open is done by the streaming thread. - FOPEN_FORSTREAMING = 1 << 3, - }; // enum ERecordFileOpenList @@ -175,8 +113,6 @@ namespace AZ::IO RFOM_Level, // during level loading till export2game -> resourcelist.txt, used to generate the list for level2level loading RFOM_NextLevel // used for level2level loading }; - // the size of the buffer that receives the full path to the file - inline static constexpr size_t MaxPath = 1024; //file location enum used in isFileExist to control where the archive system looks for the file. enum EFileSearchLocation @@ -205,63 +141,31 @@ namespace AZ::IO virtual ~IArchive() = default; - /** - * Deprecated: Use the AZ::IO::FileIOBase::ResolvePath function below that doesn't accept the nFlags or skipMods parameters - * given the source relative path, constructs the full path to the file according to the flags - * returns the pointer to the constructed path (can be either szSourcePath, or szDestPath, or NULL in case of error - */ - // - virtual const char* AdjustFileName(AZStd::string_view src, char* dst, size_t dstSize, uint32_t nFlags, bool skipMods = false) = 0; - - virtual bool Init(AZStd::string_view szBasePath) = 0; - virtual void Release() = 0; - - // Summary: - // Returns true if given archivepath is installed to HDD - // If no file path is given it will return true if whole application is installed to HDD - virtual bool IsInstalledToHDD(AZStd::string_view acFilePath = 0) const = 0; - // after this call, the archive file will be searched for files when they aren't on the OS file system // Arguments: // pName - must not be 0 - virtual bool OpenPack(AZStd::string_view pName, uint32_t nFlags = FLAGS_PATH_REAL, AZStd::intrusive_ptr pData = {}, + virtual bool OpenPack(AZStd::string_view pName, AZStd::intrusive_ptr pData = {}, AZ::IO::FixedMaxPathString* pFullPath = nullptr, bool addLevels = true) = 0; // after this call, the archive file will be searched for files when they aren't on the OS file system - virtual bool OpenPack(AZStd::string_view pBindingRoot, AZStd::string_view pName, uint32_t nFlags = FLAGS_PATH_REAL, + virtual bool OpenPack(AZStd::string_view pBindingRoot, AZStd::string_view pName, AZStd::intrusive_ptr pData = {}, AZ::IO::FixedMaxPathString* pFullPath = nullptr, bool addLevels = true) = 0; // after this call, the file will be unlocked and closed, and its contents won't be used to search for files - virtual bool ClosePack(AZStd::string_view pName, uint32_t nFlags = FLAGS_PATH_REAL) = 0; + virtual bool ClosePack(AZStd::string_view pName) = 0; // opens pack files by the path and wildcard - virtual bool OpenPacks(AZStd::string_view pWildcard, uint32_t nFlags = FLAGS_PATH_REAL, AZStd::vector* pFullPaths = nullptr) = 0; + virtual bool OpenPacks(AZStd::string_view pWildcard, AZStd::vector* pFullPaths = nullptr) = 0; // opens pack files by the path and wildcard - virtual bool OpenPacks(AZStd::string_view pBindingRoot, AZStd::string_view pWildcard, uint32_t nFlags = FLAGS_PATH_REAL, + virtual bool OpenPacks(AZStd::string_view pBindingRoot, AZStd::string_view pWildcard, AZStd::vector* pFullPaths = nullptr) = 0; // closes pack files by the path and wildcard - virtual bool ClosePacks(AZStd::string_view pWildcard, uint32_t nFlags = FLAGS_PATH_REAL) = 0; + virtual bool ClosePacks(AZStd::string_view pWildcard) = 0; //returns if a archive exists matching the wildcard virtual bool FindPacks(AZStd::string_view pWildcardIn) = 0; // Set access status of a archive files with a wildcard - virtual bool SetPacksAccessible(bool bAccessible, AZStd::string_view pWildcard, uint32_t nFlags = FLAGS_PATH_REAL) = 0; + virtual bool SetPacksAccessible(bool bAccessible, AZStd::string_view pWildcard) = 0; // Set access status of a pack file - virtual bool SetPackAccessible(bool bAccessible, AZStd::string_view pName, uint32_t nFlags = FLAGS_PATH_REAL) = 0; - - // Load or unload archive file completely to memory. - virtual bool LoadPakToMemory(AZStd::string_view pName, EInMemoryArchiveLocation eLoadToMemory, AZStd::intrusive_ptr pMemoryBlock = nullptr) = 0; - virtual void LoadPaksToMemory(int nMaxArchiveSize, bool bLoadToMemory) = 0; - - // Processes an alias command line containing multiple aliases. - virtual void ParseAliases(AZStd::string_view szCommandLine) = 0; - // adds or removes an alias from the list - virtual void SetAlias(AZStd::string_view szName, AZStd::string_view szAlias, bool bAdd) = 0; - // gets an alias from the list, if any exist. - // if bReturnSame==true, it will return the input name if an alias doesn't exist. Otherwise returns NULL - virtual const char* GetAlias(AZStd::string_view szName, bool bReturnSame = true) = 0; - - // lock all the operations - virtual void Lock() = 0; - virtual void Unlock() = 0; + virtual bool SetPackAccessible(bool bAccessible, AZStd::string_view pName) = 0; // Set and Get the localization folder name (Languages, Localization, ...) virtual void SetLocalizationFolder(AZStd::string_view sLocalizationFolder) = 0; @@ -273,28 +177,19 @@ namespace AZ::IO // ex: AZ::IO::HandleType fileHandle = FOpen( "test.txt","rbx" ); // mode x is a direct access mode, when used file reads will go directly into the low level file system without any internal data caching. // Text mode is not supported for files in Archives. - // for nFlags @see IArchive::EFOpenFlags - virtual AZ::IO::HandleType FOpen(AZStd::string_view pName, const char* mode, uint32_t nFlags = 0) = 0; - - // Read raw data from file, no endian conversion. - virtual size_t FReadRaw(void* data, size_t length, size_t elems, AZ::IO::HandleType fileHandle) = 0; - - // Read all file contents into the provided memory, nSizeOfFile must be the same as returned by GetFileSize(handle) - // Current seek pointer is ignored and reseted to 0. - // no endian conversion. - virtual size_t FReadRawAll(void* data, size_t nFileSize, AZ::IO::HandleType fileHandle) = 0; + virtual AZ::IO::HandleType FOpen(AZStd::string_view pName, const char* mode) = 0; // Get pointer to the internally cached, loaded data of the file. // WARNING! The returned pointer is only valid while the fileHandle has not been closed. virtual void* FGetCachedFileData(AZ::IO::HandleType fileHandle, size_t& nFileSize) = 0; - // Write file data, cannot be used for writing into the Archive. - // Use INestedArchive interface for writing into the archivefiles. - virtual size_t FWrite(const void* data, size_t length, size_t elems, AZ::IO::HandleType fileHandle) = 0; + // Read raw data from file, no endian conversion. + virtual size_t FRead(void* data, size_t bytesToRead, AZ::IO::HandleType fileHandle) = 0; + + // Write file data, cannot be used for writing into the Archive. + // Use INestedArchive interface for writing into the archive files. + virtual size_t FWrite(const void* data, size_t bytesToWrite, AZ::IO::HandleType fileHandle) = 0; - virtual int FPrintf(AZ::IO::HandleType fileHandle, const char* format, ...) = 0; - virtual char* FGets(char*, int, AZ::IO::HandleType) = 0; - virtual int Getc(AZ::IO::HandleType) = 0; virtual size_t FGetSize(AZ::IO::HandleType fileHandle) = 0; virtual size_t FGetSize(AZStd::string_view pName, bool bAllowUseFileSystem = false) = 0; virtual bool IsInPak(AZ::IO::HandleType fileHandle) = 0; @@ -318,7 +213,6 @@ namespace AZ::IO virtual AZStd::intrusive_ptr PoolAllocMemoryBlock(size_t nSize, const char* sUsage, size_t nAlign = 1) = 0; // Arguments: - // nFlags is a combination of EPathResolutionRules flags. virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, EFileSearchType searchType = eFileSearchType_AllowInZipsOnly) = 0; virtual ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator handle) = 0; virtual bool FindClose(AZ::IO::ArchiveFileIterator handle) = 0; @@ -334,9 +228,6 @@ namespace AZ::IO virtual IArchive::SignedFileSize GetFileSizeOnDisk(AZStd::string_view filename) = 0; - // creates a directory - virtual bool MakeDir(AZStd::string_view szPath) = 0; - // open the physical archive file - creates if it doesn't exist // returns NULL if it's invalid or can't open the file // nFlags is a combination of flags from EArchiveFlags enum. @@ -344,8 +235,8 @@ namespace AZ::IO AZStd::intrusive_ptr pData = nullptr) = 0; // returns the path to the archive in which the file was opened - // returns NULL if the file is a physical file, and "" if the path to archive is unknown (shouldn't ever happen) - virtual const char* GetFileArchivePath(AZ::IO::HandleType fileHandle) = 0; + // returns empty path view if the file is a physical file + virtual AZ::IO::PathView GetFileArchivePath(AZ::IO::HandleType fileHandle) = 0; // compresses the raw data into raw data. The buffer for compressed data itself with the heap passed. Uses method 8 (deflate) // returns one of the Z_* errors (Z_OK upon success) @@ -378,25 +269,7 @@ namespace AZ::IO // get the current mode, can be set by RecordFileOpen() virtual IArchive::ERecordFileOpenList GetRecordFileOpenList() = 0; - // computes CRC (zip compatible) for a file - // useful if a huge uncompressed file is generation in non continuous way - // good for big files - low memory overhead (1MB) - // Arguments: - // szPath - must not be 0 - // Returns: - // error code - virtual uint32_t ComputeCRC(AZStd::string_view szPath, uint32_t nFileOpenFlags = 0) = 0; - - // computes MD5 checksum for a file - // good for big files - low memory overhead (1MB) - // Arguments: - // szPath - must not be 0 - // md5 - destination array of uint8_t [16] - // Returns: - // true if success, false on failure - virtual bool ComputeMD5(AZStd::string_view szPath, uint8_t* md5, uint32_t nFileOpenFlags = 0, bool useDirectFileAccess = false) = 0; - - // useful for gathering file access statistics, assert if it was inserted already but then it does not become insersted + // useful for gathering file access statistics, assert if it was inserted already but then it does not become inserted // Arguments: // pSink - must not be 0 virtual void RegisterFileAccessSink(IArchiveFileAccessSink* pSink) = 0; @@ -408,8 +281,6 @@ namespace AZ::IO // When enabled, files accessed at runtime will be tracked virtual void DisableRuntimeFileAccess(bool status) = 0; virtual bool DisableRuntimeFileAccess(bool status, AZStd::thread_id threadId) = 0; - virtual bool CheckFileAccessDisabled(AZStd::string_view name, const char* mode) = 0; - virtual void SetRenderThreadId(AZStd::thread_id renderThreadId) = 0; // gets the current pak priority virtual ArchiveLocationPriority GetPakPriority() const = 0; @@ -431,21 +302,6 @@ namespace AZ::IO using LevelPackCloseEvent = AZ::Event; virtual auto GetLevelPackCloseEvent()->LevelPackCloseEvent* = 0; - // Type-safe endian conversion read. - template - size_t FRead(T* data, size_t elems, AZ::IO::HandleType fileHandle, bool bSwapEndian = false) - { - size_t count = FReadRaw(data, sizeof(T), elems, fileHandle); - SwapEndian(data, count, bSwapEndian); - return count; - } - // Type-independent Write. - template - void FWrite(T* data, size_t elems, AZ::IO::HandleType fileHandle) - { - FWrite((void*)data, sizeof(T), elems, fileHandle); - } - inline static constexpr IArchive::SignedFileSize FILE_NOT_PRESENT = -1; }; diff --git a/Code/Framework/AzFramework/AzFramework/Archive/INestedArchive.h b/Code/Framework/AzFramework/AzFramework/Archive/INestedArchive.h index b45d705259..f85fd273ce 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/INestedArchive.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/INestedArchive.h @@ -9,9 +9,9 @@ #pragma once +#include #include #include -#include #include namespace AZ::IO @@ -71,28 +71,10 @@ namespace AZ::IO // multiple times FLAGS_DONT_COMPACT = 1 << 5, - // flag is set when complete pak has been loaded into memory - FLAGS_IN_MEMORY = 1 << 6, - FLAGS_IN_MEMORY_CPU = 1 << 7, - FLAGS_IN_MEMORY_MASK = FLAGS_IN_MEMORY | FLAGS_IN_MEMORY_CPU, - - // Store all file names as crc32 in a flat directory structure. - FLAGS_FILENAMES_AS_CRC32 = 1 << 8, - - // flag is set when pak is stored on HDD - FLAGS_ON_HDD = 1 << 9, - - //Override pak - paks opened with this flag go at the end of the list and contents will be found before other paks - //Used for patching - FLAGS_OVERRIDE_PAK = 1 << 10, - // Disable a pak file without unloading it, this flag is used in combination with patches and multiplayer - // to ensure that specific paks stay in the position(to keep the same priority) but beeing disabled + // to ensure that specific paks stay in the position(to keep the same priority) but being disabled // when running multiplayer FLAGS_DISABLE_PAK = 1 << 11, - - // flag is set when pak is inside another pak - FLAGS_INSIDE_PAK = 1 << 12, }; using Handle = void*; @@ -122,7 +104,7 @@ namespace AZ::IO virtual int StartContinuousFileUpdate(AZStd::string_view szRelativePath, uint64_t nSize) = 0; // Summary: - // Adds a new file to the zip or update an existing's segment if it is not compressed - just stored + // Adds a new file to the zip or update an existing segment if it is not compressed - just stored // adds a directory (creates several nested directories if needed) // ( name might be misleading as if nOverwriteSeekPos is used the update is not continuous ) // Arguments: @@ -164,7 +146,7 @@ namespace AZ::IO // Summary: // Get the full path to the archive file. - virtual const char* GetFullPath() const = 0; + virtual AZ::IO::PathView GetFullPath() const = 0; // Summary: // Get the flags of this object. diff --git a/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.cpp index dc1d4aa864..1e0f237df5 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.cpp @@ -174,7 +174,7 @@ namespace AZ::IO return m_pCache->ReadFile(reinterpret_cast(fileHandle), nullptr, pBuffer); } - const char* NestedArchive::GetFullPath() const + AZ::IO::PathView NestedArchive::GetFullPath() const { return m_pCache->GetFilePath(); } @@ -193,19 +193,9 @@ namespace AZ::IO if (nFlagsToSet & FLAGS_RELATIVE_PATHS_ONLY) { m_nFlags |= FLAGS_RELATIVE_PATHS_ONLY; - } - - if (nFlagsToSet & FLAGS_ON_HDD) - { - m_nFlags |= FLAGS_ON_HDD; - } - - if (nFlagsToSet & FLAGS_RELATIVE_PATHS_ONLY || - nFlagsToSet & FLAGS_ON_HDD) - { - // we don't support changing of any other flags return true; } + return false; } @@ -252,20 +242,12 @@ namespace AZ::IO return AZ::IO::FixedMaxPathString{ szRelativePath }; } - if ((szRelativePath.size() > 1 && szRelativePath[1] == ':') || (m_nFlags & FLAGS_ABSOLUTE_PATHS)) + if ((m_nFlags & FLAGS_ABSOLUTE_PATHS) == FLAGS_ABSOLUTE_PATHS) { // make the normalized full path and try to match it against the binding root of this object - auto resolvedPath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(szRelativePath); - - // Make sure the resolve path is longer than the bind root and that it starts with the bind root - if (!resolvedPath || resolvedPath->Native().size() <= m_strBindRoot.size() || azstrnicmp(resolvedPath->c_str(), m_strBindRoot.c_str(), m_strBindRoot.size()) != 0) - { - return {}; - } - - // Remove the bind root prefix from the resolved path - resolvedPath->Native().erase(0, m_strBindRoot.size() + 1); - return resolvedPath->Native(); + AZ::IO::FixedMaxPath resolvedPath; + AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(resolvedPath, szRelativePath); + return resolvedPath.LexicallyProximate(m_strBindRoot).Native(); } return AZ::IO::FixedMaxPathString{ szRelativePath }; diff --git a/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.h b/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.h index 8ab943a24e..34bbcdc201 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.h @@ -19,15 +19,15 @@ namespace AZ::IO { bool operator()(const INestedArchive* left, const INestedArchive* right) const { - return azstricmp(left->GetFullPath(), right->GetFullPath()) < 0; + return left->GetFullPath() < right->GetFullPath(); } bool operator()(AZStd::string_view left, const INestedArchive* right) const { - return azstrnicmp(left.data(), right->GetFullPath(), left.size()) < 0; + return AZ::IO::PathView(left) < right->GetFullPath(); } bool operator()(const INestedArchive* left, AZStd::string_view right) const { - return azstrnicmp(left->GetFullPath(), right.data(), right.size()) < 0; + return left->GetFullPath() < AZ::IO::PathView(right); } }; @@ -40,7 +40,7 @@ namespace AZ::IO NestedArchive(IArchive* pArchive, AZStd::string_view strBindRoot, ZipDir::CachePtr pCache, uint32_t nFlags = 0); ~NestedArchive() override; - auto GetRootFolderHandle() -> Handle; + auto GetRootFolderHandle() -> Handle override; // Adds a new file to the zip or update an existing one // adds a directory (creates several nested directories if needed) @@ -66,26 +66,26 @@ namespace AZ::IO int RemoveDir(AZStd::string_view szRelativePath) override; // deletes all files from the archive - int RemoveAll(); + int RemoveAll() override; // finds the file; you don't have to close the returned handle - Handle FindFile(AZStd::string_view szRelativePath); + Handle FindFile(AZStd::string_view szRelativePath) override; // returns the size of the file (unpacked) by the handle - uint64_t GetFileSize(Handle fileHandle); + uint64_t GetFileSize(Handle fileHandle) override; // reads the file into the preallocated buffer (must be at least the size of GetFileSize()) - int ReadFile(Handle fileHandle, void* pBuffer); + int ReadFile(Handle fileHandle, void* pBuffer) override; // returns the full path to the archive file - const char* GetFullPath() const; + AZ::IO::PathView GetFullPath() const override; ZipDir::Cache* GetCache(); - uint32_t GetFlags() const; - bool SetFlags(uint32_t nFlagsToSet); - bool ResetFlags(uint32_t nFlagsToReset); + uint32_t GetFlags() const override; + bool SetFlags(uint32_t nFlagsToSet) override; + bool ResetFlags(uint32_t nFlagsToReset) override; - bool SetPackAccessible(bool bAccessible); + bool SetPackAccessible(bool bAccessible) override; protected: // returns the pointer to the relative file path to be passed @@ -95,7 +95,7 @@ namespace AZ::IO ZipDir::CachePtr m_pCache; // the binding root may be empty string - in this case, the absolute path binding won't work - AZStd::string m_strBindRoot; + AZ::IO::Path m_strBindRoot; IArchive* m_archive{}; uint32_t m_nFlags{}; }; diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.cpp index 81f26d78b8..d17dbd0837 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -104,24 +103,21 @@ namespace AZ::IO::ZipDir : m_pCache(pCache) , m_bCommitted(false) { - AZ::IO::PathString normalizedPath{ szRelativePath }; - AZ::StringFunc::Path::Normalize(normalizedPath); - AZStd::to_lower(AZStd::begin(normalizedPath), AZStd::end(normalizedPath)); // Update the cache string pool with the relative path to the file - auto pathIt = m_pCache->m_relativePathPool.emplace(normalizedPath); + auto pathIt = m_pCache->m_relativePathPool.emplace(AZ::IO::PathView(szRelativePath).LexicallyNormal()); m_szRelativePath = *pathIt.first; // this is the name of the directory - create it or find it - m_pFileEntry = m_pCache->GetRoot()->Add(m_szRelativePath); + m_pFileEntry = m_pCache->GetRoot()->Add(m_szRelativePath.Native()); if (m_pFileEntry && az_archive_zip_directory_cache_verbosity) { - AZ_TracePrintf("Archive", R"(File "%s" has been added to archive at root "%s")", normalizedPath.c_str(), pCache->GetFilePath()); + AZ_TracePrintf("Archive", R"(File "%s" has been added to archive at root "%s")", pathIt.first->c_str(), pCache->GetFilePath()); } } ~FileEntryTransactionAdd() { if (m_pFileEntry && !m_bCommitted) { - m_pCache->RemoveFile(m_szRelativePath); + m_pCache->RemoveFile(m_szRelativePath.Native()); m_pCache->m_relativePathPool.erase(m_szRelativePath); } } @@ -131,11 +127,11 @@ namespace AZ::IO::ZipDir } AZStd::string_view GetRelativePath() const { - return m_szRelativePath; + return m_szRelativePath.Native(); } private: Cache* m_pCache; - AZStd::string_view m_szRelativePath; + AZ::IO::PathView m_szRelativePath; FileEntry* m_pFileEntry; bool m_bCommitted; }; @@ -587,34 +583,27 @@ namespace AZ::IO::ZipDir // deletes the file from the archive ErrorEnum Cache::RemoveFile(AZStd::string_view szRelativePathSrc) { - // Normalize and lower case the relative path - AZ::IO::PathString szRelativePath{ szRelativePathSrc }; - AZ::StringFunc::Path::Normalize(szRelativePath); - AZStd::to_lower(AZStd::begin(szRelativePath), AZStd::end(szRelativePath)); - AZStd::string_view normalizedRelativePath = szRelativePath; - - // find the last slash in the path - size_t slashOffset = normalizedRelativePath.find_last_of(AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR); + AZ::IO::PathView szRelativePath{ szRelativePathSrc }; AZStd::string_view fileName; // the name of the file to delete FileEntryTree* pDir; // the dir from which the subdir will be deleted - if (slashOffset != AZStd::string_view::npos) + if (szRelativePath.HasParentPath()) { FindDir fd(GetRoot()); // the directory to remove - pDir = fd.FindExact(normalizedRelativePath.substr(0, slashOffset)); + pDir = fd.FindExact(szRelativePath.ParentPath()); if (!pDir) { return ZD_ERROR_DIR_NOT_FOUND;// there is no such directory } - fileName = normalizedRelativePath.substr(slashOffset + 1); + fileName = szRelativePath.Filename().Native(); } else { pDir = GetRoot(); - fileName = normalizedRelativePath; + fileName = szRelativePath.Native(); } ErrorEnum e = pDir->RemoveFile(fileName); @@ -625,7 +614,7 @@ namespace AZ::IO::ZipDir if (az_archive_zip_directory_cache_verbosity) { AZ_TracePrintf("Archive", R"(File "%.*s" has been remove from archive at root "%s")", - aznumeric_cast(fileName.size()), fileName.data(), GetFilePath()); + AZ_STRING_ARG(szRelativePath.Native()), GetFilePath()); } } return e; @@ -635,45 +624,38 @@ namespace AZ::IO::ZipDir // deletes the directory, with all its descendants (files and subdirs) ErrorEnum Cache::RemoveDir(AZStd::string_view szRelativePathSrc) { - // Normalize and lower case the relative path - AZ::IO::PathString szRelativePath{ szRelativePathSrc }; - AZ::StringFunc::Path::Normalize(szRelativePath); - AZStd::to_lower(AZStd::begin(szRelativePath), AZStd::end(szRelativePath)); - AZStd::string_view normalizedRelativePath = szRelativePath; - - // find the last slash in the path - size_t slashOffset = normalizedRelativePath.find_last_of(AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR); + AZ::IO::PathView szRelativePath{ szRelativePathSrc }; AZStd::string_view dirName; // the name of the dir to delete FileEntryTree* pDir; // the dir from which the subdir will be deleted - if (slashOffset != AZStd::string_view::npos) + if (szRelativePath.HasParentPath()) { FindDir fd(GetRoot()); // the directory to remove - pDir = fd.FindExact(normalizedRelativePath.substr(0, slashOffset)); + pDir = fd.FindExact(szRelativePath.ParentPath()); if (!pDir) { return ZD_ERROR_DIR_NOT_FOUND;// there is no such directory } - dirName = normalizedRelativePath.substr(slashOffset + 1); + dirName = szRelativePath.Filename().Native(); } else { pDir = GetRoot(); - dirName = normalizedRelativePath; + dirName = szRelativePath.Native(); } - ErrorEnum e = pDir->RemoveDir(normalizedRelativePath); + ErrorEnum e = pDir->RemoveDir(dirName); if (e == ZD_ERROR_SUCCESS) { m_nFlags |= FLAGS_UNCOMPACTED | FLAGS_CDR_DIRTY; if (az_archive_zip_directory_cache_verbosity) { - AZ_TracePrintf("Archive", R"(File "%.*s" has been remove from archive at root "%s")", - aznumeric_cast(normalizedRelativePath.size()), normalizedRelativePath.data(), GetFilePath()); + AZ_TracePrintf("Archive", R"(Directory "%.*s" has been remove from archive at root "%s")", + AZ_STRING_ARG(szRelativePath.Native()), GetFilePath()); } } return e; @@ -769,9 +751,7 @@ namespace AZ::IO::ZipDir // finds the file by exact path FileEntry* Cache::FindFile(AZStd::string_view szPathSrc, [[maybe_unused]] bool bFullInfo) { - AZ::IO::PathString szPath{ szPathSrc }; - AZ::StringFunc::Path::Normalize(szPath); - AZStd::to_lower(AZStd::begin(szPath), AZStd::end(szPath)); + AZ::IO::PathView szPath{ szPathSrc }; ZipDir::FindFile fd(GetRoot()); FileEntry* fileEntry = fd.FindExact(szPath); @@ -779,19 +759,13 @@ namespace AZ::IO::ZipDir { if (az_archive_zip_directory_cache_verbosity) { - AZ_TracePrintf("Archive", "FindExact failed to find file %s at root %s", szPath.c_str(), GetFilePath()); + AZ_TracePrintf("Archive", "FindExact failed to find file %.*s at root %s", AZ_STRING_ARG(szPath.Native()), GetFilePath()); } return {}; } return fileEntry; } - // returns the size of memory occupied by the instance referred to by this cache - size_t Cache::GetSize() const - { - return sizeof(*this) + m_strFilePath.capacity() + m_treeDir.GetSize() - sizeof(m_treeDir); - } - // refreshes information about the given file entry into this file entry ErrorEnum Cache::Refresh(FileEntryBase* pFileEntry) { @@ -800,7 +774,7 @@ namespace AZ::IO::ZipDir return ZD_ERROR_INVALID_CALL; } - if (pFileEntry->nFileDataOffset != pFileEntry->INVALID_DATA_OFFSET) + if (pFileEntry->nFileDataOffset != FileEntryBase::INVALID_DATA_OFFSET) { return ZD_ERROR_SUCCESS; // the data offset has been successfully read.. } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.h b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.h index 35bf0ae251..646410f8db 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.h @@ -16,6 +16,7 @@ #pragma once #include +#include #include #include #include @@ -89,9 +90,6 @@ namespace AZ::IO::ZipDir // refreshes information about the given file entry into this file entry ErrorEnum Refresh(FileEntryBase* pFileEntry); - // returns the size of memory occupied by the instance of this cache - size_t GetSize() const; - // QUICK check to determine whether the file entry belongs to this object bool IsOwnerOf(const FileEntry* pFileEntry) const { @@ -100,9 +98,9 @@ namespace AZ::IO::ZipDir // returns the string - path to the zip file from which this object was constructed. // this will be "" if the object was constructed with a factory that wasn't created with FLAGS_MEMORIZE_ZIP_PATH - const char* GetFilePath() const + AZ::IO::PathView GetFilePath() const { - return m_strFilePath.c_str(); + return m_strFilePath; } FileEntryTree* GetRoot() @@ -135,10 +133,10 @@ namespace AZ::IO::ZipDir FileEntryTree m_treeDir; AZ::IO::HandleType m_fileHandle; AZ::IAllocatorAllocate* m_allocator; - AZStd::string m_strFilePath; + AZ::IO::Path m_strFilePath; // String Pool for persistently storing paths as long as they reside in the cache - AZStd::unordered_set m_relativePathPool; + AZStd::unordered_set m_relativePathPool; // offset to the start of CDR in the file,even if there's no CDR there currently // when a new file is added, it can start from here, but this value will need to be updated then diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.cpp index 0092c7c8f8..5c5e93d441 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.cpp @@ -41,13 +41,6 @@ namespace AZ::IO::ZipDir m_encryptedHeaders = ZipFile::HEADERS_NOT_ENCRYPTED; m_signedHeaders = ZipFile::HEADERS_NOT_SIGNED; - if (m_nFlags & FLAGS_FILENAMES_AS_CRC32) - { - m_bBuildFileEntryMap = false; - m_bBuildFileEntryTree = false; - m_bBuildOptimizedFileEntry = true; - } - if (m_nFlags & FLAGS_READ_INSIDE_PAK) { m_fileExt.m_fileIOBase = AZ::IO::FileIOBase::GetInstance(); @@ -88,12 +81,12 @@ namespace AZ::IO::ZipDir if (m_fileExt.m_fileHandle == AZ::IO::InvalidHandle) { - THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Could not open file in binary mode for reading"); + AZ_Warning("Archive", false, R"(ZD_ERROR_IO_FAILED: Could not open file "%s" in binary mode for reading)", szFileName); return {}; } if (!ReadCache(*pCache)) { - THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Could not read the CDR of the pack file."); + AZ_Warning("Archive", false, R"(ZD_ERROR_IO_FAILED: Could not read the CDR of the pack file "%s".)", pCache->m_strFilePath.c_str()); return {}; } } @@ -113,12 +106,12 @@ namespace AZ::IO::ZipDir size_t nFileSize = (size_t)Tell(); Seek(0, SEEK_SET); - AZ_Assert(nFileSize != 0, "File of size 0 will not be open for reading"); + AZ_Warning("Archive", nFileSize != 0, R"(ZD_ERROR_IO_FAILED: File "%s" with size 0 will not be open for reading)", szFileName); if (nFileSize) { if (!ReadCache(*pCache)) { - THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Could not open file in binary mode for reading"); + AZ_Warning("Archive", false, R"(ZD_ERROR_IO_FAILED: Could not open file "%s" in binary mode for reading)", szFileName); return {}; } bOpenForWriting = false; @@ -143,7 +136,7 @@ namespace AZ::IO::ZipDir if (m_fileExt.m_fileHandle == AZ::IO::InvalidHandle) { - THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Could not open file in binary mode for appending (read/write)"); + AZ_Warning("Archive", false, R"(ZD_ERROR_IO_FAILED: Could not open file "%s" in binary mode for appending (read/write))", szFileName); return {}; } } @@ -211,7 +204,7 @@ namespace AZ::IO::ZipDir if (m_headerExtended.nHeaderSize != sizeof(m_headerExtended)) { // Extended Header is not valid - THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Bad extended header"); + AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: Bad extended header"); return false; } //We have the header, so read the encryption and signing techniques @@ -224,7 +217,7 @@ namespace AZ::IO::ZipDir if (m_headerExtended.nEncryption != ZipFile::HEADERS_NOT_ENCRYPTED && m_encryptedHeaders != ZipFile::HEADERS_NOT_ENCRYPTED) { //Encryption technique has been specified in both the disk number (old technique) and the custom header (new technique). - THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Unexpected encryption technique in header"); + AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: Unexpected encryption technique in header"); return false; } else @@ -240,7 +233,7 @@ namespace AZ::IO::ZipDir break; default: // Unexpected technique - THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Bad encryption technique in header"); + AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: Bad encryption technique in header"); return false; } } @@ -255,7 +248,7 @@ namespace AZ::IO::ZipDir break; default: // Unexpected technique - THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Bad signing technique in header"); + AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: Bad signing technique in header"); return false; } @@ -266,7 +259,7 @@ namespace AZ::IO::ZipDir Read(&m_headerSignature, sizeof(m_headerSignature)); if (m_headerSignature.nHeaderSize != sizeof(m_headerSignature)) { - THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Bad signature header"); + AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: Bad signature header"); return false; } } @@ -274,7 +267,7 @@ namespace AZ::IO::ZipDir else { // Unexpected technique - THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Comment field is the wrong length"); + AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: Comment field is the wrong length"); return false; } } @@ -285,7 +278,7 @@ namespace AZ::IO::ZipDir || m_CDREnd.nCDRStartDisk != 0 || m_CDREnd.numEntriesOnDisk != m_CDREnd.numEntriesTotal) { - THROW_ZIPDIR_ERROR(ZD_ERROR_UNSUPPORTED, "Multivolume archive detected. Current version of ZipDir does not support multivolume archives"); + AZ_Warning("Archive", false, "ZD_ERROR_UNSUPPORTED: Multivolume archive detected.Current version of ZipDir does not support multivolume archives"); return false; } @@ -295,7 +288,7 @@ namespace AZ::IO::ZipDir || m_CDREnd.lCDRSize > m_nCDREndPos || m_CDREnd.lCDROffset + m_CDREnd.lCDRSize > m_nCDREndPos) { - THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "The central directory offset or size are out of range, the pak is probably corrupt, try to repare or delete the file"); + AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: The central directory offset or size are out of range, the pak is probably corrupt, try to repare or delete the file"); return false; } @@ -394,7 +387,12 @@ namespace AZ::IO::ZipDir // if there's nothing to search if (nNewBufPos >= nOldBufPos) { - THROW_ZIPDIR_ERROR(ZD_ERROR_NO_CDR, "Cannot find Central Directory Record in pak. This is either not a pak file, or a pak file without Central Directory. It does not mean that the data is permanently lost, but it may be severely damaged. Please repair the file with external tools, there may be enough information left to recover the file completely."); // we didn't find anything + AZ_Warning("Archive", false, "ZD_ERROR_NO_CDR: Cannot find Central Directory Record in pak." + " This is either not a pak file, or a pak file without Central Directory." + " It does not mean that the data is permanently lost," + " but it may be severely damaged." + " Please repair the file with external tools," + " there may be enough information left to recover the file completely."); // we didn't find anything return false; } @@ -418,7 +416,11 @@ namespace AZ::IO::ZipDir } else { - THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Central Directory Record is followed by a comment of inconsistent length. This might be a minor misconsistency, please try to repair the file. However, it is dangerous to open the file because I will have to guess some structure offsets, which can lead to permanent unrecoverable damage of the archive content"); + AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT:" + " Central Directory Record is followed by a comment of inconsistent length." + " This might be a minor misconsistency, please try to repair the file.However," + " it is dangerous to open the file because I will have to guess some structure offsets," + " which can lead to permanent unrecoverable damage of the archive content"); return false; } } @@ -436,7 +438,7 @@ namespace AZ::IO::ZipDir nOldBufPos = nNewBufPos; memmove(&pReservedBuffer[CDRSearchWindowSize], pWindow, sizeof(ZipFile::CDREnd) - 1); } - THROW_ZIPDIR_ERROR(ZD_ERROR_UNEXPECTED, "The program flow may not have possibly lead here. This error is unexplainable"); // we shouldn't be here + AZ_Assert(false, "ZD_ERROR_UNEXPECTED: The program flow may not have possibly lead here. This error is unexplainable"); // we shouldn't be here return false; } @@ -460,13 +462,13 @@ namespace AZ::IO::ZipDir if (pBuffer.empty()) // couldn't allocate enough memory for temporary copy of CDR { - THROW_ZIPDIR_ERROR(ZD_ERROR_NO_MEMORY, "Not enough memory to cache Central Directory record for fast initialization. This error may not happen on non-console systems"); + AZ_Warning("Archive", false, "ZD_ERROR_NO_MEMORY: Not enough memory to cache Central Directory record for fast initialization. This error may not happen on non-console systems"); return false; } if (!ReadHeaderData(&pBuffer[0], m_CDREnd.lCDRSize)) { - THROW_ZIPDIR_ERROR(ZD_ERROR_CORRUPTED_DATA, "Archive contains corrupted CDR."); + AZ_Warning("Archive", false, "ZD_ERROR_CORRUPTED_DATA: Archive contains corrupted CDR."); return false; } @@ -482,7 +484,7 @@ namespace AZ::IO::ZipDir if ((pFile->nVersionNeeded & 0xFF) > 20) { - THROW_ZIPDIR_ERROR(ZD_ERROR_UNSUPPORTED, "Cannot read the archive file (nVersionNeeded > 20)."); + AZ_Warning("Archive", false, "ZD_ERROR_UNSUPPORTED: Cannot read the archive file (nVersionNeeded > 20)."); return false; } //if (pFile->lSignature != pFile->SIGNATURE) // Timur, Dont compare signatures as signatue in memory can be overwritten by the code below @@ -492,7 +494,8 @@ namespace AZ::IO::ZipDir // if the record overlaps with the End Of CDR structure, something is wrong if (pEndOfRecord > pEndOfData) { - THROW_ZIPDIR_ERROR(ZD_ERROR_CDR_IS_CORRUPT, "Central Directory record is either corrupt, or truncated, or missing. Cannot read the archive directory"); + AZ_Warning("Archive", false, "ZD_ERROR_CDR_IS_CORRUPT: Central Directory record is either corrupt, or truncated, or missing." + " Cannot read the archive directory"); return false; } @@ -550,18 +553,22 @@ namespace AZ::IO::ZipDir ////////////////////////////////////////////////////////////////////////// // give the CDR File Header entry, reads the local file header to validate - // and determine where the actual file lies + // and determine where the actual file resides void CacheFactory::AddFileEntry(char* strFilePath, const ZipFile::CDRFileHeader* pFileHeader, const SExtraZipFileData& extra) { if (pFileHeader->lLocalHeaderOffset > m_CDREnd.lCDROffset) { - THROW_ZIPDIR_ERROR(ZD_ERROR_CDR_IS_CORRUPT, "Central Directory contains file descriptors pointing outside the archive file boundaries. The archive file is either truncated or damaged. Please try to repair the file"); // the file offset is beyond the CDR: impossible + AZ_Warning("Archive", false, "ZD_ERROR_CDR_IS_CORRUPT:" + " Central Directory contains file descriptors pointing outside the archive file boundaries." + " The archive file is either truncated or damaged.Please try to repair the file"); // the file offset is beyond the CDR: impossible return; } if ((pFileHeader->nMethod == ZipFile::METHOD_STORE || pFileHeader->nMethod == ZipFile::METHOD_STORE_AND_STREAMCIPHER_KEYTABLE) && pFileHeader->desc.lSizeUncompressed != pFileHeader->desc.lSizeCompressed) { - THROW_ZIPDIR_ERROR(ZD_ERROR_VALIDATION_FAILED, "File with STORE compression method declares its compressed size not matching its uncompressed size. File descriptor is inconsistent, archive content may be damaged, please try to repair the archive"); + AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED:" + " File with STORE compression method declares its compressed size not matching its uncompressed size." + " File descriptor is inconsistent, archive content may be damaged, please try to repair the archive"); return; } @@ -593,8 +600,7 @@ namespace AZ::IO::ZipDir if (m_encryptedHeaders != ZipFile::HEADERS_NOT_ENCRYPTED) { // use CDR instead of local header - // The pak encryption tool asserts that there is no extra data at the end of the local file header, so don't add any extra data from the CDR header. - fileEntry.nFileDataOffset = pFileHeader->lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + pFileHeader->nFileNameLength; + fileEntry.nFileDataOffset = pFileHeader->lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + pFileHeader->nFileNameLength + pFileHeader->nExtraFieldLength; } else { @@ -617,7 +623,9 @@ namespace AZ::IO::ZipDir //|| pFileHeader->nLastModTime != pLocalFileHeader->nLastModTime ) { - THROW_ZIPDIR_ERROR(ZD_ERROR_VALIDATION_FAILED, "The local file header descriptor doesn't match the basic parameters declared in the global file header in the file. The archive content is misconsistent and may be damaged. Please try to repair the archive"); + AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED:" + " The local file header descriptor doesn't match the basic parameters declared in the global file header in the file." + " The archive content is misconsistent and may be damaged. Please try to repair the archive"); return; } @@ -628,7 +636,9 @@ namespace AZ::IO::ZipDir if (!AZStd::equal(zipFileDataBegin, zipFileDataEnd, reinterpret_cast(pFileHeader + 1), CompareNoCase)) { // either file name, or the extra field do not match - THROW_ZIPDIR_ERROR(ZD_ERROR_VALIDATION_FAILED, "The local file header contains file name which does not match the file name of the global file header. The archive content is misconsistent with its directory. Please repair the archive"); + AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED:" + " The local file header contains file name which does not match the file name of the global file header." + " The archive content is misconsistent with its directory. Please repair the archive"); return; } @@ -642,7 +652,9 @@ namespace AZ::IO::ZipDir if (fileEntry.nFileDataOffset >= m_nCDREndPos) { - THROW_ZIPDIR_ERROR(ZD_ERROR_VALIDATION_FAILED, "The global file header declares the file which crosses the boundaries of the archive. The archive is either corrupted or truncated, please try to repair it"); + AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED:" + " The global file header declares the file which crosses the boundaries of the archive." + " The archive is either corrupted or truncated, please try to repair it"); return; } @@ -686,29 +698,29 @@ namespace AZ::IO::ZipDir case Z_OK: break; case Z_MEM_ERROR: - THROW_ZIPDIR_ERROR(ZD_ERROR_ZLIB_NO_MEMORY, "ZLib reported out-of-memory error"); + AZ_Warning("Archive", false, "ZD_ERROR_ZLIB_NO_MEMORY: ZLib reported out-of-memory error"); return; case Z_BUF_ERROR: - THROW_ZIPDIR_ERROR(ZD_ERROR_ZLIB_CORRUPTED_DATA, "ZLib reported compressed stream buffer error"); + AZ_Warning("Archive", false, "ZD_ERROR_ZLIB_CORRUPTED_DATA: ZLib reported compressed stream buffer error"); return; case Z_DATA_ERROR: - THROW_ZIPDIR_ERROR(ZD_ERROR_ZLIB_CORRUPTED_DATA, "ZLib reported compressed stream data error"); + AZ_Warning("Archive", false, "ZD_ERROR_ZLIB_CORRUPTED_DATA: ZLib reported compressed stream data error"); return; default: - THROW_ZIPDIR_ERROR(ZD_ERROR_ZLIB_FAILED, "ZLib reported an unexpected unknown error"); + AZ_Warning("Archive", false, "ZD_ERROR_ZLIB_FAILED: ZLib reported an unexpected unknown error"); return; } if (nDestSize != fileEntry.desc.lSizeUncompressed) { - THROW_ZIPDIR_ERROR(ZD_ERROR_CORRUPTED_DATA, "Uncompressed stream doesn't match the size of uncompressed file stored in the archive file headers"); + AZ_Warning("Archive", false, "ZD_ERROR_CORRUPTED_DATA: Uncompressed stream doesn't match the size of uncompressed file stored in the archive file headers"); return; } uLong uCRC32 = AZ::Crc32((Bytef*)pUncompressed, nDestSize); if (uCRC32 != fileEntry.desc.lCRC32) { - THROW_ZIPDIR_ERROR(ZD_ERROR_CRC32_CHECK, "Uncompressed stream CRC32 check failed"); + AZ_Warning("Archive", false, "ZD_ERROR_CRC32_CHECK: Uncompressed stream CRC32 check failed"); return; } } @@ -737,7 +749,7 @@ namespace AZ::IO::ZipDir { if (FSeek(&m_fileExt, nPos, nOrigin)) { - THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Cannot fseek() to the new position in the file. This is unexpected error and should not happen under any circumstances. Perhaps some network or disk failure error has caused this"); + AZ_Warning("Archive", false, "ZD_ERROR_IO_FAILED: Cannot fseek() to the new position in the file. This is unexpected error and should not happen under any circumstances. Perhaps some network or disk failure error has caused this"); return; } } @@ -747,7 +759,7 @@ namespace AZ::IO::ZipDir int64_t nPos = FTell(&m_fileExt); if (nPos == -1) { - THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Cannot ftell() position in the archive. This is unexpected error and should not happen under any circumstances. Perhaps some network or disk failure error has caused this"); + AZ_Warning("Archive", false, "ZD_ERROR_IO_FAILED: Cannot ftell() position in the archive. This is unexpected error and should not happen under any circumstances. Perhaps some network or disk failure error has caused this"); return 0; } return nPos; @@ -757,7 +769,7 @@ namespace AZ::IO::ZipDir { if (FRead(&m_fileExt, pDest, nSize, 1) != 1) { - THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Cannot fread() a portion of data from archive"); + AZ_Warning("Archive", false, "ZD_ERROR_IO_FAILED: Cannot fread() a portion of data from archive"); return false; } return true; diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.h b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.h index 1f27cb5504..c31d4d7dfd 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.h @@ -33,20 +33,13 @@ namespace AZ::IO::ZipDir // if this is set, the archive will be created anew (the existing file will be overwritten) FLAGS_CREATE_NEW = 1 << 3, - // Cache will be loaded completely into the memory. - FLAGS_IN_MEMORY = 1 << 4, - FLAGS_IN_MEMORY_CPU = 1 << 5, - - // Store all file names as crc32 in a flat directory structure. - FLAGS_FILENAMES_AS_CRC32 = 1 << 6, - // if this is set, zip path will be searched inside other zips FLAGS_READ_INSIDE_PAK = 1 << 7, }; // initializes the internal structures // nFlags can have FLAGS_READ_ONLY flag, in this case the object will be opened only for reading - CacheFactory (InitMethodEnum nInitMethod, uint32_t nFlags = 0); + CacheFactory(InitMethodEnum nInitMethod, uint32_t nFlags = 0); ~CacheFactory(); // the new function creates a new cache diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirFind.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirFind.cpp index 30c8676f75..4fb1a54ded 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirFind.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirFind.cpp @@ -17,7 +17,7 @@ namespace AZ::IO::ZipDir { - bool FindFile::FindFirst(AZStd::string_view szWildcard) + bool FindFile::FindFirst(AZ::IO::PathView szWildcard) { if (!PreFind(szWildcard)) { @@ -29,7 +29,7 @@ namespace AZ::IO::ZipDir return SkipNonMatchingFiles(); } - bool FindDir::FindFirst(AZStd::string_view szWildcard) + bool FindDir::FindFirst(AZ::IO::PathView szWildcard) { if (!PreFind(szWildcard)) { @@ -42,37 +42,20 @@ namespace AZ::IO::ZipDir } // matches the file wildcard in the m_szWildcard to the given file/dir name - // this takes into account the fact that xxx. is the alias name for xxx - bool FindData::MatchWildcard(AZStd::string_view szName) + bool FindData::MatchWildcard(AZ::IO::PathView szName) { - if (AZStd::wildcard_match(m_szWildcard, szName)) - { - return true; - } - - // check if the file object name contains extension sign (.) - size_t extensionOffset = szName.find('.'); - if (extensionOffset != AZStd::string_view::npos) - { - return false; - } - - // no extension sign - add it - AZStd::fixed_string szAlias{ szName }; - szAlias.push_back('.'); - - return AZStd::wildcard_match(m_szWildcard, szAlias); + return szName.Match(m_szWildcard.Native()); } - FileEntry* FindFile::FindExact(AZStd::string_view szPath) + FileEntry* FindFile::FindExact(AZ::IO::PathView szPath) { if (!PreFind(szPath)) { return nullptr; } - FileEntryTree::FileMap::iterator itFile = m_pDirHeader->FindFile(m_szWildcard.c_str()); + FileEntryTree::FileMap::iterator itFile = m_pDirHeader->FindFile(m_szWildcard); if (itFile == m_pDirHeader->GetFileEnd()) { m_pDirHeader = nullptr; // we didn't find it, fail the search @@ -84,7 +67,7 @@ namespace AZ::IO::ZipDir return m_pDirHeader->GetFileEntry(m_itFile); } - FileEntryTree* FindDir::FindExact(AZStd::string_view szPath) + FileEntryTree* FindDir::FindExact(AZ::IO::PathView szPath) { if (!PreFind(szPath)) { @@ -97,40 +80,50 @@ namespace AZ::IO::ZipDir ////////////////////////////////////////////////////////////////////////// // after this call returns successfully (with true returned), the m_szWildcard - // contains the file name/wildcard and m_pDirHeader contains the directory where + // contains the file name/glob and m_pDirHeader contains the directory where // the file (s) are to be found - bool FindData::PreFind(AZStd::string_view szWildcard) + bool FindData::PreFind(AZ::IO::PathView pathGlob) { if (!m_pRoot) { return false; } - // start the search from the root - m_pDirHeader = m_pRoot; - m_szWildcard = szWildcard; - - // for each path directory, copy it into the wildcard buffer and try to find the subdirectory - for (AZStd::optional pathEntry = AZ::StringFunc::TokenizeNext(szWildcard, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR); pathEntry; - pathEntry = AZ::StringFunc::TokenizeNext(szWildcard, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR)) + FileEntryTree* entryTreeHeader = m_pRoot; + // If there is a root path in the glob path, attempt to locate it from the root + if (AZ::IO::PathView rootPath = m_szWildcard.RootPath(); !rootPath.empty()) { - // Update wildcard to new path entry - m_szWildcard = *pathEntry; - - // If the wildcard parameter that has been passed to TokenizeNext is empty - // Then pathEntry is the final portion of the path - if (!szWildcard.empty()) + FileEntryTree* dirEntry = entryTreeHeader->FindDir(rootPath); + if (dirEntry == nullptr) { - FileEntryTree* dirEntry = m_pDirHeader->FindDir(*pathEntry); - if (!dirEntry) - { - m_pDirHeader = nullptr; // an intermediate directory has not been found continue the search - return false; - } - m_pDirHeader = dirEntry->GetDirectory(); + return false; } + + entryTreeHeader = dirEntry->GetDirectory(); + pathGlob = pathGlob.RelativePath(); + } + + + AZ::IO::PathView filenameSegment = pathGlob; + // Recurse through the directories within the file tree for each remaining parent path segment + // of pathGlob parameter + auto parentPathIter = pathGlob.begin(); + for (auto filenamePathIter = parentPathIter == pathGlob.end() ? pathGlob.end() : AZStd::next(parentPathIter, 1); + filenamePathIter != pathGlob.end(); ++parentPathIter, ++filenamePathIter) + { + FileEntryTree* dirEntry = entryTreeHeader->FindDir(*parentPathIter); + if (dirEntry == nullptr) + { + return false; + } + entryTreeHeader = dirEntry->GetDirectory(); + filenameSegment = *filenamePathIter; } + // At this point the all the intermediate directories have been found + // so update the directory header to point at the last file entry tree + m_pDirHeader = entryTreeHeader; + m_szWildcard = filenameSegment; return true; } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirFind.h b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirFind.h index 2bb3932aa9..f9b773a42d 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirFind.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirFind.h @@ -38,11 +38,10 @@ namespace AZ::IO::ZipDir // after this call returns successfully (with true returned), the m_szWildcard // contains the file name/wildcard and m_pDirHeader contains the directory where // the file (s) are to be found - bool PreFind(AZStd::string_view szWildcard); + bool PreFind(AZ::IO::PathView szWildcard); // matches the file wildcard in the m_szWildcard to the given file/dir name - // this takes into account the fact that xxx. is the alias name for xxx - bool MatchWildcard(AZStd::string_view szName); + bool MatchWildcard(AZ::IO::PathView szName); // the directory inside which the current object (file or directory) is being searched FileEntryTree* m_pDirHeader{}; @@ -50,7 +49,7 @@ namespace AZ::IO::ZipDir FileEntryTree* m_pRoot{}; // the root of the zip file in which to search // the actual wildcard being used in the current scan - the file name wildcard only! - AZStd::fixed_string m_szWildcard; + AZ::IO::FixedMaxPath m_szWildcard; }; class FindFile @@ -66,9 +65,9 @@ namespace AZ::IO::ZipDir { } // if bExactFile is passed, only the file is searched, and besides with the exact name as passed (no wildcards) - bool FindFirst(AZStd::string_view szWildcard); + bool FindFirst(AZ::IO::PathView szWildcard); - FileEntry* FindExact(AZStd::string_view szPath); + FileEntry* FindExact(AZ::IO::PathView szPath); // goes on to the next file entry bool FindNext(); @@ -94,9 +93,9 @@ namespace AZ::IO::ZipDir { } // if bExactFile is passed, only the file is searched, and besides with the exact name as passed (no wildcards) - bool FindFirst(AZStd::string_view szWildcard); + bool FindFirst(AZ::IO::PathView szWildcard); - FileEntryTree* FindExact(AZStd::string_view szPath); + FileEntryTree* FindExact(AZ::IO::PathView szPath); // goes on to the next file entry bool FindNext(); diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirList.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirList.cpp index 2f96a93a82..729f394b9d 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirList.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirList.cpp @@ -68,14 +68,14 @@ namespace AZ::IO::ZipDir { for (FileEntryTree::SubdirMap::iterator it = pTree->GetDirBegin(); it != pTree->GetDirEnd(); ++it) { - AddAllFiles(it->second.get(), AZStd::string::format("%.*s%.*s/", aznumeric_cast(strRoot.size()), strRoot.data(), aznumeric_cast(it->first.size()), it->first.data())); + AddAllFiles(it->second.get(), (AZ::IO::Path(strRoot) / it->first).Native()); } for (FileEntryTree::FileMap::iterator it = pTree->GetFileBegin(); it != pTree->GetFileEnd(); ++it) { FileRecord rec; rec.pFileEntryBase = pTree->GetFileEntry(it); - rec.strPath = AZStd::string::format("%.*s%.*s", aznumeric_cast(strRoot.size()), strRoot.data(), aznumeric_cast(it->first.size()), it->first.data()); + rec.strPath = (AZ::IO::Path(strRoot) / it->first).Native(); push_back(rec); } } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirStructures.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirStructures.cpp index 1d09705900..cea517decd 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirStructures.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirStructures.cpp @@ -187,8 +187,7 @@ namespace AZ::IO::ZipDir::ZipDirStructuresInternal // If src/dst overlap (in place decompress), then inflate in chunks, copying src locally to ensure // pointers don't foul each other. - bool bIndependantBlocks = ((pInput + nInputLen) <= pOutput) || (pInput >= (pOutput + nOutputLen)); - if (bIndependantBlocks) + if ((pInput + nInputLen) <= pOutput || pInput >= (pOutput + nOutputLen)) { pZStream->next_in = (Bytef*)pInput; pZStream->avail_in = aznumeric_cast(nInputLen); @@ -260,8 +259,7 @@ namespace AZ::IO::ZipDir::ZipDirStructuresInternal // If src/dst overlap (in place decompress), then inflate in chunks, copying src locally to ensure // pointers don't foul each other. - bool bIndependantBlocks = ((pIn + nIn) <= stream.next_out) || (pIn >= (stream.next_out + stream.avail_out)); - if (bIndependantBlocks) + if ((pIn + nIn) <= stream.next_out || pIn >= (stream.next_out + stream.avail_out)) { stream.next_in = pIn; stream.avail_in = nIn; @@ -432,18 +430,18 @@ namespace AZ::IO::ZipDir bool CZipFile::EvaluateSectorSize(const char* filename) { - char volume[AZ_MAX_PATH_LEN]; + AZ::IO::FixedMaxPath volume; - if (AZ::StringFunc::Path::IsRelative(filename)) + if (AZ::IO::PathView(filename).IsRelative()) { - AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(filename, volume, AZ_ARRAY_SIZE(volume)); + AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(volume, filename); } else { - azstrcpy(volume, AZ_ARRAY_SIZE(volume), filename); + volume = filename; } - AZ::IO::FixedMaxPathString drive{ AZ::IO::PathView(volume).RootName().Native() }; + AZ::IO::FixedMaxPath drive = volume.RootName(); if (drive.empty()) { return false; @@ -498,18 +496,18 @@ namespace AZ::IO::ZipDir ////////////////////////////////////////////////////////////////////////// FileEntryBase::FileEntryBase(const ZipFile::CDRFileHeader& header, const SExtraZipFileData& extra) { - this->desc = header.desc; - this->nFileHeaderOffset = header.lLocalHeaderOffset; - //this->nFileDataOffset = INVALID_DATA_OFFSET; // we don't know yet - this->nMethod = header.nMethod; - this->nNameOffset = 0; // we don't know yet - this->nLastModTime = header.nLastModTime; - this->nLastModDate = header.nLastModDate; - this->nNTFS_LastModifyTime = extra.nLastModifyTime; + desc = header.desc; + nFileHeaderOffset = header.lLocalHeaderOffset; + + nMethod = header.nMethod; + nNameOffset = 0; // we don't know yet + nLastModTime = header.nLastModTime; + nLastModDate = header.nLastModDate; + nNTFS_LastModifyTime = extra.nLastModifyTime; // make an estimation (at least this offset should be there), but we don't actually know yet - this->nFileDataOffset = header.lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + header.nFileNameLength; - this->nEOFOffset = header.lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + header.nFileNameLength + header.desc.lSizeCompressed; + nFileDataOffset = header.lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + header.nFileNameLength + header.nExtraFieldLength; + nEOFOffset = nFileDataOffset + header.desc.lSizeCompressed; } // Uncompresses raw (without wrapping) data that is compressed with method 8 (deflated) in the Zip file @@ -666,7 +664,9 @@ namespace AZ::IO::ZipDir DirEntry* pEnd = pBegin + this->numDirs; DirEntry* pEntry = AZStd::lower_bound(pBegin, pEnd, szName, pred); #if AZ_TRAIT_LEGACY_CRYPAK_UNIX_LIKE_FILE_SYSTEM - if (pEntry != pEnd && !azstrnicmp(szName.data(), pEntry->GetName(pNamePool), szName.size())) + AZ::IO::PathView searchPath(szName, AZ::IO::WindowsPathSeparator); + AZ::IO::PathView entryPath(pEntry->GetName(pNamePool), AZ::IO::WindowsPathSeparator); + if (pEntry != pEnd && searchPath == entryPath) #else if (pEntry != pEnd && szName == pEntry->GetName(pNamePool)) #endif @@ -690,7 +690,9 @@ namespace AZ::IO::ZipDir FileEntry* pEnd = pBegin + this->numFiles; FileEntry* pEntry = AZStd::lower_bound(pBegin, pEnd, szName, pred); #if AZ_TRAIT_LEGACY_CRYPAK_UNIX_LIKE_FILE_SYSTEM - if (pEntry != pEnd && !azstrnicmp(szName.data(), pEntry->GetName(pNamePool), szName.size())) + AZ::IO::PathView searchPath(szName, AZ::IO::WindowsPathSeparator); + AZ::IO::PathView entryPath(pEntry->GetName(pNamePool), AZ::IO::WindowsPathSeparator); + if (pEntry != pEnd && searchPath == entryPath) #else if (pEntry != pEnd && szName == pEntry->GetName(pNamePool)) #endif @@ -813,8 +815,6 @@ namespace AZ::IO::ZipDir header.nFileNameLength = aznumeric_cast(nFileNameLength); header.nExtraFieldLength = 0; - pFileEntry->nFileDataOffset = pFileEntry->nFileHeaderOffset + sizeof(header) + header.nFileNameLength; - pFileEntry->nEOFOffset = pFileEntry->nFileDataOffset + pFileEntry->desc.lSizeCompressed; if (!AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, &header, sizeof(header))) { return ZD_ERROR_IO_FAILED; @@ -990,13 +990,6 @@ namespace AZ::IO::ZipDir } ////////////////////////////////////////////////////////////////////////// - uint32_t FileNameHash(AZStd::string_view filename) - { - AZ::IO::StackString pathname{ filename }; - AZStd::replace(AZStd::begin(pathname), AZStd::end(pathname), AZ_WRONG_DATABASE_SEPARATOR, AZ_CORRECT_DATABASE_SEPARATOR); - - return AZ::Crc32(pathname); - } int64_t FSeek(CZipFile* file, int64_t origin, int command) { diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirStructures.h b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirStructures.h index 2f9046f53e..9295a7dd95 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirStructures.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirStructures.h @@ -119,8 +119,6 @@ namespace AZ::IO::ZipDir const char* m_szDescription; }; -#define THROW_ZIPDIR_ERROR(ZD_ERR, DESC) AZ_Warning("Archive", false, DESC) - // possible initialization methods enum InitMethodEnum { @@ -157,8 +155,6 @@ namespace AZ::IO::ZipDir int FEof(CZipFile* zipFile); - uint32_t FileNameHash(AZStd::string_view filename); - ////////////////////////////////////////////////////////////////////////// struct SExtraZipFileData @@ -173,7 +169,7 @@ namespace AZ::IO::ZipDir inline static constexpr uint32_t INVALID_DATA_OFFSET = 0xFFFFFFFF; ZipFile::DataDescriptor desc{}; - uint32_t nFileDataOffset{}; // offset of the packed info inside the file; NOTE: this can be INVALID_DATA_OFFSET, if not calculated yet! + uint32_t nFileDataOffset{ INVALID_DATA_OFFSET }; // offset of the packed info inside the file; NOTE: this can be INVALID_DATA_OFFSET, if not calculated yet! uint32_t nFileHeaderOffset{ INVALID_DATA_OFFSET }; // offset of the local file header uint32_t nNameOffset{}; // offset of the file name in the name pool for the directory diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirTree.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirTree.cpp index 213287ef74..e772cb596a 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirTree.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirTree.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -18,37 +17,42 @@ namespace AZ::IO::ZipDir { // Adds or finds the file. Returns non-initialized structure if it was added, // or an IsInitialized() structure if it was found - FileEntry* FileEntryTree::Add(AZStd::string_view szPath) + FileEntry* FileEntryTree::Add(AZ::IO::PathView inputPathView) { - AZStd::optional pathEntry = AZ::StringFunc::TokenizeNext(szPath, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR); - if (!pathEntry) + if (inputPathView.empty()) { AZ_Assert(false, "An empty file path cannot be added to the zip file entry tree"); return nullptr; } // If a path separator was found, add a subdirectory - if (!szPath.empty()) + auto inputPathIter = inputPathView.begin(); + AZ::IO::PathView firstPathSegment(*inputPathIter); + auto inputPathNextIter = inputPathIter == inputPathView.end() ? inputPathView.end() : AZStd::next(inputPathIter, 1); + AZ::IO::PathView remainingPath = inputPathNextIter != inputPathView.end() ? + AZStd::string_view(inputPathNextIter->Native().begin(), inputPathView.Native().end()) + : AZStd::string_view{}; + if (!remainingPath.empty()) { - auto dirEntryIter = m_mapDirs.find(*pathEntry); + auto dirEntryIter = m_mapDirs.find(firstPathSegment); // we have a subdirectory here - create the file in it if (dirEntryIter == m_mapDirs.end()) { - dirEntryIter = m_mapDirs.emplace(*pathEntry, AZStd::make_unique()).first; + dirEntryIter = m_mapDirs.emplace(firstPathSegment, AZStd::make_unique()).first; } - return dirEntryIter->second->Add(szPath); + return dirEntryIter->second->Add(remainingPath); } // Add the filename - auto fileEntryIter = m_mapFiles.find(*pathEntry); + auto fileEntryIter = m_mapFiles.find(firstPathSegment); if (fileEntryIter == m_mapFiles.end()) { - fileEntryIter = m_mapFiles.emplace(*pathEntry, AZStd::make_unique()).first; + fileEntryIter = m_mapFiles.emplace(firstPathSegment, AZStd::make_unique()).first; } return fileEntryIter->second.get(); } // adds a file to this directory - ErrorEnum FileEntryTree::Add(AZStd::string_view szPath, const FileEntryBase& file) + ErrorEnum FileEntryTree::Add(AZ::IO::PathView szPath, const FileEntryBase& file) { FileEntry* pFile = Add(szPath); if (!pFile) @@ -63,7 +67,7 @@ namespace AZ::IO::ZipDir return ZD_ERROR_SUCCESS; } - // returns the number of files in this tree, including this and sublevels + // returns the number of files in this tree, including this and subdirectories uint32_t FileEntryTree::NumFilesTotal() const { uint32_t numFiles = aznumeric_cast(m_mapFiles.size()); @@ -91,21 +95,6 @@ namespace AZ::IO::ZipDir m_mapFiles.clear(); } - size_t FileEntryTree::GetSize() const - { - size_t nSize = sizeof(*this); - for (const auto& [dirname, dirEntry] : m_mapDirs) - { - nSize += dirname.size() + sizeof(decltype(m_mapDirs)::value_type) + dirEntry->GetSize(); - } - - for (const auto& [filename, fileEntry] : m_mapFiles) - { - nSize += filename.size() + sizeof(decltype(m_mapFiles)::value_type); - } - return nSize; - } - bool FileEntryTree::IsOwnerOf(const FileEntry* pFileEntry) const { for (const auto& [path, fileEntry] : m_mapFiles) @@ -127,7 +116,7 @@ namespace AZ::IO::ZipDir return false; } - FileEntryTree* FileEntryTree::FindDir(AZStd::string_view szDirName) + FileEntryTree* FileEntryTree::FindDir(AZ::IO::PathView szDirName) { if (auto it = m_mapDirs.find(szDirName); it != m_mapDirs.end()) { @@ -137,7 +126,7 @@ namespace AZ::IO::ZipDir return nullptr; } - FileEntryTree::FileMap::iterator FileEntryTree::FindFile(AZStd::string_view szFileName) + FileEntryTree::FileMap::iterator FileEntryTree::FindFile(AZ::IO::PathView szFileName) { return m_mapFiles.find(szFileName); } @@ -152,7 +141,7 @@ namespace AZ::IO::ZipDir return it == GetDirEnd() ? nullptr : it->second.get(); } - ErrorEnum FileEntryTree::RemoveDir(AZStd::string_view szDirName) + ErrorEnum FileEntryTree::RemoveDir(AZ::IO::PathView szDirName) { SubdirMap::iterator itRemove = m_mapDirs.find(szDirName); if (itRemove == m_mapDirs.end()) @@ -164,7 +153,13 @@ namespace AZ::IO::ZipDir return ZD_ERROR_SUCCESS; } - ErrorEnum FileEntryTree::RemoveFile(AZStd::string_view szFileName) + ErrorEnum FileEntryTree::RemoveAll() + { + Clear(); + return ZD_ERROR_SUCCESS; + } + + ErrorEnum FileEntryTree::RemoveFile(AZ::IO::PathView szFileName) { FileMap::iterator itRemove = m_mapFiles.find(szFileName); if (itRemove == m_mapFiles.end()) diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirTree.h b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirTree.h index cfe539e896..9bdc047a7a 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirTree.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirTree.h @@ -10,6 +10,7 @@ #pragma once #include +#include #include #include #include @@ -24,12 +25,12 @@ namespace AZ::IO::ZipDir // adds a file to this directory // Function can modify szPath input - ErrorEnum Add(AZStd::string_view szPath, const FileEntryBase& file); + ErrorEnum Add(AZ::IO::PathView szPath, const FileEntryBase& file); // Adds or finds the file. Returns non-initialized structure if it was added, // or an IsInitialized() structure if it was found // Function can modify szPath input - FileEntry* Add(AZStd::string_view szPath); + FileEntry* Add(AZ::IO::PathView szPath); // returns the number of files in this tree, including this and sublevels uint32_t NumFilesTotal() const; @@ -45,24 +46,18 @@ namespace AZ::IO::ZipDir m_mapFiles.swap(rThat.m_mapFiles); } - size_t GetSize() const; - bool IsOwnerOf(const FileEntry* pFileEntry) const; // subdirectories - using SubdirMap = AZStd::map>; + using SubdirMap = AZStd::map>; // file entries - using FileMap = AZStd::map>; + using FileMap = AZStd::map>; - FileEntryTree* FindDir(AZStd::string_view szDirName); - ErrorEnum RemoveDir (AZStd::string_view szDirName); - ErrorEnum RemoveAll () - { - Clear(); - return ZD_ERROR_SUCCESS; - } - FileMap::iterator FindFile(AZStd::string_view szFileName); - ErrorEnum RemoveFile(AZStd::string_view szFileName); + FileEntryTree* FindDir(AZ::IO::PathView szDirName); + ErrorEnum RemoveDir(AZ::IO::PathView szDirName); + ErrorEnum RemoveAll(); + FileMap::iterator FindFile(AZ::IO::PathView szFileName); + ErrorEnum RemoveFile(AZ::IO::PathView szFileName); // the FileEntryTree is simultaneously an entry in the dir list AND the directory header FileEntryTree* GetDirectory() { @@ -75,8 +70,8 @@ namespace AZ::IO::ZipDir SubdirMap::iterator GetDirBegin() { return m_mapDirs.begin(); } SubdirMap::iterator GetDirEnd() { return m_mapDirs.end(); } uint32_t NumDirs() const { return aznumeric_cast(m_mapDirs.size()); } - AZStd::string_view GetFileName(FileMap::iterator it) { return it->first; } - AZStd::string_view GetDirName(SubdirMap::iterator it) { return it->first; } + AZStd::string_view GetFileName(FileMap::iterator it) { return it->first.Native(); } + AZStd::string_view GetDirName(SubdirMap::iterator it) { return it->first.Native(); } FileEntry* GetFileEntry(FileMap::iterator it); FileEntryTree* GetDirEntry(SubdirMap::iterator it); diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h index d6d67c2aa2..ac811ae478 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h @@ -866,7 +866,7 @@ namespace AzFramework FileIsReadOnlyResponse() = default; FileIsReadOnlyResponse(bool isReadOnly); - unsigned int GetMessageType() const; + unsigned int GetMessageType() const override; bool m_isReadOnly; }; @@ -945,7 +945,7 @@ namespace AzFramework FileModTimeRequest() = default; FileModTimeRequest(const AZ::OSString& filePath); - unsigned int GetMessageType() const; + unsigned int GetMessageType() const override; AZ::OSString m_filePath; }; diff --git a/Code/Framework/AzFramework/AzFramework/Asset/GenericAssetHandler.h b/Code/Framework/AzFramework/AzFramework/Asset/GenericAssetHandler.h index 5ead8154e6..4c3f911871 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/GenericAssetHandler.h +++ b/Code/Framework/AzFramework/AzFramework/Asset/GenericAssetHandler.h @@ -75,7 +75,7 @@ namespace AzFramework { public: AZ_RTTI(GenericAssetHandlerBase, "{B153B8B5-25CC-4BB7-A2BD-9A47ECF4123C}", AZ::Data::AssetHandler); - virtual ~GenericAssetHandlerBase() {} + virtual ~GenericAssetHandlerBase() = default; }; template @@ -186,7 +186,7 @@ namespace AzFramework } } - bool CanHandleAsset(const AZ::Data::AssetId& id) const + bool CanHandleAsset(const AZ::Data::AssetId& id) const override { AZStd::string assetPath; EBUS_EVENT_RESULT(assetPath, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, id); diff --git a/Code/Framework/AzFramework/AzFramework/AzFrameworkModule.cpp b/Code/Framework/AzFramework/AzFramework/AzFrameworkModule.cpp index c5fee7ec9a..83f1954e11 100644 --- a/Code/Framework/AzFramework/AzFramework/AzFrameworkModule.cpp +++ b/Code/Framework/AzFramework/AzFramework/AzFrameworkModule.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ namespace AzFramework AzFramework::CreateScriptDebugAgentFactory(), AzFramework::AssetSystem::AssetSystemComponent::CreateDescriptor(), AzFramework::InputSystemComponent::CreateDescriptor(), + AzFramework::InputContextComponent::CreateDescriptor(), #if !defined(AZCORE_EXCLUDE_LUA) AzFramework::ScriptComponent::CreateDescriptor(), diff --git a/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.h b/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.h index 16032e5337..bc1bf1a6b2 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.h @@ -28,7 +28,7 @@ namespace AzFramework // AZ::NonUniformScaleRequests::Handler ... AZ::Vector3 GetScale() const override; void SetScale(const AZ::Vector3& scale) override; - void RegisterScaleChangedEvent(AZ::NonUniformScaleChangedEvent::Handler& handler); + void RegisterScaleChangedEvent(AZ::NonUniformScaleChangedEvent::Handler& handler) override; protected: // AZ::Component ... diff --git a/Code/Framework/AzFramework/AzFramework/Entity/EntityDebugDisplayBus.h b/Code/Framework/AzFramework/AzFramework/Entity/EntityDebugDisplayBus.h index 68af9dbb26..49506364f4 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/EntityDebugDisplayBus.h +++ b/Code/Framework/AzFramework/AzFramework/Entity/EntityDebugDisplayBus.h @@ -105,7 +105,7 @@ namespace AzFramework virtual AZ::Matrix3x4 PopPremultipliedMatrix() { return AZ::Matrix3x4::CreateIdentity(); } protected: - ~DebugDisplayRequests() = default; + virtual ~DebugDisplayRequests() = default; }; /// Inherit from DebugDisplayRequestBus::Handler to implement the DebugDisplayRequests interface. diff --git a/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.h b/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.h index 56a53f3c50..f837bed14e 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.h @@ -69,7 +69,7 @@ namespace AzFramework // EntityContext AZ::Entity* CreateEntity(const char* name) override; void OnRootEntityReloaded() override; - void OnContextEntitiesAdded(const EntityList& entities); + void OnContextEntitiesAdded(const EntityList& entities) override; void OnContextReset() override; bool ValidateEntitiesAreValidForContext(const EntityList& entities) override; ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp b/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp index 62a6334b5d..a7c6b18061 100644 --- a/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp +++ b/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp @@ -34,6 +34,7 @@ namespace AzFramework { } + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override { diff --git a/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp index 50190ac7d5..4072c17ea0 100644 --- a/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp +++ b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp @@ -734,7 +734,7 @@ namespace AZ { if (AZ::StringFunc::StartsWith(pathStrView, aliasKey)) { - // Reduce of the size result result path by the size of the and add the resolved alias size + // Add to the size of result path by the resolved alias length - the alias key length AZStd::string_view postAliasView = pathStrView.substr(aliasKey.size()); size_t requiredFixedMaxPathSize = postAliasView.size(); requiredFixedMaxPathSize += aliasValue.size(); diff --git a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp index cc6154f20c..496d89d767 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp @@ -28,34 +28,10 @@ namespace AzFramework } } - //////////////////////////////////////////////////////////////////////////////////////////////// - InputChannelId::InputChannelId(const char* name) - : m_crc32(name) - { - memset(m_name, 0, AZ_ARRAY_SIZE(m_name)); - azstrncpy(m_name, NAME_BUFFER_SIZE, name, MAX_NAME_LENGTH); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - InputChannelId::InputChannelId(const InputChannelId& other) - : m_crc32(other.m_crc32) - { - memset(m_name, 0, AZ_ARRAY_SIZE(m_name)); - azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - InputChannelId& InputChannelId::operator=(const InputChannelId& other) - { - azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name); - m_crc32 = other.m_crc32; - return *this; - } - //////////////////////////////////////////////////////////////////////////////////////////////// const char* InputChannelId::GetName() const { - return m_name; + return m_name.c_str(); } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h index 5c1c58d6b1..a4d8ac83eb 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h @@ -11,6 +11,7 @@ #include #include #include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework @@ -22,8 +23,7 @@ namespace AzFramework public: //////////////////////////////////////////////////////////////////////////////////////////// // Constants - static const int NAME_BUFFER_SIZE = 64; - static const int MAX_NAME_LENGTH = NAME_BUFFER_SIZE - 1; + static constexpr int MAX_NAME_LENGTH = 64; //////////////////////////////////////////////////////////////////////////////////////////// // Allocator @@ -39,21 +39,28 @@ namespace AzFramework //////////////////////////////////////////////////////////////////////////////////////////// //! Constructor - //! \param[in] name Name of the input channel (will be truncated if exceeds MAX_NAME_LENGTH) - explicit InputChannelId(const char* name = ""); + //! \param[in] name Name of the input channel (will be ignored if exceeds MAX_NAME_LENGTH) + explicit constexpr InputChannelId(AZStd::string_view name = "") + : m_name(name) + , m_crc32(name) + { + } - //////////////////////////////////////////////////////////////////////////////////////////// - //! Copy constructor - //! \param[in] other Another instance of the class to copy from - InputChannelId(const InputChannelId& other); - - //////////////////////////////////////////////////////////////////////////////////////////// - //! Copy assignment operator - //! \param[in] other Another instance of the class to copy from - InputChannelId& operator=(const InputChannelId& other); - - //////////////////////////////////////////////////////////////////////////////////////////// - //! Default destructor + constexpr InputChannelId(const InputChannelId& other) = default; + constexpr InputChannelId(InputChannelId&& other) = default; + constexpr InputChannelId& operator=(const InputChannelId& other) + { + m_name = other.m_name; + m_crc32 = other.m_crc32; + return *this; + } + constexpr InputChannelId& operator=(InputChannelId&& other) + { + m_name = AZStd::move(other.m_name); + m_crc32 = AZStd::move(other.m_crc32); + other.m_crc32 = 0; + return *this; + } ~InputChannelId() = default; //////////////////////////////////////////////////////////////////////////////////////////// @@ -77,7 +84,7 @@ namespace AzFramework private: //////////////////////////////////////////////////////////////////////////////////////////// // Variables - char m_name[NAME_BUFFER_SIZE]; //!< Name of the input channel + AZStd::fixed_string m_name; //!< Name of the input channel AZ::Crc32 m_crc32; //!< Crc32 of the input channel }; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Input/Contexts/InputContext.h b/Code/Framework/AzFramework/AzFramework/Input/Contexts/InputContext.h index 7220b8b8d5..e7143a25b9 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Contexts/InputContext.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Contexts/InputContext.h @@ -55,6 +55,10 @@ namespace AzFramework // Allocator AZ_CLASS_ALLOCATOR(InputContext, AZ::SystemAllocator, 0); + //////////////////////////////////////////////////////////////////////////////////////////// + // Type Info + AZ_RTTI(InputContext, "{D17A85B2-405F-40AB-BBA7-F118256D39AB}", InputDevice); + //////////////////////////////////////////////////////////////////////////////////////////// //! Constructor //! \param[in] name Unique, will be truncated if exceeds InputDeviceId::MAX_NAME_LENGTH = 64 diff --git a/Code/Framework/AzFramework/AzFramework/Input/Contexts/InputContextComponent.cpp b/Code/Framework/AzFramework/AzFramework/Input/Contexts/InputContextComponent.cpp new file mode 100644 index 0000000000..ff147bd9d8 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Input/Contexts/InputContextComponent.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 +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////// +namespace AzFramework +{ + //////////////////////////////////////////////////////////////////////////////////////////////// + void InputContextComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("InputContextService", 0xa2734425)); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void InputContextComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("Unique Name", &InputContextComponent::m_uniqueName) + ->Field("Input Mappings", &InputContextComponent::m_inputMappings) + ->Field("Local Player Index", &InputContextComponent::m_localPlayerIndex) + ->Field("Input Listener Priority", &InputContextComponent::m_inputListenerPriority) + ->Field("Consumes Processed Input", &InputContextComponent::m_consumesProcessedInput) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("Input Context", + "An input context is a collection of input mappings, which map 'raw' input to custom input channels (ie. events).") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::Category, "Input") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) + ->DataElement(AZ::Edit::UIHandlers::Default, &InputContextComponent::m_uniqueName, "Unique Name", + "The name of the input context, unique among all active input contexts and input devices.\n" + "This will be truncated if its length exceeds that of InputDeviceId::MAX_NAME_LENGTH = 64") + ->DataElement(AZ::Edit::UIHandlers::Default, &InputContextComponent::m_inputMappings, "Input Mappings", + "The list of all input mappings that will be created when the input context is activated.") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::SpinBox, &InputContextComponent::m_localPlayerIndex, "Local Player Index", + "The local player index that this context will receive input from (0 based, -1 means all controllers).\n" + "Will only work on platforms such as PC where the local user id corresponds to the local player index.\n" + "For other platforms, SetLocalUserId must be called at runtime with the id of a logged in user.") + ->Attribute(AZ::Edit::Attributes::Min, -1) + ->Attribute(AZ::Edit::Attributes::Max, 3) + ->DataElement(AZ::Edit::UIHandlers::SpinBox, &InputContextComponent::m_inputListenerPriority, "Input Listener Priority", + "The priority used to sort the input context relative to all other input event listeners.\n" + "Higher numbers indicate greater priority.") + ->Attribute(AZ::Edit::Attributes::Min, InputChannelEventListener::GetPriorityLast()) + ->Attribute(AZ::Edit::Attributes::Max, InputChannelEventListener::GetPriorityFirst()) + ->DataElement(AZ::Edit::UIHandlers::CheckBox, &InputContextComponent::m_consumesProcessedInput, "Consumes Processed Input", + "Should the input context consume input that is processed by any of its input mappings?") + ; + } + } + + InputMapping::ConfigBase::Reflect(context); + InputMappingAnd::Config::Reflect(context); + InputMappingOr::Config::Reflect(context); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + InputContextComponent::~InputContextComponent() + { + Deactivate(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void InputContextComponent::Init() + { + // The local player index that this component will receive input from (0 base, -1 wildcard) + // can be set from data, but will only work on platforms where the local user id corresponds + // to a local player index. For other platforms SetLocalUserId must be called at runtime with + // the id of a logged in local user, which will overwrite anything that is set here from data. + const LocalUserId localUserId = (m_localPlayerIndex == -1) ? LocalUserIdAny : aznumeric_cast(m_localPlayerIndex); + SetLocalUserId(localUserId); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void InputContextComponent::Activate() + { + InputContextComponentRequestBus::Handler::BusConnect(GetEntityId()); + CreateInputContext(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void InputContextComponent::Deactivate() + { + ResetInputContext(); + InputContextComponentRequestBus::Handler::BusDisconnect(GetEntityId()); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void InputContextComponent::SetLocalUserId(LocalUserId localUserId) + { + // Create a new filter, or reset any existing one if we have been passed LocalUserIdAny. + if (localUserId != LocalUserIdAny) + { + m_localUserIdFilter = AZStd::make_shared(InputChannelEventFilter::AnyChannelNameCrc32, + InputChannelEventFilter::AnyDeviceNameCrc32, + aznumeric_cast(m_localPlayerIndex)); + } + else + { + m_localUserIdFilter.reset(); + } + + // Set the filter if the input context has already been created. + if (m_inputContext) + { + m_inputContext->SetFilter(m_localUserIdFilter); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void InputContextComponent::CreateInputContext() + { + if (m_uniqueName.empty()) + { + AZ_Error("InputContextComponent", false, "Cannot create input context with empty name."); + return; + } + + if (InputDeviceRequests::FindInputDevice(InputDeviceId(m_uniqueName.c_str()))) + { + AZ_Error("InputContextComponent", false, + "Cannot create input context '%s' with non-unique name.", m_uniqueName.c_str()); + return; + } + + if (m_inputMappings.empty()) + { + AZ_Error("InputContextComponent", false, + "Cannot create input context '%s' with no input mappings.", m_uniqueName.c_str()); + return; + } + + // Create the input context. + InputContext::InitData initData; + initData.autoActivate = true; + initData.filter = m_localUserIdFilter; + initData.priority = m_inputListenerPriority; + initData.consumesProcessedInput = m_consumesProcessedInput; + m_inputContext = AZStd::make_unique(m_uniqueName.c_str(), initData); + + // Create and add all input mappings. + for (const InputMapping::ConfigBase* inputMapping : m_inputMappings) + { + inputMapping->CreateInputMappingAndAddToContext(*m_inputContext); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void InputContextComponent::ResetInputContext() + { + m_inputContext.reset(); + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Input/Contexts/InputContextComponent.h b/Code/Framework/AzFramework/AzFramework/Input/Contexts/InputContextComponent.h new file mode 100644 index 0000000000..1b9286bd4c --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Input/Contexts/InputContextComponent.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////// +namespace AzFramework +{ + //////////////////////////////////////////////////////////////////////////////////////////////// + class InputContextComponentRequests : public AZ::ComponentBus + { + public: + //////////////////////////////////////////////////////////////////////////////////////////// + //! Set the local user id that the InputContextComponent should process input from + //! \param[in] localUserId Local user id the InputContextComponent should process input from + virtual void SetLocalUserId(LocalUserId localUserId) = 0; + }; + using InputContextComponentRequestBus = AZ::EBus; + + //////////////////////////////////////////////////////////////////////////////////////////////// + //! An InputContextComponent is used to configure (at edit time) the data necessary to create an + //! InputContext (at run time). The life cycle of any InputContextComponent is controlled by the + //! AZ::Entity it is attached to, adhering to the same rules as any other AZ::Component, and the + //! InputContext which it owns is created/destroyed when the component is activated/deactivated. + class InputContextComponent : public AZ::Component + , public InputContextComponentRequestBus::Handler + { + public: + //////////////////////////////////////////////////////////////////////////////////////////// + // AZ::Component Setup + AZ_COMPONENT(InputContextComponent, "{321689F8-A572-47D7-9D1C-EF9E0D2CD472}"); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! \ref AZ::ComponentDescriptor::GetProvidedServices + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! \ref AZ::ComponentDescriptor::Reflect + static void Reflect(AZ::ReflectContext* context); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Default Constructor + InputContextComponent() = default; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Destructor + ~InputContextComponent() override; + + protected: + //////////////////////////////////////////////////////////////////////////////////////////// + //! \ref AZ::Component::Init + void Init() override; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! \ref AZ::Component::Activate + void Activate() override; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! \ref AZ::Component::Deactivate + void Deactivate() override; + + //////////////////////////////////////////////////////////////////////////////////////////// + // \ref AzFramework::InputContextComponentRequests::SetLocalUserId + void SetLocalUserId(LocalUserId localUserId) override; + + private: + //////////////////////////////////////////////////////////////////////////////////////////// + //! Create the input context. + void CreateInputContext(); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Reset the input context. + void ResetInputContext(); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! The list of all input mappings that will be created when the input context is activated. + //! Reflected to EditContext, then used to create and add input mapping classes in Activate. + AZStd::vector m_inputMappings; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! The name of the input context, unique among all active input contexts and input devices. + //! This will be truncated if its length exceeds that of InputDeviceId::MAX_NAME_LENGTH = 64 + //! Reflected to EditContext, then used to create the unique input context class in Activate. + AZStd::string m_uniqueName; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Input context that is created and owned by this component. Not reflected to EditContext. + AZStd::unique_ptr m_inputContext; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Filter used to determine whether an input event should be handled by this input context. + //! Not reflected, but created inside SetLocalUserId if needed to fliter by a local user id. + AZStd::shared_ptr m_localUserIdFilter; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! The local player index that this component will receive input from (0 base, -1 wildcard). + //! Will only work on platforms where the local user id corresponds to the local player index. + //! For other platforms, SetLocalUserId must be called at runtime with id of a logged in user. + //! Reflected to EditContext, then used if needed to create the local user id filter in Init. + AZ::s32 m_localPlayerIndex = -1; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! The priority used to sort the input context relative to all other input event listeners. + //! Reflected to EditContext, then used to create the unique input context class in Activate. + AZ::s32 m_inputListenerPriority = InputChannelEventListener::GetPriorityDefault(); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Should the input context consume input that is processed by any of its input mappings? + //! Reflected to EditContext, then used to create the unique input context class in Activate. + bool m_consumesProcessedInput = false; + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp index 95886c6871..51aa008519 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp @@ -28,91 +28,6 @@ namespace AzFramework return (inputDeviceId.GetNameCrc32() == IdForIndex0.GetNameCrc32()); } - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceGamepad::Button::A("gamepad_button_a"); - const InputChannelId InputDeviceGamepad::Button::B("gamepad_button_b"); - const InputChannelId InputDeviceGamepad::Button::X("gamepad_button_x"); - const InputChannelId InputDeviceGamepad::Button::Y("gamepad_button_y"); - const InputChannelId InputDeviceGamepad::Button::L1("gamepad_button_l1"); - const InputChannelId InputDeviceGamepad::Button::R1("gamepad_button_r1"); - const InputChannelId InputDeviceGamepad::Button::L3("gamepad_button_l3"); - const InputChannelId InputDeviceGamepad::Button::R3("gamepad_button_r3"); - const InputChannelId InputDeviceGamepad::Button::DU("gamepad_button_d_up"); - const InputChannelId InputDeviceGamepad::Button::DD("gamepad_button_d_down"); - const InputChannelId InputDeviceGamepad::Button::DL("gamepad_button_d_left"); - const InputChannelId InputDeviceGamepad::Button::DR("gamepad_button_d_right"); - const InputChannelId InputDeviceGamepad::Button::Start("gamepad_button_start"); - const InputChannelId InputDeviceGamepad::Button::Select("gamepad_button_select"); - const AZStd::array InputDeviceGamepad::Button::All = - {{ - A, - B, - X, - Y, - L1, - R1, - L3, - R3, - DU, - DD, - DL, - DR, - Start, - Select - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceGamepad::Trigger::L2("gamepad_trigger_l2"); - const InputChannelId InputDeviceGamepad::Trigger::R2("gamepad_trigger_r2"); - const AZStd::array InputDeviceGamepad::Trigger::All = - {{ - L2, - R2 - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceGamepad::ThumbStickAxis2D::L("gamepad_thumbstick_l"); - const InputChannelId InputDeviceGamepad::ThumbStickAxis2D::R("gamepad_thumbstick_r"); - const AZStd::array InputDeviceGamepad::ThumbStickAxis2D::All = - {{ - L, - R - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::LX("gamepad_thumbstick_l_x"); - const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::LY("gamepad_thumbstick_l_y"); - const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::RX("gamepad_thumbstick_r_x"); - const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::RY("gamepad_thumbstick_r_y"); - const AZStd::array InputDeviceGamepad::ThumbStickAxis1D::All = - {{ - LX, - LY, - RX, - RY - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceGamepad::ThumbStickDirection::LU("gamepad_thumbstick_l_up"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::LD("gamepad_thumbstick_l_down"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::LL("gamepad_thumbstick_l_left"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::LR("gamepad_thumbstick_l_right"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::RU("gamepad_thumbstick_r_up"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::RD("gamepad_thumbstick_r_down"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::RL("gamepad_thumbstick_r_left"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::RR("gamepad_thumbstick_r_right"); - const AZStd::array InputDeviceGamepad::ThumbStickDirection::All = - {{ - LU, - LD, - LL, - LR, - RU, - RD, - RL, - RR - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceGamepad::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h index f4fddbb24e..286b4f0df3 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h @@ -59,75 +59,115 @@ namespace AzFramework //! All the input channel ids that identify game-pad digital button input struct Button { - static const InputChannelId A; //!< The bottom diamond face button - static const InputChannelId B; //!< The right diamond face button - static const InputChannelId X; //!< The left diamond face button - static const InputChannelId Y; //!< The top diamond face button - static const InputChannelId L1; //!< The top-left shoulder bumper button - static const InputChannelId R1; //!< The top-right shoulder bumper button - static const InputChannelId L3; //!< The left thumb-stick click button - static const InputChannelId R3; //!< The right thumb-stick click button - static const InputChannelId DU; //!< The up directional pad button - static const InputChannelId DD; //!< The down directional pad button - static const InputChannelId DL; //!< The left directional pad button - static const InputChannelId DR; //!< The right directional pad button - static const InputChannelId Start; //!< The start/pause/options button - static const InputChannelId Select; //!< The select/back button + static constexpr inline InputChannelId A{"gamepad_button_a"}; //!< The bottom diamond face button + static constexpr inline InputChannelId B{"gamepad_button_b"}; //!< The right diamond face button + static constexpr inline InputChannelId X{"gamepad_button_x"}; //!< The left diamond face button + static constexpr inline InputChannelId Y{"gamepad_button_y"}; //!< The top diamond face button + static constexpr inline InputChannelId L1{"gamepad_button_l1"}; //!< The top-left shoulder bumper button + static constexpr inline InputChannelId R1{"gamepad_button_r1"}; //!< The top-right shoulder bumper button + static constexpr inline InputChannelId L3{"gamepad_button_l3"}; //!< The left thumb-stick click button + static constexpr inline InputChannelId R3{"gamepad_button_r3"}; //!< The right thumb-stick click button + static constexpr inline InputChannelId DU{"gamepad_button_d_up"}; //!< The up directional pad button + static constexpr inline InputChannelId DD{"gamepad_button_d_down"}; //!< The down directional pad button + static constexpr inline InputChannelId DL{"gamepad_button_d_left"}; //!< The left directional pad button + static constexpr inline InputChannelId DR{"gamepad_button_d_right"}; //!< The right directional pad button + static constexpr inline InputChannelId Start{"gamepad_button_start"}; //!< The start/pause/options button + static constexpr inline InputChannelId Select{"gamepad_button_select"}; //!< The select/back button //!< All digital game-pad button ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + A, + B, + X, + Y, + L1, + R1, + L3, + R3, + DU, + DD, + DL, + DR, + Start, + Select + }; }; //////////////////////////////////////////////////////////////////////////////////////////// //! All the input channel ids that identify game-pad analog trigger input struct Trigger { - static const InputChannelId L2; //!< The bottom-left shoulder trigger - static const InputChannelId R2; //!< The bottom-right shoulder trigger + static constexpr inline InputChannelId L2{"gamepad_trigger_l2"}; //!< The bottom-left shoulder trigger + static constexpr inline InputChannelId R2{"gamepad_trigger_r2"}; //!< The bottom-right shoulder trigger //!< All analog game-pad trigger ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + L2, + R2 + }; }; //////////////////////////////////////////////////////////////////////////////////////////// //! All the input channel ids that identify game-pad thumb-stick 2D axis input struct ThumbStickAxis2D { - static const InputChannelId L; //!< The left-hand thumb-stick - static const InputChannelId R; //!< The right-hand thumb-stick + static constexpr inline InputChannelId L{"gamepad_thumbstick_l"}; //!< The left-hand thumb-stick + static constexpr inline InputChannelId R{"gamepad_thumbstick_r"}; //!< The right-hand thumb-stick //!< All game-pad thumb-stick 2D axis input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + L, + R + }; }; //////////////////////////////////////////////////////////////////////////////////////////// //! All the input channel ids that identify game-pad thumb-stick 1D axis input struct ThumbStickAxis1D { - static const InputChannelId LX; //!< X-axis of the left-hand thumb-stick - static const InputChannelId LY; //!< Y-axis of the left-hand thumb-stick - static const InputChannelId RX; //!< X-axis of the right-hand thumb-stick - static const InputChannelId RY; //!< Y-axis of the right-hand thumb-stick + static constexpr inline InputChannelId LX{"gamepad_thumbstick_l_x"}; //!< X-axis of the left-hand thumb-stick + static constexpr inline InputChannelId LY{"gamepad_thumbstick_l_y"}; //!< Y-axis of the left-hand thumb-stick + static constexpr inline InputChannelId RX{"gamepad_thumbstick_r_x"}; //!< X-axis of the right-hand thumb-stick + static constexpr inline InputChannelId RY{"gamepad_thumbstick_r_y"}; //!< Y-axis of the right-hand thumb-stick //!< All game-pad thumb-stick 1D axis input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + LX, + LY, + RX, + RY + }; }; //////////////////////////////////////////////////////////////////////////////////////////// //! All the input channel ids that identify game-pad thumb-stick directional input struct ThumbStickDirection { - static const InputChannelId LU; //!< Up on the left-hand thumb-stick - static const InputChannelId LD; //!< Down on the left-hand thumb-stick - static const InputChannelId LL; //!< Left on the left-hand thumb-stick - static const InputChannelId LR; //!< Right on the left-hand thumb-stick - static const InputChannelId RU; //!< Up on the left-hand thumb-stick - static const InputChannelId RD; //!< Down on the left-hand thumb-stick - static const InputChannelId RL; //!< Left on the left-hand thumb-stick - static const InputChannelId RR; //!< Right on the left-hand thumb-stick + static constexpr inline InputChannelId LU{"gamepad_thumbstick_l_up"}; //!< Up on the left-hand thumb-stick + static constexpr inline InputChannelId LD{"gamepad_thumbstick_l_down"}; //!< Down on the left-hand thumb-stick + static constexpr inline InputChannelId LL{"gamepad_thumbstick_l_left"}; //!< Left on the left-hand thumb-stick + static constexpr inline InputChannelId LR{"gamepad_thumbstick_l_right"}; //!< Right on the left-hand thumb-stick + static constexpr inline InputChannelId RU{"gamepad_thumbstick_r_up"}; //!< Up on the left-hand thumb-stick + static constexpr inline InputChannelId RD{"gamepad_thumbstick_r_down"}; //!< Down on the left-hand thumb-stick + static constexpr inline InputChannelId RL{"gamepad_thumbstick_r_left"}; //!< Left on the left-hand thumb-stick + static constexpr inline InputChannelId RR{"gamepad_thumbstick_r_right"}; //!< Right on the left-hand thumb-stick //!< All game-pad thumb-stick directional input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + LU, + LD, + LL, + LR, + RU, + RD, + RL, + RR + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp index dd84fd9633..d1117ea895 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp @@ -24,279 +24,6 @@ namespace AzFramework return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32()); } - //////////////////////////////////////////////////////////////////////////////////////////////// - // Alphanumeric Keys - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric0("keyboard_key_alphanumeric_0"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric1("keyboard_key_alphanumeric_1"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric2("keyboard_key_alphanumeric_2"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric3("keyboard_key_alphanumeric_3"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric4("keyboard_key_alphanumeric_4"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric5("keyboard_key_alphanumeric_5"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric6("keyboard_key_alphanumeric_6"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric7("keyboard_key_alphanumeric_7"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric8("keyboard_key_alphanumeric_8"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric9("keyboard_key_alphanumeric_9"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericA("keyboard_key_alphanumeric_A"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericB("keyboard_key_alphanumeric_B"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericC("keyboard_key_alphanumeric_C"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericD("keyboard_key_alphanumeric_D"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericE("keyboard_key_alphanumeric_E"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericF("keyboard_key_alphanumeric_F"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericG("keyboard_key_alphanumeric_G"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericH("keyboard_key_alphanumeric_H"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericI("keyboard_key_alphanumeric_I"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericJ("keyboard_key_alphanumeric_J"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericK("keyboard_key_alphanumeric_K"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericL("keyboard_key_alphanumeric_L"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericM("keyboard_key_alphanumeric_M"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericN("keyboard_key_alphanumeric_N"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericO("keyboard_key_alphanumeric_O"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericP("keyboard_key_alphanumeric_P"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericQ("keyboard_key_alphanumeric_Q"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericR("keyboard_key_alphanumeric_R"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericS("keyboard_key_alphanumeric_S"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericT("keyboard_key_alphanumeric_T"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericU("keyboard_key_alphanumeric_U"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericV("keyboard_key_alphanumeric_V"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericW("keyboard_key_alphanumeric_W"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericX("keyboard_key_alphanumeric_X"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericY("keyboard_key_alphanumeric_Y"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericZ("keyboard_key_alphanumeric_Z"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Edit (and escape) Keys - const InputChannelId InputDeviceKeyboard::Key::EditBackspace("keyboard_key_edit_backspace"); - const InputChannelId InputDeviceKeyboard::Key::EditCapsLock("keyboard_key_edit_capslock"); - const InputChannelId InputDeviceKeyboard::Key::EditEnter("keyboard_key_edit_enter"); - const InputChannelId InputDeviceKeyboard::Key::EditSpace("keyboard_key_edit_space"); - const InputChannelId InputDeviceKeyboard::Key::EditTab("keyboard_key_edit_tab"); - const InputChannelId InputDeviceKeyboard::Key::Escape("keyboard_key_escape"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Function Keys - const InputChannelId InputDeviceKeyboard::Key::Function01("keyboard_key_function_F01"); - const InputChannelId InputDeviceKeyboard::Key::Function02("keyboard_key_function_F02"); - const InputChannelId InputDeviceKeyboard::Key::Function03("keyboard_key_function_F03"); - const InputChannelId InputDeviceKeyboard::Key::Function04("keyboard_key_function_F04"); - const InputChannelId InputDeviceKeyboard::Key::Function05("keyboard_key_function_F05"); - const InputChannelId InputDeviceKeyboard::Key::Function06("keyboard_key_function_F06"); - const InputChannelId InputDeviceKeyboard::Key::Function07("keyboard_key_function_F07"); - const InputChannelId InputDeviceKeyboard::Key::Function08("keyboard_key_function_F08"); - const InputChannelId InputDeviceKeyboard::Key::Function09("keyboard_key_function_F09"); - const InputChannelId InputDeviceKeyboard::Key::Function10("keyboard_key_function_F10"); - const InputChannelId InputDeviceKeyboard::Key::Function11("keyboard_key_function_F11"); - const InputChannelId InputDeviceKeyboard::Key::Function12("keyboard_key_function_F12"); - const InputChannelId InputDeviceKeyboard::Key::Function13("keyboard_key_function_F13"); - const InputChannelId InputDeviceKeyboard::Key::Function14("keyboard_key_function_F14"); - const InputChannelId InputDeviceKeyboard::Key::Function15("keyboard_key_function_F15"); - const InputChannelId InputDeviceKeyboard::Key::Function16("keyboard_key_function_F16"); - const InputChannelId InputDeviceKeyboard::Key::Function17("keyboard_key_function_F17"); - const InputChannelId InputDeviceKeyboard::Key::Function18("keyboard_key_function_F18"); - const InputChannelId InputDeviceKeyboard::Key::Function19("keyboard_key_function_F19"); - const InputChannelId InputDeviceKeyboard::Key::Function20("keyboard_key_function_F20"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Modifier Keys - const InputChannelId InputDeviceKeyboard::Key::ModifierAltL("keyboard_key_modifier_alt_l"); - const InputChannelId InputDeviceKeyboard::Key::ModifierAltR("keyboard_key_modifier_alt_r"); - const InputChannelId InputDeviceKeyboard::Key::ModifierCtrlL("keyboard_key_modifier_ctrl_l"); - const InputChannelId InputDeviceKeyboard::Key::ModifierCtrlR("keyboard_key_modifier_ctrl_r"); - const InputChannelId InputDeviceKeyboard::Key::ModifierShiftL("keyboard_key_modifier_shift_l"); - const InputChannelId InputDeviceKeyboard::Key::ModifierShiftR("keyboard_key_modifier_shift_r"); - const InputChannelId InputDeviceKeyboard::Key::ModifierSuperL("keyboard_key_modifier_super_l"); - const InputChannelId InputDeviceKeyboard::Key::ModifierSuperR("keyboard_key_modifier_super_r"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Navigation Keys - const InputChannelId InputDeviceKeyboard::Key::NavigationArrowDown("keyboard_key_navigation_arrow_down"); - const InputChannelId InputDeviceKeyboard::Key::NavigationArrowLeft("keyboard_key_navigation_arrow_left"); - const InputChannelId InputDeviceKeyboard::Key::NavigationArrowRight("keyboard_key_navigation_arrow_right"); - const InputChannelId InputDeviceKeyboard::Key::NavigationArrowUp("keyboard_key_navigation_arrow_up"); - const InputChannelId InputDeviceKeyboard::Key::NavigationDelete("keyboard_key_navigation_delete"); - const InputChannelId InputDeviceKeyboard::Key::NavigationEnd("keyboard_key_navigation_end"); - const InputChannelId InputDeviceKeyboard::Key::NavigationHome("keyboard_key_navigation_home"); - const InputChannelId InputDeviceKeyboard::Key::NavigationInsert("keyboard_key_navigation_insert"); - const InputChannelId InputDeviceKeyboard::Key::NavigationPageDown("keyboard_key_navigation_page_down"); - const InputChannelId InputDeviceKeyboard::Key::NavigationPageUp("keyboard_key_navigation_page_up"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Numpad Keys - const InputChannelId InputDeviceKeyboard::Key::NumLock("keyboard_key_num_lock"); - const InputChannelId InputDeviceKeyboard::Key::NumPad0("keyboard_key_numpad_0"); - const InputChannelId InputDeviceKeyboard::Key::NumPad1("keyboard_key_numpad_1"); - const InputChannelId InputDeviceKeyboard::Key::NumPad2("keyboard_key_numpad_2"); - const InputChannelId InputDeviceKeyboard::Key::NumPad3("keyboard_key_numpad_3"); - const InputChannelId InputDeviceKeyboard::Key::NumPad4("keyboard_key_numpad_4"); - const InputChannelId InputDeviceKeyboard::Key::NumPad5("keyboard_key_numpad_5"); - const InputChannelId InputDeviceKeyboard::Key::NumPad6("keyboard_key_numpad_6"); - const InputChannelId InputDeviceKeyboard::Key::NumPad7("keyboard_key_numpad_7"); - const InputChannelId InputDeviceKeyboard::Key::NumPad8("keyboard_key_numpad_8"); - const InputChannelId InputDeviceKeyboard::Key::NumPad9("keyboard_key_numpad_9"); - const InputChannelId InputDeviceKeyboard::Key::NumPadAdd("keyboard_key_numpad_add"); - const InputChannelId InputDeviceKeyboard::Key::NumPadDecimal("keyboard_key_numpad_decimal"); - const InputChannelId InputDeviceKeyboard::Key::NumPadDivide("keyboard_key_numpad_divide"); - const InputChannelId InputDeviceKeyboard::Key::NumPadEnter("keyboard_key_numpad_enter"); - const InputChannelId InputDeviceKeyboard::Key::NumPadMultiply("keyboard_key_numpad_multiply"); - const InputChannelId InputDeviceKeyboard::Key::NumPadSubtract("keyboard_key_numpad_subtract"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Punctuation Keys - const InputChannelId InputDeviceKeyboard::Key::PunctuationApostrophe("keyboard_key_punctuation_apostrophe"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationBackslash("keyboard_key_punctuation_backslash"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationBracketL("keyboard_key_punctuation_bracket_l"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationBracketR("keyboard_key_punctuation_bracket_r"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationComma("keyboard_key_punctuation_comma"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationEquals("keyboard_key_punctuation_equals"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationHyphen("keyboard_key_punctuation_hyphen"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationPeriod("keyboard_key_punctuation_period"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationSemicolon("keyboard_key_punctuation_semicolon"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationSlash("keyboard_key_punctuation_slash"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationTilde("keyboard_key_punctuation_tilde"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Supplementary ISO Key - const InputChannelId InputDeviceKeyboard::Key::SupplementaryISO("keyboard_key_supplementary_iso"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Windows System Keys - const InputChannelId InputDeviceKeyboard::Key::WindowsSystemPause("keyboard_key_windows_system_pause"); - const InputChannelId InputDeviceKeyboard::Key::WindowsSystemPrint("keyboard_key_windows_system_print"); - const InputChannelId InputDeviceKeyboard::Key::WindowsSystemScrollLock("keyboard_key_windows_system_scroll_lock"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - const AZStd::array InputDeviceKeyboard::Key::All = - {{ - // Alphanumeric Keys - Alphanumeric0, - Alphanumeric1, - Alphanumeric2, - Alphanumeric3, - Alphanumeric4, - Alphanumeric5, - Alphanumeric6, - Alphanumeric7, - Alphanumeric8, - Alphanumeric9, - AlphanumericA, - AlphanumericB, - AlphanumericC, - AlphanumericD, - AlphanumericE, - AlphanumericF, - AlphanumericG, - AlphanumericH, - AlphanumericI, - AlphanumericJ, - AlphanumericK, - AlphanumericL, - AlphanumericM, - AlphanumericN, - AlphanumericO, - AlphanumericP, - AlphanumericQ, - AlphanumericR, - AlphanumericS, - AlphanumericT, - AlphanumericU, - AlphanumericV, - AlphanumericW, - AlphanumericX, - AlphanumericY, - AlphanumericZ, - - // Edit (and escape) Keys - EditBackspace, - EditCapsLock, - EditEnter, - EditSpace, - EditTab, - Escape, - - // Function Keys - Function01, - Function02, - Function03, - Function04, - Function05, - Function06, - Function07, - Function08, - Function09, - Function10, - Function11, - Function12, - Function13, - Function14, - Function15, - Function16, - Function17, - Function18, - Function19, - Function20, - - // Modifier Keys - ModifierAltL, - ModifierAltR, - ModifierCtrlL, - ModifierCtrlR, - ModifierShiftL, - ModifierShiftR, - ModifierSuperL, - ModifierSuperR, - - // Navigation Keys - NavigationArrowDown, - NavigationArrowLeft, - NavigationArrowRight, - NavigationArrowUp, - NavigationDelete, - NavigationEnd, - NavigationHome, - NavigationInsert, - NavigationPageDown, - NavigationPageUp, - - // Numpad Keys - NumLock, - NumPad0, - NumPad1, - NumPad2, - NumPad3, - NumPad4, - NumPad5, - NumPad6, - NumPad7, - NumPad8, - NumPad9, - NumPadAdd, - NumPadDecimal, - NumPadDivide, - NumPadEnter, - NumPadMultiply, - NumPadSubtract, - - // Punctuation Keys - PunctuationApostrophe, - PunctuationBackslash, - PunctuationBracketL, - PunctuationBracketR, - PunctuationComma, - PunctuationEquals, - PunctuationHyphen, - PunctuationPeriod, - PunctuationSemicolon, - PunctuationSlash, - PunctuationTilde, - - // Supplementary ISO Key - SupplementaryISO, - - // Windows System Keys - WindowsSystemPause, - WindowsSystemPrint, - WindowsSystemScrollLock - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// ModifierKeyMask GetCorrespondingModifierKeyMask(const InputChannelId& channelId) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h index 5588f94d69..e3c21ec326 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h @@ -94,137 +94,268 @@ namespace AzFramework struct Key { // Alphanumeric Keys - static const InputChannelId Alphanumeric0; //!< The 0 key - static const InputChannelId Alphanumeric1; //!< The 1 key - static const InputChannelId Alphanumeric2; //!< The 2 key - static const InputChannelId Alphanumeric3; //!< The 3 key - static const InputChannelId Alphanumeric4; //!< The 4 key - static const InputChannelId Alphanumeric5; //!< The 5 key - static const InputChannelId Alphanumeric6; //!< The 6 key - static const InputChannelId Alphanumeric7; //!< The 7 key - static const InputChannelId Alphanumeric8; //!< The 8 key - static const InputChannelId Alphanumeric9; //!< The 9 key - static const InputChannelId AlphanumericA; //!< The A key - static const InputChannelId AlphanumericB; //!< The B key - static const InputChannelId AlphanumericC; //!< The C key - static const InputChannelId AlphanumericD; //!< The D key - static const InputChannelId AlphanumericE; //!< The E key - static const InputChannelId AlphanumericF; //!< The F key - static const InputChannelId AlphanumericG; //!< The G key - static const InputChannelId AlphanumericH; //!< The H key - static const InputChannelId AlphanumericI; //!< The I key - static const InputChannelId AlphanumericJ; //!< The J key - static const InputChannelId AlphanumericK; //!< The K key - static const InputChannelId AlphanumericL; //!< The L key - static const InputChannelId AlphanumericM; //!< The M key - static const InputChannelId AlphanumericN; //!< The N key - static const InputChannelId AlphanumericO; //!< The O key - static const InputChannelId AlphanumericP; //!< The P key - static const InputChannelId AlphanumericQ; //!< The Q key - static const InputChannelId AlphanumericR; //!< The R key - static const InputChannelId AlphanumericS; //!< The S key - static const InputChannelId AlphanumericT; //!< The T key - static const InputChannelId AlphanumericU; //!< The U key - static const InputChannelId AlphanumericV; //!< The V key - static const InputChannelId AlphanumericW; //!< The W key - static const InputChannelId AlphanumericX; //!< The X key - static const InputChannelId AlphanumericY; //!< The Y key - static const InputChannelId AlphanumericZ; //!< The Z key + static constexpr inline InputChannelId Alphanumeric0{"keyboard_key_alphanumeric_0"}; //!< The 0 key + static constexpr inline InputChannelId Alphanumeric1{"keyboard_key_alphanumeric_1"}; //!< The 1 key + static constexpr inline InputChannelId Alphanumeric2{"keyboard_key_alphanumeric_2"}; //!< The 2 key + static constexpr inline InputChannelId Alphanumeric3{"keyboard_key_alphanumeric_3"}; //!< The 3 key + static constexpr inline InputChannelId Alphanumeric4{"keyboard_key_alphanumeric_4"}; //!< The 4 key + static constexpr inline InputChannelId Alphanumeric5{"keyboard_key_alphanumeric_5"}; //!< The 5 key + static constexpr inline InputChannelId Alphanumeric6{"keyboard_key_alphanumeric_6"}; //!< The 6 key + static constexpr inline InputChannelId Alphanumeric7{"keyboard_key_alphanumeric_7"}; //!< The 7 key + static constexpr inline InputChannelId Alphanumeric8{"keyboard_key_alphanumeric_8"}; //!< The 8 key + static constexpr inline InputChannelId Alphanumeric9{"keyboard_key_alphanumeric_9"}; //!< The 9 key + static constexpr inline InputChannelId AlphanumericA{"keyboard_key_alphanumeric_A"}; //!< The A key + static constexpr inline InputChannelId AlphanumericB{"keyboard_key_alphanumeric_B"}; //!< The B key + static constexpr inline InputChannelId AlphanumericC{"keyboard_key_alphanumeric_C"}; //!< The C key + static constexpr inline InputChannelId AlphanumericD{"keyboard_key_alphanumeric_D"}; //!< The D key + static constexpr inline InputChannelId AlphanumericE{"keyboard_key_alphanumeric_E"}; //!< The E key + static constexpr inline InputChannelId AlphanumericF{"keyboard_key_alphanumeric_F"}; //!< The F key + static constexpr inline InputChannelId AlphanumericG{"keyboard_key_alphanumeric_G"}; //!< The G key + static constexpr inline InputChannelId AlphanumericH{"keyboard_key_alphanumeric_H"}; //!< The H key + static constexpr inline InputChannelId AlphanumericI{"keyboard_key_alphanumeric_I"}; //!< The I key + static constexpr inline InputChannelId AlphanumericJ{"keyboard_key_alphanumeric_J"}; //!< The J key + static constexpr inline InputChannelId AlphanumericK{"keyboard_key_alphanumeric_K"}; //!< The K key + static constexpr inline InputChannelId AlphanumericL{"keyboard_key_alphanumeric_L"}; //!< The L key + static constexpr inline InputChannelId AlphanumericM{"keyboard_key_alphanumeric_M"}; //!< The M key + static constexpr inline InputChannelId AlphanumericN{"keyboard_key_alphanumeric_N"}; //!< The N key + static constexpr inline InputChannelId AlphanumericO{"keyboard_key_alphanumeric_O"}; //!< The O key + static constexpr inline InputChannelId AlphanumericP{"keyboard_key_alphanumeric_P"}; //!< The P key + static constexpr inline InputChannelId AlphanumericQ{"keyboard_key_alphanumeric_Q"}; //!< The Q key + static constexpr inline InputChannelId AlphanumericR{"keyboard_key_alphanumeric_R"}; //!< The R key + static constexpr inline InputChannelId AlphanumericS{"keyboard_key_alphanumeric_S"}; //!< The S key + static constexpr inline InputChannelId AlphanumericT{"keyboard_key_alphanumeric_T"}; //!< The T key + static constexpr inline InputChannelId AlphanumericU{"keyboard_key_alphanumeric_U"}; //!< The U key + static constexpr inline InputChannelId AlphanumericV{"keyboard_key_alphanumeric_V"}; //!< The V key + static constexpr inline InputChannelId AlphanumericW{"keyboard_key_alphanumeric_W"}; //!< The W key + static constexpr inline InputChannelId AlphanumericX{"keyboard_key_alphanumeric_X"}; //!< The X key + static constexpr inline InputChannelId AlphanumericY{"keyboard_key_alphanumeric_Y"}; //!< The Y key + static constexpr inline InputChannelId AlphanumericZ{"keyboard_key_alphanumeric_Z"}; //!< The Z key - // Edit (and escape) Keys - static const InputChannelId EditBackspace; //!< The backspace key - static const InputChannelId EditCapsLock; //!< The caps lock key - static const InputChannelId EditEnter; //!< The enter/return key - static const InputChannelId EditSpace; //!< The spacebar key - static const InputChannelId EditTab; //!< The tab key - static const InputChannelId Escape; //!< The escape key + // Edit {and escape} Keys + static constexpr inline InputChannelId EditBackspace{"keyboard_key_edit_backspace"}; //!< The backspace key + static constexpr inline InputChannelId EditCapsLock{"keyboard_key_edit_capslock"}; //!< The caps lock key + static constexpr inline InputChannelId EditEnter{"keyboard_key_edit_enter"}; //!< The enter/return key + static constexpr inline InputChannelId EditSpace{"keyboard_key_edit_space"}; //!< The spacebar key + static constexpr inline InputChannelId EditTab{"keyboard_key_edit_tab"}; //!< The tab key + static constexpr inline InputChannelId Escape{"keyboard_key_escape"}; //!< The escape key // Function Keys - static const InputChannelId Function01; //!< The F1 key - static const InputChannelId Function02; //!< The F2 key - static const InputChannelId Function03; //!< The F3 key - static const InputChannelId Function04; //!< The F4 key - static const InputChannelId Function05; //!< The F5 key - static const InputChannelId Function06; //!< The F6 key - static const InputChannelId Function07; //!< The F7 key - static const InputChannelId Function08; //!< The F8 key - static const InputChannelId Function09; //!< The F9 key - static const InputChannelId Function10; //!< The F10 key - static const InputChannelId Function11; //!< The F11 key - static const InputChannelId Function12; //!< The F12 key - static const InputChannelId Function13; //!< The F13 key - static const InputChannelId Function14; //!< The F14 key - static const InputChannelId Function15; //!< The F15 key - static const InputChannelId Function16; //!< The F16 key - static const InputChannelId Function17; //!< The F17 key - static const InputChannelId Function18; //!< The F18 key - static const InputChannelId Function19; //!< The F19 key - static const InputChannelId Function20; //!< The F20 key + static constexpr inline InputChannelId Function01{"keyboard_key_function_F01"}; //!< The F1 key + static constexpr inline InputChannelId Function02{"keyboard_key_function_F02"}; //!< The F2 key + static constexpr inline InputChannelId Function03{"keyboard_key_function_F03"}; //!< The F3 key + static constexpr inline InputChannelId Function04{"keyboard_key_function_F04"}; //!< The F4 key + static constexpr inline InputChannelId Function05{"keyboard_key_function_F05"}; //!< The F5 key + static constexpr inline InputChannelId Function06{"keyboard_key_function_F06"}; //!< The F6 key + static constexpr inline InputChannelId Function07{"keyboard_key_function_F07"}; //!< The F7 key + static constexpr inline InputChannelId Function08{"keyboard_key_function_F08"}; //!< The F8 key + static constexpr inline InputChannelId Function09{"keyboard_key_function_F09"}; //!< The F9 key + static constexpr inline InputChannelId Function10{"keyboard_key_function_F10"}; //!< The F10 key + static constexpr inline InputChannelId Function11{"keyboard_key_function_F11"}; //!< The F11 key + static constexpr inline InputChannelId Function12{"keyboard_key_function_F12"}; //!< The F12 key + static constexpr inline InputChannelId Function13{"keyboard_key_function_F13"}; //!< The F13 key + static constexpr inline InputChannelId Function14{"keyboard_key_function_F14"}; //!< The F14 key + static constexpr inline InputChannelId Function15{"keyboard_key_function_F15"}; //!< The F15 key + static constexpr inline InputChannelId Function16{"keyboard_key_function_F16"}; //!< The F16 key + static constexpr inline InputChannelId Function17{"keyboard_key_function_F17"}; //!< The F17 key + static constexpr inline InputChannelId Function18{"keyboard_key_function_F18"}; //!< The F18 key + static constexpr inline InputChannelId Function19{"keyboard_key_function_F19"}; //!< The F19 key + static constexpr inline InputChannelId Function20{"keyboard_key_function_F20"}; //!< The F20 key // Modifier Keys - static const InputChannelId ModifierAltL; //!< The left alt/option key - static const InputChannelId ModifierAltR; //!< The right alt/option key - static const InputChannelId ModifierCtrlL; //!< The left control key - static const InputChannelId ModifierCtrlR; //!< The right control key - static const InputChannelId ModifierShiftL; //!< The left shift key - static const InputChannelId ModifierShiftR; //!< The right shift key - static const InputChannelId ModifierSuperL; //!< The left super (windows or apple) key - static const InputChannelId ModifierSuperR; //!< The right super (windows or apple) key + static constexpr inline InputChannelId ModifierAltL{"keyboard_key_modifier_alt_l"}; //!< The left alt/option key + static constexpr inline InputChannelId ModifierAltR{"keyboard_key_modifier_alt_r"}; //!< The right alt/option key + static constexpr inline InputChannelId ModifierCtrlL{"keyboard_key_modifier_ctrl_l"}; //!< The left control key + static constexpr inline InputChannelId ModifierCtrlR{"keyboard_key_modifier_ctrl_r"}; //!< The right control key + static constexpr inline InputChannelId ModifierShiftL{"keyboard_key_modifier_shift_l"}; //!< The left shift key + static constexpr inline InputChannelId ModifierShiftR{"keyboard_key_modifier_shift_r"}; //!< The right shift key + static constexpr inline InputChannelId ModifierSuperL{"keyboard_key_modifier_super_l"}; //!< The left super {windows or apple} key + static constexpr inline InputChannelId ModifierSuperR{"keyboard_key_modifier_super_r"}; //!< The right super {windows or apple} key // Navigation Keys - static const InputChannelId NavigationArrowDown; //!< The down arrow key - static const InputChannelId NavigationArrowLeft; //!< The left arrow key - static const InputChannelId NavigationArrowRight; //!< The right arrow key - static const InputChannelId NavigationArrowUp; //!< The up arrow key - static const InputChannelId NavigationDelete; //!< The delete key - static const InputChannelId NavigationEnd; //!< The end key - static const InputChannelId NavigationHome; //!< The home key - static const InputChannelId NavigationInsert; //!< The insert key - static const InputChannelId NavigationPageDown; //!< The page down key - static const InputChannelId NavigationPageUp; //!< The page up key + static constexpr inline InputChannelId NavigationArrowDown{"keyboard_key_navigation_arrow_down"}; //!< The down arrow key + static constexpr inline InputChannelId NavigationArrowLeft{"keyboard_key_navigation_arrow_left"}; //!< The left arrow key + static constexpr inline InputChannelId NavigationArrowRight{"keyboard_key_navigation_arrow_right"}; //!< The right arrow key + static constexpr inline InputChannelId NavigationArrowUp{"keyboard_key_navigation_arrow_up"}; //!< The up arrow key + static constexpr inline InputChannelId NavigationDelete{"keyboard_key_navigation_delete"}; //!< The delete key + static constexpr inline InputChannelId NavigationEnd{"keyboard_key_navigation_end"}; //!< The end key + static constexpr inline InputChannelId NavigationHome{"keyboard_key_navigation_home"}; //!< The home key + static constexpr inline InputChannelId NavigationInsert{"keyboard_key_navigation_insert"}; //!< The insert key + static constexpr inline InputChannelId NavigationPageDown{"keyboard_key_navigation_page_down"}; //!< The page down key + static constexpr inline InputChannelId NavigationPageUp{"keyboard_key_navigation_page_up"}; //!< The page up key // Numpad Keys - static const InputChannelId NumLock; //!< The num lock key (the clear key on apple keyboards) - static const InputChannelId NumPad0; //!< The numpad 0 key - static const InputChannelId NumPad1; //!< The numpad 1 key - static const InputChannelId NumPad2; //!< The numpad 2 key - static const InputChannelId NumPad3; //!< The numpad 3 key - static const InputChannelId NumPad4; //!< The numpad 4 key - static const InputChannelId NumPad5; //!< The numpad 5 key - static const InputChannelId NumPad6; //!< The numpad 6 key - static const InputChannelId NumPad7; //!< The numpad 7 key - static const InputChannelId NumPad8; //!< The numpad 8 key - static const InputChannelId NumPad9; //!< The numpad 9 key - static const InputChannelId NumPadAdd; //!< The numpad add key - static const InputChannelId NumPadDecimal; //!< The numpad decimal key - static const InputChannelId NumPadDivide; //!< The numpad divide key - static const InputChannelId NumPadEnter; //!< The numpad enter key - static const InputChannelId NumPadMultiply; //!< The numpad multiply key - static const InputChannelId NumPadSubtract; //!< The numpad subtract key + static constexpr inline InputChannelId NumLock{"keyboard_key_num_lock"}; //!< The num lock key {the clear key on apple keyboards} + static constexpr inline InputChannelId NumPad0{"keyboard_key_numpad_0"}; //!< The numpad 0 key + static constexpr inline InputChannelId NumPad1{"keyboard_key_numpad_1"}; //!< The numpad 1 key + static constexpr inline InputChannelId NumPad2{"keyboard_key_numpad_2"}; //!< The numpad 2 key + static constexpr inline InputChannelId NumPad3{"keyboard_key_numpad_3"}; //!< The numpad 3 key + static constexpr inline InputChannelId NumPad4{"keyboard_key_numpad_4"}; //!< The numpad 4 key + static constexpr inline InputChannelId NumPad5{"keyboard_key_numpad_5"}; //!< The numpad 5 key + static constexpr inline InputChannelId NumPad6{"keyboard_key_numpad_6"}; //!< The numpad 6 key + static constexpr inline InputChannelId NumPad7{"keyboard_key_numpad_7"}; //!< The numpad 7 key + static constexpr inline InputChannelId NumPad8{"keyboard_key_numpad_8"}; //!< The numpad 8 key + static constexpr inline InputChannelId NumPad9{"keyboard_key_numpad_9"}; //!< The numpad 9 key + static constexpr inline InputChannelId NumPadAdd{"keyboard_key_numpad_add"}; //!< The numpad add key + static constexpr inline InputChannelId NumPadDecimal{"keyboard_key_numpad_decimal"}; //!< The numpad decimal key + static constexpr inline InputChannelId NumPadDivide{"keyboard_key_numpad_divide"}; //!< The numpad divide key + static constexpr inline InputChannelId NumPadEnter{"keyboard_key_numpad_enter"}; //!< The numpad enter key + static constexpr inline InputChannelId NumPadMultiply{"keyboard_key_numpad_multiply"}; //!< The numpad multiply key + static constexpr inline InputChannelId NumPadSubtract{"keyboard_key_numpad_subtract"}; //!< The numpad subtract key // Punctuation Keys - static const InputChannelId PunctuationApostrophe; //!< The apostrophe key - static const InputChannelId PunctuationBackslash; //!< The backslash key - static const InputChannelId PunctuationBracketL; //!< The left bracket key - static const InputChannelId PunctuationBracketR; //!< The right bracket key - static const InputChannelId PunctuationComma; //!< The comma key - static const InputChannelId PunctuationEquals; //!< The equals key - static const InputChannelId PunctuationHyphen; //!< The hyphen/underscore key - static const InputChannelId PunctuationPeriod; //!< The period key - static const InputChannelId PunctuationSemicolon; //!< The semicolon key - static const InputChannelId PunctuationSlash; //!< The (forward) slash key - static const InputChannelId PunctuationTilde; //!< The tilde/grave key + static constexpr inline InputChannelId PunctuationApostrophe{"keyboard_key_punctuation_apostrophe"}; //!< The apostrophe key + static constexpr inline InputChannelId PunctuationBackslash{"keyboard_key_punctuation_backslash"}; //!< The backslash key + static constexpr inline InputChannelId PunctuationBracketL{"keyboard_key_punctuation_bracket_l"}; //!< The left bracket key + static constexpr inline InputChannelId PunctuationBracketR{"keyboard_key_punctuation_bracket_r"}; //!< The right bracket key + static constexpr inline InputChannelId PunctuationComma{"keyboard_key_punctuation_comma"}; //!< The comma key + static constexpr inline InputChannelId PunctuationEquals{"keyboard_key_punctuation_equals"}; //!< The equals key + static constexpr inline InputChannelId PunctuationHyphen{"keyboard_key_punctuation_hyphen"}; //!< The hyphen/underscore key + static constexpr inline InputChannelId PunctuationPeriod{"keyboard_key_punctuation_period"}; //!< The period key + static constexpr inline InputChannelId PunctuationSemicolon{"keyboard_key_punctuation_semicolon"}; //!< The semicolon key + static constexpr inline InputChannelId PunctuationSlash{"keyboard_key_punctuation_slash"}; //!< The {forward} slash key + static constexpr inline InputChannelId PunctuationTilde{"keyboard_key_punctuation_tilde"}; //!< The tilde/grave key // Supplementary ISO Key - static const InputChannelId SupplementaryISO; //!< The supplementary ISO layout key + static constexpr inline InputChannelId SupplementaryISO{"keyboard_key_supplementary_iso"}; //!< The supplementary ISO layout key // Windows System Keys - static const InputChannelId WindowsSystemPause; //!< The windows pause key - static const InputChannelId WindowsSystemPrint; //!< The windows print key - static const InputChannelId WindowsSystemScrollLock; //!< The windows scroll lock key + static constexpr inline InputChannelId WindowsSystemPause{"keyboard_key_windows_system_pause"}; //!< The windows pause key + static constexpr inline InputChannelId WindowsSystemPrint{"keyboard_key_windows_system_print"}; //!< The windows print key + static constexpr inline InputChannelId WindowsSystemScrollLock{"keyboard_key_windows_system_scroll_lock"}; //!< The windows scroll lock key //!< All keyboard key ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + // Alphanumeric Keys + Alphanumeric0, + Alphanumeric1, + Alphanumeric2, + Alphanumeric3, + Alphanumeric4, + Alphanumeric5, + Alphanumeric6, + Alphanumeric7, + Alphanumeric8, + Alphanumeric9, + AlphanumericA, + AlphanumericB, + AlphanumericC, + AlphanumericD, + AlphanumericE, + AlphanumericF, + AlphanumericG, + AlphanumericH, + AlphanumericI, + AlphanumericJ, + AlphanumericK, + AlphanumericL, + AlphanumericM, + AlphanumericN, + AlphanumericO, + AlphanumericP, + AlphanumericQ, + AlphanumericR, + AlphanumericS, + AlphanumericT, + AlphanumericU, + AlphanumericV, + AlphanumericW, + AlphanumericX, + AlphanumericY, + AlphanumericZ, + + // Edit (and escape) Keys + EditBackspace, + EditCapsLock, + EditEnter, + EditSpace, + EditTab, + Escape, + + // Function Keys + Function01, + Function02, + Function03, + Function04, + Function05, + Function06, + Function07, + Function08, + Function09, + Function10, + Function11, + Function12, + Function13, + Function14, + Function15, + Function16, + Function17, + Function18, + Function19, + Function20, + + // Modifier Keys + ModifierAltL, + ModifierAltR, + ModifierCtrlL, + ModifierCtrlR, + ModifierShiftL, + ModifierShiftR, + ModifierSuperL, + ModifierSuperR, + + // Navigation Keys + NavigationArrowDown, + NavigationArrowLeft, + NavigationArrowRight, + NavigationArrowUp, + NavigationDelete, + NavigationEnd, + NavigationHome, + NavigationInsert, + NavigationPageDown, + NavigationPageUp, + + // Numpad Keys + NumLock, + NumPad0, + NumPad1, + NumPad2, + NumPad3, + NumPad4, + NumPad5, + NumPad6, + NumPad7, + NumPad8, + NumPad9, + NumPadAdd, + NumPadDecimal, + NumPadDivide, + NumPadEnter, + NumPadMultiply, + NumPadSubtract, + + // Punctuation Keys + PunctuationApostrophe, + PunctuationBackslash, + PunctuationBracketL, + PunctuationBracketR, + PunctuationComma, + PunctuationEquals, + PunctuationHyphen, + PunctuationPeriod, + PunctuationSemicolon, + PunctuationSlash, + PunctuationTilde, + + // Supplementary ISO Key + SupplementaryISO, + + // Windows System Keys + WindowsSystemPause, + WindowsSystemPrint, + WindowsSystemScrollLock + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp index 34c72f20d2..c144f63d2c 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp @@ -23,44 +23,6 @@ namespace AzFramework return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32()); } - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMotion::Acceleration::Gravity("motion_acceleration_gravity"); - const InputChannelId InputDeviceMotion::Acceleration::Raw("motion_acceleration_raw"); - const InputChannelId InputDeviceMotion::Acceleration::User("motion_acceleration_user"); - const AZStd::array InputDeviceMotion::Acceleration::All = - {{ - Gravity, - Raw, - User - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMotion::RotationRate::Raw("motion_rotation_rate_raw"); - const InputChannelId InputDeviceMotion::RotationRate::Unbiased("motion_rotation_rate_unbiased"); - const AZStd::array InputDeviceMotion::RotationRate::All = - {{ - Raw, - Unbiased - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMotion::MagneticField::North("motion_magnetic_field_north"); - const InputChannelId InputDeviceMotion::MagneticField::Raw("motion_magnetic_field_raw"); - const InputChannelId InputDeviceMotion::MagneticField::Unbiased("motion_magnetic_field_unbiased"); - const AZStd::array InputDeviceMotion::MagneticField::All = - {{ - North, - Raw, - Unbiased - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMotion::Orientation::Current("motion_orientation_current"); - const AZStd::array InputDeviceMotion::Orientation::All = - {{ - Current - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceMotion::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h index 1e020a6e02..872bd1cfa0 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h @@ -44,12 +44,17 @@ namespace AzFramework //! - InputMotionSensorRequests::SetInputChannelEnabled struct Acceleration { - static const InputChannelId Gravity; - static const InputChannelId Raw; - static const InputChannelId User; + static constexpr inline InputChannelId Gravity{"motion_acceleration_gravity"}; + static constexpr inline InputChannelId Raw{"motion_acceleration_raw"}; + static constexpr inline InputChannelId User{"motion_acceleration_user"}; //!< All acceleration input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + Gravity, + Raw, + User + }; }; //////////////////////////////////////////////////////////////////////////////////////////// @@ -60,11 +65,15 @@ namespace AzFramework //! - InputMotionSensorRequests::SetInputChannelEnabled struct RotationRate { - static const InputChannelId Raw; - static const InputChannelId Unbiased; + static constexpr inline InputChannelId Raw{"motion_rotation_rate_raw"}; + static constexpr inline InputChannelId Unbiased{"motion_rotation_rate_unbiased"}; //!< All rotation rate input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + Raw, + Unbiased + }; }; //////////////////////////////////////////////////////////////////////////////////////////// @@ -75,12 +84,17 @@ namespace AzFramework //! - InputMotionSensorRequests::SetInputChannelEnabled struct MagneticField { - static const InputChannelId North; - static const InputChannelId Raw; - static const InputChannelId Unbiased; + static constexpr inline InputChannelId North{"motion_magnetic_field_north"}; + static constexpr inline InputChannelId Raw{"motion_magnetic_field_raw"}; + static constexpr inline InputChannelId Unbiased{"motion_magnetic_field_unbiased"}; //!< All magnetic field input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + North, + Raw, + Unbiased + }; }; //////////////////////////////////////////////////////////////////////////////////////////// @@ -91,10 +105,13 @@ namespace AzFramework //! - InputMotionSensorRequests::SetInputChannelEnabled struct Orientation { - static const InputChannelId Current; + static constexpr inline InputChannelId Current{"motion_orientation_current"}; //!< All orientation input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + Current + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp index 2cc4573bce..e9af4cc4ce 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp @@ -33,35 +33,6 @@ namespace AzFramework return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32()); } - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMouse::Button::Left("mouse_button_left"); - const InputChannelId InputDeviceMouse::Button::Right("mouse_button_right"); - const InputChannelId InputDeviceMouse::Button::Middle("mouse_button_middle"); - const InputChannelId InputDeviceMouse::Button::Other1("mouse_button_other1"); - const InputChannelId InputDeviceMouse::Button::Other2("mouse_button_other2"); - const AZStd::array InputDeviceMouse::Button::All = - {{ - Left, - Right, - Middle, - Other1, - Other2 - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMouse::Movement::X("mouse_delta_x"); - const InputChannelId InputDeviceMouse::Movement::Y("mouse_delta_y"); - const InputChannelId InputDeviceMouse::Movement::Z("mouse_delta_z"); - const AZStd::array InputDeviceMouse::Movement::All = - {{ - X, - Y, - Z - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMouse::SystemCursorPosition("mouse_system_cursor_position"); - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceMouse::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h index 99fe5b820c..3b35e03f1b 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h @@ -66,14 +66,21 @@ namespace AzFramework //! been implemented for windows simply to provide for backwards compatibility with CryInput. struct Button { - static const InputChannelId Left; //!< The left mouse button - static const InputChannelId Right; //!< The right mouse button - static const InputChannelId Middle; //!< The middle mouse button - static const InputChannelId Other1; //!< DEPRECATED: the x1 mouse button - static const InputChannelId Other2; //!< DEPRECATED: the x2 mouse button + static constexpr inline InputChannelId Left{"mouse_button_left"}; //!< The left mouse button + static constexpr inline InputChannelId Right{"mouse_button_right"}; //!< The right mouse button + static constexpr inline InputChannelId Middle{"mouse_button_middle"}; //!< The middle mouse button + static constexpr inline InputChannelId Other1{"mouse_button_other1"}; //!< DEPRECATED: the x1 mouse button + static constexpr inline InputChannelId Other2{"mouse_button_other2"}; //!< DEPRECATED: the x2 mouse button //!< All mouse button ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + Left, + Right, + Middle, + Other1, + Other2 + }; }; //////////////////////////////////////////////////////////////////////////////////////////// @@ -82,12 +89,17 @@ namespace AzFramework //! directly correlate to the mouse position (which is queried directly from the system). struct Movement { - static const InputChannelId X; //!< Raw horizontal mouse movement over the last frame - static const InputChannelId Y; //!< Raw vertical mouse movement over the last frame - static const InputChannelId Z; //!< Raw mouse wheel movement over the last frame + static constexpr inline InputChannelId X{"mouse_delta_x"}; //!< Raw horizontal mouse movement over the last frame + static constexpr inline InputChannelId Y{"mouse_delta_y"}; //!< Raw vertical mouse movement over the last frame + static constexpr inline InputChannelId Z{"mouse_delta_z"}; //!< Raw mouse wheel movement over the last frame //!< All mouse movement ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + X, + Y, + Z + }; }; //////////////////////////////////////////////////////////////////////////////////////////// @@ -96,7 +108,7 @@ namespace AzFramework //! the system cursor is hidden or visible. When the system cursor has been constrained to //! the active window values will be in the [0.0, 1.0] range, but not when unconstrained. //! See also InputSystemCursorRequests::SetSystemCursorState and GetSystemCursorState. - static const InputChannelId SystemCursorPosition; + static constexpr inline InputChannelId SystemCursorPosition{"mouse_system_cursor_position"}; //////////////////////////////////////////////////////////////////////////////////////////// // Allocator diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp index 07d10090a3..be850e9289 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp @@ -24,31 +24,6 @@ namespace AzFramework return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32()); } - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceTouch::Touch::Index0("touch_index_0"); - const InputChannelId InputDeviceTouch::Touch::Index1("touch_index_1"); - const InputChannelId InputDeviceTouch::Touch::Index2("touch_index_2"); - const InputChannelId InputDeviceTouch::Touch::Index3("touch_index_3"); - const InputChannelId InputDeviceTouch::Touch::Index4("touch_index_4"); - const InputChannelId InputDeviceTouch::Touch::Index5("touch_index_5"); - const InputChannelId InputDeviceTouch::Touch::Index6("touch_index_6"); - const InputChannelId InputDeviceTouch::Touch::Index7("touch_index_7"); - const InputChannelId InputDeviceTouch::Touch::Index8("touch_index_8"); - const InputChannelId InputDeviceTouch::Touch::Index9("touch_index_9"); - const AZStd::array InputDeviceTouch::Touch::All = - {{ - Index0, - Index1, - Index2, - Index3, - Index4, - Index5, - Index6, - Index7, - Index8, - Index9 - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceTouch::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h index 6e069c44ba..d21834c22a 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h @@ -38,19 +38,31 @@ namespace AzFramework //! track is arbitrary, but ten seems to be more than sufficient for most game applications. struct Touch { - static const InputChannelId Index0; //!< Touch index 0 - static const InputChannelId Index1; //!< Touch index 1 - static const InputChannelId Index2; //!< Touch index 2 - static const InputChannelId Index3; //!< Touch index 3 - static const InputChannelId Index4; //!< Touch index 4 - static const InputChannelId Index5; //!< Touch index 5 - static const InputChannelId Index6; //!< Touch index 6 - static const InputChannelId Index7; //!< Touch index 7 - static const InputChannelId Index8; //!< Touch index 8 - static const InputChannelId Index9; //!< Touch index 9 + static constexpr inline InputChannelId Index0{"touch_index_0"}; //!< Touch index 0 + static constexpr inline InputChannelId Index1{"touch_index_1"}; //!< Touch index 1 + static constexpr inline InputChannelId Index2{"touch_index_2"}; //!< Touch index 2 + static constexpr inline InputChannelId Index3{"touch_index_3"}; //!< Touch index 3 + static constexpr inline InputChannelId Index4{"touch_index_4"}; //!< Touch index 4 + static constexpr inline InputChannelId Index5{"touch_index_5"}; //!< Touch index 5 + static constexpr inline InputChannelId Index6{"touch_index_6"}; //!< Touch index 6 + static constexpr inline InputChannelId Index7{"touch_index_7"}; //!< Touch index 7 + static constexpr inline InputChannelId Index8{"touch_index_8"}; //!< Touch index 8 + static constexpr inline InputChannelId Index9{"touch_index_9"}; //!< Touch index 9 //!< All touch input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + Index0, + Index1, + Index2, + Index3, + Index4, + Index5, + Index6, + Index7, + Index8, + Index9 + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp index bc23826ebc..f3024f11ab 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp @@ -23,17 +23,6 @@ namespace AzFramework return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32()); } - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceVirtualKeyboard::Command::EditEnter("virtual_keyboard_edit_enter"); - const InputChannelId InputDeviceVirtualKeyboard::Command::EditClear("virtual_keyboard_edit_clear"); - const InputChannelId InputDeviceVirtualKeyboard::Command::NavigationBack("virtual_keyboard_navigation_back"); - const AZStd::array InputDeviceVirtualKeyboard::Command::All = - {{ - EditClear, - EditEnter, - NavigationBack - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceVirtualKeyboard::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h index 43e2815875..6f62c3ec61 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h @@ -39,17 +39,22 @@ namespace AzFramework struct Command { //!< The clear command used to indicate the user wants to clear the active text field - static const InputChannelId EditClear; + static constexpr inline InputChannelId EditClear{"virtual_keyboard_edit_enter"}; //!< The enter/return/close command used to indicate the user has finished text editing - static const InputChannelId EditEnter; + static constexpr inline InputChannelId EditEnter{"virtual_keyboard_edit_clear"}; //!< The back command used to indicate the user wants to navigate 'backwards'. //!< This is specific to android devices, and does not have an ios equivalent. - static const InputChannelId NavigationBack; + static constexpr inline InputChannelId NavigationBack{"virtual_keyboard_navigation_back"}; //!< All virtual keyboard command ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + EditClear, + EditEnter, + NavigationBack + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMapping.cpp b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMapping.cpp index 2f848368ca..b8a261677f 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMapping.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMapping.cpp @@ -9,9 +9,156 @@ #include #include +#include +#include +#include + //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { + //////////////////////////////////////////////////////////////////////////////////////////////// + void InputMapping::InputChannelNameFilteredByDeviceType::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("Input Device Type", &InputChannelNameFilteredByDeviceType::m_inputDeviceType) + ->Field("Input Channel Name", &InputChannelNameFilteredByDeviceType::m_inputChannelName) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("InputChannelNameFilteredByDeviceType", + "An input channel name (filtered by an input device type) to add to the input mapping.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputChannelNameFilteredByDeviceType::GetNameLabelOverride) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &InputChannelNameFilteredByDeviceType::m_inputDeviceType, "Input Device Type", + "The type of input device by which to filter input channel names.") + ->Attribute(AZ::Edit::Attributes::StringList, &InputChannelNameFilteredByDeviceType::GetValidInputDeviceTypes) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &InputChannelNameFilteredByDeviceType::OnInputDeviceTypeSelected) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &InputChannelNameFilteredByDeviceType::m_inputChannelName, "Input Channel Name", + "The input channel name to add to the input mapping.") + ->Attribute(AZ::Edit::Attributes::StringList, &InputChannelNameFilteredByDeviceType::GetValidInputChannelNamesBySelectedDevice) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) + ; + } + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + InputMapping::InputChannelNameFilteredByDeviceType::InputChannelNameFilteredByDeviceType() + { + // Try initialize the selected input device type and input channel name to something valid. + if (m_inputDeviceType.empty()) + { + const AZStd::vector validInputDeviceTypes = GetValidInputDeviceTypes(); + if (!validInputDeviceTypes.empty()) + { + m_inputDeviceType = validInputDeviceTypes[0]; + OnInputDeviceTypeSelected(); + } + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + AZ::Crc32 InputMapping::InputChannelNameFilteredByDeviceType::OnInputDeviceTypeSelected() + { + const AZStd::vector validInputNames = GetValidInputChannelNamesBySelectedDevice(); + if (!validInputNames.empty()) + { + m_inputChannelName = validInputNames[0]; + } + return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + AZStd::string InputMapping::InputChannelNameFilteredByDeviceType::GetNameLabelOverride() const + { + return m_inputChannelName.empty() ? "" : m_outputInputChannelName; + } + //////////////////////////////////////////////////////////////////////////////////////////////// InputMapping::InputMapping(const InputChannelId& inputChannelId, const InputContext& inputContext) : InputChannel(inputChannelId, inputContext) diff --git a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMapping.h b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMapping.h index 93ca96eded..c2f82fb7f8 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMapping.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMapping.h @@ -12,6 +12,7 @@ #include #include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework @@ -26,6 +27,111 @@ namespace AzFramework class InputMapping : public InputChannel { public: + //////////////////////////////////////////////////////////////////////////////////////////// + //! Convenience class that allows for selection of an input channel name filtered by device. + struct InputChannelNameFilteredByDeviceType + { + public: + //////////////////////////////////////////////////////////////////////////////////////// + // Allocator + AZ_CLASS_ALLOCATOR(InputChannelNameFilteredByDeviceType, AZ::SystemAllocator, 0); + + //////////////////////////////////////////////////////////////////////////////////////// + // Type Info + AZ_RTTI(InputChannelNameFilteredByDeviceType, "{68CC4865-1C0E-4E2E-BDAE-AF42EA30DBE8}"); + + //////////////////////////////////////////////////////////////////////////////////////// + // Reflection + static void Reflect(AZ::ReflectContext* context); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Constructor + InputChannelNameFilteredByDeviceType(); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Destructor + virtual ~InputChannelNameFilteredByDeviceType() = default; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Get the currently selected input device type. + //! \return Currently selected input device type. + inline const AZStd::string& GetInputDeviceType() const { return m_inputDeviceType; } + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Get the currently selected input channel name. + //! \return Currently selected input channel name. + inline const AZStd::string& GetInputChannelName() const { return m_inputChannelName; } + + protected: + //////////////////////////////////////////////////////////////////////////////////////////// + //! Called when an input device type is selected. + //! \return The AZ::Edit::PropertyRefreshLevels to apply to the property tree view. + virtual AZ::Crc32 OnInputDeviceTypeSelected(); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Get the name label override to display. + //! \return Name label override to display. + virtual AZStd::string GetNameLabelOverride() const; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Get the valid input device types for this input mapping. + //! \return Valid input device types for this input mapping. + virtual AZStd::vector GetValidInputDeviceTypes() const; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Get the valid input channel names for this input mapping given the selected device type. + //! \return Valid input channel names for this input mapping given the selected device type. + virtual AZStd::vector GetValidInputChannelNamesBySelectedDevice() const; + + private: + //////////////////////////////////////////////////////////////////////////////////////////// + // Variables + AZStd::string m_inputDeviceType; //!< The currently selected input device type. + AZStd::string m_inputChannelName; //!< The currently selected input channel name. + }; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Base class for input mapping configuration values that are exposed to the editor. + class ConfigBase + { + public: + //////////////////////////////////////////////////////////////////////////////////////// + // Allocator + AZ_CLASS_ALLOCATOR(ConfigBase, AZ::SystemAllocator, 0); + + //////////////////////////////////////////////////////////////////////////////////////// + // Type Info + AZ_RTTI(ConfigBase, "{72EBBBCC-D57E-4085-AFD9-4910506010B6}"); + + //////////////////////////////////////////////////////////////////////////////////////// + // Reflection + static void Reflect(AZ::ReflectContext* context); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Destructor + virtual ~ConfigBase() = default; + + //////////////////////////////////////////////////////////////////////////////////////// + //! Create an input mapping and add it to the input context. + //! \param[in] inputContext Input context that the input mapping will be added to. + AZStd::shared_ptr CreateInputMappingAndAddToContext(InputContext& inputContext) const; + + //////////////////////////////////////////////////////////////////////////////////////// + //! Override to create the relevant input mapping. + //! \param[in] inputContext Input context that owns the input mapping. + virtual AZStd::shared_ptr CreateInputMapping(const InputContext& inputContext) const = 0; + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Get the name label override to display. + //! \return Name label override to display. + virtual AZStd::string GetNameLabelOverride() const; + + protected: + //////////////////////////////////////////////////////////////////////////////////////// + //! The unique input channel name (event) output by the input mapping. + AZStd::string m_outputInputChannelName; + }; + //////////////////////////////////////////////////////////////////////////////////////////// // Allocator AZ_CLASS_ALLOCATOR(InputMapping, AZ::SystemAllocator, 0); diff --git a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.cpp b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.cpp index 5d371888da..6837807f4e 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.cpp @@ -8,9 +8,72 @@ #include +#include +#include +#include + //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { + //////////////////////////////////////////////////////////////////////////////////////////////// + void InputMappingAnd::Config::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("Source Input Channel Names", &Config::m_sourceInputChannelNames) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("Input Mapping: And", + "Maps multiple different input sources to a single output using 'AND' logic.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputMappingAnd::Config::GetNameLabelOverride) + ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_sourceInputChannelNames, "Source Input Channel Names", + "The source input channel names that will be mapped to the output input channel name.") + ; + } + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + AZStd::shared_ptr InputMappingAnd::Config::CreateInputMapping(const InputContext& inputContext) const + { + if (m_outputInputChannelName.empty()) + { + AZ_Error("InputMappingAnd::Config", false, "Cannot create input mapping with empty name."); + return nullptr; + } + + if (InputChannelRequests::FindInputChannel(InputChannelId(m_outputInputChannelName.c_str()))) + { + AZ_Error("InputMappingAnd::Config", false, + "Cannot create input mapping '%s' with non-unique name.", m_outputInputChannelName.c_str()); + return nullptr; + } + + if (m_sourceInputChannelNames.empty()) + { + AZ_Error("InputMappingAnd::Config", false, + "Cannot create input mapping '%s' with no source inputs.", m_outputInputChannelName.c_str()); + return nullptr; + } + + const InputChannelId outputInputChannelId(m_outputInputChannelName.c_str()); + AZStd::shared_ptr inputMapping = AZStd::make_shared(outputInputChannelId, + inputContext); + for (const InputChannelNameFilteredByDeviceType& sourceInputChannelName : m_sourceInputChannelNames) + { + const InputChannelId sourceInputChannelId(sourceInputChannelName.GetInputChannelName().c_str()); + inputMapping->AddSourceInput(sourceInputChannelId); + } + return inputMapping; + } + //////////////////////////////////////////////////////////////////////////////////////////////// InputMappingAnd::InputMappingAnd(const InputChannelId& inputChannelId, const InputContext& inputContext) : InputMapping(inputChannelId, inputContext) diff --git a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.h b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.h index d7a767e3a5..61e54497db 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.h @@ -19,6 +19,38 @@ namespace AzFramework class InputMappingAnd : public InputMapping { public: + //////////////////////////////////////////////////////////////////////////////////////////// + //! The input mapping configuration values that are exposed to the editor. + class Config : public InputMapping::ConfigBase + { + public: + //////////////////////////////////////////////////////////////////////////////////////// + // Allocator + AZ_CLASS_ALLOCATOR(Config, AZ::SystemAllocator, 0); + + //////////////////////////////////////////////////////////////////////////////////////// + // Type Info + AZ_RTTI(Config, "{54E972F3-0477-4E2E-93F5-4E06ED755DF6}", InputMapping::ConfigBase); + + //////////////////////////////////////////////////////////////////////////////////////// + // Reflection + static void Reflect(AZ::ReflectContext* context); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Destructor + ~Config() override = default; + + protected: + //////////////////////////////////////////////////////////////////////////////////////// + //! \ref AzFramework::InputMapping::Type::CreateInputMapping + AZStd::shared_ptr CreateInputMapping(const InputContext& inputContext) const override; + + private: + //////////////////////////////////////////////////////////////////////////////////////// + //! The source input channel names that will be mapped to the output input channel name. + AZStd::vector m_sourceInputChannelNames; + }; + //////////////////////////////////////////////////////////////////////////////////////////// // Allocator AZ_CLASS_ALLOCATOR(InputMappingAnd, AZ::SystemAllocator, 0); diff --git a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.cpp b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.cpp index 459d32dc68..bc47065c05 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.cpp @@ -8,9 +8,72 @@ #include +#include +#include +#include + //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { + //////////////////////////////////////////////////////////////////////////////////////////////// + void InputMappingOr::Config::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("Source Input Channel Names", &Config::m_sourceInputChannelNames) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("Input Mapping: Or", + "Maps multiple different input sources to a single output using 'OR' logic.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputMappingOr::Config::GetNameLabelOverride) + ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_sourceInputChannelNames, "Source Input Channel Names", + "The source input channel names that will be mapped to the output input channel name.") + ; + } + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + AZStd::shared_ptr InputMappingOr::Config::CreateInputMapping(const InputContext& inputContext) const + { + if (m_outputInputChannelName.empty()) + { + AZ_Error("InputMappingOr::Config", false, "Cannot create input mapping with empty name."); + return nullptr; + } + + if (InputChannelRequests::FindInputChannel(InputChannelId(m_outputInputChannelName.c_str()))) + { + AZ_Error("InputMappingOr::Config", false, + "Cannot create input mapping '%s' with non-unique name.", m_outputInputChannelName.c_str()); + return nullptr; + } + + if (m_sourceInputChannelNames.empty()) + { + AZ_Error("InputMappingOr::Config", false, + "Cannot create input mapping '%s' with no source inputs.", m_outputInputChannelName.c_str()); + return nullptr; + } + + const InputChannelId outputInputChannelId(m_outputInputChannelName.c_str()); + AZStd::shared_ptr inputMapping = AZStd::make_shared(outputInputChannelId, + inputContext); + for (const InputChannelNameFilteredByDeviceType& sourceInputChannelName : m_sourceInputChannelNames) + { + const InputChannelId sourceInputChannelId(sourceInputChannelName.GetInputChannelName().c_str()); + inputMapping->AddSourceInput(sourceInputChannelId); + } + return inputMapping; + } + //////////////////////////////////////////////////////////////////////////////////////////////// InputMappingOr::InputMappingOr(const InputChannelId& inputChannelId, const InputContext& inputContext) : InputMapping(inputChannelId, inputContext) diff --git a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.h b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.h index b8359f828e..a0440d1855 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.h @@ -19,6 +19,38 @@ namespace AzFramework class InputMappingOr : public InputMapping { public: + //////////////////////////////////////////////////////////////////////////////////////////// + //! The input mapping configuration values that are exposed to the editor. + class Config : public InputMapping::ConfigBase + { + public: + //////////////////////////////////////////////////////////////////////////////////////// + // Allocator + AZ_CLASS_ALLOCATOR(Config, AZ::SystemAllocator, 0); + + //////////////////////////////////////////////////////////////////////////////////////// + // Type Info + AZ_RTTI(Config, "{428AFDD4-D353-494A-BBAC-37E00F82CFFD}", InputMapping::ConfigBase); + + //////////////////////////////////////////////////////////////////////////////////////// + // Reflection + static void Reflect(AZ::ReflectContext* context); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Destructor + ~Config() override = default; + + protected: + //////////////////////////////////////////////////////////////////////////////////////// + //! \ref AzFramework::InputMapping::Type::CreateInputMapping + AZStd::shared_ptr CreateInputMapping(const InputContext& inputContext) const override; + + private: + //////////////////////////////////////////////////////////////////////////////////////// + //! The source input channel names that will be mapped to the output input channel name. + AZStd::vector m_sourceInputChannelNames; + }; + //////////////////////////////////////////////////////////////////////////////////////////// // Allocator AZ_CLASS_ALLOCATOR(InputMappingOr, AZ::SystemAllocator, 0); diff --git a/Code/Framework/AzFramework/AzFramework/Logging/LoggingComponent.h b/Code/Framework/AzFramework/AzFramework/Logging/LoggingComponent.h index 98eb6df95a..73f2ff2c75 100644 --- a/Code/Framework/AzFramework/AzFramework/Logging/LoggingComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Logging/LoggingComponent.h @@ -31,9 +31,9 @@ namespace AzFramework ////////////////////////////////////////////////////////////////////////// // AZ::Component - virtual void Init(); - virtual void Activate(); - virtual void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp index d915680760..171d626b27 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp @@ -36,6 +36,16 @@ namespace AzFramework return m_end; } + const AZ::Entity* const* SpawnableEntityContainerView::begin() const + { + return m_begin; + } + + const AZ::Entity* const* SpawnableEntityContainerView::end() const + { + return m_end; + } + const AZ::Entity* const* SpawnableEntityContainerView::cbegin() { return m_begin; @@ -46,11 +56,28 @@ namespace AzFramework return m_end; } - size_t SpawnableEntityContainerView::size() + AZ::Entity* SpawnableEntityContainerView::operator[](size_t n) + { + AZ_Assert(n < size(), "Index %zu is out of bounds (size: %llu) for Spawnable Entity Container View", n, size()); + return *(m_begin + n); + } + + const AZ::Entity* SpawnableEntityContainerView::operator[](size_t n) const + { + AZ_Assert(n < size(), "Index %zu is out of bounds (size: %llu) for Spawnable Entity Container View", n, size()); + return *(m_begin + n); + } + + size_t SpawnableEntityContainerView::size() const { return AZStd::distance(m_begin, m_end); } + bool SpawnableEntityContainerView::empty() const + { + return m_begin == m_end; + } + // // SpawnableConstEntityContainerView @@ -78,6 +105,16 @@ namespace AzFramework return m_end; } + const AZ::Entity* const* SpawnableConstEntityContainerView::begin() const + { + return m_begin; + } + + const AZ::Entity* const* SpawnableConstEntityContainerView::end() const + { + return m_end; + } + const AZ::Entity* const* SpawnableConstEntityContainerView::cbegin() { return m_begin; @@ -88,11 +125,28 @@ namespace AzFramework return m_end; } - size_t SpawnableConstEntityContainerView::size() + const AZ::Entity* SpawnableConstEntityContainerView::operator[](size_t n) + { + AZ_Assert(n < size(), "Index %zu is out of bounds (size: %llu) for Spawnable Const Entity Container View", n, size()); + return *(m_begin + n); + } + + const AZ::Entity* SpawnableConstEntityContainerView::operator[](size_t n) const + { + AZ_Assert(n < size(), "Index %zu is out of bounds (size: %llu) for Spawnable Entity Container View", n, size()); + return *(m_begin + n); + } + + size_t SpawnableConstEntityContainerView::size() const { return AZStd::distance(m_begin, m_end); } + bool SpawnableConstEntityContainerView::empty() const + { + return m_begin == m_end; + } + // // SpawnableIndexEntityPair diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index 3eea87b511..74a17020df 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -36,11 +36,18 @@ namespace AzFramework SpawnableEntityContainerView(AZ::Entity** begin, size_t length); SpawnableEntityContainerView(AZ::Entity** begin, AZ::Entity** end); - AZ::Entity** begin(); - AZ::Entity** end(); - const AZ::Entity* const* cbegin(); - const AZ::Entity* const* cend(); - size_t size(); + [[nodiscard]] AZ::Entity** begin(); + [[nodiscard]] AZ::Entity** end(); + [[nodiscard]] const AZ::Entity* const* begin() const; + [[nodiscard]] const AZ::Entity* const* end() const; + [[nodiscard]] const AZ::Entity* const* cbegin(); + [[nodiscard]] const AZ::Entity* const* cend(); + + [[nodiscard]] AZ::Entity* operator[](size_t n); + [[nodiscard]] const AZ::Entity* operator[](size_t n) const; + + [[nodiscard]] size_t size() const; + [[nodiscard]] bool empty() const; private: AZ::Entity** m_begin; @@ -53,11 +60,18 @@ namespace AzFramework SpawnableConstEntityContainerView(AZ::Entity** begin, size_t length); SpawnableConstEntityContainerView(AZ::Entity** begin, AZ::Entity** end); - const AZ::Entity* const* begin(); - const AZ::Entity* const* end(); - const AZ::Entity* const* cbegin(); - const AZ::Entity* const* cend(); - size_t size(); + [[nodiscard]] const AZ::Entity* const* begin(); + [[nodiscard]] const AZ::Entity* const* end(); + [[nodiscard]] const AZ::Entity* const* begin() const; + [[nodiscard]] const AZ::Entity* const* end() const; + [[nodiscard]] const AZ::Entity* const* cbegin(); + [[nodiscard]] const AZ::Entity* const* cend(); + + [[nodiscard]] const AZ::Entity* operator[](size_t n); + [[nodiscard]] const AZ::Entity* operator[](size_t n) const; + + [[nodiscard]] size_t size() const; + [[nodiscard]] bool empty() const; private: AZ::Entity** m_begin; diff --git a/Code/Framework/AzFramework/AzFramework/TargetManagement/TargetManagementAPI.h b/Code/Framework/AzFramework/AzFramework/TargetManagement/TargetManagementAPI.h index 6582e145cb..bbfb4c88c1 100644 --- a/Code/Framework/AzFramework/AzFramework/TargetManagement/TargetManagementAPI.h +++ b/Code/Framework/AzFramework/AzFramework/TargetManagement/TargetManagementAPI.h @@ -195,7 +195,7 @@ namespace AzFramework TmMsgCallback(const MsgCB& cb = NULL) : m_cb(cb) {} - virtual void OnReceivedMsg(TmMsgPtr msg) + void OnReceivedMsg(TmMsgPtr msg) override { if (m_cb) { diff --git a/Code/Framework/AzFramework/AzFramework/UnitTest/TestDebugDisplayRequests.h b/Code/Framework/AzFramework/AzFramework/UnitTest/TestDebugDisplayRequests.h index d4c6992057..72b15c1a69 100644 --- a/Code/Framework/AzFramework/AzFramework/UnitTest/TestDebugDisplayRequests.h +++ b/Code/Framework/AzFramework/AzFramework/UnitTest/TestDebugDisplayRequests.h @@ -19,6 +19,8 @@ namespace UnitTest { public: TestDebugDisplayRequests(); + ~TestDebugDisplayRequests() override = default; + const AZStd::vector& GetPoints() const; void ClearPoints(); //! Returns the AABB of the points generated from received draw calls. @@ -27,7 +29,9 @@ namespace UnitTest // DebugDisplayRequests ... void DrawWireBox(const AZ::Vector3& min, const AZ::Vector3& max) override; void DrawSolidBox(const AZ::Vector3& min, const AZ::Vector3& max) override; + using AzFramework::DebugDisplayRequests::DrawWireQuad; void DrawWireQuad(float width, float height) override; + using AzFramework::DebugDisplayRequests::DrawQuad; void DrawQuad(float width, float height) override; void DrawTriangles(const AZStd::vector& vertices, const AZ::Color& color) override; void DrawTrianglesIndexed(const AZStd::vector& vertices, const AZStd::vector& indices, const AZ::Color& color) override; diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp index b909689c22..1b3645630e 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp @@ -9,8 +9,19 @@ #include #include +#include + namespace AzFramework { + ClickDetector::ClickDetector() + { + m_timeNowFn = [] + { + const auto now = AZStd::chrono::high_resolution_clock::now(); + return AZStd::chrono::time_point_cast(now).time_since_epoch(); + }; + } + ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta) { const auto previousDetectionState = m_detectionState; @@ -26,11 +37,13 @@ namespace AzFramework if (clickEvent == ClickEvent::Down) { - const auto now = std::chrono::steady_clock::now(); + const auto now = m_timeNowFn(); if (m_tryBeginTime) { - const std::chrono::duration diff = now - m_tryBeginTime.value(); - if (diff.count() < m_doubleClickInterval) + using FloatingPointSeconds = AZStd::chrono::duration; + + const auto diff = now - m_tryBeginTime.value(); + if (FloatingPointSeconds(diff).count() < m_doubleClickInterval) { return ClickOutcome::Nil; } @@ -43,7 +56,8 @@ namespace AzFramework } else if (clickEvent == ClickEvent::Up) { - const auto clickOutcome = [detectionState = m_detectionState] { + const auto clickOutcome = [detectionState = m_detectionState] + { if (detectionState == DetectionState::WaitingForMove) { return ClickOutcome::Click; @@ -66,4 +80,9 @@ namespace AzFramework return ClickOutcome::Nil; } + + void ClickDetector::OverrideTimeNowFn(AZStd::function timeNowFn) + { + m_timeNowFn = AZStd::move(timeNowFn); + } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.h b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.h index f95924550a..70bdeb4619 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include @@ -21,10 +22,9 @@ namespace AzFramework //! (mouse down with movement and then mouse up). class ClickDetector { - //! Alias for recording time of mouse down events - using Time = std::chrono::time_point; - public: + ClickDetector(); + //! Internal representation of click event (map from external event for this when //! calling DetectClick). enum class ClickEvent @@ -51,6 +51,10 @@ namespace AzFramework void SetDoubleClickInterval(float doubleClickInterval); //! Override the dead zone before a 'move' outcome will be triggered. void SetDeadZone(float deadZone); + //! Override how the current time is retrieved. + //! This is helpful to override when it comes to simulating different passages of + //! time to avoid double click issues in tests for example. + void OverrideTimeNowFn(AZStd::function timeNowFn); private: //! Internal state of ClickDetector based on incoming events. @@ -65,7 +69,9 @@ namespace AzFramework float m_deadZone = 2.0f; //!< How far to move before a click is cancelled (when Move will fire). float m_doubleClickInterval = 0.4f; //!< Default double click interval, can be overridden. DetectionState m_detectionState; //!< Internal state of ClickDetector. - AZStd::optional diff --git a/Code/Tools/AssetProcessor/Platform/Mac/main_dummy.cpp b/Code/Tools/AssetProcessor/Platform/Mac/main_dummy.cpp deleted file mode 100644 index 3eed37e555..0000000000 --- a/Code/Tools/AssetProcessor/Platform/Mac/main_dummy.cpp +++ /dev/null @@ -1,75 +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 - -int main(int argc, char* argv[]) -{ - // Create a ComponentApplication to initialize the AZ::SystemAllocator and initialize the SettingsRegistry - AZ::ComponentApplication::Descriptor desc; - AZ::ComponentApplication application; - application.Create(desc); - - AZStd::vector envVars; - - const char* homePath = std::getenv("HOME"); - envVars.push_back(AZStd::string::format("HOME=%s", homePath)); - - if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) - { - const char* dyldLibPathOrig = std::getenv("DYLD_LIBRARY_PATH"); - AZStd::string dyldSearchPath = AZStd::string::format("DYLD_LIBRARY_PATH=%s", dyldLibPathOrig); - if (AZ::IO::FixedMaxPath projectModulePath; - settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath)) - { - dyldSearchPath.append(":"); - dyldSearchPath.append(projectModulePath.c_str()); - } - - if (AZ::IO::FixedMaxPath installedBinariesFolder; - settingsRegistry->Get(installedBinariesFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) - { - if (AZ::IO::FixedMaxPath engineRootFolder; - settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder)) - { - installedBinariesFolder = engineRootFolder / installedBinariesFolder; - dyldSearchPath.append(":"); - dyldSearchPath.append(installedBinariesFolder.c_str()); - } - } - envVars.push_back(dyldSearchPath); - } - - AZStd::string commandArgs; - for (int i = 1; i < argc; i++) - { - commandArgs.append(argv[i]); - commandArgs.append(" "); - } - - AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - AZ::IO::Path processPath{ AZ::IO::PathView(AZ::Utils::GetExecutableDirectory()) }; - processPath /= "AssetProcessor"; - processLaunchInfo.m_processExecutableString = AZStd::move(processPath.Native()); - processLaunchInfo.m_commandlineParameters = commandArgs; - processLaunchInfo.m_environmentVariables = &envVars; - processLaunchInfo.m_showWindow = true; - - AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); - - application.Destroy(); - - return 0; -} - diff --git a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h index 6d25b654c5..84e96bcf3e 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h +++ b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h @@ -122,8 +122,8 @@ namespace AssetProcessor ////////////////////////////////////////////////////////////////////////// // AzToolsFramework::ToolsAssetSystemBus::Handler - void RegisterSourceAssetType(const AZ::Data::AssetType& assetType, const char* assetFileFilter); - void UnregisterSourceAssetType(const AZ::Data::AssetType& assetType); + void RegisterSourceAssetType(const AZ::Data::AssetType& assetType, const char* assetFileFilter) override; + void UnregisterSourceAssetType(const AZ::Data::AssetType& assetType) override; ////////////////////////////////////////////////////////////////////////// //! given some absolute path, please respond with its relative product path. For now, this will be a @@ -163,9 +163,9 @@ namespace AssetProcessor AZ::Outcome, AZStd::string> GetAllProductDependenciesFilter( const AZ::Data::AssetId& id, const AZStd::unordered_set& exclusionList, - const AZStd::vector& wildcardPatternExclusionList); + const AZStd::vector& wildcardPatternExclusionList) override; - bool DoesAssetIdMatchWildcardPattern(const AZ::Data::AssetId& assetId, const AZStd::string& wildcardPattern); + bool DoesAssetIdMatchWildcardPattern(const AZ::Data::AssetId& assetId, const AZStd::string& wildcardPattern) override; void AddAssetDependencies( const AZ::Data::AssetId& searchAssetId, diff --git a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp index 7c88c4324c..e6d9894116 100644 --- a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp +++ b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp @@ -191,10 +191,51 @@ namespace AssetProcessor response.m_createJobOutputs.push_back(AZStd::move(job)); } - AZ::IO::Path settingsRegistryWildcard = AZ::SettingsRegistryInterface::RegistryFolder; + AZ::IO::Path settingsRegistryWildcard = AZStd::string_view(AZ::Utils::GetEnginePath()); + settingsRegistryWildcard /= AZ::SettingsRegistryInterface::RegistryFolder; settingsRegistryWildcard /= "*.setreg"; response.m_sourceFileDependencyList.emplace_back(AZStd::move(settingsRegistryWildcard.Native()), AZ::Uuid::CreateNull(), AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Wildcards); + + auto projectPath = AZ::IO::Path(AZStd::string_view(AZ::Utils::GetProjectPath())); + response.m_sourceFileDependencyList.emplace_back( + AZStd::move((projectPath / AZ::SettingsRegistryInterface::RegistryFolder / "*.setreg").Native()), + AZ::Uuid::CreateNull(), + AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Wildcards); + response.m_sourceFileDependencyList.emplace_back( + AZStd::move((projectPath / AZ::SettingsRegistryInterface::DevUserRegistryFolder / "*.setreg").Native()), + AZ::Uuid::CreateNull(), + AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Wildcards); + + if (auto settingsRegistry = AZ::Interface::Get(); settingsRegistry != nullptr) + { + AZStd::vector gemInfos; + if (AzFramework::GetGemsInfo(gemInfos, *settingsRegistry)) + { + // Gather unique list of Settings Registry wildcard directories + AZStd::vector gemSettingsRegistryWildcards; + for (const AzFramework::GemInfo& gemInfo : gemInfos) + { + for (const AZ::IO::Path& absoluteSourcePath : gemInfo.m_absoluteSourcePaths) + { + auto gemSettingsRegistryWildcard = absoluteSourcePath / AZ::SettingsRegistryInterface::RegistryFolder / "*.setreg"; + if (auto foundIt = AZStd::find(gemSettingsRegistryWildcards.begin(), gemSettingsRegistryWildcards.end(), gemSettingsRegistryWildcard); + foundIt == gemSettingsRegistryWildcards.end()) + { + gemSettingsRegistryWildcards.emplace_back(gemSettingsRegistryWildcard); + } + } + } + + // Add to the Source File Dependency list + for (AZ::IO::Path& gemSettingsRegistryWildcard : gemSettingsRegistryWildcards) + { + response.m_sourceFileDependencyList.emplace_back( + AZStd::move(gemSettingsRegistryWildcard.Native()), AZ::Uuid::CreateNull(), + AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Wildcards); + } + } + } response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; } diff --git a/Code/Tools/AssetProcessor/native/resourcecompiler/rcjoblistmodel.h b/Code/Tools/AssetProcessor/native/resourcecompiler/rcjoblistmodel.h index 97a59c90ea..5c055087a8 100644 --- a/Code/Tools/AssetProcessor/native/resourcecompiler/rcjoblistmodel.h +++ b/Code/Tools/AssetProcessor/native/resourcecompiler/rcjoblistmodel.h @@ -67,7 +67,7 @@ namespace AssetProcessor int columnCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - QVariant data(const QModelIndex& index, int role) const; + QVariant data(const QModelIndex& index, int role) const override; void markAsProcessing(RCJob* rcJob); void markAsStarted(RCJob* rcJob); diff --git a/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/SerializationDependenciesTests.cpp b/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/SerializationDependenciesTests.cpp index cb7b1b5c4d..86c363075d 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/SerializationDependenciesTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/SerializationDependenciesTests.cpp @@ -95,7 +95,7 @@ namespace SerializationDependencyTests // Use an arbitrary ID for the asset type. return AZ::Data::AssetType("{03FD33E2-DA2F-4021-A266-0DC9714FF84D}"); } - virtual const char* GetFileFilter() const + const char* GetFileFilter() const override { return nullptr; } diff --git a/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.h b/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.h index 32230be23a..5a6efb2622 100644 --- a/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.h +++ b/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.h @@ -81,6 +81,8 @@ public: struct MockRecognizerConfiguration : public RecognizerConfiguration { + virtual ~MockRecognizerConfiguration() = default; + const RecognizerContainer& GetAssetRecognizerContainer() const override { return m_recognizerContainer; diff --git a/Code/Tools/AssetProcessor/native/unittests/AssetRequestHandlerUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/AssetRequestHandlerUnitTests.cpp index 010d1cc069..8b0ee56c32 100644 --- a/Code/Tools/AssetProcessor/native/unittests/AssetRequestHandlerUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/AssetRequestHandlerUnitTests.cpp @@ -49,13 +49,13 @@ namespace bool m_deleteFenceFileResult = false; protected: - virtual QString CreateFenceFile(unsigned int fenceId) + QString CreateFenceFile(unsigned int fenceId) override { m_numTimesCreateFenceFileCalled++; m_fenceId = fenceId; return m_fenceFileName; } - virtual bool DeleteFenceFile(QString fenceFileName) + bool DeleteFenceFile(QString fenceFileName) override { m_numTimesDeleteFenceFileCalled++; return m_deleteFenceFileResult; diff --git a/Code/Tools/AssetProcessor/native/unittests/MockApplicationManager.cpp b/Code/Tools/AssetProcessor/native/unittests/MockApplicationManager.cpp index bd03e3b225..696ecc710a 100644 --- a/Code/Tools/AssetProcessor/native/unittests/MockApplicationManager.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/MockApplicationManager.cpp @@ -22,6 +22,8 @@ namespace AssetProcessor struct MockRecognizerConfiguration : public RecognizerConfiguration { + virtual ~MockRecognizerConfiguration() = default; + const RecognizerContainer& GetAssetRecognizerContainer() const override { return m_container; diff --git a/Code/Tools/AssetProcessor/native/unittests/UtilitiesUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/UtilitiesUnitTests.cpp index bbeafda342..9f2d855811 100644 --- a/Code/Tools/AssetProcessor/native/unittests/UtilitiesUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/UtilitiesUnitTests.cpp @@ -543,7 +543,7 @@ public: Q_EMIT UnitTestPassed(); } - bool OnPreAssert(const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) + bool OnPreAssert(const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) override { m_assertTriggered = true; return true; diff --git a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.h b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.h index 7e6347b4d1..886880df3a 100644 --- a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.h +++ b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.h @@ -114,7 +114,7 @@ public: void Rescan(); - bool IsAssetProcessorManagerIdle() const; + bool IsAssetProcessorManagerIdle() const override; bool CheckFullIdle(); Q_SIGNALS: void CheckAssetProcessorManagerIdleState(); diff --git a/Code/Tools/AssetProcessor/native/utilities/AssetServerHandler.h b/Code/Tools/AssetProcessor/native/utilities/AssetServerHandler.h index 840c3e6f5b..04f00e6dff 100644 --- a/Code/Tools/AssetProcessor/native/utilities/AssetServerHandler.h +++ b/Code/Tools/AssetProcessor/native/utilities/AssetServerHandler.h @@ -22,7 +22,7 @@ namespace AssetProcessor virtual ~AssetServerHandler(); ////////////////////////////////////////////////////////////////////////// // AssetServerBus::Handler overrides - bool IsServerAddressValid(); + bool IsServerAddressValid() override; //! StoreJobResult will store all the files in the the temp folder provided by AP to a zip file on the network drive //! whose file name will be based on the server key bool StoreJobResult(const AssetProcessor::BuilderParams& builderParams, AZStd::vector& sourceFileList) override; diff --git a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp index a1bc508899..52df8e901d 100644 --- a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp @@ -84,6 +84,8 @@ namespace AssetProcessor return !m_platformIdentifierStack.empty() ? AZ::SettingsRegistryInterface::VisitResponse::Continue : AZ::SettingsRegistryInterface::VisitResponse::Skip; } + + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override { if (m_platformIdentifierStack.empty()) @@ -114,6 +116,7 @@ namespace AssetProcessor struct MetaDataTypesVisitor : AZ::SettingsRegistryInterface::Visitor { + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override { m_metaDataTypes.push_back({ AZ::IO::PathView(valueName, AZ::IO::PosixPathSeparator).LexicallyNormal().String(), value }); diff --git a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.h b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.h index 2c3615c4fc..2c04ca1fad 100644 --- a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.h +++ b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.h @@ -49,6 +49,7 @@ namespace AssetProcessor { } + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override; AZ::SettingsRegistryInterface* m_settingsRegistry; @@ -129,6 +130,8 @@ namespace AssetProcessor { AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName, AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override; + + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override; void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override; @@ -152,6 +155,8 @@ namespace AssetProcessor { AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName, AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override; + + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override; AZStd::vector m_excludeAssetRecognizers; @@ -169,6 +174,8 @@ namespace AssetProcessor } AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName, AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override; + + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value) override; void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override; void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override; diff --git a/Code/Tools/BundleLauncher/CMakeLists.txt b/Code/Tools/BundleLauncher/CMakeLists.txt new file mode 100644 index 0000000000..812cb21099 --- /dev/null +++ b/Code/Tools/BundleLauncher/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# 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 is the launcher that will be used by the O3DE_SDK.app bundle +# generated by the cmake install process for Mac. +if(NOT ${PAL_PLATFORM_NAME} STREQUAL Mac) + return() +endif() + +ly_add_target( + NAME O3DE_SDK EXECUTABLE + NAMESPACE AZ + FILES_CMAKE + O3DE_SDK_files.cmake + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzFramework +) diff --git a/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp b/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp new file mode 100644 index 0000000000..0c362ac829 --- /dev/null +++ b/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp @@ -0,0 +1,63 @@ +/* + * 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 + +int main(int argc, char* argv[]) +{ + // We need to pass in the engine path since we won't be able to find it by searching upwards. + // We can't use any containers that use our custom allocator till after the call to ComponentApplication::Create() + AZ::IO::FixedMaxPath processPath = AZ::Utils::GetExecutableDirectory(); + AZ::IO::FixedMaxPath enginePath = (processPath / "../Engine").LexicallyNormal(); + auto enginePathParam = AZ::SettingsRegistryInterface::FixedValueString::format(R"(--engine-path="%s")", enginePath.c_str()); + // Uses the fixed_vector deduction guide to determine the type is AZStd::fixed_vector + AZStd::fixed_vector commandLineParams{ processPath.Native().data(), enginePathParam.data() }; + + + // Create a ComponentApplication to initialize the AZ::SystemAllocator and initialize the SettingsRegistry + AZ::ComponentApplication application(static_cast(commandLineParams.size()), commandLineParams.data()); + application.Create(AZ::ComponentApplication::Descriptor()); + + AZ::IO::FixedMaxPath installedBinariesFolder; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + if (settingsRegistry->Get(installedBinariesFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) + { + installedBinariesFolder = enginePath / installedBinariesFolder; + } + } + + AZ::IO::FixedMaxPath shellPath = "/bin/sh"; + AZStd::string parameters = AZStd::string::format("-c \"export LY_CMAKE_PATH=/usr/local/bin && \"%s/python/get_python.sh\"\"", enginePath.c_str()); + AzFramework::ProcessLauncher::ProcessLaunchInfo shellProcessLaunch; + shellProcessLaunch.m_processExecutableString = AZStd::move(shellPath.Native()); + shellProcessLaunch.m_commandlineParameters = parameters; + shellProcessLaunch.m_showWindow = true; + shellProcessLaunch.m_workingDirectory = enginePath.String(); + AZStd::unique_ptr shellProcess(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE)); + shellProcess->WaitForProcessToExit(120); + shellProcess.reset(); + + AZ::IO::FixedMaxPath projectManagerPath = installedBinariesFolder/"o3de.app"/"Contents"/"MacOS"/"o3de"; + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; + processLaunchInfo.m_processExecutableString = AZStd::move(projectManagerPath.Native()); + processLaunchInfo.m_showWindow = true; + AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); + + application.Destroy(); + + return 0; +} + diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/iOS/tool_dependencies_ios.cmake b/Code/Tools/BundleLauncher/O3DE_SDK_files.cmake similarity index 85% rename from Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/iOS/tool_dependencies_ios.cmake rename to Code/Tools/BundleLauncher/O3DE_SDK_files.cmake index 5bf4d7cb7e..04989c9df2 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/iOS/tool_dependencies_ios.cmake +++ b/Code/Tools/BundleLauncher/O3DE_SDK_files.cmake @@ -6,5 +6,6 @@ # # -set(GEM_DEPENDENCIES +set(FILES + O3DE_SDK_Launcher.cpp ) diff --git a/Code/Tools/BundleLauncher/info.plist b/Code/Tools/BundleLauncher/info.plist new file mode 100644 index 0000000000..5b3dd43b37 --- /dev/null +++ b/Code/Tools/BundleLauncher/info.plist @@ -0,0 +1,18 @@ + + + + + CFBundleExecutable + O3DE_SDK + CFBundleIdentifier + org.O3DE.O3DE_SDK + CFBundlePackageType + APPL + CFBundleSignature + ???? + NSHumanReadableCopyright + Copyright (c) Contributors to the Open 3D Engine Project. + NSPrincipalClass + NSApplication + + diff --git a/Code/Tools/CMakeLists.txt b/Code/Tools/CMakeLists.txt index 66c43e53e5..8107089433 100644 --- a/Code/Tools/CMakeLists.txt +++ b/Code/Tools/CMakeLists.txt @@ -20,3 +20,4 @@ add_subdirectory(GridHub) add_subdirectory(Standalone) add_subdirectory(TestImpactFramework) add_subdirectory(ProjectManager) +add_subdirectory(BundleLauncher) diff --git a/Code/Tools/DeltaCataloger/Tests/tests_main.cpp b/Code/Tools/DeltaCataloger/Tests/tests_main.cpp index 34dd6dbddb..1a3b86d34b 100644 --- a/Code/Tools/DeltaCataloger/Tests/tests_main.cpp +++ b/Code/Tools/DeltaCataloger/Tests/tests_main.cpp @@ -84,7 +84,7 @@ protected: } - bool OnPreError([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) + bool OnPreError([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override { return true; } diff --git a/Code/Tools/GridHub/GridHub/gridhub.hxx b/Code/Tools/GridHub/GridHub/gridhub.hxx index 849dbb92e1..14ed468345 100644 --- a/Code/Tools/GridHub/GridHub/gridhub.hxx +++ b/Code/Tools/GridHub/GridHub/gridhub.hxx @@ -57,8 +57,8 @@ public slots: protected: void SanityCheckDetectionTimeout(); - void timerEvent(QTimerEvent *event); - void closeEvent(QCloseEvent *event); + void timerEvent(QTimerEvent *event) override; + void closeEvent(QCloseEvent *event) override; void SystemTick(); private: @@ -125,7 +125,7 @@ public: /// Callback that is called when the Session service is ready to process sessions. void OnSessionServiceReady() override {} /// Callback that notifies the title when a game search query have completed. - void OnGridSearchComplete(GridMate::GridSearch* gridSearch) { (void)gridSearch; } + void OnGridSearchComplete(GridMate::GridSearch* gridSearch) override { (void)gridSearch; } /// Callback that notifies the title when a new member joins the game session. void OnMemberJoined(GridMate::GridSession* session, GridMate::GridMember* member) override; /// Callback that notifies the title that a member is leaving the game session. member pointer is NOT valid after the callback returns. @@ -133,25 +133,25 @@ public: // \todo a better way will be (after we solve migration) is to supply a reason to OnMemberLeaving... like the member was kicked. // this will require that we actually remove the replica at the same moment. /// Callback that host decided to kick a member. You will receive a OnMemberLeaving when the actual member leaves the session. - void OnMemberKicked(GridMate::GridSession* session, GridMate::GridMember* member, AZ::u8 reason) { (void)session;(void)member;(void)reason; } + void OnMemberKicked(GridMate::GridSession* session, GridMate::GridMember* member, AZ::u8 reason) override { (void)session;(void)member;(void)reason; } /// After this callback it is safe to access session features. If host session is fully operational if client wait for OnSessionJoined. void OnSessionCreated(GridMate::GridSession* session) override; /// Called on client machines to indicate that we join successfully. - void OnSessionJoined(GridMate::GridSession* session) { (void)session; } + void OnSessionJoined(GridMate::GridSession* session) override { (void)session; } /// Callback that notifies the title when a session will be left. session pointer is NOT valid after the callback returns. void OnSessionDelete(GridMate::GridSession* session) override; /// Called when a session error occurs. - void OnSessionError(GridMate::GridSession* session, const AZStd::string& errorMsg ) { (void)session; (void)errorMsg; } + void OnSessionError(GridMate::GridSession* session, const AZStd::string& errorMsg ) override { (void)session; (void)errorMsg; } /// Called when the actual game(match) starts - void OnSessionStart(GridMate::GridSession* session) { (void)session; } + void OnSessionStart(GridMate::GridSession* session) override { (void)session; } /// Called when the actual game(match) ends - void OnSessionEnd(GridMate::GridSession* session) { (void)session; } + void OnSessionEnd(GridMate::GridSession* session) override { (void)session; } /// Called when we start a host migration. - void OnMigrationStart(GridMate::GridSession* session) { (void)session; } + void OnMigrationStart(GridMate::GridSession* session) override { (void)session; } /// Called so the user can select a member that should be the new Host. Value will be ignored if NULL, current host or the member has invalid connection id. - void OnMigrationElectHost(GridMate::GridSession* session,GridMate::GridMember*& newHost) { (void)session;(void)newHost; } + void OnMigrationElectHost(GridMate::GridSession* session,GridMate::GridMember*& newHost) override { (void)session;(void)newHost; } /// Called when the host migration has completed. - void OnMigrationEnd(GridMate::GridSession* session,GridMate::GridMember* newHost) { (void)session;(void)newHost; } + void OnMigrationEnd(GridMate::GridSession* session,GridMate::GridMember* newHost) override { (void)session;(void)newHost; } ////////////////////////////////////////////////////////////////////////// void SetUI(GridHub* ui) { m_ui = ui; } diff --git a/Code/Tools/GridHub/GridHub/main.cpp b/Code/Tools/GridHub/GridHub/main.cpp index 3b853910be..fe27301714 100644 --- a/Code/Tools/GridHub/GridHub/main.cpp +++ b/Code/Tools/GridHub/GridHub/main.cpp @@ -140,7 +140,7 @@ protected: * ComponentApplication::RegisterCoreComponents and then register the application * specific core components. */ - virtual void RegisterCoreComponents(); + void RegisterCoreComponents() override; /** AZ::SystemTickBus::Handler @@ -220,7 +220,7 @@ public: } //virtual bool QGridHubApplication::winEventFilter( MSG *msg , long *result) - virtual bool nativeEventFilter(const QByteArray &eventType, void *message, [[maybe_unused]] long *result) + bool nativeEventFilter(const QByteArray &eventType, void *message, [[maybe_unused]] long *result) override { #ifdef AZ_PLATFORM_WINDOWS if ((eventType == "windows_generic_MSG")||(eventType == "windows_dispatcher_MSG")) diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp index cffac78365..2987fc4dc2 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp @@ -7,14 +7,69 @@ */ #include +#include +#include + +#include +#include namespace O3DE::ProjectManager { - AZ::Outcome ProjectBuilderWorker::BuildProjectForPlatform() + AZ::Outcome ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const { - QString error = tr("Automatic building on Linux not currently supported!"); - QStringToAZTracePrint(error); - return AZ::Failure(error); + // Attempt to use the Ninja build system if it is installed (described in the o3de documentation) if possible, + // otherwise default to the the default for Linux (Unix Makefiles) + auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}, QProcessEnvironment::systemEnvironment()); + QString cmakeGenerator = (whichNinjaResult.IsSuccess()) ? "Ninja Multi-Config" : "Unix Makefiles"; + bool compileProfileOnBuild = (whichNinjaResult.IsSuccess()); + + // On Linux the default compiler is gcc. For O3DE, it is clang, so we need to specify the version of clang that is detected + // in order to get the compiler option. + auto compilerOptionResult = ProjectUtils::FindSupportedCompilerForPlatform(); + if (!compilerOptionResult.IsSuccess()) + { + return AZ::Failure(compilerOptionResult.GetError()); + } + auto clangCompilers = compilerOptionResult.GetValue().split('|'); + AZ_Assert(clangCompilers.length()==2, "Invalid clang compiler pair specification"); + + QString clangCompilerOption = clangCompilers[0]; + QString clangPPCompilerOption = clangCompilers[1]; + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + QStringList generateProjectArgs = QStringList{ProjectCMakeCommand, + "-B", ProjectBuildPathPostfix, + "-S", ".", + QString("-G%1").arg(cmakeGenerator), + QString("-DCMAKE_C_COMPILER=").append(clangCompilerOption), + QString("-DCMAKE_CXX_COMPILER=").append(clangPPCompilerOption), + QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath)}; + if (!compileProfileOnBuild) + { + generateProjectArgs.append("-DCMAKE_BUILD_TYPE=profile"); + } + return AZ::Success(generateProjectArgs); } - + + AZ::Outcome ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const + { + auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}, QProcessEnvironment::systemEnvironment()); + bool compileProfileOnBuild = (whichNinjaResult.IsSuccess()); + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher"; + + QStringList buildProjectArgs = QStringList{ProjectCMakeCommand, + "--build", ProjectBuildPathPostfix, + "--target", launcherTargetName, ProjectCMakeBuildTargetEditor}; + if (compileProfileOnBuild) + { + buildProjectArgs.append(QStringList{"--config","profile"}); + } + return AZ::Success(buildProjectArgs); + } + + AZ::Outcome ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const + { + return AZ::Success(QStringList{"kill", "-9", pidToKill}); + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp index 64d18ec605..56feb9f70a 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp @@ -6,15 +6,48 @@ */ #include +#include +#include namespace O3DE::ProjectManager { namespace ProjectUtils { - AZ::Outcome FindSupportedCompilerForPlatform() + // The list of clang C/C++ compiler command lines to validate on the host Linux system + const QStringList SupportedClangCommands = {"clang-12|clang++-12"}; + + AZ::Outcome GetCommandLineProcessEnvironment() { - // Compiler detection not supported on platform - return AZ::Success(); + return AZ::Success(QProcessEnvironment(QProcessEnvironment::systemEnvironment())); + } + + AZ::Outcome FindSupportedCompilerForPlatform() + { + // Validate that cmake is installed and is in the command line + auto whichCMakeResult = ProjectUtils::ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, QProcessEnvironment::systemEnvironment()); + if (!whichCMakeResult.IsSuccess()) + { + return AZ::Failure(QObject::tr("CMake not found. \n\n" + "Make sure that the minimum version of CMake is installed and available from the command prompt. " + "Refer to the O3DE requirements page for more information.")); + } + + // Look for the first compatible version of clang. The list below will contain the known clang compilers that have been tested for O3DE. + for (const QString& supportClangCommand : SupportedClangCommands) + { + auto clangCompilers = supportClangCommand.split('|'); + AZ_Assert(clangCompilers.length()==2, "Invalid clang compiler pair specification"); + + auto whichClangResult = ProjectUtils::ExecuteCommandResult("which", QStringList{clangCompilers[0]}, QProcessEnvironment::systemEnvironment()); + auto whichClangPPResult = ProjectUtils::ExecuteCommandResult("which", QStringList{clangCompilers[1]}, QProcessEnvironment::systemEnvironment()); + if (whichClangResult.IsSuccess() && whichClangPPResult.IsSuccess()) + { + return AZ::Success(supportClangCommand); + } + } + return AZ::Failure(QObject::tr("Clang not found. \n\n" + "Make sure that the clang is installed and available from the command prompt. " + "Refer to the O3DE requirements page for more information.")); } } // namespace ProjectUtils diff --git a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac.cmake b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac.cmake index 7a325ca97e..5cd1fb5a22 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac.cmake +++ b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac.cmake @@ -5,3 +5,4 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT # # + diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp index 7f4c5eb5d0..ab412d84d8 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp @@ -7,14 +7,82 @@ */ #include +#include +#include + +#include +#include namespace O3DE::ProjectManager { - AZ::Outcome ProjectBuilderWorker::BuildProjectForPlatform() + namespace Internal { - QString error = tr("Automatic building on MacOS not currently supported!"); - QStringToAZTracePrint(error); - return AZ::Failure(error); + AZ::Outcome QueryInstalledCmakeFullPath() + { + auto environmentRequest = ProjectUtils::GetCommandLineProcessEnvironment(); + if (!environmentRequest.IsSuccess()) + { + return AZ::Failure(environmentRequest.GetError()); + } + auto currentEnvironment = environmentRequest.GetValue(); + + auto queryCmakeInstalled = ProjectUtils::ExecuteCommandResult("which", + QStringList{ProjectCMakeCommand}, + currentEnvironment); + if (!queryCmakeInstalled.IsSuccess()) + { + return AZ::Failure(QObject::tr("Unable to detect CMake on this host.")); + } + QString cmakeInstalledPath = queryCmakeInstalled.GetValue().split("\n")[0]; + return AZ::Success(cmakeInstalledPath); + } + } + + AZ::Outcome ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const + { + // For Mac, we need to resolve the full path of cmake and use that in the process request. For + // some reason, 'which' will resolve the full path, but when you just specify cmake with the same + // environment, it is unable to resolve. To work around this, we will use 'which' to resolve the + // full path and then use it as the command argument + auto cmakeInstalledPathQuery = Internal::QueryInstalledCmakeFullPath(); + if (!cmakeInstalledPathQuery.IsSuccess()) + { + return AZ::Failure(cmakeInstalledPathQuery.GetError()); + } + QString cmakeInstalledPath = cmakeInstalledPathQuery.GetValue(); + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + + return AZ::Success(QStringList{cmakeInstalledPath, + "-B", targetBuildPath, + "-S", m_projectInfo.m_path, + "-GXcode"}); + } + + AZ::Outcome ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const + { + // For Mac, we need to resolve the full path of cmake and use that in the process request. For + // some reason, 'which' will resolve the full path, but when you just specify cmake with the same + // environment, it is unable to resolve. To work around this, we will use 'which' to resolve the + // full path and then use it as the command argument + auto cmakeInstalledPathQuery = Internal::QueryInstalledCmakeFullPath(); + if (!cmakeInstalledPathQuery.IsSuccess()) + { + return AZ::Failure(cmakeInstalledPathQuery.GetError()); + } + + QString cmakeInstalledPath = cmakeInstalledPathQuery.GetValue(); + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher"; + + return AZ::Success(QStringList{cmakeInstalledPath, + "--build", targetBuildPath, + "--config", "profile", + "--target", launcherTargetName, ProjectCMakeBuildTargetEditor}); + } + + AZ::Outcome ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const + { + return AZ::Success(QStringList{"kill", "-9", pidToKill}); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp index 64d18ec605..0d150abc3b 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp @@ -7,15 +7,59 @@ #include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils { - AZ::Outcome FindSupportedCompilerForPlatform() + AZ::Outcome GetCommandLineProcessEnvironment() { - // Compiler detection not supported on platform - return AZ::Success(); + // For CMake on Mac, if its installed through home-brew, then it will be installed + // under /usr/local/bin, which may not be in the system PATH environment. + // Add that path for the command line process so that it will be able to locate + // a home-brew installed version of CMake + QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); + QString pathValue = currentEnvironment.value("PATH"); + pathValue += ":/usr/local/bin"; + currentEnvironment.insert("PATH", pathValue); + return AZ::Success(currentEnvironment); } - + + AZ::Outcome FindSupportedCompilerForPlatform() + { + QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); + QString pathValue = currentEnvironment.value("PATH"); + pathValue += ":/usr/local/bin"; + currentEnvironment.insert("PATH", pathValue); + + // Validate that we have cmake installed first + auto queryCmakeInstalled = ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, currentEnvironment); + if (!queryCmakeInstalled.IsSuccess()) + { + return AZ::Failure(QObject::tr("Unable to detect CMake on this host.")); + } + QString cmakeInstalledPath = queryCmakeInstalled.GetValue().split("\n")[0]; + + // Query the version of the installed cmake + auto queryCmakeVersionQuery = ExecuteCommandResult(cmakeInstalledPath, QStringList{"-version"}, currentEnvironment); + if (!queryCmakeVersionQuery.IsSuccess()) + { + return AZ::Failure(QObject::tr("Unable to determine the version of CMake on this host.")); + } + AZ_TracePrintf("Project Manager", "Cmake version %s detected.", queryCmakeVersionQuery.GetValue().split("\n")[0].toUtf8().constData()); + + // Query for the version of xcodebuild (if installed) + auto queryXcodeBuildVersion = ExecuteCommandResult("xcodebuild", QStringList{"-version"}, currentEnvironment); + if (!queryCmakeInstalled.IsSuccess()) + { + return AZ::Failure(QObject::tr("Unable to detect XCodeBuilder on this host.")); + } + QString xcodeBuilderVersionNumber = queryXcodeBuildVersion.GetValue().split("\n")[0]; + AZ_TracePrintf("Project Manager", "XcodeBuilder version %s detected.", xcodeBuilderVersionNumber.toUtf8().constData()); + + + return AZ::Success(xcodeBuilderVersionNumber); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp index 8856e2312a..075c6de774 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp @@ -8,189 +8,37 @@ #include #include -#include #include -#include -#include -#include -#include -#include +#include namespace O3DE::ProjectManager { - AZ::Outcome ProjectBuilderWorker::BuildProjectForPlatform() + AZ::Outcome ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const { - // Check if we are trying to cancel task - if (QThread::currentThread()->isInterruptionRequested()) - { - QStringToAZTracePrint(BuildCancelled); - return AZ::Failure(BuildCancelled); - } + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); - QFile logFile(GetLogFilePath()); - if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) - { - QString error = tr("Failed to open log file."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } + return AZ::Success(QStringList{ ProjectCMakeCommand, + "-B", targetBuildPath, + "-S", m_projectInfo.m_path, + QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath), + "-DLY_UNITY_BUILD=ON" } ); + } - EngineInfo engineInfo; + AZ::Outcome ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const + { + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher"; - AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); - if (engineInfoResult.IsSuccess()) - { - engineInfo = engineInfoResult.GetValue(); - } - else - { - QString error = tr("Failed to get engine info."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } + return AZ::Success(QStringList{ ProjectCMakeCommand, + "--build", targetBuildPath, + "--config", "profile", + "--target", launcherTargetName, ProjectCMakeBuildTargetEditor }); + } - QTextStream logStream(&logFile); - if (QThread::currentThread()->isInterruptionRequested()) - { - logFile.close(); - QStringToAZTracePrint(BuildCancelled); - return AZ::Failure(BuildCancelled); - } - - // Show some kind of progress with very approximate estimates - UpdateProgress(++m_progressEstimate); - - QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); - // Append cmake path to PATH incase it is missing - QDir cmakePath(engineInfo.m_path); - cmakePath.cd("cmake/runtime/bin"); - QString pathValue = currentEnvironment.value("PATH"); - pathValue += ";" + cmakePath.path(); - currentEnvironment.insert("PATH", pathValue); - - m_configProjectProcess = new QProcess(this); - m_configProjectProcess->setProcessChannelMode(QProcess::MergedChannels); - m_configProjectProcess->setWorkingDirectory(m_projectInfo.m_path); - m_configProjectProcess->setProcessEnvironment(currentEnvironment); - - m_configProjectProcess->start( - "cmake", - QStringList - { - "-B", - QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), - "-S", - m_projectInfo.m_path, - "-G", - "Visual Studio 16", - "-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath, - "-DLY_UNITY_BUILD=1" - }); - - if (!m_configProjectProcess->waitForStarted()) - { - QString error = tr("Configuring project failed to start."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } - bool containsGeneratingDone = false; - while (m_configProjectProcess->waitForReadyRead(MaxBuildTimeMSecs)) - { - QString configOutput = m_configProjectProcess->readAllStandardOutput(); - - if (configOutput.contains("Generating done")) - { - containsGeneratingDone = true; - } - - logStream << configOutput; - logStream.flush(); - - UpdateProgress(qMin(++m_progressEstimate, 19)); - - if (QThread::currentThread()->isInterruptionRequested()) - { - logFile.close(); - m_configProjectProcess->close(); - QStringToAZTracePrint(BuildCancelled); - return AZ::Failure(BuildCancelled); - } - } - - if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit - || m_configProjectProcess->exitCode() != 0 - || !containsGeneratingDone) - { - QString error = tr("Configuring project failed. See log for details."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } - - UpdateProgress(++m_progressEstimate); - - m_buildProjectProcess = new QProcess(this); - m_buildProjectProcess->setProcessChannelMode(QProcess::MergedChannels); - m_buildProjectProcess->setWorkingDirectory(m_projectInfo.m_path); - m_buildProjectProcess->setProcessEnvironment(currentEnvironment); - - m_buildProjectProcess->start( - "cmake", - QStringList - { - "--build", - QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), - "--target", - m_projectInfo.m_projectName + ".GameLauncher", - "Editor", - "--config", - "profile" - }); - - if (!m_buildProjectProcess->waitForStarted()) - { - QString error = tr("Building project failed to start."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } - - // There are a lot of steps when building so estimate around 800 more steps ((100 - 20) * 10) remaining - m_progressEstimate = 200; - while (m_buildProjectProcess->waitForReadyRead(MaxBuildTimeMSecs)) - { - logStream << m_buildProjectProcess->readAllStandardOutput(); - logStream.flush(); - - // Show 1% progress for every 10 steps completed - UpdateProgress(qMin(++m_progressEstimate / 10, 99)); - - if (QThread::currentThread()->isInterruptionRequested()) - { - // QProcess is unable to kill its child processes so we need to ask the operating system to do that for us - QProcess killBuildProcess; - killBuildProcess.setProcessChannelMode(QProcess::MergedChannels); - killBuildProcess.start( - "cmd.exe", QStringList{ "/C", "taskkill", "/pid", QString::number(m_buildProjectProcess->processId()), "/f", "/t" }); - killBuildProcess.waitForFinished(); - - logStream << "Killing Project Build."; - logStream << killBuildProcess.readAllStandardOutput(); - m_buildProjectProcess->kill(); - logFile.close(); - QStringToAZTracePrint(BuildCancelled); - return AZ::Failure(BuildCancelled); - } - } - - if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit - || m_configProjectProcess->exitCode() != 0) - { - QString error = tr("Building project failed. See log for details."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } - - return AZ::Success(); + AZ::Outcome ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const + { + return AZ::Success(QStringList { "cmd.exe", "/C", "taskkill", "/pid", pidToKill, "/f", "/t" } ); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp index 7ca51fb9c5..d012ca8921 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp @@ -7,6 +7,8 @@ #include +#include + #include #include #include @@ -16,8 +18,44 @@ namespace O3DE::ProjectManager { namespace ProjectUtils { - AZ::Outcome FindSupportedCompilerForPlatform() + AZ::Outcome GetCommandLineProcessEnvironment() { + // Use the engine path to insert a path for cmake + auto engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); + if (!engineInfoResult.IsSuccess()) + { + return AZ::Failure(QObject::tr("Failed to get engine info")); + } + auto engineInfo = engineInfoResult.GetValue(); + + QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); + + // Append cmake path to PATH incase it is missing + QDir cmakePath(engineInfo.m_path); + cmakePath.cd("cmake/runtime/bin"); + QString pathValue = currentEnvironment.value("PATH"); + pathValue += ";" + cmakePath.path(); + currentEnvironment.insert("PATH", pathValue); + return AZ::Success(currentEnvironment); + } + + AZ::Outcome FindSupportedCompilerForPlatform() + { + // Validate that cmake is installed + auto cmakeProcessEnvResult = GetCommandLineProcessEnvironment(); + if (!cmakeProcessEnvResult.IsSuccess()) + { + return AZ::Failure(cmakeProcessEnvResult.GetError()); + } + auto cmakeVersionQueryResult = ExecuteCommandResult("cmake", QStringList{"--version"}, cmakeProcessEnvResult.GetValue()); + if (!cmakeVersionQueryResult.IsSuccess()) + { + return AZ::Failure(QObject::tr("CMake not found. \n\n" + "Make sure that the minimum version of CMake is installed and available from the command prompt. " + "Refer to the O3DE requirements for more information.")); + } + + // Validate that the minimal version of visual studio is installed QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); QString programFilesPath = environment.value("ProgramFiles(x86)"); QString vsWherePath = QDir(programFilesPath).filePath("Microsoft Visual Studio/Installer/vswhere.exe"); @@ -25,27 +63,31 @@ namespace O3DE::ProjectManager QFileInfo vsWhereFile(vsWherePath); if (vsWhereFile.exists() && vsWhereFile.isFile()) { - QProcess vsWhereProcess; - vsWhereProcess.setProcessChannelMode(QProcess::MergedChannels); + QStringList vsWhereBaseArguments = QStringList{"-version", + "16.9.2", + "-latest", + "-requires", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64"}; - vsWhereProcess.start( - vsWherePath, - QStringList{ - "-version", - "16.9.2", - "-latest", - "-requires", - "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "-property", - "isComplete" - }); + QProcess vsWhereIsCompleteProcess; + vsWhereIsCompleteProcess.setProcessChannelMode(QProcess::MergedChannels); - if (vsWhereProcess.waitForStarted() && vsWhereProcess.waitForFinished()) + vsWhereIsCompleteProcess.start(vsWherePath, vsWhereBaseArguments + QStringList{ "-property", "isComplete" }); + + if (vsWhereIsCompleteProcess.waitForStarted() && vsWhereIsCompleteProcess.waitForFinished()) { - QString vsWhereOutput(vsWhereProcess.readAllStandardOutput()); - if (vsWhereOutput.startsWith("1")) + QString vsWhereIsCompleteOutput(vsWhereIsCompleteProcess.readAllStandardOutput()); + if (vsWhereIsCompleteOutput.startsWith("1")) { - return AZ::Success(); + QProcess vsWhereCompilerVersionProcess; + vsWhereCompilerVersionProcess.setProcessChannelMode(QProcess::MergedChannels); + vsWhereCompilerVersionProcess.start(vsWherePath, vsWhereBaseArguments + QStringList{"-property", "catalog_productDisplayVersion"}); + + if (vsWhereCompilerVersionProcess.waitForStarted() && vsWhereCompilerVersionProcess.waitForFinished()) + { + QString vsWhereCompilerVersionOutput(vsWhereCompilerVersionProcess.readAllStandardOutput()); + return AZ::Success(vsWhereCompilerVersionOutput); + } } } } diff --git a/Code/Tools/ProjectManager/Resources/Delete.svg b/Code/Tools/ProjectManager/Resources/Delete.svg new file mode 100644 index 0000000000..f932c71544 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Delete.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Code/Tools/ProjectManager/Resources/Edit.svg b/Code/Tools/ProjectManager/Resources/Edit.svg new file mode 100644 index 0000000000..3ee9bbbfae --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Edit.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index 2e93e9eca9..30bcc1ace5 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -35,5 +35,8 @@ Backgrounds/DefaultBackground.jpg Backgrounds/FtueBackground.jpg FeatureTagClose.svg + Refresh.svg + Edit.svg + Delete.svg diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 17c5077d83..957b2b4fa6 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -498,6 +498,18 @@ QProgressBar::chunk { font-size: 10px; } +/************** Gems SubWidget **************/ + +#gemSubWidgetTitleLabel { + color: #FFFFFF; + font-size: 16px; +} + +#gemSubWidgetTextLabel { + color: #DDDDDD; + font-size: 10px; +} + /************** Gem Catalog (Inspector) **************/ #GemCatalogInspector { @@ -518,3 +530,99 @@ QProgressBar::chunk { font-size: 12px; font-weight: 600; } + +/************** Engine **************/ + +#engineTab::tab-bar { + left: 60px; +} + +#engineTabBar::tab { + height: 50px; + background-color: transparent; + font-weight: 400; + font-size: 18px; + min-width: 160px; +} + +#engineTabBar::tab:selected { + border-bottom: 3px solid #94D2FF; + color: #94D2FF; + font-weight: 600; +} +#engineTabBar::tab:hover { + color: #94D2FF; + font-weight: 600; +} +#engineTabBar::tab:pressed { + color: #66bcfa; +} + +#engineTopFrame { + background-color:#1E252F; +} + +/************** Gem Repo **************/ + +#gemRepoHeaderLabel { + font-size: 12px; +} + +#gemRepoHeaderRefreshButton { + background-color: transparent; + qproperty-flat: true; + qproperty-iconSize: 14px; +} + +#gemRepoHeaderAddButton { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #888888, stop: 1.0 #555555); + qproperty-flat: true; + margin-right:30px; + min-width:120px; + max-width:120px; + min-height:24px; + max-height:24px; + border-radius: 3px; + text-align:center; + font-size:12px; + font-weight:600; +} +#gemRepoHeaderAddButton:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #999999, stop: 1.0 #666666); +} +#gemRepoHeaderAddButton:pressed { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #555555, stop: 1.0 #777777); +} + +#gemRepoHeaderTable { + background-color: transparent; + max-height: 30px; +} + +#gemRepoListHeader { + background-color: transparent; +} + +#gemRepoInspector { + background: #444444; +} + +/************** Gem Repo Inspector **************/ + +#gemRepoInspectorNameLabel { + font-size: 18px; + color: #FFFFFF; +} + +#gemRepoInspectorBodyLabel { + font-size: 12px; + color: #DDDDDD; +} + +#gemRepoInspectorAddInfoTitleLabel { + font-size: 16px; + color: #FFFFFF; +} \ No newline at end of file diff --git a/Code/Tools/ProjectManager/Resources/Refresh.svg b/Code/Tools/ProjectManager/Resources/Refresh.svg new file mode 100644 index 0000000000..80cc892c68 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Refresh.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp new file mode 100644 index 0000000000..c78a9426db --- /dev/null +++ b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp @@ -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 + * + */ + +#include +#include +#include + +#include +#include +#include + +namespace O3DE::ProjectManager +{ + EngineScreenCtrl::EngineScreenCtrl(QWidget* parent) + : ScreenWidget(parent) + { + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setContentsMargins(0, 0, 0, 0); + + QFrame* topBarFrameWidget = new QFrame(this); + topBarFrameWidget->setObjectName("engineTopFrame"); + QHBoxLayout* topBarHLayout = new QHBoxLayout(); + topBarHLayout->setContentsMargins(0, 0, 0, 0); + + topBarFrameWidget->setLayout(topBarHLayout); + + QTabWidget* tabWidget = new QTabWidget(); + tabWidget->setObjectName("engineTab"); + tabWidget->tabBar()->setObjectName("engineTabBar"); + tabWidget->tabBar()->setFocusPolicy(Qt::TabFocus); + + m_engineSettingsScreen = new EngineSettingsScreen(); + m_gemRepoScreen = new GemRepoScreen(); + + tabWidget->addTab(m_engineSettingsScreen, tr("General")); + tabWidget->addTab(m_gemRepoScreen, tr("Gem Repositories")); + topBarHLayout->addWidget(tabWidget); + + vLayout->addWidget(topBarFrameWidget); + + setLayout(vLayout); + } + + ProjectManagerScreen EngineScreenCtrl::GetScreenEnum() + { + return ProjectManagerScreen::UpdateProject; + } + + QString EngineScreenCtrl::GetTabText() + { + return tr("Engine"); + } + + bool EngineScreenCtrl::IsTab() + { + return true; + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h new file mode 100644 index 0000000000..9e799f13e7 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#endif + +namespace O3DE::ProjectManager +{ + QT_FORWARD_DECLARE_CLASS(EngineSettingsScreen) + QT_FORWARD_DECLARE_CLASS(GemRepoScreen) + + class EngineScreenCtrl + : public ScreenWidget + { + public: + explicit EngineScreenCtrl(QWidget* parent = nullptr); + ~EngineScreenCtrl() = default; + ProjectManagerScreen GetScreenEnum() override; + + QString GetTabText() override; + bool IsTab() override; + + EngineSettingsScreen* m_engineSettingsScreen = nullptr; + GemRepoScreen* m_gemRepoScreen = nullptr; + }; + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp index 0c24aac9f1..dec1c9a257 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp @@ -7,15 +7,16 @@ */ #include -#include -#include -#include -#include #include #include #include #include +#include +#include +#include +#include + namespace O3DE::ProjectManager { EngineSettingsScreen::EngineSettingsScreen(QWidget* parent) @@ -78,16 +79,6 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::EngineSettings; } - QString EngineSettingsScreen::GetTabText() - { - return tr("Engine"); - } - - bool EngineSettingsScreen::IsTab() - { - return true; - } - void EngineSettingsScreen::OnTextChanged() { // save engine settings diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h index 9d212c44b1..2f16400405 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h @@ -24,8 +24,6 @@ namespace O3DE::ProjectManager ~EngineSettingsScreen() = default; ProjectManagerScreen GetScreenEnum() override; - QString GetTabText() override; - bool IsTab() override; protected slots: void OnTextChanged(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 909cd93cda..9fca6040d4 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -23,7 +24,7 @@ namespace O3DE::ProjectManager m_layout = new QVBoxLayout(); m_layout->setSpacing(0); - m_layout->setMargin(0); + m_layout->setMargin(5); m_layout->setAlignment(Qt::AlignTop); setLayout(m_layout); @@ -41,74 +42,111 @@ namespace O3DE::ProjectManager hLayout->addWidget(closeButton); m_layout->addLayout(hLayout); - // enabled - { - m_enabledWidget = new QWidget(); - m_enabledWidget->setFixedWidth(s_width); - m_layout->addWidget(m_enabledWidget); + // added + CreateGemSection( tr("Gem to be activated"), tr("Gems to be activated"), [=] + { + QVector gems; + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(/*includeDependencies=*/false); - QVBoxLayout* layout = new QVBoxLayout(); - layout->setAlignment(Qt::AlignTop); - m_enabledWidget->setLayout(layout); + // don't include gems that were already active because they were dependencies + for (const QModelIndex& modelIndex : toBeAdded) + { + if (!GemModel::WasPreviouslyAddedDependency(modelIndex)) + { + gems.push_back(modelIndex); + } + } + return gems; + }); - m_enabledLabel = new QLabel(); - m_enabledLabel->setObjectName("GemCatalogCartOverlaySectionLabel"); - layout->addWidget(m_enabledLabel); - m_enabledTagContainer = new TagContainerWidget(); - layout->addWidget(m_enabledTagContainer); - } + // removed + CreateGemSection( tr("Gem to be deactivated"), tr("Gems to be deactivated"), [=] + { + QVector gems; + const QVector toBeAdded = m_gemModel->GatherGemsToBeRemoved(/*includeDependencies=*/false); - // disabled - { - m_disabledWidget = new QWidget(); - m_disabledWidget->setFixedWidth(s_width); - m_layout->addWidget(m_disabledWidget); + // don't include gems that are still active because they are dependencies + for (const QModelIndex& modelIndex : toBeAdded) + { + if (!GemModel::IsAddedDependency(modelIndex)) + { + gems.push_back(modelIndex); + } + } + return gems; + }); - QVBoxLayout* layout = new QVBoxLayout(); - layout->setAlignment(Qt::AlignTop); - m_disabledWidget->setLayout(layout); + // added dependencies + CreateGemSection( tr("Dependency to be activated"), tr("Dependencies to be activated"), [=] + { + QVector dependencies; + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(/*includeDependencies=*/true); - m_disabledLabel = new QLabel(); - m_disabledLabel->setObjectName("GemCatalogCartOverlaySectionLabel"); - layout->addWidget(m_disabledLabel); - m_disabledTagContainer = new TagContainerWidget(); - layout->addWidget(m_disabledTagContainer); - } + // only include gems that are dependencies and not explicitly added + for (const QModelIndex& modelIndex : toBeAdded) + { + if (GemModel::IsAddedDependency(modelIndex) && !GemModel::IsAdded(modelIndex)) + { + dependencies.push_back(modelIndex); + } + } + return dependencies; + }); + + // removed dependencies + CreateGemSection( tr("Dependency to be deactivated"), tr("Dependencies to be deactivated"), [=] + { + QVector dependencies; + const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(/*includeDependencies=*/true); + + // don't include gems that were explicitly removed - those are listed in a different section + for (const QModelIndex& modelIndex : toBeRemoved) + { + if (!GemModel::WasPreviouslyAdded(modelIndex)) + { + dependencies.push_back(modelIndex); + } + } + return dependencies; + }); setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); - - Update(); - connect(gemModel, &GemModel::dataChanged, this, [=] - { - Update(); - }); } - void CartOverlayWidget::Update() + void CartOverlayWidget::CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices) { - const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); - if (toBeAdded.isEmpty()) - { - m_enabledWidget->hide(); - } - else - { - m_enabledTagContainer->Update(ConvertFromModelIndices(toBeAdded)); - m_enabledLabel->setText(QString("%1 %2").arg(QString::number(toBeAdded.size()), tr("Gems to be enabled"))); - m_enabledWidget->show(); - } + QWidget* widget = new QWidget(); + widget->setFixedWidth(s_width); + m_layout->addWidget(widget); - const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); - if (toBeRemoved.isEmpty()) + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + widget->setLayout(layout); + + QLabel* label = new QLabel(); + label->setObjectName("GemCatalogCartOverlaySectionLabel"); + layout->addWidget(label); + + TagContainerWidget* tagContainer = new TagContainerWidget(); + layout->addWidget(tagContainer); + + auto update = [=]() { - m_disabledWidget->hide(); - } - else - { - m_disabledTagContainer->Update(ConvertFromModelIndices(toBeRemoved)); - m_disabledLabel->setText(QString("%1 %2").arg(QString::number(toBeRemoved.size()), tr("Gems to be disabled"))); - m_disabledWidget->show(); - } + const QVector tagIndices = getTagIndices(); + if (tagIndices.isEmpty()) + { + widget->hide(); + } + else + { + tagContainer->Update(ConvertFromModelIndices(tagIndices)); + label->setText(QString("%1 %2").arg(tagIndices.size()).arg(tagIndices.size() == 1 ? singularTitle : pluralTitle)); + widget->show(); + } + }; + + connect(m_gemModel, &GemModel::dataChanged, this, update); + update(); } QStringList CartOverlayWidget::ConvertFromModelIndices(const QVector& gems) const @@ -154,15 +192,15 @@ namespace O3DE::ProjectManager // Adjust the label text whenever the model gets updated. connect(gemModel, &GemModel::dataChanged, [=] { - const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); - const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(/*includeDependencies=*/true); + const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(/*includeDependencies=*/true); const int count = toBeAdded.size() + toBeRemoved.size(); m_countLabel->setText(QString::number(count)); m_dropDownButton->setVisible(!toBeAdded.isEmpty() || !toBeRemoved.isEmpty()); - // Automatically close the overlay window in case there are no gems to be enabled or disabled anymore. + // Automatically close the overlay window in case there are no gems to be activated or deactivated anymore. if (m_cartOverlay && toBeAdded.isEmpty() && toBeRemoved.isEmpty()) { m_cartOverlay->deleteLater(); @@ -186,8 +224,8 @@ namespace O3DE::ProjectManager void CartButton::ShowOverlay() { - const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); - const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(/*includeDependencies=*/true); + const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(/*includeDependencies=*/true); if (toBeAdded.isEmpty() && toBeRemoved.isEmpty()) { return; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index 4c21fbbbe3..2cfda4c790 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -8,6 +8,8 @@ #pragma once +#include + #if !defined(Q_MOC_RUN) #include #include @@ -30,22 +32,16 @@ namespace O3DE::ProjectManager public: CartOverlayWidget(GemModel* gemModel, QWidget* parent = nullptr); - void Update(); private: QStringList ConvertFromModelIndices(const QVector& gems) const; + using GetTagIndicesCallback = AZStd::function()>; + void CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices); + QVBoxLayout* m_layout = nullptr; GemModel* m_gemModel = nullptr; - QWidget* m_enabledWidget = nullptr; - QLabel* m_enabledLabel = nullptr; - TagContainerWidget* m_enabledTagContainer = nullptr; - - QWidget* m_disabledWidget = nullptr; - QLabel* m_disabledLabel = nullptr; - TagContainerWidget* m_disabledTagContainer = nullptr; - inline constexpr static int s_width = 240; }; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 04d4d6999b..a41a81b448 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -100,6 +100,8 @@ namespace O3DE::ProjectManager m_gemModel->AddGem(gemInfo); } + m_gemModel->UpdateGemDependencies(); + // Gather enabled gems for the given project. auto enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath); if (enabledGemNamesResult.IsSuccess()) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp index c9ea138b55..b425c15dee 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp @@ -226,13 +226,20 @@ namespace O3DE::ProjectManager QVector elementCounts; const int totalGems = m_gemModel->rowCount(); const int selectedGemTotal = m_gemModel->TotalAddedGems(); + const int enabledGemTotal = m_gemModel->TotalAddedGems(/*includeDependencies=*/true); - elementNames.push_back(GemSortFilterProxyModel::GetGemStatusString(GemSortFilterProxyModel::GemStatus::Unselected)); + elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Unselected)); elementCounts.push_back(totalGems - selectedGemTotal); - elementNames.push_back(GemSortFilterProxyModel::GetGemStatusString(GemSortFilterProxyModel::GemStatus::Selected)); + elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Selected)); elementCounts.push_back(selectedGemTotal); + elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Inactive)); + elementCounts.push_back(totalGems - enabledGemTotal); + + elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Active)); + elementCounts.push_back(enabledGemTotal); + bool wasCollapsed = false; if (m_statusFilter) { @@ -253,48 +260,53 @@ namespace O3DE::ProjectManager m_statusFilter->deleteLater(); m_statusFilter = filterWidget; - const GemSortFilterProxyModel::GemStatus currentFilterState = m_filterProxyModel->GetGemStatus(); const QList buttons = m_statusFilter->GetButtonGroup()->buttons(); - for (int statusFilterIndex = 0; statusFilterIndex < buttons.size(); ++statusFilterIndex) + + QAbstractButton* unselectedButton = buttons[0]; + QAbstractButton* selectedButton = buttons[1]; + unselectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Unselected); + selectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Selected); + + auto updateGemSelection = [=]([[maybe_unused]] bool checked) { - const GemSortFilterProxyModel::GemStatus gemStatus = static_cast(statusFilterIndex); - QAbstractButton* button = buttons[statusFilterIndex]; - - if (static_cast(statusFilterIndex) == currentFilterState) + if (unselectedButton->isChecked() && !selectedButton->isChecked()) { - button->setChecked(true); + m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Unselected); } + else if (!unselectedButton->isChecked() && selectedButton->isChecked()) + { + m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Selected); + } + else + { + m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::NoFilter); + } + }; + connect(unselectedButton, &QAbstractButton::toggled, this, updateGemSelection); + connect(selectedButton, &QAbstractButton::toggled, this, updateGemSelection); - connect( - button, &QAbstractButton::toggled, this, - [=](bool checked) - { - GemSortFilterProxyModel::GemStatus filterStatus = m_filterProxyModel->GetGemStatus(); - if (checked) - { - if (filterStatus == GemSortFilterProxyModel::GemStatus::NoFilter) - { - filterStatus = gemStatus; - } - else - { - filterStatus = GemSortFilterProxyModel::GemStatus::NoFilter; - } - } - else - { - if (filterStatus != gemStatus) - { - filterStatus = static_cast(!gemStatus); - } - else - { - filterStatus = GemSortFilterProxyModel::GemStatus::NoFilter; - } - } - m_filterProxyModel->SetGemStatus(filterStatus); - }); - } + QAbstractButton* inactiveButton = buttons[2]; + QAbstractButton* activeButton = buttons[3]; + inactiveButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Inactive); + activeButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Active); + + auto updateGemActive = [=]([[maybe_unused]] bool checked) + { + if (inactiveButton->isChecked() && !activeButton->isChecked()) + { + m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Inactive); + } + else if (!inactiveButton->isChecked() && activeButton->isChecked()) + { + m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Active); + } + else + { + m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::NoFilter); + } + }; + connect(inactiveButton, &QAbstractButton::toggled, this, updateGemActive); + connect(activeButton, &QAbstractButton::toggled, this, updateGemActive); } void GemFilterWidget::AddGemOriginFilter() @@ -487,7 +499,7 @@ namespace O3DE::ProjectManager const QString& feature = elementNames[i]; QAbstractButton* button = buttons[i]; - // Adjust the proxy model and enable or disable the clicked feature used for filtering. + // Adjust the proxy model and enable the clicked feature used for filtering. connect(button, &QAbstractButton::toggled, this, [=](bool checked) { QSet features = m_filterProxyModel->GetFeatures(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h index 4312b08998..311eeb93f6 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h @@ -75,8 +75,7 @@ namespace O3DE::ProjectManager QString m_version = "Unknown Version"; QString m_lastUpdatedDate = "Unknown Date"; int m_binarySizeInKB = 0; - QStringList m_dependingGemUuids; - QStringList m_conflictingGemUuids; + QStringList m_dependencies; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp index 6dd6c52612..7630e92e88 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp @@ -8,6 +8,7 @@ #include #include + #include #include #include @@ -83,14 +84,13 @@ namespace O3DE::ProjectManager m_reqirementsTextLabel->hide(); } - // Depending and conflicting gems + // Depending gems m_dependingGems->Update("Depending Gems", "The following Gems will be automatically enabled with this Gem.", m_model->GetDependingGemNames(modelIndex)); - m_conflictingGems->Update("Conflicting Gems", "The following Gems will be automatically disabled with this Gem.", m_model->GetConflictingGemNames(modelIndex)); // Additional information m_versionLabel->setText(QString("Gem Version: %1").arg(m_model->GetVersion(modelIndex))); m_lastUpdatedLabel->setText(QString("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex))); - m_binarySizeLabel->setText(QString("Binary Size: %1 KB").arg(QString::number(m_model->GetBinarySizeInKB(modelIndex)))); + m_binarySizeLabel->setText(QString("Binary Size: %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex))); m_mainWidget->adjustSize(); m_mainWidget->show(); @@ -173,15 +173,11 @@ namespace O3DE::ProjectManager m_mainLayout->addSpacing(20); - // Depending and conflicting gems + // Depending gems m_dependingGems = new GemsSubWidget(); m_mainLayout->addWidget(m_dependingGems); m_mainLayout->addSpacing(20); - m_conflictingGems = new GemsSubWidget(); - m_mainLayout->addWidget(m_conflictingGems); - m_mainLayout->addSpacing(20); - // Additional information QLabel* additionalInfoLabel = CreateStyledLabel(m_mainLayout, 14, s_headerColor); additionalInfoLabel->setText("Additional Information"); @@ -190,27 +186,4 @@ namespace O3DE::ProjectManager m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); m_binarySizeLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); } - - GemInspector::GemsSubWidget::GemsSubWidget(QWidget* parent) - : QWidget(parent) - { - m_layout = new QVBoxLayout(); - m_layout->setAlignment(Qt::AlignTop); - m_layout->setMargin(0); - setLayout(m_layout); - - m_titleLabel = GemInspector::CreateStyledLabel(m_layout, 16, s_headerColor); - m_textLabel = GemInspector::CreateStyledLabel(m_layout, 10, s_textColor); - m_textLabel->setWordWrap(true); - - m_tagWidget = new TagContainerWidget(); - m_layout->addWidget(m_tagWidget); - } - - void GemInspector::GemsSubWidget::Update(const QString& title, const QString& text, const QStringList& gemNames) - { - m_titleLabel->setText(title); - m_textLabel->setText(text); - m_tagWidget->Update(gemNames); - } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h index 97c23f7df2..ca36cef240 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h @@ -9,10 +9,11 @@ #pragma once #if !defined(Q_MOC_RUN) -#include -#include #include #include +#include +#include + #include #include #include @@ -43,21 +44,6 @@ namespace O3DE::ProjectManager void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); private: - // Title, description and tag widget container used for the depending and conflicting gems - class GemsSubWidget - : public QWidget - { - public: - GemsSubWidget(QWidget* parent = nullptr); - void Update(const QString& title, const QString& text, const QStringList& gemNames); - - private: - QLabel* m_titleLabel = nullptr; - QLabel* m_textLabel = nullptr; - QVBoxLayout* m_layout = nullptr; - TagContainerWidget* m_tagWidget = nullptr; - }; - void InitMainWidget(); GemModel* m_model = nullptr; @@ -78,7 +64,6 @@ namespace O3DE::ProjectManager // Depending and conflicting gems GemsSubWidget* m_dependingGems = nullptr; - GemsSubWidget* m_conflictingGems = nullptr; // Additional information QLabel* m_versionLabel = nullptr; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index 99a2cd8db7..dc24c13009 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -8,9 +8,14 @@ #include #include +#include #include +#include #include #include +#include +#include +#include namespace O3DE::ProjectManager { @@ -149,8 +154,7 @@ namespace O3DE::ProjectManager return true; } } - - if (event->type() == QEvent::MouseButtonPress) + else if (event->type() == QEvent::MouseButtonPress ) { QMouseEvent* mouseEvent = static_cast(event); @@ -169,6 +173,69 @@ namespace O3DE::ProjectManager return QStyledItemDelegate::editorEvent(event, model, option, modelIndex); } + QString GetGemNameList(const QVector modelIndices) + { + QString gemNameList; + for (int i = 0; i < modelIndices.size(); ++i) + { + if (!gemNameList.isEmpty()) + { + if (i == modelIndices.size() - 1) + { + gemNameList.append(" and "); + } + else + { + gemNameList.append(", "); + } + } + + gemNameList.append(GemModel::GetDisplayName(modelIndices[i])); + } + + return gemNameList; + } + + bool GemItemDelegate::helpEvent(QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option, const QModelIndex& index) + { + if (event->type() == QEvent::ToolTip) + { + QRect fullRect, itemRect, contentRect; + CalcRects(option, fullRect, itemRect, contentRect); + const QRect buttonRect = CalcButtonRect(contentRect); + if (buttonRect.contains(event->pos())) + { + if (!QToolTip::isVisible()) + { + if(GemModel::IsAddedDependency(index) && !GemModel::IsAdded(index)) + { + const GemModel* gemModel = GemModel::GetSourceModel(index.model()); + AZ_Assert(gemModel, "Failed to obtain GemModel"); + + // we only want to display the gems that must be de-selected to automatically + // disable this dependency, so don't include any that haven't been selected (added) + constexpr bool addedOnly = true; + QVector dependents = gemModel->GatherDependentGems(index, addedOnly); + QString nameList = GetGemNameList(dependents); + if (!nameList.isEmpty()) + { + QToolTip::showText(event->globalPos(), tr("This gem is a dependency of %1.\nTo disable this gem, first disable %1.").arg(nameList)); + } + } + } + return true; + } + else if (QToolTip::isVisible()) + { + QToolTip::hideText(); + event->ignore(); + return true; + } + } + + return QStyledItemDelegate::helpEvent(event, view, option, index); + } + void GemItemDelegate::CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const { outFullRect = QRect(option.rect); @@ -260,14 +327,20 @@ namespace O3DE::ProjectManager const QRect buttonRect = CalcButtonRect(contentRect); QPoint circleCenter; - const bool isAdded = GemModel::IsAdded(modelIndex); - if (isAdded) + if (GemModel::IsAdded(modelIndex)) { painter->setBrush(m_buttonEnabledColor); painter->setPen(m_buttonEnabledColor); circleCenter = buttonRect.center() + QPoint(buttonRect.width() / 2 - s_buttonBorderRadius + 1, 1); } + else if (GemModel::IsAddedDependency(modelIndex)) + { + painter->setBrush(m_buttonImplicitlyEnabledColor); + painter->setPen(m_buttonImplicitlyEnabledColor); + + circleCenter = buttonRect.center() + QPoint(buttonRect.width() / 2 - s_buttonBorderRadius + 1, 1); + } else { circleCenter = buttonRect.center() + QPoint(-buttonRect.width() / 2 + s_buttonBorderRadius, 1); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h index a0a3dbb36a..d842f63ae7 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h @@ -29,7 +29,6 @@ namespace O3DE::ProjectManager ~GemItemDelegate() = default; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override; - bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) override; QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override; // Colors @@ -39,6 +38,7 @@ namespace O3DE::ProjectManager const QColor m_itemBackgroundColor = QColor("#404040"); // Background color of the gem item const QColor m_borderColor = QColor("#1E70EB"); const QColor m_buttonEnabledColor = QColor("#00B931"); + const QColor m_buttonImplicitlyEnabledColor = QColor("#BCBCBE"); // Item inline constexpr static int s_height = 105; // Gem item total height @@ -65,6 +65,9 @@ namespace O3DE::ProjectManager inline constexpr static int s_featureTagSpacing = 7; protected: + bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) override; + bool helpEvent(QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option, const QModelIndex& index) override; + void CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const; QRect GetTextRect(QFont& font, const QString& text, qreal fontSize) const; QRect CalcButtonRect(const QRect& contentRect) const; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp index 16234f4900..ab51c7511c 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp @@ -60,11 +60,14 @@ namespace O3DE::ProjectManager QLabel* showCountLabel = new QLabel(); showCountLabel->setObjectName("GemCatalogHeaderShowCountLabel"); topLayout->addWidget(showCountLabel); - connect(proxyModel, &GemSortFilterProxyModel::OnInvalidated, this, [=] - { + + auto refreshGemCountUI = [=]() { const int numGemsShown = proxyModel->rowCount(); showCountLabel->setText(QString(tr("showing %1 Gems")).arg(numGemsShown)); - }); + }; + + connect(proxyModel, &GemSortFilterProxyModel::OnInvalidated, this, refreshGemCountUI); + connect(proxyModel->GetSourceModel(), &GemModel::dataChanged, this, refreshGemCountUI); topLayout->addSpacing(GemItemDelegate::s_contentMargins.right() + GemItemDelegate::s_borderWidth); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp index d393a30ed9..afdb9697c9 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp @@ -9,9 +9,26 @@ #include #include #include +#include namespace O3DE::ProjectManager { + class GemListViewProxyStyle : public QProxyStyle + { + public: + using QProxyStyle::QProxyStyle; + int styleHint(StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const override + { + if (hint == QStyle::SH_ToolTip_WakeUpDelay || hint == QStyle::SH_ToolTip_FallAsleepDelay) + { + // no delay + return 0; + } + + return QProxyStyle::styleHint(hint, option, widget, returnData); + } + }; + GemListView::GemListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent) : QListView(parent) { @@ -21,5 +38,8 @@ namespace O3DE::ProjectManager setModel(model); setSelectionModel(selectionModel); setItemDelegate(new GemItemDelegate(model, this)); + + // use a custom proxy style so we get immediate tooltips for gem radio buttons + setStyle(new GemListViewProxyStyle(this->style())); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 7daea174e7..0941541793 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -8,6 +8,7 @@ #include #include +#include #include namespace O3DE::ProjectManager @@ -40,8 +41,7 @@ namespace O3DE::ProjectManager item->setData(gemInfo.m_isAdded, RoleIsAdded); item->setData(gemInfo.m_directoryLink, RoleDirectoryLink); item->setData(gemInfo.m_documentationLink, RoleDocLink); - item->setData(gemInfo.m_dependingGemUuids, RoleDependingGems); - item->setData(gemInfo.m_conflictingGemUuids, RoleConflictingGems); + item->setData(gemInfo.m_dependencies, RoleDependingGems); item->setData(gemInfo.m_version, RoleVersion); item->setData(gemInfo.m_lastUpdatedDate, RoleLastUpdated); item->setData(gemInfo.m_binarySizeInKB, RoleBinarySize); @@ -60,6 +60,39 @@ namespace O3DE::ProjectManager clear(); } + void GemModel::UpdateGemDependencies() + { + m_gemDependencyMap.clear(); + m_gemReverseDependencyMap.clear(); + + for (auto iter = m_nameToIndexMap.begin(); iter != m_nameToIndexMap.end(); ++iter) + { + const QString& key = iter.key(); + const QModelIndex modelIndex = iter.value(); + QSet dependencies; + GetAllDependingGems(modelIndex, dependencies); + if (!dependencies.isEmpty()) + { + m_gemDependencyMap.insert(key, dependencies); + } + } + + for (auto iter = m_gemDependencyMap.begin(); iter != m_gemDependencyMap.end(); ++iter) + { + const QString& dependant = iter.key(); + for (const QModelIndex& dependency : iter.value()) + { + const QString& dependencyName = dependency.data(RoleName).toString(); + if (!m_gemReverseDependencyMap.contains(dependencyName)) + { + m_gemReverseDependencyMap.insert(dependencyName, QSet()); + } + + m_gemReverseDependencyMap[dependencyName].insert(m_nameToIndexMap[dependant]); + } + } + } + QString GemModel::GetName(const QModelIndex& modelIndex) { return modelIndex.data(RoleName).toString(); @@ -125,49 +158,46 @@ namespace O3DE::ProjectManager return {}; } - void GemModel::FindGemNamesByNameStrings(QStringList& inOutGemNames) + void GemModel::FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames) { - for (QString& dependingGemString : inOutGemNames) + for (QString& name : inOutGemNames) { - QModelIndex modelIndex = FindIndexByNameString(dependingGemString); + QModelIndex modelIndex = FindIndexByNameString(name); if (modelIndex.isValid()) { - dependingGemString = GetDisplayName(modelIndex); + name = GetDisplayName(modelIndex); } } } - QStringList GemModel::GetDependingGemUuids(const QModelIndex& modelIndex) + QStringList GemModel::GetDependingGems(const QModelIndex& modelIndex) { return modelIndex.data(RoleDependingGems).toStringList(); } + void GemModel::GetAllDependingGems(const QModelIndex& modelIndex, QSet& inOutGems) + { + QStringList dependencies = GetDependingGems(modelIndex); + for (const QString& dependency : dependencies) + { + QModelIndex dependencyIndex = FindIndexByNameString(dependency); + if (!inOutGems.contains(dependencyIndex)) + { + inOutGems.insert(dependencyIndex); + GetAllDependingGems(dependencyIndex, inOutGems); + } + } + } + QStringList GemModel::GetDependingGemNames(const QModelIndex& modelIndex) { - QStringList result = GetDependingGemUuids(modelIndex); + QStringList result = GetDependingGems(modelIndex); if (result.isEmpty()) { return {}; } - FindGemNamesByNameStrings(result); - return result; - } - - QStringList GemModel::GetConflictingGemUuids(const QModelIndex& modelIndex) - { - return modelIndex.data(RoleConflictingGems).toStringList(); - } - - QStringList GemModel::GetConflictingGemNames(const QModelIndex& modelIndex) - { - QStringList result = GetConflictingGemUuids(modelIndex); - if (result.isEmpty()) - { - return {}; - } - - FindGemNamesByNameStrings(result); + FindGemDisplayNamesByNameStrings(result); return result; } @@ -201,29 +231,146 @@ namespace O3DE::ProjectManager return modelIndex.data(RoleRequirement).toString(); } + GemModel* GemModel::GetSourceModel(QAbstractItemModel* model) + { + GemSortFilterProxyModel* proxyModel = qobject_cast(model); + if (proxyModel) + { + return proxyModel->GetSourceModel(); + } + else + { + return qobject_cast(model); + } + } + + const GemModel* GemModel::GetSourceModel(const QAbstractItemModel* model) + { + const GemSortFilterProxyModel* proxyModel = qobject_cast(model); + if (proxyModel) + { + return proxyModel->GetSourceModel(); + } + else + { + return qobject_cast(model); + } + } + bool GemModel::IsAdded(const QModelIndex& modelIndex) { return modelIndex.data(RoleIsAdded).toBool(); } + bool GemModel::IsAddedDependency(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleIsAddedDependency).toBool(); + } + void GemModel::SetIsAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded) { model.setData(modelIndex, isAdded, RoleIsAdded); + + UpdateDependencies(model, modelIndex); + } + + bool GemModel::HasDependentGems(const QModelIndex& modelIndex) const + { + QVector dependentGems = GatherDependentGems(modelIndex); + for (const QModelIndex& dependency : dependentGems) + { + if (IsAdded(dependency)) + { + return true; + } + } + return false; + } + + void GemModel::UpdateDependencies(QAbstractItemModel& model, const QModelIndex& modelIndex) + { + GemModel* gemModel = GetSourceModel(&model); + AZ_Assert(gemModel, "Failed to obtain GemModel"); + + QVector dependencies = gemModel->GatherGemDependencies(modelIndex); + if (IsAdded(modelIndex)) + { + for (const QModelIndex& dependency : dependencies) + { + SetIsAddedDependency(*gemModel, dependency, true); + } + } + else + { + // still a dependency if some added gem depends on this one + SetIsAddedDependency(model, modelIndex, gemModel->HasDependentGems(modelIndex)); + + for (const QModelIndex& dependency : dependencies) + { + SetIsAddedDependency(*gemModel, dependency, gemModel->HasDependentGems(dependency)); + } + } + } + + void GemModel::SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded) + { + model.setData(modelIndex, isAdded, RoleIsAddedDependency); } void GemModel::SetWasPreviouslyAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool wasAdded) { model.setData(modelIndex, wasAdded, RoleWasPreviouslyAdded); + + if (wasAdded) + { + // update all dependencies + GemModel* gemModel = GetSourceModel(&model); + AZ_Assert(gemModel, "Failed to obtain GemModel"); + QVector dependencies = gemModel->GatherGemDependencies(modelIndex); + for (const QModelIndex& dependency : dependencies) + { + SetWasPreviouslyAddedDependency(*gemModel, dependency, true); + } + } } - bool GemModel::NeedsToBeAdded(const QModelIndex& modelIndex) + void GemModel::SetWasPreviouslyAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool wasAdded) { - return (!modelIndex.data(RoleWasPreviouslyAdded).toBool() && modelIndex.data(RoleIsAdded).toBool()); + model.setData(modelIndex, wasAdded, RoleWasPreviouslyAddedDependency); } - bool GemModel::NeedsToBeRemoved(const QModelIndex& modelIndex) + bool GemModel::WasPreviouslyAdded(const QModelIndex& modelIndex) { - return (modelIndex.data(RoleWasPreviouslyAdded).toBool() && !modelIndex.data(RoleIsAdded).toBool()); + return modelIndex.data(RoleWasPreviouslyAdded).toBool(); + } + + bool GemModel::WasPreviouslyAddedDependency(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleWasPreviouslyAddedDependency).toBool(); + } + + bool GemModel::NeedsToBeAdded(const QModelIndex& modelIndex, bool includeDependencies) + { + bool previouslyAdded = modelIndex.data(RoleWasPreviouslyAdded).toBool(); + bool added = modelIndex.data(RoleIsAdded).toBool(); + if (includeDependencies) + { + previouslyAdded |= modelIndex.data(RoleWasPreviouslyAddedDependency).toBool(); + added |= modelIndex.data(RoleIsAddedDependency).toBool(); + } + return !previouslyAdded && added; + } + + bool GemModel::NeedsToBeRemoved(const QModelIndex& modelIndex, bool includeDependencies) + { + bool previouslyAdded = modelIndex.data(RoleWasPreviouslyAdded).toBool(); + bool added = modelIndex.data(RoleIsAdded).toBool(); + if (includeDependencies) + { + previouslyAdded |= modelIndex.data(RoleWasPreviouslyAddedDependency).toBool(); + added |= modelIndex.data(RoleIsAddedDependency).toBool(); + } + return previouslyAdded && !added; } bool GemModel::HasRequirement(const QModelIndex& modelIndex) @@ -244,13 +391,44 @@ namespace O3DE::ProjectManager return false; } - QVector GemModel::GatherGemsToBeAdded() const + QVector GemModel::GatherGemDependencies(const QModelIndex& modelIndex) const + { + QVector result; + const QString& gemName = modelIndex.data(RoleName).toString(); + if (m_gemDependencyMap.contains(gemName)) + { + for (const QModelIndex& dependency : m_gemDependencyMap[gemName]) + { + result.push_back(dependency); + } + } + return result; + } + + QVector GemModel::GatherDependentGems(const QModelIndex& modelIndex, bool addedOnly) const + { + QVector result; + const QString& gemName = modelIndex.data(RoleName).toString(); + if (m_gemReverseDependencyMap.contains(gemName)) + { + for (const QModelIndex& dependency : m_gemReverseDependencyMap[gemName]) + { + if (!addedOnly || GemModel::IsAdded(dependency)) + { + result.push_back(dependency); + } + } + } + return result; + } + + QVector GemModel::GatherGemsToBeAdded(bool includeDependencies) const { QVector result; for (int row = 0; row < rowCount(); ++row) { const QModelIndex modelIndex = index(row, 0); - if (NeedsToBeAdded(modelIndex)) + if (NeedsToBeAdded(modelIndex, includeDependencies)) { result.push_back(modelIndex); } @@ -258,13 +436,13 @@ namespace O3DE::ProjectManager return result; } - QVector GemModel::GatherGemsToBeRemoved() const + QVector GemModel::GatherGemsToBeRemoved(bool includeDependencies) const { QVector result; for (int row = 0; row < rowCount(); ++row) { const QModelIndex modelIndex = index(row, 0); - if (NeedsToBeRemoved(modelIndex)) + if (NeedsToBeRemoved(modelIndex, includeDependencies)) { result.push_back(modelIndex); } @@ -272,13 +450,13 @@ namespace O3DE::ProjectManager return result; } - int GemModel::TotalAddedGems() const + int GemModel::TotalAddedGems(bool includeDependencies) const { int result = 0; for (int row = 0; row < rowCount(); ++row) { const QModelIndex modelIndex = index(row, 0); - if (IsAdded(modelIndex)) + if (IsAdded(modelIndex) || (includeDependencies && IsAddedDependency(modelIndex))) { ++result; } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index ce004ee875..0591094c11 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -28,13 +28,11 @@ namespace O3DE::ProjectManager void AddGem(const GemInfo& gemInfo); void Clear(); + void UpdateGemDependencies(); QModelIndex FindIndexByNameString(const QString& nameString) const; - void FindGemNamesByNameStrings(QStringList& inOutGemNames); - QStringList GetDependingGemUuids(const QModelIndex& modelIndex); QStringList GetDependingGemNames(const QModelIndex& modelIndex); - QStringList GetConflictingGemUuids(const QModelIndex& modelIndex); - QStringList GetConflictingGemNames(const QModelIndex& modelIndex); + bool HasDependentGems(const QModelIndex& modelIndex) const; static QString GetName(const QModelIndex& modelIndex); static QString GetDisplayName(const QModelIndex& modelIndex); @@ -51,22 +49,36 @@ namespace O3DE::ProjectManager static QStringList GetFeatures(const QModelIndex& modelIndex); static QString GetPath(const QModelIndex& modelIndex); static QString GetRequirement(const QModelIndex& modelIndex); + static GemModel* GetSourceModel(QAbstractItemModel* model); + static const GemModel* GetSourceModel(const QAbstractItemModel* model); static bool IsAdded(const QModelIndex& modelIndex); + static bool IsAddedDependency(const QModelIndex& modelIndex); static void SetIsAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded); + static void SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded); static void SetWasPreviouslyAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool wasAdded); - static bool NeedsToBeAdded(const QModelIndex& modelIndex); - static bool NeedsToBeRemoved(const QModelIndex& modelIndex); + static bool WasPreviouslyAdded(const QModelIndex& modelIndex); + static void SetWasPreviouslyAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool wasAdded); + static bool WasPreviouslyAddedDependency(const QModelIndex& modelIndex); + static bool NeedsToBeAdded(const QModelIndex& modelIndex, bool includeDependencies = false); + static bool NeedsToBeRemoved(const QModelIndex& modelIndex, bool includeDependencies = false); static bool HasRequirement(const QModelIndex& modelIndex); + static void UpdateDependencies(QAbstractItemModel& model, const QModelIndex& modelIndex); bool DoGemsToBeAddedHaveRequirements() const; - QVector GatherGemsToBeAdded() const; - QVector GatherGemsToBeRemoved() const; + QVector GatherGemDependencies(const QModelIndex& modelIndex) const; + QVector GatherDependentGems(const QModelIndex& modelIndex, bool addedOnly = false) const; + QVector GatherGemsToBeAdded(bool includeDependencies = false) const; + QVector GatherGemsToBeRemoved(bool includeDependencies = false) const; - int TotalAddedGems() const; + int TotalAddedGems(bool includeDependencies = false) const; private: + void FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames); + void GetAllDependingGems(const QModelIndex& modelIndex, QSet& inOutGems); + QStringList GetDependingGems(const QModelIndex& modelIndex); + enum UserRole { RoleName = Qt::UserRole, @@ -76,11 +88,12 @@ namespace O3DE::ProjectManager RolePlatforms, RoleSummary, RoleWasPreviouslyAdded, + RoleWasPreviouslyAddedDependency, RoleIsAdded, + RoleIsAddedDependency, RoleDirectoryLink, RoleDocLink, RoleDependingGems, - RoleConflictingGems, RoleVersion, RoleLastUpdated, RoleBinarySize, @@ -92,5 +105,7 @@ namespace O3DE::ProjectManager QHash m_nameToIndexMap; QItemSelectionModel* m_selectionModel = nullptr; + QHash> m_gemDependencyMap; + QHash> m_gemReverseDependencyMap; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp index 6edfced6e5..199692f200 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp @@ -50,11 +50,21 @@ namespace O3DE::ProjectManager } } - // Gem status - if (m_gemStatusFilter != GemStatus::NoFilter) + // Gem selected + if (m_gemSelectedFilter != GemSelected::NoFilter) { - const GemStatus sourceGemStatus = static_cast(GemModel::IsAdded(sourceIndex)); - if (m_gemStatusFilter != sourceGemStatus) + const GemSelected sourceGemStatus = static_cast(GemModel::IsAdded(sourceIndex)); + if (m_gemSelectedFilter != sourceGemStatus) + { + return false; + } + } + + // Gem enabled + if (m_gemActiveFilter != GemActive::NoFilter) + { + const GemActive sourceGemStatus = static_cast(GemModel::IsAdded(sourceIndex) || GemModel::IsAddedDependency(sourceIndex)); + if (m_gemActiveFilter != sourceGemStatus) { return false; } @@ -148,19 +158,31 @@ namespace O3DE::ProjectManager return true; } - QString GemSortFilterProxyModel::GetGemStatusString(GemStatus status) + QString GemSortFilterProxyModel::GetGemSelectedString(GemSelected status) { switch (status) { - case Unselected: + case GemSelected::Unselected: return "Unselected"; - case Selected: + case GemSelected::Selected: return "Selected"; default: - return ""; + return ""; } } + QString GemSortFilterProxyModel::GetGemActiveString(GemActive status) + { + switch (status) + { + case GemActive::Inactive: + return "Inactive"; + case GemActive::Active: + return "Active"; + default: + return ""; + } + } void GemSortFilterProxyModel::InvalidateFilter() { invalidate(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h index 4ec170aef2..74b1e915eb 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h @@ -25,16 +25,23 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - enum GemStatus + enum class GemSelected { NoFilter = -1, Unselected, Selected }; + enum class GemActive + { + NoFilter = -1, + Inactive, + Active + }; GemSortFilterProxyModel(GemModel* sourceModel, QObject* parent = nullptr); - static QString GetGemStatusString(GemStatus status); + static QString GetGemSelectedString(GemSelected status); + static QString GetGemActiveString(GemActive status); bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; @@ -43,8 +50,11 @@ namespace O3DE::ProjectManager void SetSearchString(const QString& searchString) { m_searchString = searchString; InvalidateFilter(); } - GemStatus GetGemStatus() const { return m_gemStatusFilter; } - void SetGemStatus(GemStatus gemStatus) { m_gemStatusFilter = gemStatus; InvalidateFilter(); } + GemSelected GetGemSelected() const { return m_gemSelectedFilter; } + void SetGemSelected(GemSelected selected) { m_gemSelectedFilter = selected; InvalidateFilter(); } + + GemActive GetGemActive() const { return m_gemActiveFilter; } + void SetGemActive(GemActive enabled) { m_gemActiveFilter = enabled; InvalidateFilter(); } GemInfo::GemOrigins GetGemOrigins() const { return m_gemOriginFilter; } void SetGemOrigins(const GemInfo::GemOrigins& gemOrigins) { m_gemOriginFilter = gemOrigins; InvalidateFilter(); } @@ -69,7 +79,8 @@ namespace O3DE::ProjectManager AzQtComponents::SelectionProxyModel* m_selectionProxyModel = nullptr; QString m_searchString; - GemStatus m_gemStatusFilter = GemStatus::NoFilter; + GemSelected m_gemSelectedFilter = GemSelected::NoFilter; + GemActive m_gemActiveFilter = GemActive::NoFilter; GemInfo::GemOrigins m_gemOriginFilter = {}; GemInfo::Platforms m_platformFilter = {}; GemInfo::Types m_typeFilter = {}; diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.cpp new file mode 100644 index 0000000000..88216398db --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace O3DE::ProjectManager +{ + GemRepoInfo::GemRepoInfo( + const QString& name, + const QString& creator, + const QDateTime& lastUpdated, + bool isEnabled = true) + : m_name(name) + , m_creator(creator) + , m_lastUpdated(lastUpdated) + , m_isEnabled(isEnabled) + { + } + + bool GemRepoInfo::IsValid() const + { + return !m_name.isEmpty(); + } + + bool GemRepoInfo::operator<(const GemRepoInfo& gemRepoInfo) const + { + return (m_lastUpdated < gemRepoInfo.m_lastUpdated); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h new file mode 100644 index 0000000000..14c76bd0c2 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#endif + +namespace O3DE::ProjectManager +{ + class GemRepoInfo + { + public: + GemRepoInfo() = default; + GemRepoInfo( + const QString& name, + const QString& creator, + const QDateTime& lastUpdated, + bool isEnabled); + + bool IsValid() const; + + bool operator<(const GemRepoInfo& gemRepoInfo) const; + + QString m_path = ""; + QString m_name = "Unknown Gem Repo Name"; + QString m_creator = "Unknown Creator"; + bool m_isEnabled = false; //! Is the repo currently enabled for this engine? + QString m_summary = "No summary provided."; + QString m_additionalInfo = ""; + QString m_directoryLink = ""; + QString m_repoLink = ""; + QStringList m_includedGemPaths = {}; + QDateTime m_lastUpdated; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp new file mode 100644 index 0000000000..93f5890b94 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp @@ -0,0 +1,144 @@ +/* + * 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 O3DE::ProjectManager +{ + GemRepoInspector::GemRepoInspector(GemRepoModel* model, QWidget* parent) + : QScrollArea(parent) + , m_model(model) + { + setObjectName("gemRepoInspector"); + setWidgetResizable(true); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + m_mainWidget = new QWidget(); + setWidget(m_mainWidget); + + m_mainLayout = new QVBoxLayout(); + m_mainLayout->setMargin(15); + m_mainLayout->setAlignment(Qt::AlignTop); + m_mainWidget->setLayout(m_mainLayout); + + InitMainWidget(); + + connect(m_model->GetSelectionModel(), &QItemSelectionModel::selectionChanged, this, &GemRepoInspector::OnSelectionChanged); + Update({}); + } + + void GemRepoInspector::OnSelectionChanged(const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected) + { + const QModelIndexList selectedIndices = selected.indexes(); + if (selectedIndices.empty()) + { + Update({}); + return; + } + + Update(selectedIndices[0]); + } + + void GemRepoInspector::Update(const QModelIndex& modelIndex) + { + if (!modelIndex.isValid()) + { + m_mainWidget->hide(); + } + + // Repo name and url link + m_nameLabel->setText(m_model->GetName(modelIndex)); + m_repoLinkLabel->setText(m_model->GetRepoLink(modelIndex)); + m_repoLinkLabel->SetUrl(m_model->GetRepoLink(modelIndex)); + + // Repo summary + m_summaryLabel->setText(m_model->GetSummary(modelIndex)); + m_summaryLabel->adjustSize(); + + // Additional information + if (m_model->HasAdditionalInfo(modelIndex)) + { + m_addInfoTitleLabel->show(); + m_addInfoTextLabel->show(); + + m_addInfoSpacer->changeSize(0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed); + + m_addInfoTextLabel->setText(m_model->GetAdditionalInfo(modelIndex)); + } + else + { + m_addInfoTitleLabel->hide(); + m_addInfoTextLabel->hide(); + + m_addInfoSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); + } + + // Included Gems + m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemNames(modelIndex)); + + m_mainWidget->adjustSize(); + m_mainWidget->show(); + } + + void GemRepoInspector::InitMainWidget() + { + // Repo name and url link + m_nameLabel = new QLabel(); + m_nameLabel->setObjectName("gemRepoInspectorNameLabel"); + m_mainLayout->addWidget(m_nameLabel); + + m_repoLinkLabel = new LinkLabel(tr("Repo Url"), QUrl(""), 12, this); + m_mainLayout->addWidget(m_repoLinkLabel); + m_mainLayout->addSpacing(5); + + // Repo summary + m_summaryLabel = new QLabel(); + m_summaryLabel->setObjectName("gemRepoInspectorBodyLabel"); + m_summaryLabel->setWordWrap(true); + m_summaryLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + m_summaryLabel->setOpenExternalLinks(true); + m_mainLayout->addWidget(m_summaryLabel); + m_mainLayout->addSpacing(20); + + // Separating line + QFrame* hLine = new QFrame(); + hLine->setFrameShape(QFrame::HLine); + hLine->setObjectName("horizontalSeparatingLine"); + m_mainLayout->addWidget(hLine); + m_mainLayout->addSpacing(10); + + // Additional information + m_addInfoTitleLabel = new QLabel(); + m_addInfoTitleLabel->setObjectName("gemRepoInspectorAddInfoTitleLabel"); + m_addInfoTitleLabel->setText(tr("Additional Information")); + m_mainLayout->addWidget(m_addInfoTitleLabel); + + m_addInfoTextLabel = new QLabel(); + m_addInfoTextLabel->setObjectName("gemRepoInspectorBodyLabel"); + m_addInfoTextLabel->setWordWrap(true); + m_addInfoTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + m_addInfoTextLabel->setOpenExternalLinks(true); + m_mainLayout->addWidget(m_addInfoTextLabel); + + // Conditional spacing for additional info section + m_addInfoSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding); + m_mainLayout->addSpacerItem(m_addInfoSpacer); + + // Included Gems + m_includedGems = new GemsSubWidget(); + m_mainLayout->addWidget(m_includedGems); + m_mainLayout->addSpacing(20); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.h new file mode 100644 index 0000000000..a14472e6a6 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include + +#include +#include +#include +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QVBoxLayout) +QT_FORWARD_DECLARE_CLASS(QLabel) + +namespace O3DE::ProjectManager +{ + class GemRepoInspector : public QScrollArea + { + Q_OBJECT // AUTOMOC + + public : explicit GemRepoInspector(GemRepoModel* model, QWidget* parent = nullptr); + ~GemRepoInspector() = default; + + void Update(const QModelIndex& modelIndex); + + private slots: + void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + + private: + void InitMainWidget(); + + GemRepoModel* m_model = nullptr; + QWidget* m_mainWidget = nullptr; + QVBoxLayout* m_mainLayout = nullptr; + + // General info section + QLabel* m_nameLabel = nullptr; + LinkLabel* m_repoLinkLabel = nullptr; + QLabel* m_summaryLabel = nullptr; + + // Additional information + QLabel* m_addInfoTitleLabel = nullptr; + QLabel* m_addInfoTextLabel = nullptr; + QSpacerItem* m_addInfoSpacer = nullptr; + + // Included Gems + GemsSubWidget* m_includedGems = nullptr; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp new file mode 100644 index 0000000000..88ccee2636 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include +#include +#include + +namespace O3DE::ProjectManager +{ + GemRepoItemDelegate::GemRepoItemDelegate(QAbstractItemModel* model, QObject* parent) + : QStyledItemDelegate(parent) + , m_model(model) + { + m_refreshIcon = QIcon(":/Refresh.svg").pixmap(s_refreshIconSize, s_refreshIconSize); + m_editIcon = QIcon(":/Edit.svg").pixmap(s_iconSize, s_iconSize); + m_deleteIcon = QIcon(":/Delete.svg").pixmap(s_iconSize, s_iconSize); + } + + void GemRepoItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const + { + if (!modelIndex.isValid()) + { + return; + } + + QStyleOptionViewItem options(option); + initStyleOption(&options, modelIndex); + + painter->setRenderHint(QPainter::Antialiasing); + + QRect fullRect, itemRect, contentRect; + CalcRects(options, fullRect, itemRect, contentRect); + QRect buttonRect = CalcButtonRect(contentRect); + + QFont standardFont(options.font); + standardFont.setPixelSize(static_cast(s_fontSize)); + QFontMetrics standardFontMetrics(standardFont); + + painter->save(); + painter->setClipping(true); + painter->setClipRect(fullRect); + painter->setFont(standardFont); + painter->setPen(m_textColor); + + // Draw background + painter->fillRect(fullRect, m_backgroundColor); + + // Draw item background + const QColor itemBackgroundColor = options.state & QStyle::State_MouseOver ? m_itemBackgroundColor.lighter(120) : m_itemBackgroundColor; + painter->fillRect(itemRect, itemBackgroundColor); + + // Draw border + if (options.state & QStyle::State_Selected) + { + painter->save(); + QPen borderPen(m_borderColor); + borderPen.setWidth(s_borderWidth); + painter->setPen(borderPen); + painter->drawRect(itemRect); + + painter->restore(); + } + + // Repo enabled + DrawButton(painter, buttonRect, modelIndex); + + // Repo name + QString repoName = GemRepoModel::GetName(modelIndex); + repoName = QFontMetrics(standardFont).elidedText(repoName, Qt::TextElideMode::ElideRight, s_nameMaxWidth); + + QRect repoNameRect = GetTextRect(standardFont, repoName, s_fontSize); + int currentHorizontalOffset = buttonRect.left() + s_buttonWidth + s_buttonSpacing; + repoNameRect.moveTo(currentHorizontalOffset, contentRect.center().y() - repoNameRect.height() / 2); + repoNameRect = painter->boundingRect(repoNameRect, Qt::TextSingleLine, repoName); + + painter->drawText(repoNameRect, Qt::TextSingleLine, repoName); + + // Rem repo creator + QString repoCreator = GemRepoModel::GetCreator(modelIndex); + repoCreator = standardFontMetrics.elidedText(repoCreator, Qt::TextElideMode::ElideRight, s_creatorMaxWidth); + + QRect repoCreatorRect = GetTextRect(standardFont, repoCreator, s_fontSize); + currentHorizontalOffset += s_nameMaxWidth + s_contentSpacing; + repoCreatorRect.moveTo(currentHorizontalOffset, contentRect.center().y() - repoCreatorRect.height() / 2); + repoCreatorRect = painter->boundingRect(repoCreatorRect, Qt::TextSingleLine, repoCreator); + + painter->drawText(repoCreatorRect, Qt::TextSingleLine, repoCreator); + + // Repo update + QString repoUpdatedDate = GemRepoModel::GetLastUpdated(modelIndex).toString("dd/MM/yyyy hh:mmap"); + repoUpdatedDate = standardFontMetrics.elidedText(repoUpdatedDate, Qt::TextElideMode::ElideRight, s_updatedMaxWidth); + + QRect repoUpdatedDateRect = GetTextRect(standardFont, repoUpdatedDate, s_fontSize); + currentHorizontalOffset += s_creatorMaxWidth + s_contentSpacing; + repoUpdatedDateRect.moveTo(currentHorizontalOffset, contentRect.center().y() - repoUpdatedDateRect.height() / 2); + repoUpdatedDateRect = painter->boundingRect(repoUpdatedDateRect, Qt::TextSingleLine, repoUpdatedDate); + + painter->drawText(repoUpdatedDateRect, Qt::TextSingleLine, repoUpdatedDate); + + // Draw refresh button + painter->drawPixmap( + repoUpdatedDateRect.left() + repoUpdatedDateRect.width() + s_refreshIconSpacing, + contentRect.center().y() - s_refreshIconSize / 3, // Dividing size by 3 centers much better + m_refreshIcon); + + if (options.state & QStyle::State_MouseOver) + { + DrawEditButtons(painter, contentRect); + } + + painter->restore(); + } + + QSize GemRepoItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const + { + QStyleOptionViewItem options(option); + initStyleOption(&options, modelIndex); + + int marginsHorizontal = s_itemMargins.left() + s_itemMargins.right() + s_contentMargins.left() + s_contentMargins.right(); + return QSize(marginsHorizontal + s_buttonWidth + s_buttonSpacing + s_nameMaxWidth + s_creatorMaxWidth + s_updatedMaxWidth + s_contentSpacing * 3, s_height); + } + + bool GemRepoItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) + { + if (!modelIndex.isValid()) + { + return false; + } + + if (event->type() == QEvent::KeyPress) + { + auto keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Space) + { + const bool isAdded = GemRepoModel::IsEnabled(modelIndex); + GemRepoModel::SetEnabled(*model, modelIndex, !isAdded); + return true; + } + } + + if (event->type() == QEvent::MouseButtonPress) + { + QMouseEvent* mouseEvent = static_cast(event); + + QRect fullRect, itemRect, contentRect; + CalcRects(option, fullRect, itemRect, contentRect); + const QRect buttonRect = CalcButtonRect(contentRect); + + if (buttonRect.contains(mouseEvent->pos())) + { + const bool isAdded = GemRepoModel::IsEnabled(modelIndex); + GemRepoModel::SetEnabled(*model, modelIndex, !isAdded); + return true; + } + } + + return QStyledItemDelegate::editorEvent(event, model, option, modelIndex); + } + + void GemRepoItemDelegate::CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const + { + outFullRect = QRect(option.rect); + outItemRect = QRect(outFullRect.adjusted(s_itemMargins.left(), s_itemMargins.top(), -s_itemMargins.right(), -s_itemMargins.bottom())); + outContentRect = QRect(outItemRect.adjusted(s_contentMargins.left(), s_contentMargins.top(), -s_contentMargins.right(), -s_contentMargins.bottom())); + } + + QRect GemRepoItemDelegate::GetTextRect(QFont& font, const QString& text, qreal fontSize) const + { + font.setPixelSize(static_cast(fontSize)); + return QFontMetrics(font).boundingRect(text); + } + + QRect GemRepoItemDelegate::CalcButtonRect(const QRect& contentRect) const + { + const QPoint topLeft = QPoint(contentRect.left(), contentRect.top() + contentRect.height() / 2 - s_buttonHeight / 2); + const QSize size = QSize(s_buttonWidth, s_buttonHeight); + return QRect(topLeft, size); + } + + void GemRepoItemDelegate::DrawButton(QPainter* painter, const QRect& buttonRect, const QModelIndex& modelIndex) const + { + painter->save(); + QPoint circleCenter; + + const bool isEnabled = GemRepoModel::IsEnabled(modelIndex); + if (isEnabled) + { + painter->setBrush(m_buttonEnabledColor); + painter->setPen(m_buttonEnabledColor); + + circleCenter = buttonRect.center() + QPoint(buttonRect.width() / 2 - s_buttonBorderRadius + 1, 1); + } + else + { + circleCenter = buttonRect.center() + QPoint(-buttonRect.width() / 2 + s_buttonBorderRadius + 1, 1); + } + + // Rounded rect + painter->drawRoundedRect(buttonRect, s_buttonBorderRadius, s_buttonBorderRadius); + + // Circle + painter->setBrush(m_textColor); + painter->drawEllipse(circleCenter, s_buttonCircleRadius, s_buttonCircleRadius); + + painter->restore(); + } + + void GemRepoItemDelegate::DrawEditButtons(QPainter* painter, const QRect& contentRect) const + { + painter->drawPixmap(contentRect.right() - s_iconSize * 2 - s_iconSpacing, contentRect.center().y() - s_iconSize / 2, m_editIcon); + painter->drawPixmap(contentRect.right() - s_iconSize, contentRect.center().y() - s_iconSize / 2, m_deleteIcon); + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.h new file mode 100644 index 0000000000..08d1fdffae --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.h @@ -0,0 +1,82 @@ +/* + * 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 +#endif + +QT_FORWARD_DECLARE_CLASS(QAbstractItemModel) +QT_FORWARD_DECLARE_CLASS(QEvent) + +namespace O3DE::ProjectManager +{ + class GemRepoItemDelegate + : public QStyledItemDelegate + { + Q_OBJECT // AUTOMOC + + public: + explicit GemRepoItemDelegate(QAbstractItemModel* model, QObject* parent = nullptr); + ~GemRepoItemDelegate() = default; + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override; + bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override; + + // Colors + const QColor m_textColor = QColor("#FFFFFF"); + const QColor m_backgroundColor = QColor("#333333"); // Outside of the actual repo item + const QColor m_itemBackgroundColor = QColor("#404040"); // Background color of the repo item + const QColor m_borderColor = QColor("#1E70EB"); + const QColor m_buttonEnabledColor = QColor("#1E70EB"); + + // Item + inline constexpr static int s_height = 72; // Repo item total height + inline constexpr static qreal s_fontSize = 12.0; + + // Margin and borders + inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/0, /*top=*/8, /*right=*/60, /*bottom=*/8); // Item border distances + inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/20, /*right=*/20, /*bottom=*/20); // Distances of the elements within an item to the item borders + inline constexpr static int s_borderWidth = 4; + + // Content + inline constexpr static int s_contentSpacing = 5; + inline constexpr static int s_nameMaxWidth = 145; + inline constexpr static int s_creatorMaxWidth = 115; + inline constexpr static int s_updatedMaxWidth = 125; + + // Button + inline constexpr static int s_buttonWidth = 32; + inline constexpr static int s_buttonHeight = 16; + inline constexpr static int s_buttonBorderRadius = 8; + inline constexpr static int s_buttonCircleRadius = s_buttonBorderRadius - 2; + inline constexpr static int s_buttonSpacing = 20; + + // Icon + inline constexpr static int s_iconSize = 24; + inline constexpr static int s_iconSpacing = 16; + inline constexpr static int s_refreshIconSize = 14; + inline constexpr static int s_refreshIconSpacing = 10; + + protected: + void CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const; + QRect GetTextRect(QFont& font, const QString& text, qreal fontSize) const; + QRect CalcButtonRect(const QRect& contentRect) const; + void DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const; + void DrawEditButtons(QPainter* painter, const QRect& contentRect) const; + + QAbstractItemModel* m_model = nullptr; + + QPixmap m_refreshIcon; + QPixmap m_editIcon; + QPixmap m_deleteIcon; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp new file mode 100644 index 0000000000..54d5b337e5 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp @@ -0,0 +1,24 @@ +/* + * 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 O3DE::ProjectManager +{ + GemRepoListView::GemRepoListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent) + : QListView(parent) + { + setObjectName("gemRepoListView"); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + + setModel(model); + setSelectionModel(selectionModel); + setItemDelegate(new GemRepoItemDelegate(model, this)); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h new file mode 100644 index 0000000000..b71b49f390 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QAbstractItemModel) + +namespace O3DE::ProjectManager +{ + class GemRepoListView + : public QListView + { + Q_OBJECT // AUTOMOC + + public: + explicit GemRepoListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent = nullptr); + ~GemRepoListView() = default; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp new file mode 100644 index 0000000000..61ac6dc8a3 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp @@ -0,0 +1,155 @@ +/* + * 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 O3DE::ProjectManager +{ + GemRepoModel::GemRepoModel(QObject* parent) + : QStandardItemModel(parent) + { + m_selectionModel = new QItemSelectionModel(this, parent); + m_gemModel = new GemModel(this); + } + + QItemSelectionModel* GemRepoModel::GetSelectionModel() const + { + return m_selectionModel; + } + + void GemRepoModel::AddGemRepo(const GemRepoInfo& gemRepoInfo) + { + QStandardItem* item = new QStandardItem(); + + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + + item->setData(gemRepoInfo.m_name, RoleName); + item->setData(gemRepoInfo.m_creator, RoleCreator); + item->setData(gemRepoInfo.m_summary, RoleSummary); + item->setData(gemRepoInfo.m_isEnabled, RoleIsEnabled); + item->setData(gemRepoInfo.m_directoryLink, RoleDirectoryLink); + item->setData(gemRepoInfo.m_repoLink, RoleRepoLink); + item->setData(gemRepoInfo.m_lastUpdated, RoleLastUpdated); + item->setData(gemRepoInfo.m_path, RolePath); + item->setData(gemRepoInfo.m_additionalInfo, RoleAdditionalInfo); + item->setData(gemRepoInfo.m_includedGemPaths, RoleIncludedGems); + + appendRow(item); + + QVector includedGemInfos = GetIncludedGemInfos(item->index()); + + for (const GemInfo& gemInfo : includedGemInfos) + { + m_gemModel->AddGem(gemInfo); + } + } + + void GemRepoModel::Clear() + { + clear(); + } + + QString GemRepoModel::GetName(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleName).toString(); + } + + QString GemRepoModel::GetCreator(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleCreator).toString(); + } + + QString GemRepoModel::GetSummary(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleSummary).toString(); + } + + QString GemRepoModel::GetAdditionalInfo(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleAdditionalInfo).toString(); + } + + QString GemRepoModel::GetDirectoryLink(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleDirectoryLink).toString(); + } + + QString GemRepoModel::GetRepoLink(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleRepoLink).toString(); + } + + QDateTime GemRepoModel::GetLastUpdated(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleLastUpdated).toDateTime(); + } + + QString GemRepoModel::GetPath(const QModelIndex& modelIndex) + { + return modelIndex.data(RolePath).toString(); + } + + QStringList GemRepoModel::GetIncludedGemPaths(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleIncludedGems).toStringList(); + } + + QStringList GemRepoModel::GetIncludedGemNames(const QModelIndex& modelIndex) + { + QStringList gemNames; + QVector gemInfos = GetIncludedGemInfos(modelIndex); + + for (const GemInfo& gemInfo : gemInfos) + { + gemNames.append(gemInfo.m_displayName); + } + + return gemNames; + } + + QVector GemRepoModel::GetIncludedGemInfos(const QModelIndex& modelIndex) + { + QVector allGemInfos; + QStringList repoGemPaths = GetIncludedGemPaths(modelIndex); + + for (const QString& gemPath : repoGemPaths) + { + AZ::Outcome gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(gemPath); + if (gemInfoResult.IsSuccess()) + { + allGemInfos.append(gemInfoResult.GetValue()); + } + else + { + QMessageBox::critical(nullptr, tr("Gem Not Found"), tr("Cannot find info for gem %1.").arg(gemPath)); + } + } + + return allGemInfos; + } + + bool GemRepoModel::IsEnabled(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleIsEnabled).toBool(); + } + + void GemRepoModel::SetEnabled(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isEnabled) + { + model.setData(modelIndex, isEnabled, RoleIsEnabled); + } + + bool GemRepoModel::HasAdditionalInfo(const QModelIndex& modelIndex) + { + return !modelIndex.data(RoleAdditionalInfo).toString().isEmpty(); + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h new file mode 100644 index 0000000000..ad139bc12b --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QItemSelectionModel) + +namespace O3DE::ProjectManager +{ + class GemRepoModel + : public QStandardItemModel + { + Q_OBJECT // AUTOMOC + + public: + explicit GemRepoModel(QObject* parent = nullptr); + QItemSelectionModel* GetSelectionModel() const; + + void AddGemRepo(const GemRepoInfo& gemInfo); + void Clear(); + + static QString GetName(const QModelIndex& modelIndex); + static QString GetCreator(const QModelIndex& modelIndex); + static QString GetSummary(const QModelIndex& modelIndex); + static QString GetAdditionalInfo(const QModelIndex& modelIndex); + static QString GetDirectoryLink(const QModelIndex& modelIndex); + static QString GetRepoLink(const QModelIndex& modelIndex); + static QDateTime GetLastUpdated(const QModelIndex& modelIndex); + static QString GetPath(const QModelIndex& modelIndex); + + static QStringList GetIncludedGemPaths(const QModelIndex& modelIndex); + static QStringList GetIncludedGemNames(const QModelIndex& modelIndex); + static QVector GetIncludedGemInfos(const QModelIndex& modelIndex); + + static bool IsEnabled(const QModelIndex& modelIndex); + static void SetEnabled(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isEnabled); + static bool HasAdditionalInfo(const QModelIndex& modelIndex); + + private: + enum UserRole + { + RoleName = Qt::UserRole, + RoleCreator, + RoleSummary, + RoleIsEnabled, + RoleDirectoryLink, + RoleRepoLink, + RoleLastUpdated, + RolePath, + RoleAdditionalInfo, + RoleIncludedGems, + }; + + QItemSelectionModel* m_selectionModel = nullptr; + + GemModel* m_gemModel = nullptr; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp new file mode 100644 index 0000000000..c0b17904f8 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + GemRepoScreen::GemRepoScreen(QWidget* parent) + : ScreenWidget(parent) + { + m_gemRepoModel = new GemRepoModel(this); + + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setMargin(0); + vLayout->setSpacing(0); + setLayout(vLayout); + + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setMargin(0); + hLayout->setSpacing(0); + vLayout->addLayout(hLayout); + + hLayout->addSpacing(60); + + QVBoxLayout* middleVLayout = new QVBoxLayout(); + middleVLayout->setMargin(0); + middleVLayout->setSpacing(0); + + middleVLayout->addSpacing(30); + + QHBoxLayout* topMiddleHLayout = new QHBoxLayout(); + topMiddleHLayout->setMargin(0); + topMiddleHLayout->setSpacing(0); + + m_lastAllUpdateLabel = new QLabel(tr("Last Updated: Never"), this); + m_lastAllUpdateLabel->setObjectName("gemRepoHeaderLabel"); + topMiddleHLayout->addWidget(m_lastAllUpdateLabel); + + topMiddleHLayout->addSpacing(20); + + m_AllUpdateButton = new QPushButton(QIcon(":/Refresh.svg"), tr("Update All"), this); + m_AllUpdateButton->setObjectName("gemRepoHeaderRefreshButton"); + topMiddleHLayout->addWidget(m_AllUpdateButton); + + topMiddleHLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + m_AddRepoButton = new QPushButton(tr("Add Repository"), this); + m_AddRepoButton->setObjectName("gemRepoHeaderAddButton"); + topMiddleHLayout->addWidget(m_AddRepoButton); + + middleVLayout->addLayout(topMiddleHLayout); + + middleVLayout->addSpacing(30); + + // Create a QTableWidget just for its header + // Using a seperate model allows the setup of a header exactly as needed + m_gemRepoHeaderTable = new QTableWidget(this); + m_gemRepoHeaderTable->setObjectName("gemRepoHeaderTable"); + m_gemRepoListHeader = m_gemRepoHeaderTable->horizontalHeader(); + m_gemRepoListHeader->setObjectName("gemRepoListHeader"); + m_gemRepoListHeader->setSectionResizeMode(QHeaderView::ResizeMode::Fixed); + + // Insert columns so the header labels will show up + m_gemRepoHeaderTable->insertColumn(0); + m_gemRepoHeaderTable->insertColumn(1); + m_gemRepoHeaderTable->insertColumn(2); + m_gemRepoHeaderTable->insertColumn(3); + m_gemRepoHeaderTable->setHorizontalHeaderLabels({ tr("Enabled"), tr("Repository Name"), tr("Creator"), tr("Updated") }); + + const int headerExtraMargin = 10; + m_gemRepoListHeader->resizeSection(0, GemRepoItemDelegate::s_buttonWidth + GemRepoItemDelegate::s_buttonSpacing - 3); + m_gemRepoListHeader->resizeSection(1, GemRepoItemDelegate::s_nameMaxWidth + GemRepoItemDelegate::s_contentSpacing - headerExtraMargin); + m_gemRepoListHeader->resizeSection(2, GemRepoItemDelegate::s_creatorMaxWidth + GemRepoItemDelegate::s_contentSpacing - headerExtraMargin); + m_gemRepoListHeader->resizeSection(3, GemRepoItemDelegate::s_updatedMaxWidth + GemRepoItemDelegate::s_contentSpacing - headerExtraMargin); + + // Required to set stylesheet in code as it will not be respected if set in qss + m_gemRepoHeaderTable->horizontalHeader()->setStyleSheet("QHeaderView::section { background-color:transparent; color:white; font-size:12px; text-align:left; border-style:none; }"); + middleVLayout->addWidget(m_gemRepoHeaderTable); + + m_gemRepoListView = new GemRepoListView(m_gemRepoModel, m_gemRepoModel->GetSelectionModel(), this); + middleVLayout->addWidget(m_gemRepoListView); + + hLayout->addLayout(middleVLayout); + + m_gemRepoInspector = new GemRepoInspector(m_gemRepoModel, this); + m_gemRepoInspector->setFixedWidth(240); + hLayout->addWidget(m_gemRepoInspector); + + Reinit(); + } + + void GemRepoScreen::Reinit() + { + m_gemRepoModel->clear(); + FillModel(); + + // Select the first entry after everything got correctly sized + QTimer::singleShot(200, [=]{ + QModelIndex firstModelIndex = m_gemRepoListView->model()->index(0,0); + m_gemRepoListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect); + }); + } + + void GemRepoScreen::FillModel() + { + AZ::Outcome, AZStd::string> allGemRepoInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoInfos(); + if (allGemRepoInfosResult.IsSuccess()) + { + // Add all available repos to the model + const QVector allGemRepoInfos = allGemRepoInfosResult.GetValue(); + for (const GemRepoInfo& gemRepoInfo : allGemRepoInfos) + { + m_gemRepoModel->AddGemRepo(gemRepoInfo); + } + } + else + { + QMessageBox::critical(this, tr("Operation failed"), tr("Cannot retrieve gem repos for engine.\n\nError:\n%2").arg(allGemRepoInfosResult.GetError().c_str())); + } + } + + ProjectManagerScreen GemRepoScreen::GetScreenEnum() + { + return ProjectManagerScreen::GemRepos; + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h new file mode 100644 index 0000000000..f7d943fc2a --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QPushButton) +QT_FORWARD_DECLARE_CLASS(QHeaderView) +QT_FORWARD_DECLARE_CLASS(QTableWidget) + +namespace O3DE::ProjectManager +{ + QT_FORWARD_DECLARE_CLASS(GemRepoInspector) + QT_FORWARD_DECLARE_CLASS(GemRepoListView) + QT_FORWARD_DECLARE_CLASS(GemRepoModel) + + class GemRepoScreen + : public ScreenWidget + { + public: + explicit GemRepoScreen(QWidget* parent = nullptr); + ~GemRepoScreen() = default; + ProjectManagerScreen GetScreenEnum() override; + + void Reinit(); + + GemRepoModel* GetGemRepoModel() const { return m_gemRepoModel; } + + private: + void FillModel(); + + QTableWidget* m_gemRepoHeaderTable = nullptr; + QHeaderView* m_gemRepoListHeader = nullptr; + GemRepoListView* m_gemRepoListView = nullptr; + GemRepoInspector* m_gemRepoInspector = nullptr; + GemRepoModel* m_gemRepoModel = nullptr; + + QLabel* m_lastAllUpdateLabel; + QPushButton* m_AllUpdateButton; + QPushButton* m_AddRepoButton; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp b/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp new file mode 100644 index 0000000000..eb24008eb1 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp @@ -0,0 +1,45 @@ +/* + * 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 O3DE::ProjectManager +{ + GemsSubWidget::GemsSubWidget(QWidget* parent) + : QWidget(parent) + { + m_layout = new QVBoxLayout(); + m_layout->setAlignment(Qt::AlignTop); + m_layout->setMargin(0); + setLayout(m_layout); + + m_titleLabel = new QLabel(); + m_titleLabel->setObjectName("gemSubWidgetTitleLabel"); + m_layout->addWidget(m_titleLabel); + + m_textLabel = new QLabel(); + m_textLabel->setObjectName("gemSubWidgetTextLabel"); + m_textLabel->setWordWrap(true); + m_layout->addWidget(m_textLabel); + + m_tagWidget = new TagContainerWidget(); + m_layout->addWidget(m_tagWidget); + } + + void GemsSubWidget::Update(const QString& title, const QString& text, const QStringList& gemNames) + { + m_titleLabel->setText(title); + m_textLabel->setText(text); + m_tagWidget->Update(gemNames); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemsSubWidget.h b/Code/Tools/ProjectManager/Source/GemsSubWidget.h new file mode 100644 index 0000000000..1b10ec8861 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemsSubWidget.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QVBoxLayout) +QT_FORWARD_DECLARE_CLASS(QLabel) + +namespace O3DE::ProjectManager +{ + // Title, description and tag widget container used for the depending and conflicting gems + class GemsSubWidget + : public QWidget + { + public: + GemsSubWidget(QWidget* parent = nullptr); + void Update(const QString& title, const QString& text, const QStringList& gemNames); + + private: + QLabel* m_titleLabel = nullptr; + QLabel* m_textLabel = nullptr; + QVBoxLayout* m_layout = nullptr; + TagContainerWidget* m_tagWidget = nullptr; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/LinkWidget.cpp b/Code/Tools/ProjectManager/Source/LinkWidget.cpp index ccee7e8ec6..9c8c78ed37 100644 --- a/Code/Tools/ProjectManager/Source/LinkWidget.cpp +++ b/Code/Tools/ProjectManager/Source/LinkWidget.cpp @@ -14,9 +14,10 @@ namespace O3DE::ProjectManager { - LinkLabel::LinkLabel(const QString& text, const QUrl& url, QWidget* parent) + LinkLabel::LinkLabel(const QString& text, const QUrl& url, int fontSize, QWidget* parent) : QLabel(text, parent) , m_url(url) + , m_fontSize(fontSize) { SetDefaultStyle(); } @@ -33,7 +34,7 @@ namespace O3DE::ProjectManager void LinkLabel::enterEvent([[maybe_unused]] QEvent* event) { - setStyleSheet("font-size: 10px; color: #94D2FF; text-decoration: underline;"); + setStyleSheet(QString("font-size: %1px; color: #94D2FF; text-decoration: underline;").arg(m_fontSize)); } void LinkLabel::leaveEvent([[maybe_unused]] QEvent* event) @@ -48,6 +49,6 @@ namespace O3DE::ProjectManager void LinkLabel::SetDefaultStyle() { - setStyleSheet("font-size: 10px; color: #94D2FF;"); + setStyleSheet(QString("font-size: %1px; color: #94D2FF;").arg(m_fontSize)); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/LinkWidget.h b/Code/Tools/ProjectManager/Source/LinkWidget.h index a50007cf1f..eb0b9bb528 100644 --- a/Code/Tools/ProjectManager/Source/LinkWidget.h +++ b/Code/Tools/ProjectManager/Source/LinkWidget.h @@ -25,7 +25,7 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - LinkLabel(const QString& text = {}, const QUrl& url = {}, QWidget* parent = nullptr); + LinkLabel(const QString& text = {}, const QUrl& url = {}, int fontSize = 10, QWidget* parent = nullptr); void SetUrl(const QUrl& url); @@ -40,5 +40,6 @@ namespace O3DE::ProjectManager private: QUrl m_url; + int m_fontSize; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp index 2fe1c6db15..c6a6b20a1d 100644 --- a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp @@ -8,8 +8,15 @@ #include #include +#include +#include #include +#include +#include +#include +#include +#include //#define MOCK_BUILD_PROJECT true @@ -67,4 +74,176 @@ namespace O3DE::ProjectManager { AZ_TracePrintf("Project Manager", error.toStdString().c_str()); } + + AZ::Outcome ProjectBuilderWorker::BuildProjectForPlatform() + { + // Check if we are trying to cancel task + if (QThread::currentThread()->isInterruptionRequested()) + { + QStringToAZTracePrint(BuildCancelled); + return AZ::Failure(BuildCancelled); + } + + QFile logFile(GetLogFilePath()); + if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) + { + QString error = tr("Failed to open log file."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + EngineInfo engineInfo; + + AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); + if (engineInfoResult.IsSuccess()) + { + engineInfo = engineInfoResult.GetValue(); + } + else + { + QString error = tr("Failed to get engine info."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + QTextStream logStream(&logFile); + if (QThread::currentThread()->isInterruptionRequested()) + { + logFile.close(); + QStringToAZTracePrint(BuildCancelled); + return AZ::Failure(BuildCancelled); + } + + // Show some kind of progress with very approximate estimates + UpdateProgress(++m_progressEstimate); + + auto currentEnvironmentRequest = ProjectUtils::GetCommandLineProcessEnvironment(); + if (!currentEnvironmentRequest.IsSuccess()) + { + QStringToAZTracePrint(currentEnvironmentRequest.GetError()); + return AZ::Failure(currentEnvironmentRequest.GetError()); + } + QProcessEnvironment currentEnvironment = currentEnvironmentRequest.GetValue(); + + m_configProjectProcess = new QProcess(this); + m_configProjectProcess->setProcessChannelMode(QProcess::MergedChannels); + m_configProjectProcess->setWorkingDirectory(m_projectInfo.m_path); + m_configProjectProcess->setProcessEnvironment(currentEnvironment); + + auto cmakeGenerateArgumentsResult = ConstructCmakeGenerateProjectArguments(engineInfo.m_thirdPartyPath); + if (!cmakeGenerateArgumentsResult.IsSuccess()) + { + QStringToAZTracePrint(cmakeGenerateArgumentsResult.GetError()); + return AZ::Failure(cmakeGenerateArgumentsResult.GetError()); + } + auto cmakeGenerateArguments = cmakeGenerateArgumentsResult.GetValue(); + m_configProjectProcess->start(cmakeGenerateArguments.front(), cmakeGenerateArguments.mid(1)); + if (!m_configProjectProcess->waitForStarted()) + { + QString error = tr("Configuring project failed to start."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + bool containsGeneratingDone = false; + while (m_configProjectProcess->waitForReadyRead(MaxBuildTimeMSecs)) + { + QString configOutput = m_configProjectProcess->readAllStandardOutput(); + + if (configOutput.contains("Generating done")) + { + containsGeneratingDone = true; + } + + logStream << configOutput; + logStream.flush(); + + UpdateProgress(qMin(++m_progressEstimate, 19)); + + if (QThread::currentThread()->isInterruptionRequested()) + { + logFile.close(); + m_configProjectProcess->close(); + QStringToAZTracePrint(BuildCancelled); + return AZ::Failure(BuildCancelled); + } + } + + if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit || m_configProjectProcess->exitCode() != 0 || + !containsGeneratingDone) + { + QString error = tr("Configuring project failed. See log for details."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + UpdateProgress(++m_progressEstimate); + + m_buildProjectProcess = new QProcess(this); + m_buildProjectProcess->setProcessChannelMode(QProcess::MergedChannels); + m_buildProjectProcess->setWorkingDirectory(m_projectInfo.m_path); + m_buildProjectProcess->setProcessEnvironment(currentEnvironment); + + auto cmakeBuildArgumentsResult = ConstructCmakeBuildCommandArguments(); + if (!cmakeBuildArgumentsResult.IsSuccess()) + { + QStringToAZTracePrint(cmakeBuildArgumentsResult.GetError()); + return AZ::Failure(cmakeBuildArgumentsResult.GetError()); + } + auto cmakeBuildArguments = cmakeBuildArgumentsResult.GetValue(); + + m_buildProjectProcess->start(cmakeBuildArguments.front(), cmakeBuildArguments.mid(1)); + if (!m_buildProjectProcess->waitForStarted()) + { + QString error = tr("Building project failed to start."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + // There are a lot of steps when building so estimate around 800 more steps ((100 - 20) * 10) remaining + m_progressEstimate = 200; + while (m_buildProjectProcess->waitForReadyRead(MaxBuildTimeMSecs)) + { + logStream << m_buildProjectProcess->readAllStandardOutput(); + logStream.flush(); + + // Show 1% progress for every 10 steps completed + UpdateProgress(qMin(++m_progressEstimate / 10, 99)); + + if (QThread::currentThread()->isInterruptionRequested()) + { + // QProcess is unable to kill its child processes so we need to ask the operating system to do that for us + auto killProcessArgumentsResult = ConstructKillProcessCommandArguments(QString::number(m_buildProjectProcess->processId())); + if (!killProcessArgumentsResult.IsSuccess()) + { + return AZ::Failure(killProcessArgumentsResult.GetError()); + } + auto killProcessArguments = killProcessArgumentsResult.GetValue(); + + + QProcess killBuildProcess; + + + killBuildProcess.setProcessChannelMode(QProcess::MergedChannels); + killBuildProcess.start(killProcessArguments.front(), killProcessArguments.mid(1)); + killBuildProcess.waitForFinished(); + + logStream << "Killing Project Build."; + logStream << killBuildProcess.readAllStandardOutput(); + m_buildProjectProcess->kill(); + logFile.close(); + QStringToAZTracePrint(BuildCancelled); + return AZ::Failure(BuildCancelled); + } + } + + if (m_buildProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit || m_buildProjectProcess->exitCode() != 0) + { + QString error = tr("Building project failed. See log for details."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + return AZ::Success(); + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h index f42c87f47b..94c4a6bd52 100644 --- a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h +++ b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h @@ -12,6 +12,7 @@ #include #include +#include #endif QT_FORWARD_DECLARE_CLASS(QProcess) @@ -44,6 +45,12 @@ namespace O3DE::ProjectManager AZ::Outcome BuildProjectForPlatform(); void QStringToAZTracePrint(const QString& error); + // Command line argument builders + AZ::Outcome ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const; + AZ::Outcome ConstructCmakeBuildCommandArguments() const; + AZ::Outcome ConstructKillProcessCommandArguments(const QString& pidToKill) const; + + QProcess* m_configProjectProcess = nullptr; QProcess* m_buildProjectProcess = nullptr; ProjectInfo m_projectInfo; diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h b/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h index 8ab5128741..264515652f 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h @@ -14,6 +14,7 @@ namespace O3DE::ProjectManager inline constexpr static int ProjectPreviewImageWidth = 210; inline constexpr static int ProjectPreviewImageHeight = 280; inline constexpr static int ProjectTemplateImageWidth = 92; + inline constexpr static int ProjectCommandLineTimeoutSeconds = 30; static const QString ProjectBuildDirectoryName = "build"; extern const QString ProjectBuildPathPostfix; @@ -21,4 +22,8 @@ namespace O3DE::ProjectManager static const QString ProjectBuildErrorLogName = "CMakeProjectBuildError.log"; static const QString ProjectCacheDirectoryName = "Cache"; static const QString ProjectPreviewImagePath = "preview.png"; + + static const QString ProjectCMakeCommand = "cmake"; + static const QString ProjectCMakeBuildTargetEditor = "Editor"; + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp index a723ccb917..d2f2d969f1 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp @@ -22,7 +22,7 @@ namespace O3DE::ProjectManager QVector screenEnums = { ProjectManagerScreen::Projects, - ProjectManagerScreen::EngineSettings, + ProjectManagerScreen::Engine, ProjectManagerScreen::CreateProject, ProjectManagerScreen::UpdateProject }; diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp index 84d731832e..81eb70391d 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp @@ -22,6 +22,8 @@ #include #include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils @@ -507,5 +509,33 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::Invalid; } + + AZ::Outcome ExecuteCommandResult( + const QString& cmd, + const QStringList& arguments, + const QProcessEnvironment& processEnv, + int commandTimeoutSeconds /*= ProjectCommandLineTimeoutSeconds*/) + { + QProcess execProcess; + execProcess.setProcessEnvironment(processEnv); + execProcess.setProcessChannelMode(QProcess::MergedChannels); + execProcess.start(cmd, arguments); + if (!execProcess.waitForStarted()) + { + return AZ::Failure(QObject::tr("Unable to start process for command '%1'").arg(cmd)); + } + + if (!execProcess.waitForFinished(commandTimeoutSeconds * 1000 /* Milliseconds per second */)) + { + return AZ::Failure(QObject::tr("Process for command '%1' timed out at %2 seconds").arg(cmd).arg(commandTimeoutSeconds)); + } + int resultCode = execProcess.exitCode(); + if (resultCode != 0) + { + return AZ::Failure(QObject::tr("Process for command '%1' failed (result code %2").arg(cmd).arg(resultCode)); + } + QString resultOutput = execProcess.readAllStandardOutput(); + return AZ::Success(resultOutput); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h index f1050531d4..0866b57923 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.h +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -9,8 +9,11 @@ #include #include +#include #include +#include + #include namespace O3DE::ProjectManager @@ -28,8 +31,17 @@ namespace O3DE::ProjectManager bool ReplaceProjectFile(const QString& origFile, const QString& newFile, QWidget* parent = nullptr, bool interactive = true); bool FindSupportedCompiler(QWidget* parent = nullptr); - AZ::Outcome FindSupportedCompilerForPlatform(); + AZ::Outcome FindSupportedCompilerForPlatform(); ProjectManagerScreen GetProjectManagerScreen(const QString& screen); + + AZ::Outcome ExecuteCommandResult( + const QString& cmd, + const QStringList& arguments, + const QProcessEnvironment& processEnv, + int commandTimeoutSeconds = ProjectCommandLineTimeoutSeconds); + + AZ::Outcome GetCommandLineProcessEnvironment(); + } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 18901946f3..284ed9dcec 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -339,7 +339,7 @@ namespace O3DE::ProjectManager { for (auto engine : allEngines) { - AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"])); + AZ::IO::FixedMaxPath enginePath(Py_To_String(engine)); if (enginePath.Compare(m_enginePath) == 0) { return; @@ -675,6 +675,14 @@ namespace O3DE::ProjectManager } } + if (data.contains("dependencies")) + { + for (auto dependency : data["dependencies"]) + { + gemInfo.m_dependencies.push_back(Py_To_String(dependency)); + } + } + QString gemType = Py_To_String_Optional(data, "type", ""); if (gemType == "Asset") { @@ -912,4 +920,53 @@ namespace O3DE::ProjectManager return AZ::Success(AZStd::move(templates)); } } + + GemRepoInfo PythonBindings::GemRepoInfoFromPath(pybind11::handle path, pybind11::handle pyEnginePath) + { + /* Placeholder Logic */ + (void)path; + (void)pyEnginePath; + + return GemRepoInfo(); + } + +//#define MOCK_GEM_REPO_INFO true + + AZ::Outcome, AZStd::string> PythonBindings::GetAllGemRepoInfos() + { + QVector gemRepos; + +#ifndef MOCK_GEM_REPO_INFO + auto result = ExecuteWithLockErrorHandling( + [&] + { + /* Placeholder Logic, o3de scripts need method added + * + for (auto path : m_manifest.attr("get_gem_repos")()) + { + gemRepos.push_back(GemRepoInfoFromPath(path, pybind11::none())); + } + * + */ + }); + if (!result.IsSuccess()) + { + return AZ::Failure(result.GetError().c_str()); + } +#else + GemRepoInfo mockJohnRepo("JohnCreates", "John Smith", QDateTime(QDate(2021, 8, 31), QTime(11, 57)), true); + mockJohnRepo.m_summary = "John's Summary. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sollicitudin dapibus urna"; + mockJohnRepo.m_repoLink = "https://github.com/o3de/o3de"; + mockJohnRepo.m_additionalInfo = "John's additional info. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sollicitu."; + gemRepos.push_back(mockJohnRepo); + + GemRepoInfo mockJaneRepo("JanesGems", "Jane Doe", QDateTime(QDate(2021, 9, 10), QTime(18, 23)), false); + mockJaneRepo.m_summary = "Jane's Summary."; + mockJaneRepo.m_repoLink = "https://github.com/o3de/o3de.org"; + gemRepos.push_back(mockJaneRepo); +#endif // MOCK_GEM_REPO_INFO + + std::sort(gemRepos.begin(), gemRepos.end()); + return AZ::Success(AZStd::move(gemRepos)); + } } diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index 8482ac56e7..3b766c3797 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -56,12 +56,16 @@ namespace O3DE::ProjectManager // ProjectTemplate AZ::Outcome> GetProjectTemplates(const QString& projectPath = {}) override; + // Gem Repos + AZ::Outcome, AZStd::string> GetAllGemRepoInfos() override; + private: AZ_DISABLE_COPY_MOVE(PythonBindings); AZ::Outcome ExecuteWithLockErrorHandling(AZStd::function executionCallback); bool ExecuteWithLock(AZStd::function executionCallback); GemInfo GemInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath); + GemRepoInfo GemRepoInfoFromPath(pybind11::handle path, pybind11::handle pyEnginePath); ProjectInfo ProjectInfoFromPath(pybind11::handle path); ProjectTemplateInfo ProjectTemplateInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath); bool RegisterThisEngine(); diff --git a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h index ca14a54630..ccf217d25b 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h +++ b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -55,15 +56,16 @@ namespace O3DE::ProjectManager // Gems /** - * Get info about a Gem - * @param path the absolute path to the Gem + * Get info about a Gem. + * @param path The absolute path to the Gem + * @param projectPath (Optional) The absolute path to the Gem project * @return an outcome with GemInfo on success */ virtual AZ::Outcome GetGemInfo(const QString& path, const QString& projectPath = {}) = 0; /** * Get all available gem infos. This concatenates gems registered by the engine and the project. - * @param path The absolute path to the project. + * @param projectPath The absolute path to the project. * @return A list of gem infos. */ virtual AZ::Outcome, AZStd::string> GetAllGemInfos(const QString& projectPath) = 0; @@ -155,6 +157,14 @@ namespace O3DE::ProjectManager * @return an outcome with ProjectTemplateInfos on success */ virtual AZ::Outcome> GetProjectTemplates(const QString& projectPath = {}) = 0; + + // Gem Repos + + /** + * Get all available gem repo infos. Gathers all repos registered with the engine. + * @return A list of gem repo infos. + */ + virtual AZ::Outcome, AZStd::string> GetAllGemRepoInfos() = 0; }; using PythonBindingsInterface = AZ::Interface; diff --git a/Code/Tools/ProjectManager/Source/ScreenDefs.h b/Code/Tools/ProjectManager/Source/ScreenDefs.h index 97ebe19751..2ced8c08c9 100644 --- a/Code/Tools/ProjectManager/Source/ScreenDefs.h +++ b/Code/Tools/ProjectManager/Source/ScreenDefs.h @@ -23,7 +23,9 @@ namespace O3DE::ProjectManager Projects, UpdateProject, UpdateProjectSettings, - EngineSettings + Engine, + EngineSettings, + GemRepos }; static QHash s_ProjectManagerStringNames = { @@ -34,7 +36,9 @@ namespace O3DE::ProjectManager { "Projects", ProjectManagerScreen::Projects}, { "UpdateProject", ProjectManagerScreen::UpdateProject}, { "UpdateProjectSettings", ProjectManagerScreen::UpdateProjectSettings}, - { "EngineSettings", ProjectManagerScreen::EngineSettings} + { "Engine", ProjectManagerScreen::Engine}, + { "EngineSettings", ProjectManagerScreen::EngineSettings}, + { "GemRepos", ProjectManagerScreen::GemRepos} }; // need to define qHash for ProjectManagerScreen when using scoped enums diff --git a/Code/Tools/ProjectManager/Source/ScreenFactory.cpp b/Code/Tools/ProjectManager/Source/ScreenFactory.cpp index f3bddfdd27..44aa713e6a 100644 --- a/Code/Tools/ProjectManager/Source/ScreenFactory.cpp +++ b/Code/Tools/ProjectManager/Source/ScreenFactory.cpp @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include namespace O3DE::ProjectManager { @@ -41,9 +43,15 @@ namespace O3DE::ProjectManager case (ProjectManagerScreen::UpdateProjectSettings): newScreen = new UpdateProjectSettingsScreen(parent); break; + case (ProjectManagerScreen::Engine): + newScreen = new EngineScreenCtrl(parent); + break; case (ProjectManagerScreen::EngineSettings): newScreen = new EngineSettingsScreen(parent); break; + case (ProjectManagerScreen::GemRepos): + newScreen = new GemRepoScreen(parent); + break; case (ProjectManagerScreen::Empty): default: newScreen = new ScreenWidget(parent); diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index 6d0f392e52..f71ae290e7 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -27,6 +27,8 @@ set(FILES Source/FormFolderBrowseEditWidget.cpp Source/FormImageBrowseEditWidget.h Source/FormImageBrowseEditWidget.cpp + Source/GemsSubWidget.h + Source/GemsSubWidget.cpp Source/PathValidator.h Source/PathValidator.cpp Source/ProjectManagerWindow.h @@ -56,6 +58,8 @@ set(FILES Source/ProjectsScreen.cpp Source/ProjectSettingsScreen.h Source/ProjectSettingsScreen.cpp + Source/EngineScreenCtrl.h + Source/EngineScreenCtrl.cpp Source/EngineSettingsScreen.h Source/EngineSettingsScreen.cpp Source/ProjectButtonWidget.h @@ -98,4 +102,16 @@ set(FILES Source/GemCatalog/GemRequirementListView.cpp Source/GemCatalog/GemSortFilterProxyModel.h Source/GemCatalog/GemSortFilterProxyModel.cpp + Source/GemRepo/GemRepoScreen.h + Source/GemRepo/GemRepoScreen.cpp + Source/GemRepo/GemRepoInfo.h + Source/GemRepo/GemRepoInfo.cpp + Source/GemRepo/GemRepoInspector.h + Source/GemRepo/GemRepoInspector.cpp + Source/GemRepo/GemRepoItemDelegate.h + Source/GemRepo/GemRepoItemDelegate.cpp + Source/GemRepo/GemRepoListView.h + Source/GemRepo/GemRepoListView.cpp + Source/GemRepo/GemRepoModel.h + Source/GemRepo/GemRepoModel.cpp ) diff --git a/Code/Tools/ProjectManager/project_manager_tests_files.cmake b/Code/Tools/ProjectManager/project_manager_tests_files.cmake index 2b22ced910..2bfe343038 100644 --- a/Code/Tools/ProjectManager/project_manager_tests_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_tests_files.cmake @@ -11,6 +11,7 @@ set(FILES Resources/ProjectManager.qss tests/ApplicationTests.cpp tests/PythonBindingsTests.cpp + tests/GemCatalogTests.cpp tests/main.cpp tests/UtilsTests.cpp ) diff --git a/Code/Tools/ProjectManager/tests/GemCatalogTests.cpp b/Code/Tools/ProjectManager/tests/GemCatalogTests.cpp new file mode 100644 index 0000000000..f5c6d5196a --- /dev/null +++ b/Code/Tools/ProjectManager/tests/GemCatalogTests.cpp @@ -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 + * + */ + +#include +#include +#include + + +namespace O3DE::ProjectManager +{ + class GemCatalogTests + : public ::UnitTest::ScopedAllocatorSetupFixture + { + public: + + GemCatalogTests() = default; + }; + + TEST_F(GemCatalogTests, GemCatalog_Displays_But_Does_Not_Add_Dependencies) + { + GemModel* gemModel = new GemModel(); + + // given 3 gems a,b,c where a depends on b which depends on c + GemInfo gemA, gemB, gemC; + QModelIndex indexA, indexB, indexC; + gemA.m_name = "a"; + gemB.m_name = "b"; + gemC.m_name = "c"; + + gemA.m_dependencies = QStringList({ "b" }); + gemB.m_dependencies = QStringList({ "c" }); + + gemModel->AddGem(gemA); + indexA = gemModel->FindIndexByNameString(gemA.m_name); + + gemModel->AddGem(gemB); + indexB = gemModel->FindIndexByNameString(gemB.m_name); + + gemModel->AddGem(gemC); + indexC = gemModel->FindIndexByNameString(gemC.m_name); + + gemModel->UpdateGemDependencies(); + + EXPECT_FALSE(GemModel::IsAdded(indexA)); + EXPECT_FALSE(GemModel::IsAddedDependency(indexB) || GemModel::IsAddedDependency(indexC)); + + // when a is added + GemModel::SetIsAdded(*gemModel, indexA, true); + + // expect b and c are now dependencies of an added gem but not themselves added + // cmake will handle dependencies + EXPECT_TRUE(GemModel::IsAddedDependency(indexB) && GemModel::IsAddedDependency(indexC)); + EXPECT_TRUE(!GemModel::IsAdded(indexB) && !GemModel::IsAdded(indexC)); + + QVector gemsToAdd = gemModel->GatherGemsToBeAdded(); + EXPECT_TRUE(gemsToAdd.size() == 1); + EXPECT_EQ(GemModel::GetName(gemsToAdd.at(0)), gemA.m_name); + } +} diff --git a/Code/Tools/RemoteConsole/Core/RemoteConsoleCore.h b/Code/Tools/RemoteConsole/Core/RemoteConsoleCore.h index d9a6f7aba6..458cfc44a2 100644 --- a/Code/Tools/RemoteConsole/Core/RemoteConsoleCore.h +++ b/Code/Tools/RemoteConsole/Core/RemoteConsoleCore.h @@ -94,11 +94,11 @@ struct SNoDataEvent { SNoDataEvent() : IRemoteEvent(T) {}; - virtual IRemoteEvent* Clone() { return new SNoDataEvent(); } + IRemoteEvent* Clone() override { return new SNoDataEvent(); } protected: - virtual void WriteToBuffer([[maybe_unused]] char* buffer, int& size, [[maybe_unused]] int maxsize) { size = 0; } - virtual IRemoteEvent* CreateFromBuffer([[maybe_unused]] const char* buffer, [[maybe_unused]] int size) { return Clone(); } + void WriteToBuffer([[maybe_unused]] char* buffer, int& size, [[maybe_unused]] int maxsize) override { size = 0; } + IRemoteEvent* CreateFromBuffer([[maybe_unused]] const char* buffer, [[maybe_unused]] int size) override { return Clone(); } }; ///////////////////////////////////////////////////////////////////////////////////////////// @@ -109,17 +109,17 @@ struct SStringEvent SStringEvent(const char* data) : IRemoteEvent(T) , m_data(data) {}; - virtual IRemoteEvent* Clone() { return new SStringEvent(GetData()); } + IRemoteEvent* Clone() override { return new SStringEvent(GetData()); } const char* GetData() const { return m_data.c_str(); } protected: - virtual void WriteToBuffer(char* buffer, int& size, int maxsize) + void WriteToBuffer(char* buffer, int& size, int maxsize) override { const char* data = GetData(); size = min((int)strlen(data), maxsize); memcpy(buffer, data, size); } - virtual IRemoteEvent* CreateFromBuffer(const char* buffer, [[maybe_unused]] int size) { return new SStringEvent(buffer); } + IRemoteEvent* CreateFromBuffer(const char* buffer, [[maybe_unused]] int size) override { return new SStringEvent(buffer); } private: AZStd::string m_data; diff --git a/Code/Tools/SceneAPI/SceneCore/Tests/Containers/SceneGraphTests.cpp b/Code/Tools/SceneAPI/SceneCore/Tests/Containers/SceneGraphTests.cpp index 87e2cbdbde..f51304c000 100644 --- a/Code/Tools/SceneAPI/SceneCore/Tests/Containers/SceneGraphTests.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Tests/Containers/SceneGraphTests.cpp @@ -108,7 +108,7 @@ namespace AZ BusDisconnect(); } - bool OnPreAssert(const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) + bool OnPreAssert(const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) override { m_assertTriggered = true; return true; diff --git a/Code/Tools/SceneAPI/SceneData/Groups/AnimationGroup.h b/Code/Tools/SceneAPI/SceneData/Groups/AnimationGroup.h index c26dc7563e..f5d9239865 100644 --- a/Code/Tools/SceneAPI/SceneData/Groups/AnimationGroup.h +++ b/Code/Tools/SceneAPI/SceneData/Groups/AnimationGroup.h @@ -38,7 +38,7 @@ namespace AZ void OverrideId(const Uuid& id); Containers::RuleContainer& GetRuleContainer() override; - const Containers::RuleContainer& GetRuleContainerConst() const; + const Containers::RuleContainer& GetRuleContainerConst() const override; const AZStd::string& GetSelectedRootBone() const override; uint32_t GetStartFrame() const override; diff --git a/Code/Tools/SceneAPI/SceneData/Groups/SkeletonGroup.h b/Code/Tools/SceneAPI/SceneData/Groups/SkeletonGroup.h index 7c6ed2dc57..3318f89b8d 100644 --- a/Code/Tools/SceneAPI/SceneData/Groups/SkeletonGroup.h +++ b/Code/Tools/SceneAPI/SceneData/Groups/SkeletonGroup.h @@ -39,8 +39,8 @@ namespace AZ const Uuid& GetId() const override; void OverrideId(const Uuid& id); - Containers::RuleContainer& GetRuleContainer(); - const Containers::RuleContainer& GetRuleContainerConst() const; + Containers::RuleContainer& GetRuleContainer() override; + const Containers::RuleContainer& GetRuleContainerConst() const override; const AZStd::string& GetSelectedRootBone() const override; void SetSelectedRootBone(const AZStd::string& selectedRootBone) override; diff --git a/Code/Tools/SceneAPI/SceneData/Groups/SkinGroup.h b/Code/Tools/SceneAPI/SceneData/Groups/SkinGroup.h index 6708654463..010091e3eb 100644 --- a/Code/Tools/SceneAPI/SceneData/Groups/SkinGroup.h +++ b/Code/Tools/SceneAPI/SceneData/Groups/SkinGroup.h @@ -46,8 +46,8 @@ namespace AZ const Uuid& GetId() const override; void OverrideId(const Uuid& id); - Containers::RuleContainer& GetRuleContainer(); - const Containers::RuleContainer& GetRuleContainerConst() const; + Containers::RuleContainer& GetRuleContainer() override; + const Containers::RuleContainer& GetRuleContainerConst() const override; DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList() override; const DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList() const override; diff --git a/Code/Tools/SceneAPI/SceneUI/RowWidgets/ManifestNameHandler.h b/Code/Tools/SceneAPI/SceneUI/RowWidgets/ManifestNameHandler.h index 8c73b5fe17..fbfd553caa 100644 --- a/Code/Tools/SceneAPI/SceneUI/RowWidgets/ManifestNameHandler.h +++ b/Code/Tools/SceneAPI/SceneUI/RowWidgets/ManifestNameHandler.h @@ -34,7 +34,7 @@ namespace AZ QWidget* CreateGUI(QWidget* parent) override; u32 GetHandlerName() const override; - bool AutoDelete() const; + bool AutoDelete() const override; void ConsumeAttribute(ManifestNameWidget* widget, u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override; diff --git a/Code/Tools/SceneAPI/SceneUI/RowWidgets/NodeListSelectionHandler.h b/Code/Tools/SceneAPI/SceneUI/RowWidgets/NodeListSelectionHandler.h index 3b8f028be8..8717e23ac4 100644 --- a/Code/Tools/SceneAPI/SceneUI/RowWidgets/NodeListSelectionHandler.h +++ b/Code/Tools/SceneAPI/SceneUI/RowWidgets/NodeListSelectionHandler.h @@ -57,7 +57,7 @@ namespace AZ QWidget* CreateGUI(QWidget* parent) override; u32 GetHandlerName() const override; - bool AutoDelete() const; + bool AutoDelete() const override; void ConsumeAttribute(NodeListSelectionWidget* widget, u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override; diff --git a/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowHandler.h b/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowHandler.h index 664fea9f85..0fd9aede88 100644 --- a/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowHandler.h +++ b/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowHandler.h @@ -33,7 +33,7 @@ namespace AZ QWidget* CreateGUI(QWidget* parent) override; u32 GetHandlerName() const override; - bool AutoDelete() const; + bool AutoDelete() const override; bool IsDefaultHandler() const override; diff --git a/Code/Tools/SerializeContextTools/Converter.cpp b/Code/Tools/SerializeContextTools/Converter.cpp index eff5f140d5..6a8bebdbfc 100644 --- a/Code/Tools/SerializeContextTools/Converter.cpp +++ b/Code/Tools/SerializeContextTools/Converter.cpp @@ -494,6 +494,7 @@ namespace AZ return AZ::SettingsRegistryInterface::VisitResponse::Continue; } + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view, [[maybe_unused]] AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override { if (m_processingSourcePathKey) @@ -656,6 +657,7 @@ namespace AZ return AZ::SettingsRegistryInterface::VisitResponse::Continue; } + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view path, AZStd::string_view valueName, [[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override { diff --git a/Code/Tools/Standalone/Source/LUA/LUAEditorContext.h b/Code/Tools/Standalone/Source/LUA/LUAEditorContext.h index fc0e78089d..f844eaf395 100644 --- a/Code/Tools/Standalone/Source/LUA/LUAEditorContext.h +++ b/Code/Tools/Standalone/Source/LUA/LUAEditorContext.h @@ -84,22 +84,22 @@ namespace LUAEditor ////////////////////////////////////////////////////////////////////////// // AZ::Component - virtual void Init(); - virtual void Activate(); - virtual void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; ////////////////////////////////////////////////////////////////////////// static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); ////////////////////////////////////////////////////////////////////////// // EditorFramework::CoreMessageBus::Handler - virtual void RunAsAnotherInstance(); + void RunAsAnotherInstance() override; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // AzToolsFramework::AssetSystemInfoBus::Handler - virtual void AssetCompilationSuccess(const AZStd::string& assetPath) override; - virtual void AssetCompilationFailed(const AZStd::string& assetPath) override; + void AssetCompilationSuccess(const AZStd::string& assetPath) override; + void AssetCompilationFailed(const AZStd::string& assetPath) override; ////////////////////////////////////////////////////////////////////////// @@ -111,110 +111,110 @@ namespace LUAEditor ////////////////////////////////////////////////////////////////////////// // ContextInterface Messages // it is an error to call GetDocumentData when the data is not yet ready. - virtual void ShowLUAEditorView(); + void ShowLUAEditorView() override; // this occurs from time to time, generally triggered when some external event occurs // that makes it suspect that its document statuses might be invalid: ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //Context_DocumentManagement Messages - virtual void OnNewDocument(const AZStd::string& assetId); - virtual void OnLoadDocument(const AZStd::string& assetId, bool errorOnNotFound); - virtual void OnCloseDocument(const AZStd::string& assetId); - virtual void OnSaveDocument(const AZStd::string& assetId, bool bCloseAfterSaved, bool bSaveAs); - virtual bool OnSaveDocumentAs(const AZStd::string& assetId, bool bCloseAfterSaved); - virtual void NotifyDocumentModified(const AZStd::string& assetId, bool modified); - virtual void DocumentCheckOutRequested(const AZStd::string& assetId); - virtual void RefreshAllDocumentPerforceStat(); - virtual void OnReloadDocument(const AZStd::string assetId); + void OnNewDocument(const AZStd::string& assetId) override; + void OnLoadDocument(const AZStd::string& assetId, bool errorOnNotFound) override; + void OnCloseDocument(const AZStd::string& assetId) override; + void OnSaveDocument(const AZStd::string& assetId, bool bCloseAfterSaved, bool bSaveAs) override; + bool OnSaveDocumentAs(const AZStd::string& assetId, bool bCloseAfterSaved) override; + void NotifyDocumentModified(const AZStd::string& assetId, bool modified) override; + void DocumentCheckOutRequested(const AZStd::string& assetId) override; + void RefreshAllDocumentPerforceStat() override; + void OnReloadDocument(const AZStd::string assetId) override; - virtual void UpdateDocumentData(const AZStd::string& assetId, const char* dataPtr, const AZStd::size_t dataLength); - virtual void GetDocumentData(const AZStd::string& assetId, const char** dataPtr, AZStd::size_t& dataLength); + void UpdateDocumentData(const AZStd::string& assetId, const char* dataPtr, const AZStd::size_t dataLength) override; + void GetDocumentData(const AZStd::string& assetId, const char** dataPtr, AZStd::size_t& dataLength) override; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // Target Manager ////////////////////////////////////////////////////////////////////////// - virtual void DesiredTargetConnected(bool connected); - virtual void DesiredTargetChanged(AZ::u32 newTargetID, AZ::u32 oldTargetID); + void DesiredTargetConnected(bool connected) override; + void DesiredTargetChanged(AZ::u32 newTargetID, AZ::u32 oldTargetID) override; ////////////////////////////////////////////////////////////////////////// //Context_DebuggerManagement Messages ////////////////////////////////////////////////////////////////////////// - void ExecuteScriptBlob(const AZStd::string& fromAssetId, bool executeLocal); - virtual void SynchronizeBreakpoints(); - virtual void CreateBreakpoint(const AZStd::string& fromAssetId, int lineNumber); - virtual void MoveBreakpoint(const AZ::Uuid& breakpointUID, int lineNumber); - virtual void DeleteBreakpoint(const AZ::Uuid& breakpointUID); - virtual void CleanUpBreakpoints(); + void ExecuteScriptBlob(const AZStd::string& fromAssetId, bool executeLocal) override; + void SynchronizeBreakpoints() override; + void CreateBreakpoint(const AZStd::string& fromAssetId, int lineNumber) override; + void MoveBreakpoint(const AZ::Uuid& breakpointUID, int lineNumber) override; + void DeleteBreakpoint(const AZ::Uuid& breakpointUID) override; + void CleanUpBreakpoints() override; // These come from the VM - virtual void OnDebuggerAttached(); - virtual void OnDebuggerRefused(); - virtual void OnDebuggerDetached(); - virtual void OnBreakpointHit(const AZStd::string& assetIdString, int lineNumber); - virtual void OnBreakpointAdded(const AZStd::string& assetIdString, int lineNumber); - virtual void OnBreakpointRemoved(const AZStd::string& assetIdString, int lineNumber); - virtual void OnReceivedAvailableContexts(const AZStd::vector& contexts); - virtual void OnReceivedRegisteredClasses(const AzFramework::ScriptUserClassList& classes); - virtual void OnReceivedRegisteredEBuses(const AzFramework::ScriptUserEBusList& ebuses); - virtual void OnReceivedRegisteredGlobals(const AzFramework::ScriptUserMethodList& methods, const AzFramework::ScriptUserPropertyList& properties); - virtual void OnReceivedLocalVariables(const AZStd::vector& vars); - virtual void OnReceivedCallstack(const AZStd::vector& callstack); - virtual void OnReceivedValueState(const AZ::ScriptContextDebug::DebugValue& value); - virtual void OnSetValueResult(const AZStd::string& name, bool success); - virtual void OnExecutionResumed(); - virtual void OnExecuteScriptResult(bool success); + void OnDebuggerAttached() override; + void OnDebuggerRefused() override; + void OnDebuggerDetached() override; + void OnBreakpointHit(const AZStd::string& assetIdString, int lineNumber) override; + void OnBreakpointAdded(const AZStd::string& assetIdString, int lineNumber) override; + void OnBreakpointRemoved(const AZStd::string& assetIdString, int lineNumber) override; + void OnReceivedAvailableContexts(const AZStd::vector& contexts) override; + void OnReceivedRegisteredClasses(const AzFramework::ScriptUserClassList& classes) override; + void OnReceivedRegisteredEBuses(const AzFramework::ScriptUserEBusList& ebuses) override; + void OnReceivedRegisteredGlobals(const AzFramework::ScriptUserMethodList& methods, const AzFramework::ScriptUserPropertyList& properties) override; + void OnReceivedLocalVariables(const AZStd::vector& vars) override; + void OnReceivedCallstack(const AZStd::vector& callstack) override; + void OnReceivedValueState(const AZ::ScriptContextDebug::DebugValue& value) override; + void OnSetValueResult(const AZStd::string& name, bool success) override; + void OnExecutionResumed() override; + void OnExecuteScriptResult(bool success) override; ////////////////////////////////////////////////////////////////////////// //BreakpointTracker Messages ////////////////////////////////////////////////////////////////////////// - virtual const BreakpointMap* RequestBreakpoints(); - virtual void RequestEditorFocus(const AZStd::string& assetIdString, int lineNumber); - virtual void RequestDeleteBreakpoint(const AZStd::string& assetIdString, int lineNumber); + const BreakpointMap* RequestBreakpoints() override; + void RequestEditorFocus(const AZStd::string& assetIdString, int lineNumber) override; + void RequestDeleteBreakpoint(const AZStd::string& assetIdString, int lineNumber) override; ////////////////////////////////////////////////////////////////////////// //StackTracker Messages ////////////////////////////////////////////////////////////////////////// - virtual void RequestStackClicked(const AZStd::string& stackString, int lineNumber); + void RequestStackClicked(const AZStd::string& stackString, int lineNumber) override; ////////////////////////////////////////////////////////////////////////// //TargetContextTracker Messages ////////////////////////////////////////////////////////////////////////// - virtual const AZStd::vector RequestTargetContexts(); - virtual const AZStd::string RequestCurrentTargetContext(); - virtual void SetCurrentTargetContext(AZStd::string& contextName); + virtual const AZStd::vector RequestTargetContexts() override; + const AZStd::string RequestCurrentTargetContext() override; + void SetCurrentTargetContext(AZStd::string& contextName) override; ////////////////////////////////////////////////////////////////////////// //Watch window messages ////////////////////////////////////////////////////////////////////////// - virtual void RequestWatchedVariable(const AZStd::string& varName); + void RequestWatchedVariable(const AZStd::string& varName) override; ////////////////////////////////////////////////////////////////////////// //Debug Request messages ////////////////////////////////////////////////////////////////////////// - virtual void RequestDetachDebugger(); - virtual void RequestAttachDebugger(); + void RequestDetachDebugger() override; + void RequestAttachDebugger() override; ////////////////////////////////////////////////////////////////////////// // AzToolsFramework CoreMessages - virtual void OnRestoreState(); // sent when everything is registered up and ready to go, this is what bootstraps stuff to get going. - virtual bool OnGetPermissionToShutDown(); - virtual bool CheckOkayToShutDown(); - virtual void OnSaveState(); // sent to everything when the app is about to shut down - do what you need to do. - virtual void OnDestroyState(); - virtual void ApplicationDeactivated(); - virtual void ApplicationActivated(); - virtual void ApplicationShow(AZ::Uuid id); - virtual void ApplicationHide(AZ::Uuid id); - virtual void ApplicationCensus(); + void OnRestoreState() override; // sent when everything is registered up and ready to go, this is what bootstraps stuff to get going. + bool OnGetPermissionToShutDown() override; + bool CheckOkayToShutDown() override; + void OnSaveState() override; // sent to everything when the app is about to shut down - do what you need to do. + void OnDestroyState() override; + void ApplicationDeactivated() override; + void ApplicationActivated() override; + void ApplicationShow(AZ::Uuid id) override; + void ApplicationHide(AZ::Uuid id) override; + void ApplicationCensus() override; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // HighlightedWords - virtual const HighlightedWords::LUAKeywordsType* GetLUAKeywords() { return &m_LUAKeywords; } - virtual const HighlightedWords::LUAKeywordsType* GetLUALibraryFunctions() { return &m_LUALibraryFunctions; } + const HighlightedWords::LUAKeywordsType* GetLUAKeywords() override { return &m_LUAKeywords; } + const HighlightedWords::LUAKeywordsType* GetLUALibraryFunctions() override { return &m_LUALibraryFunctions; } // internal data structure for the LUA debugger class/member/property reference panel // this is what we serialize and work with diff --git a/Code/Tools/Standalone/Source/LUA/LUAEditorMainWindow.hxx b/Code/Tools/Standalone/Source/LUA/LUAEditorMainWindow.hxx index c01893909b..1b23504fed 100644 --- a/Code/Tools/Standalone/Source/LUA/LUAEditorMainWindow.hxx +++ b/Code/Tools/Standalone/Source/LUA/LUAEditorMainWindow.hxx @@ -86,14 +86,14 @@ namespace LUAEditor public: AZ_CLASS_ALLOCATOR(LUAEditorMainWindow,AZ::SystemAllocator,0); LUAEditorMainWindow(QStandardItemModel* dataModel, bool connectedState, QWidget* parent = NULL, Qt::WindowFlags flags = Qt::WindowFlags()); - virtual ~LUAEditorMainWindow(void); + virtual ~LUAEditorMainWindow(); bool OnGetPermissionToShutDown(); ////////////////////////////////////////////////////////////////////////// // Qt Events private: - virtual void closeEvent(QCloseEvent* event); + void closeEvent(QCloseEvent* event) override; Ui::LUAEditorMainWindow* m_gui; AzToolsFramework::TargetSelectorButtonAction* m_pTargetButton; @@ -204,7 +204,7 @@ namespace LUAEditor AZStd::string m_lastOpenFilePath; AZStd::vector m_dProcessFindListClicked; - void OnDataLoadedAndSet(const DocumentInfo& info, LUAViewWidget* pLUAViewWidget); + void OnDataLoadedAndSet(const DocumentInfo& info, LUAViewWidget* pLUAViewWidget) override; AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* m_filterModel; QSharedPointer CreateFilter(); @@ -243,18 +243,18 @@ namespace LUAEditor // LUAEditorMainWindow Messages. public: virtual void OnCloseView(const AZStd::string& assetId); - virtual void OnFocusInEvent(const AZStd::string& assetId); - virtual void OnFocusOutEvent(const AZStd::string& assetId); - virtual void OnRequestCheckOut(const AZStd::string& assetId); - virtual void OnConnectedToTarget(); - virtual void OnDisconnectedFromTarget(); - virtual void OnConnectedToDebugger(); - virtual void OnDisconnectedFromDebugger(); + void OnFocusInEvent(const AZStd::string& assetId) override; + void OnFocusOutEvent(const AZStd::string& assetId) override; + void OnRequestCheckOut(const AZStd::string& assetId) override; + void OnConnectedToTarget() override; + void OnDisconnectedFromTarget() override; + void OnConnectedToDebugger() override; + void OnDisconnectedFromDebugger() override; void Repaint() override; ////////////////////////////////////////////////////////////////////////// - virtual void dragEnterEvent(QDragEnterEvent *pEvent); - virtual void dropEvent(QDropEvent *pEvent); + void dragEnterEvent(QDragEnterEvent *pEvent) override; + void dropEvent(QDropEvent *pEvent) override; void IgnoreFocusEvents(bool ignore) { m_bIgnoreFocusRequests = ignore; } @@ -327,7 +327,7 @@ namespace LUAEditor // support for windows-ish Ctrl+Tab cycling through documents via the above Tab actions typedef AZStd::list TrackedLUACtrlTabOrder; TrackedLUACtrlTabOrder m_CtrlTabOrder; - bool eventFilter(QObject *obj, QEvent *event); + bool eventFilter(QObject *obj, QEvent *event) override; AZStd::string m_StoredTabAssetId; bool m_bIgnoreFocusRequests; @@ -338,10 +338,10 @@ namespace LUAEditor ////////////////////////////////////////////////////////////////////////// //Debugger Messages, from the LUAEditor::LUABreakpointTrackerMessages::Bus - virtual void BreakpointsUpdate(const LUAEditor::BreakpointMap& uniqueBreakpoints); - virtual void BreakpointHit(const LUAEditor::Breakpoint& breakpoint); - virtual void BreakpointResume(); - virtual void OnExecuteScriptResult(bool success); + void BreakpointsUpdate(const LUAEditor::BreakpointMap& uniqueBreakpoints) override; + void BreakpointHit(const LUAEditor::Breakpoint& breakpoint) override; + void BreakpointResume() override; + void OnExecuteScriptResult(bool success) override; ////////////////////////////////////////////////////////////////////////// // track activity and synchronize the appropriate widgets' states to match @@ -433,7 +433,7 @@ namespace LUAEditor bool m_bAutoReloadUnmodifiedFiles = false; LUAEditorMainWindowSavedState() {} - void Init(const QByteArray& windowState,const QByteArray& windowGeom) + void Init(const QByteArray& windowState,const QByteArray& windowGeom) override { AzToolsFramework::MainWindowSavedState::Init(windowState, windowGeom); } diff --git a/Code/Tools/Standalone/Source/LUA/LUAEditorView.hxx b/Code/Tools/Standalone/Source/LUA/LUAEditorView.hxx index 70e4ba625f..ada99929aa 100644 --- a/Code/Tools/Standalone/Source/LUA/LUAEditorView.hxx +++ b/Code/Tools/Standalone/Source/LUA/LUAEditorView.hxx @@ -73,7 +73,7 @@ namespace LUAEditor LUADockWidget* luaDockWidget(){ return m_pLUADockWidget;} void SetLuaDockWidget(LUADockWidget* pLUADockWidget){m_pLUADockWidget = pLUADockWidget;} - virtual void dropEvent(QDropEvent *e); + void dropEvent(QDropEvent *e) override; // point a little arrow at this line. -1 means remove it. void UpdateCurrentExecutingLine(int lineNumber); @@ -83,9 +83,9 @@ namespace LUAEditor ////////////////////////////////////////////////////////////////////////// //Debugger Messages, from the LUAEditor::LUABreakpointTrackerMessages::Bus - virtual void BreakpointsUpdate(const LUAEditor::BreakpointMap& uniqueBreakpoints); - virtual void BreakpointHit(const LUAEditor::Breakpoint& breakpoint); - virtual void BreakpointResume(); + void BreakpointsUpdate(const LUAEditor::BreakpointMap& uniqueBreakpoints) override; + void BreakpointHit(const LUAEditor::Breakpoint& breakpoint) override; + void BreakpointResume() override; void BreakpointToggle(int line); @@ -153,7 +153,7 @@ namespace LUAEditor void RegainFocus(); private: - virtual void keyPressEvent(QKeyEvent *ev); + void keyPressEvent(QKeyEvent *ev) override; int CalcDocPosition(int line, int column); template //callabe must take a const QTextCursor& as a parameter void UpdateCursor(Callable callable); diff --git a/Code/Tools/Standalone/Source/StandaloneToolsApplication.h b/Code/Tools/Standalone/Source/StandaloneToolsApplication.h index 82028d7b9f..4ca45b8669 100644 --- a/Code/Tools/Standalone/Source/StandaloneToolsApplication.h +++ b/Code/Tools/Standalone/Source/StandaloneToolsApplication.h @@ -42,7 +42,7 @@ namespace StandaloneTools bool LaunchDiscoveryService(); // AZ::UserSettingsFileLocatorBus::Handler - AZStd::string ResolveFilePath(AZ::u32 /*providerId*/); + AZStd::string ResolveFilePath(AZ::u32 /*providerId*/) override; ////////////////////////////////////////////////////////////////////////// }; } diff --git a/Code/Tools/Standalone/Source/Telemetry/TelemetryComponent.h b/Code/Tools/Standalone/Source/Telemetry/TelemetryComponent.h index a715466cdb..f924908ea2 100644 --- a/Code/Tools/Standalone/Source/Telemetry/TelemetryComponent.h +++ b/Code/Tools/Standalone/Source/Telemetry/TelemetryComponent.h @@ -26,8 +26,8 @@ namespace Telemetry ////////////////// // AZ::Component - void Activate(); - void Deactivate(); + void Activate() override; + void Deactivate() override; static void Reflect(AZ::ReflectContext* context); ////////////////// diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientSequenceReport.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientSequenceReport.h index b39c2c8bcb..b668bda155 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientSequenceReport.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientSequenceReport.h @@ -122,12 +122,14 @@ namespace TestImpact }; //! Base class for all sequence report types. - template + template class SequenceReportBase { public: + static constexpr SequenceReportType ReportType = Type; + using PolicyState = PolicyStateType; + //! Constructs the report for a sequence of selected tests. - //! @param type The type of sequence this report is generated for. //! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time. //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). //! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty). @@ -136,33 +138,49 @@ namespace TestImpact //! @param selectedTestRuns The target names of the selected test runs. //! @param selectedTestRunReport The report for the set of selected test runs. SequenceReportBase( - SequenceReportType type, size_t maxConcurrency, - const AZStd::optional& testTargetTimeout, - const AZStd::optional& globalTimeout, - const PolicyStateType& policyState, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + PolicyStateType policyState, SuiteType suiteType, - const TestRunSelection& selectedTestRuns, - TestRunReport&& selectedTestRunReport) - : m_type(type) - , m_maxConcurrency(maxConcurrency) - , m_testTargetTimeout(testTargetTimeout) - , m_globalTimeout(globalTimeout) - , m_policyState(policyState) + TestRunSelection selectedTestRuns, + TestRunReport selectedTestRunReport) + : m_maxConcurrency(maxConcurrency) + , m_testTargetTimeout(AZStd::move(testTargetTimeout)) + , m_globalTimeout(AZStd::move(globalTimeout)) + , m_policyState(AZStd::move(policyState)) , m_suite(suiteType) - , m_selectedTestRuns(selectedTestRuns) + , m_selectedTestRuns(AZStd::move(selectedTestRuns)) , m_selectedTestRunReport(AZStd::move(selectedTestRunReport)) { } - virtual ~SequenceReportBase() = default; - - //! Returns the identifying type for this sequence report. - SequenceReportType GetType() const + SequenceReportBase(SequenceReportBase&& report) + : SequenceReportBase( + AZStd::move(report.m_maxConcurrency), + AZStd::move(report.m_testTargetTimeout), + AZStd::move(report.m_globalTimeout), + AZStd::move(report.m_policyState), + AZStd::move(report.m_suite), + AZStd::move(report.m_selectedTestRuns), + AZStd::move(report.m_selectedTestRunReport)) { - return m_type; } + SequenceReportBase(const SequenceReportBase& report) + : SequenceReportBase( + report.m_maxConcurrency, + report.m_testTargetTimeout, + report.m_globalTimeout, + report.m_policyState, + report.m_suite, + report.m_selectedTestRuns, + report.m_selectedTestRunReport) + { + } + + virtual ~SequenceReportBase() = default; + //! Returns the maximum concurrency for this sequence. size_t GetMaxConcurrency() const { @@ -284,7 +302,6 @@ namespace TestImpact } private: - SequenceReportType m_type; size_t m_maxConcurrency = 0; AZStd::optional m_testTargetTimeout; AZStd::optional m_globalTimeout; @@ -296,58 +313,27 @@ namespace TestImpact //! Report type for regular test sequences. class RegularSequenceReport - : public SequenceReportBase + : public SequenceReportBase { public: - //! Constructs the report for a regular sequence. - //! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time. - //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). - //! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty). - //! @param policyState The policy state this sequence was executed under. - //! @param suiteType The suite from which the tests have been selected from. - //! @param selectedTestRuns The target names of the selected test runs. - //! @param selectedTestRunReport The report for the set of selected test runs. - RegularSequenceReport( - size_t maxConcurrency, - const AZStd::optional& testTargetTimeout, - const AZStd::optional& globalTimeout, - const SequencePolicyState& policyState, - SuiteType suiteType, - const TestRunSelection& selectedTestRuns, - TestRunReport&& selectedTestRunReport); + using SequenceReportBase::SequenceReportBase; }; //! Report type for seed test sequences. class SeedSequenceReport - : public SequenceReportBase + : public SequenceReportBase { public: - //! Constructs the report for a seed sequence. - //! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time. - //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). - //! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty). - //! @param policyState The policy state this sequence was executed under. - //! @param suiteType The suite from which the tests have been selected from. - //! @param selectedTestRuns The target names of the selected test runs. - //! @param selectedTestRunReport The report for the set of selected test runs. - SeedSequenceReport( - size_t maxConcurrency, - const AZStd::optional& testTargetTimeout, - const AZStd::optional& globalTimeout, - const SequencePolicyState& policyState, - SuiteType suiteType, - const TestRunSelection& selectedTestRuns, - TestRunReport&& selectedTestRunReport); + using SequenceReportBase::SequenceReportBase; }; //! Report detailing a test run sequence of selected and drafted tests. - template + template class DraftingSequenceReportBase - : public SequenceReportBase + : public SequenceReportBase { public: //! Constructs the report for sequences that draft in previously failed/newly added test targets. - //! @param type The type of sequence this report is generated for. //! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time. //! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty). //! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty). @@ -358,18 +344,16 @@ namespace TestImpact //! @param selectedTestRunReport The report for the set of selected test runs. //! @param draftedTestRunReport The report for the set of drafted test runs. DraftingSequenceReportBase( - SequenceReportType type, size_t maxConcurrency, - const AZStd::optional& testTargetTimeout, - const AZStd::optional& globalTimeout, - const PolicyStateType& policyState, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + PolicyStateType policyState, SuiteType suiteType, - const TestRunSelection& selectedTestRuns, - const AZStd::vector& draftedTestRuns, + TestRunSelection selectedTestRuns, + AZStd::vector draftedTestRuns, TestRunReport&& selectedTestRunReport, TestRunReport&& draftedTestRunReport) - : SequenceReportBase ( - type, + : SequenceReportBase( maxConcurrency, testTargetTimeout, globalTimeout, @@ -377,7 +361,17 @@ namespace TestImpact suiteType, selectedTestRuns, AZStd::move(selectedTestRunReport)) - , m_draftedTestRuns(draftedTestRuns) + , m_draftedTestRuns(AZStd::move(draftedTestRuns)) + , m_draftedTestRunReport(AZStd::move(draftedTestRunReport)) + { + } + + DraftingSequenceReportBase( + SequenceReportBase&& report, + AZStd::vector draftedTestRuns, + TestRunReport&& draftedTestRunReport) + : SequenceReportBase(AZStd::move(report)) + , m_draftedTestRuns(AZStd::move(draftedTestRuns)) , m_draftedTestRunReport(AZStd::move(draftedTestRunReport)) { } @@ -456,7 +450,7 @@ namespace TestImpact //! Report detailing an impact analysis sequence of selected, discarded and drafted tests. class ImpactAnalysisSequenceReport - : public DraftingSequenceReportBase + : public DraftingSequenceReportBase { public: //! Constructs the report for an impact analysis sequence. @@ -471,16 +465,18 @@ namespace TestImpact //! @param draftedTestRunReport The report for the set of drafted test runs. ImpactAnalysisSequenceReport( size_t maxConcurrency, - const AZStd::optional& testTargetTimeout, - const AZStd::optional& globalTimeout, - const ImpactAnalysisSequencePolicyState& policyState, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + ImpactAnalysisSequencePolicyState policyState, SuiteType suiteType, - const TestRunSelection& selectedTestRuns, - const AZStd::vector& discardedTestRuns, - const AZStd::vector& draftedTestRuns, + TestRunSelection selectedTestRuns, + AZStd::vector discardedTestRuns, + AZStd::vector draftedTestRuns, TestRunReport&& selectedTestRunReport, TestRunReport&& draftedTestRunReport); + ImpactAnalysisSequenceReport(DraftingSequenceReportBase&& report, AZStd::vector discardedTestRuns); + //! Returns the test runs discarded from running in the sequence. const AZStd::vector& GetDiscardedTestRuns() const; private: @@ -489,7 +485,7 @@ namespace TestImpact //! Report detailing an impact analysis sequence of selected, discarded and drafted test runs. class SafeImpactAnalysisSequenceReport - : public DraftingSequenceReportBase + : public DraftingSequenceReportBase { public: //! Constructs the report for a sequence of selected, discarded and drafted test runs. @@ -506,17 +502,20 @@ namespace TestImpact //! @param draftedTestRunReport The report for the set of drafted test runs. SafeImpactAnalysisSequenceReport( size_t maxConcurrency, - const AZStd::optional& testTargetTimeout, - const AZStd::optional& globalTimeout, - const SafeImpactAnalysisSequencePolicyState& policyState, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + SafeImpactAnalysisSequencePolicyState policyState, SuiteType suiteType, - const TestRunSelection& selectedTestRuns, - const TestRunSelection& discardedTestRuns, - const AZStd::vector& draftedTestRuns, + TestRunSelection selectedTestRuns, + TestRunSelection discardedTestRuns, + AZStd::vector draftedTestRuns, TestRunReport&& selectedTestRunReport, TestRunReport&& discardedTestRunReport, TestRunReport&& draftedTestRunReport); + SafeImpactAnalysisSequenceReport( + DraftingSequenceReportBase&& report, TestRunSelection discardedTestRuns, TestRunReport&& discardedTestRunReport); + // SequenceReport overrides ... AZStd::chrono::milliseconds GetDuration() const override; TestSequenceResult GetResult() const override; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientSequenceReportSerializer.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientSequenceReportSerializer.h index fc226588e7..06bb0cdaa1 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientSequenceReportSerializer.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientSequenceReportSerializer.h @@ -14,15 +14,27 @@ namespace TestImpact { - //! Serializes a regular sequence report to JSON format. + //! Serializes a regular sequence report to Json format. AZStd::string SerializeSequenceReport(const Client::RegularSequenceReport& sequenceReport); - //! Serializes a seed sequence report to JSON format. + //! Serializes a seed sequence report to Json format. AZStd::string SerializeSequenceReport(const Client::SeedSequenceReport& sequenceReport); - //! Serializes an impact analysis sequence report to JSON format. + //! Serializes an impact analysis sequence report to Json format. AZStd::string SerializeSequenceReport(const Client::ImpactAnalysisSequenceReport& sequenceReport); - //! Serializes a safe impact analysis sequence report to JSON format. + //! Serializes a safe impact analysis sequence report to Json format. AZStd::string SerializeSequenceReport(const Client::SafeImpactAnalysisSequenceReport& sequenceReport); + + //! Deserialize a regular sequence report from Json format. + Client::RegularSequenceReport DeserializeRegularSequenceReport(const AZStd::string& sequenceReportJson); + + //! Deserialize a seed sequence report from Json format. + Client::SeedSequenceReport DeserializeSeedSequenceReport(const AZStd::string& sequenceReportJson); + + //! Deserialize an impact analysis sequence report from Json format. + Client::ImpactAnalysisSequenceReport DeserializeImpactAnalysisSequenceReport(const AZStd::string& sequenceReportJson); + + //! Deserialize a safe impact analysis sequence report from Json format. + Client::SafeImpactAnalysisSequenceReport DeserializeSafeImpactAnalysisSequenceReport(const AZStd::string& sequenceReportJson); } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h index c4625e9d2c..f7afd49b26 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactUtils.h @@ -103,4 +103,43 @@ namespace TestImpact //! User-friendly names for the client test result types. AZStd::string ClientTestResultAsString(Client::TestResult result); + + //! User-friendly names for the suite types. + SuiteType SuiteTypeFromString(const AZStd::string& suiteType); + + //! Returns the sequence report type for the specified string. + Client::SequenceReportType SequenceReportTypeFromString(const AZStd::string& type); + + //! Returns the test run result for the specified string. + Client::TestRunResult TestRunResultFromString(const AZStd::string& result); + + //! Returns the test result for the specified string. + Client::TestResult TestResultFromString(const AZStd::string& result); + + //! Returns the test sequence result for the specified string. + TestSequenceResult TestSequenceResultFromString(const AZStd::string& result); + + //! Returns the execution failure policy for the specified string. + Policy::ExecutionFailure ExecutionFailurePolicyFromString(const AZStd::string& executionFailurePolicy); + + //! Returns the failed test coverage policy for the specified string. + Policy::FailedTestCoverage FailedTestCoveragePolicyFromString(const AZStd::string& failedTestCoveragePolicy); + + //! Returns the test prioritization policy for the specified string. + Policy::TestPrioritization TestPrioritizationPolicyFromString(const AZStd::string& testPrioritizationPolicy); + + //! Returns the test failure policy for the specified string. + Policy::TestFailure TestFailurePolicyFromString(const AZStd::string& testFailurePolicy); + + //! Returns the integrity failure policy for the specified string. + Policy::IntegrityFailure IntegrityFailurePolicyFromString(const AZStd::string& integrityFailurePolicy); + + //! Returns the dynamic dependency map policy for the specified string. + Policy::DynamicDependencyMap DynamicDependencyMapPolicyFromString(const AZStd::string& dynamicDependencyMapPolicy); + + //! Returns the test sharding policy for the specified string. + Policy::TestSharding TestShardingPolicyFromString(const AZStd::string& testShardingPolicy); + + //! Returns the target output capture policy for the specified string. + Policy::TargetOutputCapture TargetOutputCapturePolicyFromString(const AZStd::string& targetOutputCapturePolicy); } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientSequenceReport.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientSequenceReport.cpp index 3f4656758a..17157e70fc 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientSequenceReport.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientSequenceReport.cpp @@ -159,69 +159,35 @@ namespace TestImpact return m_totalNumDisabledTests; } - RegularSequenceReport::RegularSequenceReport( + ImpactAnalysisSequenceReport::ImpactAnalysisSequenceReport( size_t maxConcurrency, - const AZStd::optional& testTargetTimeout, - const AZStd::optional& globalTimeout, - const SequencePolicyState& policyState, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + ImpactAnalysisSequencePolicyState policyState, SuiteType suiteType, - const TestRunSelection& selectedTestRuns, - TestRunReport&& selectedTestRunReport) - : SequenceReportBase( - SequenceReportType::RegularSequence, - maxConcurrency, - testTargetTimeout, - globalTimeout, - policyState, - suiteType, - selectedTestRuns, - AZStd::move(selectedTestRunReport)) - { - } - - SeedSequenceReport::SeedSequenceReport( - size_t maxConcurrency, - const AZStd::optional& testTargetTimeout, - const AZStd::optional& globalTimeout, - const SequencePolicyState& policyState, - SuiteType suiteType, - const TestRunSelection& selectedTestRuns, - TestRunReport&& selectedTestRunReport) - : SequenceReportBase( - SequenceReportType::SeedSequence, - maxConcurrency, - testTargetTimeout, - globalTimeout, - policyState, - suiteType, - selectedTestRuns, - AZStd::move(selectedTestRunReport)) + TestRunSelection selectedTestRuns, + AZStd::vector discardedTestRuns, + AZStd::vector draftedTestRuns, + TestRunReport&& selectedTestRunReport, + TestRunReport&& draftedTestRunReport) + : DraftingSequenceReportBase( + maxConcurrency, + AZStd::move(testTargetTimeout), + AZStd::move(globalTimeout), + AZStd::move(policyState), + suiteType, + AZStd::move(selectedTestRuns), + AZStd::move(draftedTestRuns), + AZStd::move(selectedTestRunReport), + AZStd::move(draftedTestRunReport)) + , m_discardedTestRuns(discardedTestRuns) { } ImpactAnalysisSequenceReport::ImpactAnalysisSequenceReport( - size_t maxConcurrency, - const AZStd::optional& testTargetTimeout, - const AZStd::optional& globalTimeout, - const ImpactAnalysisSequencePolicyState& policyState, - SuiteType suiteType, - const TestRunSelection& selectedTestRuns, - const AZStd::vector& discardedTestRuns, - const AZStd::vector& draftedTestRuns, - TestRunReport&& selectedTestRunReport, - TestRunReport&& draftedTestRunReport) - : DraftingSequenceReportBase( - SequenceReportType::ImpactAnalysisSequence, - maxConcurrency, - testTargetTimeout, - globalTimeout, - policyState, - suiteType, - selectedTestRuns, - draftedTestRuns, - AZStd::move(selectedTestRunReport), - AZStd::move(draftedTestRunReport)) - , m_discardedTestRuns(discardedTestRuns) + DraftingSequenceReportBase&& report, AZStd::vector discardedTestRuns) + : DraftingSequenceReportBase(AZStd::move(report)) + , m_discardedTestRuns(AZStd::move(discardedTestRuns)) { } @@ -232,25 +198,24 @@ namespace TestImpact SafeImpactAnalysisSequenceReport::SafeImpactAnalysisSequenceReport( size_t maxConcurrency, - const AZStd::optional& testTargetTimeout, - const AZStd::optional& globalTimeout, - const SafeImpactAnalysisSequencePolicyState& policyState, + AZStd::optional testTargetTimeout, + AZStd::optional globalTimeout, + SafeImpactAnalysisSequencePolicyState policyState, SuiteType suiteType, - const TestRunSelection& selectedTestRuns, - const TestRunSelection& discardedTestRuns, - const AZStd::vector& draftedTestRuns, + TestRunSelection selectedTestRuns, + TestRunSelection discardedTestRuns, + AZStd::vector draftedTestRuns, TestRunReport&& selectedTestRunReport, TestRunReport&& discardedTestRunReport, TestRunReport&& draftedTestRunReport) : DraftingSequenceReportBase( - SequenceReportType::SafeImpactAnalysisSequence, maxConcurrency, - testTargetTimeout, - globalTimeout, - policyState, + AZStd::move(testTargetTimeout), + AZStd::move(globalTimeout), + AZStd::move(policyState), suiteType, - selectedTestRuns, - draftedTestRuns, + AZStd::move(selectedTestRuns), + AZStd::move(draftedTestRuns), AZStd::move(selectedTestRunReport), AZStd::move(draftedTestRunReport)) , m_discardedTestRuns(discardedTestRuns) @@ -258,6 +223,14 @@ namespace TestImpact { } + SafeImpactAnalysisSequenceReport::SafeImpactAnalysisSequenceReport( + DraftingSequenceReportBase&& report, TestRunSelection discardedTestRuns, TestRunReport&& discardedTestRunReport) + : DraftingSequenceReportBase(AZStd::move(report)) + , m_discardedTestRuns(AZStd::move(discardedTestRuns)) + , m_discardedTestRunReport(AZStd::move(discardedTestRunReport)) + { + } + TestSequenceResult SafeImpactAnalysisSequenceReport::GetResult() const { return CalculateMultiTestSequenceResult({ DraftingSequenceReportBase::GetResult(), m_discardedTestRunReport.GetResult() }); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientSequenceReportSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientSequenceReportSerializer.cpp index 99a187fe16..944fd4ac38 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientSequenceReportSerializer.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientSequenceReportSerializer.cpp @@ -21,7 +21,7 @@ namespace TestImpact { namespace SequenceReportFields { - // Keys for pertinent JSON node and attribute names + // Keys for pertinent Json node and attribute names constexpr const char* Keys[] = { "name", @@ -417,13 +417,13 @@ namespace TestImpact writer.String(DynamicDependencyMapPolicyAsString(policyState.m_dynamicDependencyMap).c_str()); } - template + template void SerializeSequenceReportBaseMembers( - const Client::SequenceReportBase& sequenceReport, rapidjson::PrettyWriter& writer) + const SequenceReportBaseType& sequenceReport, rapidjson::PrettyWriter& writer) { // Type writer.Key(SequenceReportFields::Keys[SequenceReportFields::Type]); - writer.String(SequenceReportTypeAsString(sequenceReport.GetType()).c_str()); + writer.String(SequenceReportTypeAsString(sequenceReport.ReportType).c_str()); // Test target timeout writer.Key(SequenceReportFields::Keys[SequenceReportFields::TestTargetTimeout]); @@ -510,9 +510,9 @@ namespace TestImpact writer.Uint64(sequenceReport.GetTotalNumDisabledTests()); } - template + template void SerializeDraftingSequenceReportMembers( - const Client::DraftingSequenceReportBase& sequenceReport, rapidjson::PrettyWriter& writer) + const DraftingSequenceReportBaseType& sequenceReport, rapidjson::PrettyWriter& writer) { SerializeSequenceReportBaseMembers(sequenceReport, writer); @@ -603,4 +603,254 @@ namespace TestImpact return stringBuffer.GetString(); } + + AZStd::chrono::high_resolution_clock::time_point TimePointFromMsInt64(AZ::s64 ms) + { + return AZStd::chrono::high_resolution_clock::time_point(AZStd::chrono::milliseconds(ms)); + } + + AZStd::vector DeserializeTests(const rapidjson::Value& serialTests) + { + AZStd::vector tests; + tests.reserve(serialTests[SequenceReportFields::Keys[SequenceReportFields::Tests]].GetArray().Size()); + for (const auto& test : serialTests[SequenceReportFields::Keys[SequenceReportFields::Tests]].GetArray()) + { + const AZStd::string name = test[SequenceReportFields::Keys[SequenceReportFields::Name]].GetString(); + const auto result = TestResultFromString(test[SequenceReportFields::Keys[SequenceReportFields::Result]].GetString()); + tests.emplace_back(name, result); + } + + return tests; + } + + Client::TestRunBase DeserializeTestRunBase(const rapidjson::Value& serialTestRun) + { + return Client::TestRunBase( + serialTestRun[SequenceReportFields::Keys[SequenceReportFields::Name]].GetString(), + serialTestRun[SequenceReportFields::Keys[SequenceReportFields::CommandArgs]].GetString(), + TimePointFromMsInt64(serialTestRun[SequenceReportFields::Keys[SequenceReportFields::StartTime]].GetInt64()), + AZStd::chrono::milliseconds(serialTestRun[SequenceReportFields::Keys[SequenceReportFields::Duration]].GetInt64()), + TestRunResultFromString(serialTestRun[SequenceReportFields::Keys[SequenceReportFields::Result]].GetString())); + } + + template + AZStd::vector DeserializeTestRuns(const rapidjson::Value& serialTestRuns) + { + AZStd::vector testRuns; + testRuns.reserve(serialTestRuns.GetArray().Size()); + for (const auto& testRun : serialTestRuns.GetArray()) + { + testRuns.emplace_back(DeserializeTestRunBase(testRun)); + } + + return testRuns; + } + + template + AZStd::vector DeserializeCompletedTestRuns(const rapidjson::Value& serialCompletedTestRuns) + { + AZStd::vector testRuns; + testRuns.reserve(serialCompletedTestRuns.GetArray().Size()); + for (const auto& testRun : serialCompletedTestRuns.GetArray()) + { + testRuns.emplace_back( + DeserializeTestRunBase(testRun), DeserializeTests(testRun[SequenceReportFields::Keys[SequenceReportFields::Tests]])); + } + + return testRuns; + } + + Client::TestRunReport DeserializeTestRunReport(const rapidjson::Value& serialTestRunReport) + { + return Client::TestRunReport( + TestSequenceResultFromString(serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::Result]].GetString()), + TimePointFromMsInt64(serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::StartTime]].GetInt64()), + AZStd::chrono::milliseconds(serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::Duration]].GetInt64()), + DeserializeCompletedTestRuns( + serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::PassingTestRuns]]), + DeserializeCompletedTestRuns( + serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::FailingTestRuns]]), + DeserializeTestRuns( + serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::ExecutionFailureTestRuns]]), + DeserializeTestRuns( + serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::TimedOutTestRuns]]), + DeserializeTestRuns( + serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::UnexecutedTestRuns]])); + } + + Client::TestRunSelection DeserializeTestSelection(const rapidjson::Value& serialTestRunSelection) + { + const auto extractTestTargetNames = [](const rapidjson::Value& serialTestTargets) + { + AZStd::vector testTargets; + testTargets.reserve(serialTestTargets.GetArray().Size()); + for (const auto& testTarget : serialTestTargets.GetArray()) + { + testTargets.emplace_back(testTarget.GetString()); + } + + return testTargets; + }; + + return Client::TestRunSelection( + extractTestTargetNames(serialTestRunSelection[SequenceReportFields::Keys[SequenceReportFields::IncludedTestRuns]]), + extractTestTargetNames(serialTestRunSelection[SequenceReportFields::Keys[SequenceReportFields::ExcludedTestRuns]])); + } + + PolicyStateBase DeserializePolicyStateBaseMembers(const rapidjson::Value& serialPolicyState) + { + return + { + ExecutionFailurePolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::ExecutionFailure]].GetString()), + FailedTestCoveragePolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::CoverageFailure]].GetString()), + TestFailurePolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::TestFailure]].GetString()), + IntegrityFailurePolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::IntegrityFailure]].GetString()), + TestShardingPolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::TestSharding]].GetString()), + TargetOutputCapturePolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::TargetOutputCapture]].GetString()) + }; + } + + SequencePolicyState DeserializePolicyStateMembers(const rapidjson::Value& serialPolicyState) + { + return { DeserializePolicyStateBaseMembers(serialPolicyState) }; + } + + SafeImpactAnalysisSequencePolicyState DeserializeSafeImpactAnalysisPolicyStateMembers(const rapidjson::Value& serialPolicyState) + { + return + { + DeserializePolicyStateBaseMembers(serialPolicyState), + TestPrioritizationPolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::TestPrioritization]].GetString()) + }; + } + + ImpactAnalysisSequencePolicyState DeserializeImpactAnalysisSequencePolicyStateMembers(const rapidjson::Value& serialPolicyState) + { + return + { + DeserializePolicyStateBaseMembers(serialPolicyState), + TestPrioritizationPolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::TestPrioritization]].GetString()), + DynamicDependencyMapPolicyFromString( + serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::DynamicDependencyMap]].GetString()) + }; + } + + template + PolicyStateType DeserializePolicyStateType(const rapidjson::Value& serialPolicyStateType) + { + if constexpr (AZStd::is_same_v) + { + return DeserializePolicyStateMembers(serialPolicyStateType); + } + else if constexpr (AZStd::is_same_v) + { + return DeserializeSafeImpactAnalysisPolicyStateMembers(serialPolicyStateType); + } + else if constexpr (AZStd::is_same_v) + { + return DeserializeImpactAnalysisSequencePolicyStateMembers(serialPolicyStateType); + } + else + { + static_assert(false, "Template paramater must be a valid policy state type"); + } + } + + template + SequenceReportBaseType DeserialiseSequenceReportBase(const rapidjson::Value& serialSequenceReportBase) + { + const auto type = SequenceReportTypeFromString(serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::Type]].GetString()); + AZ_TestImpact_Eval( + type == SequenceReportBaseType::ReportType, + SequenceReportException, AZStd::string::format( + "The JSON sequence report type '%s' does not match the constructed report type", + serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::Type]].GetString())); + + const auto testTargetTimeout = + serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::TestTargetTimeout]].GetUint64(); + const auto globalTimeout = + serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::GlobalTimeout]].GetUint64(); + + return SequenceReportBaseType( + serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::MaxConcurrency]].GetUint64(), + testTargetTimeout ? AZStd::optional{ testTargetTimeout } : AZStd::nullopt, + globalTimeout ? AZStd::optional{ globalTimeout } : AZStd::nullopt, + DeserializePolicyStateType(serialSequenceReportBase), + SuiteTypeFromString(serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::Suite]].GetString()), + DeserializeTestSelection(serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::SelectedTestRuns]]), + DeserializeTestRunReport(serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::SelectedTestRunReport]])); + } + + template + Client::DraftingSequenceReportBase + DeserializeDraftingSequenceReportBase(const rapidjson::Value& serialDraftingSequenceReportBase) + { + AZStd::vector draftingTestRuns; + draftingTestRuns.reserve( + serialDraftingSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::DraftedTestRuns]].GetArray().Size()); + for (const auto& testRun : + serialDraftingSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::DraftedTestRuns]].GetArray()) + { + draftingTestRuns.emplace_back(testRun.GetString()); + } + + using SequenceBase = + Client::SequenceReportBase; + using DraftingSequenceBase = + Client::DraftingSequenceReportBase; + + return DraftingSequenceBase( + DeserialiseSequenceReportBase(serialDraftingSequenceReportBase), + AZStd::move(draftingTestRuns), + DeserializeTestRunReport(serialDraftingSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::DraftedTestRunReport]])); + } + + rapidjson::Document OpenSequenceReportJson(const AZStd::string& sequenceReportJson) + { + rapidjson::Document doc; + + if (doc.Parse<0>(sequenceReportJson.c_str()).HasParseError()) + { + throw SequenceReportException("Could not parse sequence report data"); + } + + return doc; + } + + Client::RegularSequenceReport DeserializeRegularSequenceReport(const AZStd::string& sequenceReportJson) + { + const auto doc = OpenSequenceReportJson(sequenceReportJson); + return DeserialiseSequenceReportBase(doc); + } + + Client::SeedSequenceReport DeserializeSeedSequenceReport(const AZStd::string& sequenceReportJson) + { + const auto doc = OpenSequenceReportJson(sequenceReportJson); + return DeserialiseSequenceReportBase(doc); + } + + Client::ImpactAnalysisSequenceReport DeserializeImpactAnalysisSequenceReport(const AZStd::string& sequenceReportJson) + { + const auto doc = OpenSequenceReportJson(sequenceReportJson); + + AZStd::vector discardedTestRuns; + discardedTestRuns.reserve(doc[SequenceReportFields::Keys[SequenceReportFields::DiscardedTestRuns]].GetArray().Size()); + for (const auto& testRun : doc[SequenceReportFields::Keys[SequenceReportFields::DiscardedTestRuns]].GetArray()) + { + discardedTestRuns.emplace_back(testRun.GetString()); + } + + return Client::ImpactAnalysisSequenceReport( + DeserializeDraftingSequenceReportBase(doc), AZStd::move(discardedTestRuns)); + } + + Client::SafeImpactAnalysisSequenceReport DeserializeSafeImpactAnalysisSequenceReport(const AZStd::string& sequenceReportJson) + { + const auto doc = OpenSequenceReportJson(sequenceReportJson); + + return Client::SafeImpactAnalysisSequenceReport( + DeserializeDraftingSequenceReportBase(doc), + DeserializeTestSelection(doc[SequenceReportFields::Keys[SequenceReportFields::DiscardedTestRuns]]), + DeserializeTestRunReport(doc[SequenceReportFields::Keys[SequenceReportFields::DiscardedTestRunReport]])); + } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactUtils.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactUtils.cpp index 3f6c9dafd4..ecfdec287b 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactUtils.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactUtils.cpp @@ -243,4 +243,256 @@ namespace TestImpact throw(Exception(AZStd::string::format("Unexpected client test case result: %u", aznumeric_cast(result)))); } } + + SuiteType SuiteTypeFromString(const AZStd::string& suiteType) + { + if (suiteType == SuiteTypeAsString(SuiteType::Main)) + { + return SuiteType::Main; + } + else if (suiteType == SuiteTypeAsString(SuiteType::Periodic)) + { + return SuiteType::Periodic; + } + else if (suiteType == SuiteTypeAsString(SuiteType::Sandbox)) + { + return SuiteType::Sandbox; + } + else + { + throw Exception(AZStd::string::format("Unexpected suite type: '%s'", suiteType.c_str())); + } + } + + Client::SequenceReportType SequenceReportTypeFromString(const AZStd::string& type) + { + if (type == SequenceReportTypeAsString(Client::SequenceReportType::ImpactAnalysisSequence)) + { + return Client::SequenceReportType::ImpactAnalysisSequence; + } + else if (type == SequenceReportTypeAsString(Client::SequenceReportType::RegularSequence)) + { + return Client::SequenceReportType::RegularSequence; + } + else if (type == SequenceReportTypeAsString(Client::SequenceReportType::SafeImpactAnalysisSequence)) + { + return Client::SequenceReportType::SafeImpactAnalysisSequence; + } + else if (type == SequenceReportTypeAsString(Client::SequenceReportType::SeedSequence)) + { + return Client::SequenceReportType::SeedSequence; + } + else + { + throw Exception(AZStd::string::format("Unexpected sequence report type: '%s'", type.c_str())); + } + } + + Client::TestRunResult TestRunResultFromString(const AZStd::string& result) + { + if (result == TestRunResultAsString(Client::TestRunResult::AllTestsPass)) + { + return Client::TestRunResult::AllTestsPass; + } + else if (result == TestRunResultAsString(Client::TestRunResult::FailedToExecute)) + { + return Client::TestRunResult::FailedToExecute; + } + else if (result == TestRunResultAsString(Client::TestRunResult::NotRun)) + { + return Client::TestRunResult::NotRun; + } + else if (result == TestRunResultAsString(Client::TestRunResult::TestFailures)) + { + return Client::TestRunResult::TestFailures; + } + else if (result == TestRunResultAsString(Client::TestRunResult::Timeout)) + { + return Client::TestRunResult::Timeout; + } + else + { + throw Exception(AZStd::string::format("Unexpected client test run result: '%s'", result.c_str())); + } + } + + Client::TestResult TestResultFromString(const AZStd::string& result) + { + if (result == ClientTestResultAsString(Client::TestResult::Failed)) + { + return Client::TestResult::Failed; + } + else if (result == ClientTestResultAsString(Client::TestResult::NotRun)) + { + return Client::TestResult::NotRun; + } + else if (result == ClientTestResultAsString(Client::TestResult::Passed)) + { + return Client::TestResult::Passed; + } + else + { + throw Exception(AZStd::string::format("Unexpected client test result: '%s'", result.c_str())); + } + } + + TestSequenceResult TestSequenceResultFromString(const AZStd::string& result) + { + if (result == TestSequenceResultAsString(TestSequenceResult::Failure)) + { + return TestSequenceResult::Failure; + } + else if (result == TestSequenceResultAsString(TestSequenceResult::Success)) + { + return TestSequenceResult::Success; + } + else if (result == TestSequenceResultAsString(TestSequenceResult::Timeout)) + { + return TestSequenceResult::Timeout; + } + else + { + throw Exception(AZStd::string::format("Unexpected test sequence result: '%s'", result.c_str())); + } + } + + Policy::ExecutionFailure ExecutionFailurePolicyFromString(const AZStd::string& executionFailurePolicy) + { + if (executionFailurePolicy == ExecutionFailurePolicyAsString(Policy::ExecutionFailure::Abort)) + { + return Policy::ExecutionFailure::Abort; + } + else if (executionFailurePolicy == ExecutionFailurePolicyAsString(Policy::ExecutionFailure::Continue)) + { + return Policy::ExecutionFailure::Continue; + } + else if (executionFailurePolicy == ExecutionFailurePolicyAsString(Policy::ExecutionFailure::Ignore)) + { + return Policy::ExecutionFailure::Ignore; + } + else + { + throw Exception(AZStd::string::format("Unexpected execution failure policy: '%s'", executionFailurePolicy.c_str())); + } + } + + Policy::FailedTestCoverage FailedTestCoveragePolicyFromString(const AZStd::string& failedTestCoveragePolicy) + { + if (failedTestCoveragePolicy == FailedTestCoveragePolicyAsString(Policy::FailedTestCoverage::Discard)) + { + return Policy::FailedTestCoverage::Discard; + } + else if (failedTestCoveragePolicy == FailedTestCoveragePolicyAsString(Policy::FailedTestCoverage::Keep)) + { + return Policy::FailedTestCoverage::Keep; + } + else + { + throw Exception(AZStd::string::format("Unexpected failed test coverage policy: '%s'", failedTestCoveragePolicy.c_str())); + } + } + + Policy::TestPrioritization TestPrioritizationPolicyFromString(const AZStd::string& testPrioritizationPolicy) + { + if (testPrioritizationPolicy == TestPrioritizationPolicyAsString(Policy::TestPrioritization::DependencyLocality)) + { + return Policy::TestPrioritization::DependencyLocality; + } + else if (testPrioritizationPolicy == TestPrioritizationPolicyAsString(Policy::TestPrioritization::None)) + { + return Policy::TestPrioritization::None; + } + else + { + throw Exception(AZStd::string::format("Unexpected test prioritization policy: '%s'", testPrioritizationPolicy.c_str())); + } + } + + Policy::TestFailure TestFailurePolicyFromString(const AZStd::string& testFailurePolicy) + { + if (testFailurePolicy == TestFailurePolicyAsString(Policy::TestFailure::Abort)) + { + return Policy::TestFailure::Abort; + } + else if (testFailurePolicy == TestFailurePolicyAsString(Policy::TestFailure::Continue)) + { + return Policy::TestFailure::Continue; + } + else + { + throw Exception(AZStd::string::format("Unexpected test failure policy: '%s'", testFailurePolicy.c_str())); + } + } + + Policy::IntegrityFailure IntegrityFailurePolicyFromString(const AZStd::string& integrityFailurePolicy) + { + if (integrityFailurePolicy == IntegrityFailurePolicyAsString(Policy::IntegrityFailure::Abort)) + { + return Policy::IntegrityFailure::Abort; + } + else if (integrityFailurePolicy == IntegrityFailurePolicyAsString(Policy::IntegrityFailure::Continue)) + { + return Policy::IntegrityFailure::Continue; + } + else + { + throw Exception(AZStd::string::format("Unexpected integration failure policy: '%s'", integrityFailurePolicy.c_str())); + } + } + + Policy::DynamicDependencyMap DynamicDependencyMapPolicyFromString(const AZStd::string& dynamicDependencyMapPolicy) + { + if (dynamicDependencyMapPolicy == DynamicDependencyMapPolicyAsString(Policy::DynamicDependencyMap::Discard)) + { + return Policy::DynamicDependencyMap::Discard; + } + else if (dynamicDependencyMapPolicy == DynamicDependencyMapPolicyAsString(Policy::DynamicDependencyMap::Update)) + { + return Policy::DynamicDependencyMap::Update; + } + else + { + throw Exception(AZStd::string::format("Unexpected dynamic dependency map policy: '%s'", dynamicDependencyMapPolicy.c_str())); + } + } + + Policy::TestSharding TestShardingPolicyFromString(const AZStd::string& testShardingPolicy) + { + if (testShardingPolicy == TestShardingPolicyAsString(Policy::TestSharding::Always)) + { + return Policy::TestSharding::Always; + } + else if (testShardingPolicy == TestShardingPolicyAsString(Policy::TestSharding::Never)) + { + return Policy::TestSharding::Never; + } + else + { + throw Exception(AZStd::string::format("Unexpected test sharding policy: '%s'", testShardingPolicy.c_str())); + } + } + + Policy::TargetOutputCapture TargetOutputCapturePolicyFromString(const AZStd::string& targetOutputCapturePolicy) + { + if (targetOutputCapturePolicy == TargetOutputCapturePolicyAsString(Policy::TargetOutputCapture::File)) + { + return Policy::TargetOutputCapture::File; + } + else if (targetOutputCapturePolicy == TargetOutputCapturePolicyAsString(Policy::TargetOutputCapture::None)) + { + return Policy::TargetOutputCapture::None; + } + else if (targetOutputCapturePolicy == TargetOutputCapturePolicyAsString(Policy::TargetOutputCapture::StdOut)) + { + return Policy::TargetOutputCapture::StdOut; + } + else if (targetOutputCapturePolicy == TargetOutputCapturePolicyAsString(Policy::TargetOutputCapture::StdOutAndFile)) + { + return Policy::TargetOutputCapture::StdOutAndFile; + } + else + { + throw Exception(AZStd::string::format("Unexpected target output capture policy: '%s'", targetOutputCapturePolicy.c_str())); + } + } } // namespace TestImpact diff --git a/Gems/AWSClientAuth/cdk/utils/name_utils.py b/Gems/AWSClientAuth/cdk/utils/name_utils.py index 4921b735eb..ea9dd68060 100755 --- a/Gems/AWSClientAuth/cdk/utils/name_utils.py +++ b/Gems/AWSClientAuth/cdk/utils/name_utils.py @@ -6,10 +6,11 @@ SPDX-License-Identifier: Apache-2.0 OR MIT """ import re from aws_cdk import core +from .resource_name_sanitizer import sanitize_resource_name def format_aws_resource_name(feature_name: str, project_name: str, env: core.Environment, resource_type: str): - return f'{project_name}-{feature_name}-{resource_type}-{env.region}' + return sanitize_resource_name(f'{project_name}-{feature_name}-{resource_type}-{env.region}', resource_type) def format_aws_resource_id(feature_name: str, project_name: str, env: core.Environment, resource_type: str): @@ -31,4 +32,5 @@ def format_aws_resource_authenticated_id(feature_name: str, project_name: str, e def format_aws_resource_authenticated_name(feature_name: str, project_name: str, env: core.Environment, resource_type: str, authenticated: bool): authenticated_string = 'Authenticated' if authenticated else 'Unauthenticated' - return f'{project_name}{feature_name}{resource_type}{authenticated_string}-{env.region}' + return sanitize_resource_name( + f'{project_name}{feature_name}{resource_type}{authenticated_string}-{env.region}', resource_type) diff --git a/Gems/AWSClientAuth/cdk/utils/resource_name_sanitizer.py b/Gems/AWSClientAuth/cdk/utils/resource_name_sanitizer.py new file mode 100644 index 0000000000..d4a2d77f44 --- /dev/null +++ b/Gems/AWSClientAuth/cdk/utils/resource_name_sanitizer.py @@ -0,0 +1,45 @@ +""" +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 hashlib +from aws_cdk import ( + core, + aws_cognito as cognito, + aws_iam as iam +) + +MAX_RESOURCE_NAME_LENGTH_MAPPING = { + core.Stack.__name__: 128, + iam.Role.__name__: 64, + iam.ManagedPolicy.__name__: 144, + cognito.CfnUserPoolClient.__name__: 128, + cognito.CfnUserPool.__name__: 128, + cognito.CfnIdentityPool.__name__: 128 + +} + + +def sanitize_resource_name(resource_name: str, resource_type: str) -> str: + """ + Truncate the resource name if its length exceeds the limit. + This is the best effort for sanitizing resource names based on the AWS documents since each AWS service + has its unique restrictions. Customers can extend this function for validation or sanitization. + + :param resource_name: Original name of the resource. + :param resource_type: Type of the resource. + :return Sanitized resource name that can be deployed with AWS. + """ + result = resource_name + if not MAX_RESOURCE_NAME_LENGTH_MAPPING.get(resource_type): + return result + + if len(resource_name) > MAX_RESOURCE_NAME_LENGTH_MAPPING[resource_type]: + # PYTHONHASHSEED is set to "random" by default in Python 3.3 and up. Cannot use + # the built-in hash function here since it will give a different return value in each session + digest = "-%x" % (int(hashlib.md5(resource_name.encode('ascii', 'ignore')).hexdigest(), 16) & 0xffffffff) + result = resource_name[:MAX_RESOURCE_NAME_LENGTH_MAPPING[resource_type] - len(digest)] + digest + return result diff --git a/Gems/AWSClientAuth/gem.json b/Gems/AWSClientAuth/gem.json index 42996e7f4f..75c07d025b 100644 --- a/Gems/AWSClientAuth/gem.json +++ b/Gems/AWSClientAuth/gem.json @@ -5,9 +5,19 @@ "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "AWS Client Auth provides client authentication and AWS authorization solution.", - "canonical_tags": ["Gem"], - "user_tags": ["AWS", "Network", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AWS", + "Network", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/", + "dependencies": [ + "AWSCore", + "HttpRequestor" + ] } diff --git a/Gems/AWSCore/Code/Include/Public/Framework/ServiceRequestJobConfig.h b/Gems/AWSCore/Code/Include/Public/Framework/ServiceRequestJobConfig.h index 240331496e..10b14ed5af 100644 --- a/Gems/AWSCore/Code/Include/Public/Framework/ServiceRequestJobConfig.h +++ b/Gems/AWSCore/Code/Include/Public/Framework/ServiceRequestJobConfig.h @@ -75,7 +75,7 @@ namespace AWSCore return (m_requestUrl.length() > 0); } - std::shared_ptr GetCredentialsProvider() + std::shared_ptr GetCredentialsProvider() override { ServiceClientJobConfigType::EnsureSettingsApplied(); return m_credentialsProvider; diff --git a/Gems/AWSCore/Code/Source/Framework/JsonObjectHandler.cpp b/Gems/AWSCore/Code/Source/Framework/JsonObjectHandler.cpp index d041a97428..07df7806a9 100644 --- a/Gems/AWSCore/Code/Source/Framework/JsonObjectHandler.cpp +++ b/Gems/AWSCore/Code/Source/Framework/JsonObjectHandler.cpp @@ -19,6 +19,7 @@ namespace AWSCore { public: + virtual ~JsonReaderHandler() = default; using Ch = char; using SizeType = rapidjson::SizeType; diff --git a/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp index 9b90d5ca8d..4290713e5c 100644 --- a/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp +++ b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp @@ -21,6 +21,8 @@ namespace AWSCoreUnitTest : public AWSCore::JsonReader { public: + virtual ~JsonReaderMock() = default; + MOCK_METHOD0(Ignore, bool()); MOCK_METHOD1(Accept, bool(bool& target)); MOCK_METHOD1(Accept, bool(AZStd::string& target)); diff --git a/Gems/AWSCore/gem.json b/Gems/AWSCore/gem.json index 4c7889ace4..1bb9da9192 100644 --- a/Gems/AWSCore/gem.json +++ b/Gems/AWSCore/gem.json @@ -5,9 +5,16 @@ "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS Core Gem provides basic shared AWS functionality such as AWS SDK initialization and client configuration, and is automatically added when selecting any AWS feature Gem.", - "canonical_tags": ["Gem"], - "user_tags": ["AWS", "Network", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AWS", + "Network", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-core/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-core/", + "dependencies": [] } diff --git a/Gems/AWSGameLift/gem.json b/Gems/AWSGameLift/gem.json index 7711495ee7..a0d7f62cd1 100644 --- a/Gems/AWSGameLift/gem.json +++ b/Gems/AWSGameLift/gem.json @@ -5,9 +5,18 @@ "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS GameLift Gem provides a framework to extend O3DE networking layer to work with GameLift resources via GameLift server and client SDK.", - "canonical_tags": ["Gem"], - "user_tags": ["AWS", "Framework", "Network"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AWS", + "Framework", + "Network" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/", + "dependencies": [ + "AWSCore" + ] } diff --git a/Gems/AWSMetrics/Code/Tests/AWSMetricsServiceApiTest.cpp b/Gems/AWSMetrics/Code/Tests/AWSMetricsServiceApiTest.cpp index bbfc206ab0..8844e727b6 100644 --- a/Gems/AWSMetrics/Code/Tests/AWSMetricsServiceApiTest.cpp +++ b/Gems/AWSMetrics/Code/Tests/AWSMetricsServiceApiTest.cpp @@ -19,6 +19,8 @@ namespace AWSMetrics : public AWSCore::JsonReader { public: + virtual ~JsonReaderMock() = default; + MOCK_METHOD0(Ignore, bool()); MOCK_METHOD1(Accept, bool(bool& target)); MOCK_METHOD1(Accept, bool(AZStd::string& target)); diff --git a/Gems/AWSMetrics/cdk/aws_metrics/aws_metrics_stack.py b/Gems/AWSMetrics/cdk/aws_metrics/aws_metrics_stack.py index 337a14a301..41d70f83ec 100755 --- a/Gems/AWSMetrics/cdk/aws_metrics/aws_metrics_stack.py +++ b/Gems/AWSMetrics/cdk/aws_metrics/aws_metrics_stack.py @@ -53,6 +53,7 @@ class AWSMetricsStack(core.Stack): self._batch_processing = BatchProcessing( self, input_stream_arn=self._data_ingestion.input_stream_arn, + application_name=application_name, analytics_bucket_arn=self._data_lake_integration.analytics_bucket_arn, events_database_name=self._data_lake_integration.events_database_name, events_table_name=self._data_lake_integration.events_table_name @@ -60,6 +61,7 @@ class AWSMetricsStack(core.Stack): self._batch_analytics = BatchAnalytics( self, + application_name=application_name, analytics_bucket_name=self._data_lake_integration.analytics_bucket_name, events_database_name=self._data_lake_integration.events_database_name, events_table_name=self._data_lake_integration.events_table_name diff --git a/Gems/AWSMetrics/cdk/aws_metrics/batch_analytics.py b/Gems/AWSMetrics/cdk/aws_metrics/batch_analytics.py index 8398113bc4..709335f9ce 100755 --- a/Gems/AWSMetrics/cdk/aws_metrics/batch_analytics.py +++ b/Gems/AWSMetrics/cdk/aws_metrics/batch_analytics.py @@ -20,10 +20,12 @@ class BatchAnalytics: """ def __init__(self, stack: core.Construct, + application_name: str, analytics_bucket_name: str, events_database_name: str, events_table_name) -> None: self._stack = stack + self._application_name = application_name self._analytics_bucket_name = analytics_bucket_name self._events_database_name = events_database_name self._events_table_name = events_table_name @@ -58,6 +60,12 @@ class BatchAnalytics: ) ) ) + core.CfnOutput( + self._stack, + id='AthenaWorkGroupName', + description='Name of the Athena work group that contains sample queries', + export_name=f"{self._application_name}:AthenaWorkGroup", + value=self._athena_work_group.name) def _create_athena_queries(self) -> None: """ @@ -114,6 +122,9 @@ class BatchAnalytics: ) ] + for named_query in self._named_queries: + named_query.node.add_dependency(self._athena_work_group) + @property def athena_work_group_name(self) -> athena.CfnWorkGroup.name: return self._athena_work_group.name diff --git a/Gems/AWSMetrics/cdk/aws_metrics/batch_processing.py b/Gems/AWSMetrics/cdk/aws_metrics/batch_processing.py index 4dbb3b2120..803f6076c8 100755 --- a/Gems/AWSMetrics/cdk/aws_metrics/batch_processing.py +++ b/Gems/AWSMetrics/cdk/aws_metrics/batch_processing.py @@ -26,11 +26,13 @@ class BatchProcessing: """ def __init__(self, stack: core.Construct, + application_name: str, input_stream_arn: str, analytics_bucket_arn: str, events_database_name: str, events_table_name) -> None: self._stack = stack + self._application_name = application_name self._input_stream_arn = input_stream_arn self._analytics_bucket_arn = analytics_bucket_arn self._events_database_name = events_database_name @@ -60,6 +62,12 @@ class BatchProcessing: os.path.join(os.path.dirname(__file__), 'lambdas', 'events_processing_lambda')), role=self._events_processing_lambda_role ) + core.CfnOutput( + self._stack, + id='EventProcessingLambdaName', + description='Lambda function for processing metrics events data.', + export_name=f"{self._application_name}:EventProcessingLambda", + value=self._events_processing_lambda.function_name) def _create_events_processing_lambda_role(self, function_name: str) -> None: """ diff --git a/Gems/AWSMetrics/cdk/aws_metrics/dashboard.py b/Gems/AWSMetrics/cdk/aws_metrics/dashboard.py index 32ff0d9c84..616643ebb1 100755 --- a/Gems/AWSMetrics/cdk/aws_metrics/dashboard.py +++ b/Gems/AWSMetrics/cdk/aws_metrics/dashboard.py @@ -52,7 +52,7 @@ class Dashboard: max_width=aws_metrics_constants.DASHBOARD_MAX_WIDGET_WIDTH) ) - dashboard_output = core.CfnOutput( + core.CfnOutput( stack, id='DashboardName', description='CloudWatch dashboard to monitor the operational health and real-time metrics', diff --git a/Gems/AWSMetrics/cdk/aws_metrics/data_ingestion.py b/Gems/AWSMetrics/cdk/aws_metrics/data_ingestion.py index a21e629c38..cc11796ee9 100755 --- a/Gems/AWSMetrics/cdk/aws_metrics/data_ingestion.py +++ b/Gems/AWSMetrics/cdk/aws_metrics/data_ingestion.py @@ -69,14 +69,14 @@ class DataIngestion: cfn_rest_api.add_property_deletion_override("BodyS3Location") cfn_rest_api.add_property_override("FailOnWarnings", True) - api_id_output = core.CfnOutput( + core.CfnOutput( self._stack, id='RESTApiId', description='Service API Id for the analytics pipeline', export_name=f"{application_name}:RestApiId", value=self._rest_api.rest_api_id) - stage_output = core.CfnOutput( + core.CfnOutput( self._stack, id='RESTApiStage', description='Stage for the REST API deployment', diff --git a/Gems/AWSMetrics/cdk/aws_metrics/data_lake_integration.py b/Gems/AWSMetrics/cdk/aws_metrics/data_lake_integration.py index e47b1a95c2..a0b93eb212 100755 --- a/Gems/AWSMetrics/cdk/aws_metrics/data_lake_integration.py +++ b/Gems/AWSMetrics/cdk/aws_metrics/data_lake_integration.py @@ -67,7 +67,7 @@ class DataLakeIntegration: cfn_bucket = self._analytics_bucket.node.find_child('Resource') cfn_bucket.apply_removal_policy(core.RemovalPolicy.DESTROY) - analytics_bucket_output = core.CfnOutput( + core.CfnOutput( self._stack, id='AnalyticsBucketName', description='Name of the S3 bucket for storing metrics event data', @@ -89,6 +89,12 @@ class DataLakeIntegration: name=f'{self._stack.stack_name}-EventsDatabase'.lower() ) ) + core.CfnOutput( + self._stack, + id='EventDatabaseName', + description='Glue database for metrics events.', + export_name=f"{self._application_name}:EventsDatabase", + value=self._events_database.ref) def _create_events_table(self) -> None: """ @@ -199,7 +205,7 @@ class DataLakeIntegration: configuration=aws_metrics_constants.CRAWLER_CONFIGURATION ) - events_crawler_output = core.CfnOutput( + core.CfnOutput( self._stack, id='EventsCrawlerName', description='Glue Crawler to populate the AWS Glue Data Catalog with metrics events tables', diff --git a/Gems/AWSMetrics/cdk/aws_metrics/real_time_data_processing.py b/Gems/AWSMetrics/cdk/aws_metrics/real_time_data_processing.py index 4ef9caa022..ffd74a51a9 100755 --- a/Gems/AWSMetrics/cdk/aws_metrics/real_time_data_processing.py +++ b/Gems/AWSMetrics/cdk/aws_metrics/real_time_data_processing.py @@ -113,7 +113,7 @@ class RealTimeDataProcessing: ), ) - analytics_application_output = core.CfnOutput( + core.CfnOutput( self._stack, id='AnalyticsApplicationName', description='Kinesis Data Analytics application to process the real-time metrics data', @@ -199,6 +199,12 @@ class RealTimeDataProcessing: os.path.join(os.path.dirname(__file__), 'lambdas', 'analytics_processing_lambda')), role=self._analytics_processing_lambda_role ) + core.CfnOutput( + self._stack, + id='AnalyticsProcessingLambdaName', + description='Lambda function for sending processed data to CloudWatch.', + export_name=f"{self._application_name}:AnalyticsProcessingLambda", + value=self._analytics_processing_lambda.function_name) def _create_analytics_processing_lambda_role(self, function_name: str) -> iam.Role: """ diff --git a/Gems/AWSMetrics/gem.json b/Gems/AWSMetrics/gem.json index 5faeb66787..df16890012 100644 --- a/Gems/AWSMetrics/gem.json +++ b/Gems/AWSMetrics/gem.json @@ -5,9 +5,18 @@ "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS Metrics Gem provides a solution for AWS metrics submission and analytics.", - "canonical_tags": ["Gem"], - "user_tags": ["AWS", "Network", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AWS", + "Network", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/", + "dependencies": [ + "AWSCore" + ] } diff --git a/Gems/Achievements/Code/Source/AchievementsSystemComponent.h b/Gems/Achievements/Code/Source/AchievementsSystemComponent.h index 840974a2dc..25b28ae690 100644 --- a/Gems/Achievements/Code/Source/AchievementsSystemComponent.h +++ b/Gems/Achievements/Code/Source/AchievementsSystemComponent.h @@ -42,7 +42,7 @@ namespace Achievements //////////////////////////////////////////////////////////////////////////////////////// // AchievementsRequestBus interface implementation void UnlockAchievement(const UnlockAchievementParams& params) override; - void QueryAchievementDetails(const QueryAchievementParams& params); + void QueryAchievementDetails(const QueryAchievementParams& params) override; public: //////////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/Achievements/gem.json b/Gems/Achievements/gem.json index ca2bc7f3e9..bd643f471a 100644 --- a/Gems/Achievements/gem.json +++ b/Gems/Achievements/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Achievements Gem provides a target platform agnostic interface for retrieving achievement details and unlocking achievements.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Achievements"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Achievements" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/achievements/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/achievements/", + "dependencies": [] } diff --git a/Gems/AssetMemoryAnalyzer/gem.json b/Gems/AssetMemoryAnalyzer/gem.json index 544e0f7948..902102fa27 100644 --- a/Gems/AssetMemoryAnalyzer/gem.json +++ b/Gems/AssetMemoryAnalyzer/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Asset Memory Analyzer Gem provides tools to profile asset memory usage in Open 3D Engine through ImGUI (Immediate Mode Graphical User Interface).", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Utility", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Utility", + "Tools" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/asset-memory-analyzer/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/asset-memory-analyzer/", + "dependencies": [ + "ImGui" + ] } diff --git a/Gems/AssetValidation/Code/Source/AssetValidationSystemComponent.h b/Gems/AssetValidation/Code/Source/AssetValidationSystemComponent.h index 8f7a177ca2..e8b2c0acc3 100644 --- a/Gems/AssetValidation/Code/Source/AssetValidationSystemComponent.h +++ b/Gems/AssetValidation/Code/Source/AssetValidationSystemComponent.h @@ -92,7 +92,7 @@ namespace AssetValidation //////////////////////////////////////////////////////////////////////// // ArchiveNotificationBus interface implementation - void FileAccess(const char* filePath) /*override*/; + void FileAccess(const char* filePath) override /*override*/; //////////////////////////////////////////////////////////////////////// bool AddSeedList(const char* seedPath) override; diff --git a/Gems/AssetValidation/gem.json b/Gems/AssetValidation/gem.json index 83e37ddaf5..1e57f60dc4 100644 --- a/Gems/AssetValidation/gem.json +++ b/Gems/AssetValidation/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Asset Validation Gem provides seed-related commands to ensure assets have valid seeds for asset bundling.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Utility", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Utility", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/asset-validation/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/asset-validation/", + "dependencies": [] } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt b/Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt index dfeee011cc..1463124e27 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/CMakeLists.txt @@ -62,6 +62,7 @@ ly_add_target( 3rdParty::Qt::Core 3rdParty::Qt::Widgets 3rdParty::Qt::Gui + 3rdParty::astc-encoder 3rdParty::etc2comp 3rdParty::PVRTexTool 3rdParty::squish-ccr @@ -77,8 +78,6 @@ ly_add_target( Gem::Atom_RPI.Public Gem::Atom_RHI.Reflect Gem::Atom_Utils.Static - RUNTIME_DEPENDENCIES - 3rdParty::ASTCEncoder ) ly_add_source_properties( diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ASTCCompressor.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ASTCCompressor.cpp new file mode 100644 index 0000000000..4ef47c043d --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ASTCCompressor.cpp @@ -0,0 +1,322 @@ +/* + * 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 + + +namespace ImageProcessingAtom +{ + bool ASTCCompressor::IsCompressedPixelFormatSupported(EPixelFormat fmt) + { + return IsASTCFormat(fmt); + } + + bool ASTCCompressor::IsUncompressedPixelFormatSupported(EPixelFormat fmt) + { + // astc encoder requires the compress input image or decompress output image to have four channels + switch (fmt) + { + // uint 8 + case ePixelFormat_R8G8B8A8: + case ePixelFormat_R8G8B8X8: + // fp16 + case ePixelFormat_R16G16B16A16F: + // fp32 + case ePixelFormat_R32G32B32A32F: + return true; + default: + return false; + } + } + + EPixelFormat ASTCCompressor::GetSuggestedUncompressedFormat([[maybe_unused]] EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) const + { + if (IsUncompressedPixelFormatSupported(uncompressedfmt)) + { + return uncompressedfmt; + } + + auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(uncompressedfmt); + switch (formatInfo->eSampleType) + { + case ESampleType::eSampleType_Half: + return ePixelFormat_R16G16B16A16F; + case ESampleType::eSampleType_Float: + return ePixelFormat_R32G32B32A32F; + } + + return ePixelFormat_R8G8B8A8; + } + + ColorSpace ASTCCompressor::GetSupportedColorSpace([[maybe_unused]] EPixelFormat compressFormat) const + { + return ColorSpace::autoSelect; + } + + const char* ASTCCompressor::GetName() const + { + return "ASTCCompressor"; + } + + bool ASTCCompressor::DoesSupportDecompress([[maybe_unused]] EPixelFormat fmtDst) + { + return true; + } + + astcenc_profile GetAstcProfile(bool isSrgb, EPixelFormat pixelFormat) + { + // select profile depends on LDR or HDR, SRGB or Linear + // ASTCENC_PRF_LDR + // ASTCENC_PRF_LDR_SRGB + // ASTCENC_PRF_HDR_RGB_LDR_A + // ASTCENC_PRF_HDR + + auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat); + bool isHDR = formatInfo->eSampleType == ESampleType::eSampleType_Half || formatInfo->eSampleType == ESampleType::eSampleType_Float; + astcenc_profile profile; + if (isHDR) + { + // HDR is not support in core vulkan 1.1 for android. + // https://arm-software.github.io/vulkan-sdk/_a_s_t_c.html + profile = isSrgb?ASTCENC_PRF_HDR_RGB_LDR_A:ASTCENC_PRF_HDR; + } + else + { + + profile = isSrgb?ASTCENC_PRF_LDR_SRGB:ASTCENC_PRF_LDR; + } + return profile; + } + + astcenc_type GetAstcDataType(EPixelFormat pixelFormat) + { + auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat); + astcenc_type dataType = ASTCENC_TYPE_U8; + + switch (formatInfo->eSampleType) + { + case ESampleType::eSampleType_Uint8: + dataType = ASTCENC_TYPE_U8; + break; + case ESampleType::eSampleType_Half: + dataType = ASTCENC_TYPE_F16; + break; + case ESampleType::eSampleType_Float: + dataType = ASTCENC_TYPE_F32; + break; + default: + dataType = ASTCENC_TYPE_U8; + AZ_Assert(false, "Unsupport uncompressed format %s", formatInfo->szName); + break; + } + + return dataType; + } + + float GetAstcCompressQuality(ICompressor::EQuality quality) + { + switch (quality) + { + case ICompressor::EQuality::eQuality_Fast: + return ASTCENC_PRE_FAST; + case ICompressor::EQuality::eQuality_Slow: + return ASTCENC_PRE_THOROUGH; + case ICompressor::EQuality::eQuality_Preview: + case ICompressor::EQuality::eQuality_Normal: + default: + return ASTCENC_PRE_MEDIUM; + } + } + + IImageObjectPtr ASTCCompressor::CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption* compressOption) const + { + //validate input + EPixelFormat fmtSrc = srcImage->GetPixelFormat(); + + //src format need to be uncompressed and dst format need to compressed. + if (!IsUncompressedPixelFormatSupported(fmtSrc) || !IsCompressedPixelFormatSupported(fmtDst)) + { + return nullptr; + } + + astcenc_swizzle swizzle {ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, compressOption->discardAlpha? ASTCENC_SWZ_1:ASTCENC_SWZ_A}; + AZ::u32 flags = 0; + if (srcImage->HasImageFlags(EIF_RenormalizedTexture)) + { + ImageToProcess imageToProcess(srcImage); + imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8X8); + srcImage = imageToProcess.Get(); + fmtSrc = srcImage->GetPixelFormat(); + + flags = ASTCENC_FLG_MAP_NORMAL; + swizzle = astcenc_swizzle{ ASTCENC_SWZ_R, ASTCENC_SWZ_R, ASTCENC_SWZ_R, ASTCENC_SWZ_G }; + } + + auto dstFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtDst); + + const float quality = GetAstcCompressQuality(compressOption->compressQuality); + const astcenc_profile profile = GetAstcProfile(srcImage->HasImageFlags(EIF_SRGBRead), fmtSrc); + + astcenc_config config; + astcenc_error status; + status = astcenc_config_init(profile, dstFormatInfo->blockWidth, dstFormatInfo->blockHeight, 1, quality, flags, &config); + + //ASTCENC_FLG_MAP_NORMAL + AZ_Assert( status == ASTCENC_SUCCESS, "ERROR: Codec config init failed: %s\n", astcenc_get_error_string(status)); + + // Create a context based on the configuration + astcenc_context* context; + AZ::u32 blockCount = ((srcImage->GetWidth(0)+ dstFormatInfo->blockWidth-1)/dstFormatInfo->blockWidth) * ((srcImage->GetHeight(0) + dstFormatInfo->blockHeight-1)/dstFormatInfo->blockHeight); + AZ::u32 threadCount = AZStd::min(AZStd::thread::hardware_concurrency(), blockCount); + status = astcenc_context_alloc(&config, threadCount, &context); + AZ_Assert( status == ASTCENC_SUCCESS, "ERROR: Codec context alloc failed: %s\n", astcenc_get_error_string(status)); + + const astcenc_type dataType =GetAstcDataType(fmtSrc); + + // Compress the image for each mips + IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst)); + const AZ::u32 dstMips = dstImage->GetMipCount(); + for (AZ::u32 mip = 0; mip < dstMips; ++mip) + { + astcenc_image image; + image.dim_x = srcImage->GetWidth(mip); + image.dim_y = srcImage->GetHeight(mip); + image.dim_z = 1; + image.data_type = dataType; + + AZ::u8* srcMem; + AZ::u32 srcPitch; + srcImage->GetImagePointer(mip, srcMem, srcPitch); + image.data = reinterpret_cast(&srcMem); + + AZ::u8* dstMem; + AZ::u32 dstPitch; + dstImage->GetImagePointer(mip, dstMem, dstPitch); + AZ::u32 dataSize = dstImage->GetMipBufSize(mip); + + // Create jobs for each compression thread + auto completionJob = aznew AZ::JobCompletion(); + for (AZ::u32 threadIdx = 0; threadIdx < threadCount; threadIdx++) + { + const auto jobLambda = [&status, context, &image, &swizzle, dstMem, dataSize, threadIdx]() + { + astcenc_error error = astcenc_compress_image(context, &image, &swizzle, dstMem, dataSize, threadIdx); + if (error != ASTCENC_SUCCESS) + { + status = error; + } + }; + + AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes + simulationJob->SetDependent(completionJob); + simulationJob->Start(); + } + + if (completionJob) + { + completionJob->StartAndWaitForCompletion(); + delete completionJob; + completionJob = nullptr; + } + + if (status != ASTCENC_SUCCESS) + { + AZ_Error("Image Processing", false, "ASTCCompressor::CompressImage failed: %s\n", astcenc_get_error_string(status)); + astcenc_context_free(context); + return nullptr; + } + + // Need to reset to compress next mip + astcenc_compress_reset(context); + } + astcenc_context_free(context); + + return dstImage; + } + + IImageObjectPtr ASTCCompressor::DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const + { + //validate input + EPixelFormat fmtSrc = srcImage->GetPixelFormat(); //compressed + auto srcFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtSrc); + + if (!IsCompressedPixelFormatSupported(fmtSrc) || !IsUncompressedPixelFormatSupported(fmtDst)) + { + return nullptr; + } + + const float quality = ASTCENC_PRE_MEDIUM; + astcenc_swizzle swizzle {ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A}; + if (srcImage->HasImageFlags(EIF_RenormalizedTexture)) + { + swizzle = astcenc_swizzle{ASTCENC_SWZ_R, ASTCENC_SWZ_A, ASTCENC_SWZ_Z, ASTCENC_SWZ_1}; + } + + astcenc_config config; + astcenc_error status; + astcenc_profile profile = GetAstcProfile(srcImage->HasImageFlags(EIF_SRGBRead), fmtDst); + AZ::u32 flags = ASTCENC_FLG_DECOMPRESS_ONLY; + status = astcenc_config_init(profile, srcFormatInfo->blockWidth, srcFormatInfo->blockHeight, 1, quality, flags, &config); + + //ASTCENC_FLG_MAP_NORMAL + AZ_Assert( status == ASTCENC_SUCCESS, "astcenc_config_init failed: %s\n", astcenc_get_error_string(status)); + + // Create a context based on the configuration + const AZ::u32 threadCount = 1; // Decompress function doesn't support multiple threads + astcenc_context* context; + status = astcenc_context_alloc(&config, threadCount, &context); + AZ_Assert( status == ASTCENC_SUCCESS, "astcenc_context_alloc failed: %s\n", astcenc_get_error_string(status)); + + astcenc_type dataType =GetAstcDataType(fmtDst); + + // Decompress the image for each mips + IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst)); + const AZ::u32 dstMips = dstImage->GetMipCount(); + for (AZ::u32 mip = 0; mip < dstMips; ++mip) + { + astcenc_image image; + image.dim_x = srcImage->GetWidth(mip); + image.dim_y = srcImage->GetHeight(mip); + image.dim_z = 1; + image.data_type = dataType; + + AZ::u8* srcMem; + AZ::u32 srcPitch; + srcImage->GetImagePointer(mip, srcMem, srcPitch); + AZ::u32 srcDataSize = srcImage->GetMipBufSize(mip); + + AZ::u8* dstMem; + AZ::u32 dstPitch; + dstImage->GetImagePointer(mip, dstMem, dstPitch); + image.data = reinterpret_cast(&dstMem); + + status = astcenc_decompress_image(context, srcMem, srcDataSize, &image, &swizzle, 0); + + if (status != ASTCENC_SUCCESS) + { + AZ_Error("Image Processing", false, "ASTCCompressor::DecompressImage failed: %s\n", astcenc_get_error_string(status)); + astcenc_context_free(context); + return nullptr; + } + } + + astcenc_context_free(context); + + return dstImage; + } +} //namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ASTCCompressor.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ASTCCompressor.h new file mode 100644 index 0000000000..cf191c7072 --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ASTCCompressor.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 + +namespace ImageProcessingAtom +{ + class ASTCCompressor + : public ICompressor + { + public: + static bool IsCompressedPixelFormatSupported(EPixelFormat fmt); + static bool IsUncompressedPixelFormatSupported(EPixelFormat fmt); + static bool DoesSupportDecompress(EPixelFormat fmtDst); + + IImageObjectPtr CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption* compressOption) const override; + IImageObjectPtr DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const override; + + EPixelFormat GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) const override; + ColorSpace GetSupportedColorSpace(EPixelFormat compressFormat) const final; + const char* GetName() const final; + }; +} // namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/Compressor.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/Compressor.cpp index 9013bff1db..1dd18faaf3 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/Compressor.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/Compressor.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -15,11 +16,8 @@ namespace ImageProcessingAtom { - ICompressorPtr ICompressor::FindCompressor(EPixelFormat fmt, [[maybe_unused]] ColorSpace colorSpace, bool isCompressing) + ICompressorPtr ICompressor::FindCompressor(EPixelFormat fmt, ColorSpace colorSpace, bool isCompressing) { - // The ISPC texture compressor is able to compress BC1, BC3, BC6H and BC7 formats, and all of the ASTC formats. - // Note: The ISPC texture compressor is only able to compress images that are a multiple of the compressed format's blocksize. - // Another limitation is that the compressor requires LDR source images to be in sRGB colorspace. if (ISPCCompressor::IsCompressedPixelFormatSupported(fmt)) { if ((isCompressing && ISPCCompressor::IsSourceColorSpaceSupported(colorSpace, fmt)) || (!isCompressing && ISPCCompressor::DoesSupportDecompress(fmt))) @@ -35,6 +33,14 @@ namespace ImageProcessingAtom return ICompressorPtr(new CTSquisher()); } } + + if (ASTCCompressor::IsCompressedPixelFormatSupported(fmt)) + { + if (isCompressing || (!isCompressing && ASTCCompressor::DoesSupportDecompress(fmt))) + { + return ICompressorPtr(new ASTCCompressor()); + } + } // Both ETC2Compressor and PVRTCCompressor can process ETC formats // According to Mobile team, Etc2Com is faster than PVRTexLib, so we check with ETC2Compressor before PVRTCCompressor diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/Compressor.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/Compressor.h index dff71e59db..6920ae0cc1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/Compressor.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/Compressor.h @@ -38,8 +38,7 @@ namespace ImageProcessingAtom EQuality compressQuality = eQuality_Normal; //required for CTSquisher AZ::Vector3 rgbWeight = AZ::Vector3(0.3333f, 0.3334f, 0.3333f); - //required for ISPC texture compressor - bool ispcDiscardAlpha = false; + bool discardAlpha = false; }; public: diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ISPCTextureCompressor.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ISPCTextureCompressor.cpp index 5e96c7274d..b1b41d98bc 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ISPCTextureCompressor.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/ISPCTextureCompressor.cpp @@ -57,6 +57,12 @@ namespace ImageProcessingAtom bool ISPCCompressor::IsCompressedPixelFormatSupported(EPixelFormat fmt) { + // Even though the ISPC compressor support ASTC formats. But it has restrictions + // 1. Only supports LDR color profile + // 2. Only supports a subset of 2D block sizes + // Also it has overall lower quality compare to astc-encoder + // So we won't add ASTC as part of supported formats here + // Ref: https://solidpixel.github.io/2020/03/02/astc-compared.html switch (fmt) { case ePixelFormat_BC3: @@ -152,7 +158,7 @@ namespace ImageProcessingAtom if (compressOption) { quality = compressOption->compressQuality; - discardAlpha = compressOption->ispcDiscardAlpha; + discardAlpha = compressOption->discardAlpha; } // Get the compression profile @@ -230,24 +236,12 @@ namespace ImageProcessingAtom } break; default: - if (IsASTCFormat(destinationFormat)) - { - const PixelFormatInfo* info = CPixelFormats::GetInstance().GetPixelFormatInfo(destinationFormat); - astc_enc_settings settings = {}; - - const auto setProfile = compressionProfile->GetASTC(discardAlpha); - setProfile(&settings, info->blockWidth, info->blockHeight); - - // Compress with ASTC - CompressBlocksASTC(&sourceSurface, destinationImageData, &settings); - } - else - { - // No valid pixel format - AZ_Assert(false, "Unhandled pixel format %d", destinationFormat); - return nullptr; - } - break; + { + // No valid pixel format + AZ_Assert(false, "Unhandled pixel format %d", destinationFormat); + return nullptr; + } + break; } } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/PVRTC.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/PVRTC.cpp index 7c899e4ad2..f3a630e8c1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/PVRTC.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Compressors/PVRTC.cpp @@ -19,7 +19,7 @@ namespace ImageProcessingAtom { - // Note: PVRTexLib supports ASTC formats, ETC formats, PVRTC formats and BC formats + // Note: PVRTexLib supports ETC formats, PVRTC formats and BC formats // We haven't tested the performace to compress BC formats compare to CTSquisher // For PVRTC formats, we only added PVRTC 1 support for now // The compression for ePVRTPF_EAC_R11 and ePVRTPF_EAC_RG11 are very slow. It takes 7 and 14 minutes for a 2048x2048 texture. @@ -27,34 +27,6 @@ namespace ImageProcessingAtom { switch (fmt) { - case ePixelFormat_ASTC_4x4: - return ePVRTPF_ASTC_4x4; - case ePixelFormat_ASTC_5x4: - return ePVRTPF_ASTC_5x4; - case ePixelFormat_ASTC_5x5: - return ePVRTPF_ASTC_5x5; - case ePixelFormat_ASTC_6x5: - return ePVRTPF_ASTC_6x5; - case ePixelFormat_ASTC_6x6: - return ePVRTPF_ASTC_6x6; - case ePixelFormat_ASTC_8x5: - return ePVRTPF_ASTC_8x5; - case ePixelFormat_ASTC_8x6: - return ePVRTPF_ASTC_8x6; - case ePixelFormat_ASTC_8x8: - return ePVRTPF_ASTC_8x8; - case ePixelFormat_ASTC_10x5: - return ePVRTPF_ASTC_10x5; - case ePixelFormat_ASTC_10x6: - return ePVRTPF_ASTC_10x6; - case ePixelFormat_ASTC_10x8: - return ePVRTPF_ASTC_10x8; - case ePixelFormat_ASTC_10x10: - return ePVRTPF_ASTC_10x10; - case ePixelFormat_ASTC_12x10: - return ePVRTPF_ASTC_12x10; - case ePixelFormat_ASTC_12x12: - return ePVRTPF_ASTC_12x12; case ePixelFormat_PVRTC2: return ePVRTPF_PVRTCI_2bpp_RGBA; case ePixelFormat_PVRTC4: @@ -156,26 +128,7 @@ namespace ImageProcessingAtom internalQuality = pvrtexture::eETCSlow; } } - else if (IsASTCFormat(fmtDst)) - { - if (quality == eQuality_Preview) - { - internalQuality = pvrtexture::eASTCVeryFast; - } - else if (quality == eQuality_Fast) - { - internalQuality = pvrtexture::eASTCFast; - } - else if (quality == eQuality_Normal) - { - internalQuality = pvrtexture::eASTCMedium; - } - else - { - internalQuality = pvrtexture::eASTCThorough; - } - } - else + else { if (quality == eQuality_Preview) { @@ -252,7 +205,7 @@ namespace ImageProcessingAtom if (!isSuccess) { - AZ_Error("Image Processing", false, "Failed to compress image with PVRTexLib. You may not have astcenc.exe for compressing ASTC formates"); + AZ_Error("Image Processing", false, "Failed to compress image with PVRTexLib."); return nullptr; } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/PixelOperation.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/PixelOperation.cpp index 3e897078fa..fa4baeefc0 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/PixelOperation.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/PixelOperation.cpp @@ -248,8 +248,8 @@ namespace ImageProcessingAtom { const uint8* data = buf; r = U8ToF32(data[0]); - g = 0.f; - b = 0.f; + g = r; + b = r; a = 1.f; } @@ -333,8 +333,8 @@ namespace ImageProcessingAtom { const uint16* data = (uint16*)(buf); r = U16ToF32(data[0]); - g = 0.f; - b = 0.f; + g = r; + b = r; a = 1.f; } @@ -418,8 +418,8 @@ namespace ImageProcessingAtom { const float* data = (float*)(buf); r = data[0]; - g = 0.f; - b = 0.f; + g = r; + b = r; a = 1.f; } @@ -485,8 +485,8 @@ namespace ImageProcessingAtom { const SHalf* data = (SHalf*)(buf); r = data[0]; - g = 0.f; - b = 0.f; + g = r; + b = r; a = 1.f; } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/MipmapSettingWidget.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/MipmapSettingWidget.h index 9ea7f9c6ae..27efdb084b 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/MipmapSettingWidget.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/MipmapSettingWidget.h @@ -47,7 +47,7 @@ namespace ImageProcessingAtomEditor protected: //////////////////////////////////////////////////////////////////////// //EditorInternalNotificationBus - void OnEditorSettingsChanged(bool needRefresh, const AZStd::string& platform); + void OnEditorSettingsChanged(bool needRefresh, const AZStd::string& platform) override; //////////////////////////////////////////////////////////////////////// private: diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePreviewWidget.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePreviewWidget.h index db3c95c142..d97698cc63 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePreviewWidget.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePreviewWidget.h @@ -70,7 +70,7 @@ namespace ImageProcessingAtomEditor protected: //////////////////////////////////////////////////////////////////////// //EditorInternalNotificationBus - void OnEditorSettingsChanged(bool needRefresh, const AZStd::string& platform); + void OnEditorSettingsChanged(bool needRefresh, const AZStd::string& platform) override; //////////////////////////////////////////////////////////////////////// void resizeEvent(QResizeEvent* event) override; bool eventFilter(QObject* obj, QEvent* event) override; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePropertyEditor.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePropertyEditor.h index 988561f3ce..83bcec9c83 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePropertyEditor.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/TexturePropertyEditor.h @@ -52,7 +52,7 @@ namespace ImageProcessingAtomEditor //////////////////////////////////////////////////////////////////////// //EditorInternalNotificationBus - void OnEditorSettingsChanged(bool needRefresh, const AZStd::string& platform); + void OnEditorSettingsChanged(bool needRefresh, const AZStd::string& platform) override; //////////////////////////////////////////////////////////////////////// bool event(QEvent* event) override; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp index 9b41645280..5995146c59 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp @@ -117,6 +117,11 @@ namespace ImageProcessingAtom } } + const ImageConvertProcessDescriptor* ImageConvertProcess::GetInputDesc() const + { + return m_input.get(); + } + ImageConvertProcess::ImageConvertProcess(AZStd::unique_ptr&& descriptor) : m_image(nullptr) , m_progressStep(0) @@ -554,12 +559,6 @@ namespace ImageProcessingAtom // pixel format conversion bool ImageConvertProcess::ConvertPixelformat() { - //For ASTC compression we need to clear out the alpha to get accurate rgb compression. - if(m_alphaImage && IsASTCFormat(m_input->m_presetSetting.m_pixelFormat)) - { - m_image->Get()->Swizzle("rgb1"); - } - //set up compress option ICompressor::EQuality quality; if (m_input->m_isPreview) @@ -574,7 +573,14 @@ namespace ImageProcessingAtom // set the compression options m_image->GetCompressOption().compressQuality = quality; m_image->GetCompressOption().rgbWeight = m_input->m_presetSetting.GetColorWeight(); - m_image->GetCompressOption().ispcDiscardAlpha = m_input->m_presetSetting.m_discardAlpha; + m_image->GetCompressOption().discardAlpha = m_input->m_presetSetting.m_discardAlpha; + + //For ASTC compression we need to clear out the alpha to get accurate rgb compression. + if(m_alphaImage && IsASTCFormat(m_input->m_presetSetting.m_pixelFormat)) + { + m_image->GetCompressOption().discardAlpha = true; + } + m_image->ConvertFormat(m_input->m_presetSetting.m_pixelFormat); return true; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h index 190e68ca31..6ab866ee09 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h @@ -122,6 +122,8 @@ namespace ImageProcessingAtom // Get output JobProducts and append them to the outProducts vector. void GetAppendOutputProducts(AZStd::vector& outProducts); + const ImageConvertProcessDescriptor* GetInputDesc() const; + private: //input image and settings AZStd::shared_ptr m_input; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp index 0656fb4e46..c4240306c4 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp @@ -12,8 +12,12 @@ #include +#include + #include #include +#include +#include #include #include #include @@ -123,6 +127,9 @@ namespace UnitTest AZStd::string m_outputRootFolder; AZStd::string m_outputFolder; + AZStd::unique_ptr m_jobManager; + AZStd::unique_ptr m_jobContext; + void SetUp() override { AllocatorsBase::SetupAllocator(); @@ -159,6 +166,27 @@ namespace UnitTest m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get()); BuilderPluginComponent::Reflect(m_jsonRegistrationContext.get()); + // Setup job context for job system + JobManagerDesc jobManagerDesc; + JobManagerThreadDesc threadDesc; +#if AZ_TRAIT_SET_JOB_PROCESSOR_ID + threadDesc.m_cpuId = 0; // Don't set processors IDs on windows +#endif + + uint32_t numWorkerThreads = AZStd::thread::hardware_concurrency(); + + for (unsigned int i = 0; i < numWorkerThreads; ++i) + { + jobManagerDesc.m_workerThreads.push_back(threadDesc); +#if AZ_TRAIT_SET_JOB_PROCESSOR_ID + threadDesc.m_cpuId++; +#endif + } + + m_jobManager = AZStd::make_unique(jobManagerDesc); + m_jobContext = AZStd::make_unique(*m_jobManager); + JobContext::SetGlobalContext(m_jobContext.get()); + // Startup default local FileIO (hits OSAllocator) if not already setup. if (AZ::IO::FileIOBase::GetInstance() == nullptr) { @@ -192,6 +220,10 @@ namespace UnitTest delete AZ::IO::FileIOBase::GetInstance(); AZ::IO::FileIOBase::SetInstance(nullptr); + JobContext::SetGlobalContext(nullptr); + m_jobContext = nullptr; + m_jobManager = nullptr; + m_jsonRegistrationContext->EnableRemoveReflection(); m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get()); BuilderPluginComponent::Reflect(m_jsonRegistrationContext.get()); @@ -223,7 +255,7 @@ namespace UnitTest Image_512X288_RGB8_Tga, Image_1024X1024_RGB8_Tif, Image_UpperCase_Tga, - Image_512x512_Normal_Tga, // QImage doesn't support loading this file. + Image_1024x1024_normal_tiff, Image_128x128_Transparent_Tga, Image_237x177_RGB_Jpg, Image_GreyScale_Png, @@ -251,7 +283,7 @@ namespace UnitTest m_imagFileNameMap[Image_512X288_RGB8_Tga] = m_testFileFolder + "512x288_24bit.tga"; m_imagFileNameMap[Image_1024X1024_RGB8_Tif] = m_testFileFolder + "1024x1024_24bit.tif"; m_imagFileNameMap[Image_UpperCase_Tga] = m_testFileFolder + "uppercase.TGA"; - m_imagFileNameMap[Image_512x512_Normal_Tga] = m_testFileFolder + "512x512_RGB_N.tga"; + m_imagFileNameMap[Image_1024x1024_normal_tiff] = m_testFileFolder + "1024x1024_normal.tiff"; m_imagFileNameMap[Image_128x128_Transparent_Tga] = m_testFileFolder + "128x128_RGBA8.tga"; m_imagFileNameMap[Image_237x177_RGB_Jpg] = m_testFileFolder + "237x177_RGB.jpg"; m_imagFileNameMap[Image_GreyScale_Png] = m_testFileFolder + "greyscale.png"; @@ -801,8 +833,7 @@ namespace UnitTest auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat); if (formatInfo->bCompressed) { - // exclude astc formats until we add astc compressor to all platforms - // exclude pvrtc formats (deprecating) + // skip ASTC formats which are tested in TestConvertASTCCompressor if (!IsASTCFormat(pixelFormat) && pixelFormat != ePixelFormat_PVRTC2 && pixelFormat != ePixelFormat_PVRTC4 && !IsETCFormat(pixelFormat)) // skip ETC since it's very slow @@ -830,32 +861,121 @@ namespace UnitTest continue; } - [[maybe_unused]] auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat); - imageToProcess.Set(srcImage); - imageToProcess.ConvertFormat(pixelFormat); + auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat); + ColorSpace sourceColorSpace = srcImage->HasImageFlags(EIF_SRGBRead) ? ColorSpace::sRGB : ColorSpace::linear; + ICompressorPtr compressor = ICompressor::FindCompressor(pixelFormat, sourceColorSpace, true); - if (!imageToProcess.Get()) + if (!compressor) { AZ_Warning("test", false, "unsupported format: %s", formatInfo->szName); continue; } + + imageToProcess.Set(srcImage); + imageToProcess.ConvertFormat(pixelFormat); + ASSERT_TRUE(imageToProcess.Get()); ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == pixelFormat); - // Get compressor name - ColorSpace sourceColorSpace = srcImage->HasImageFlags(EIF_SRGBRead) ? ColorSpace::sRGB : ColorSpace::linear; - ICompressorPtr compressor = ICompressor::FindCompressor(pixelFormat, sourceColorSpace, true); + //convert back to an uncompressed format and expect it will be successful + imageToProcess.ConvertFormat(srcImage->GetPixelFormat()); + ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == srcImage->GetPixelFormat()); - //save the image to a file so we can check the visual result + // Save the image to a file so we can check the visual result AZStd::string outputName = AZStd::string::format("%s_%s", imageName.c_str(), compressor->GetName()); SaveImageToFile(imageToProcess.Get(), outputName, 1); - - //convert back to an uncompressed format and expect it will be successful - imageToProcess.ConvertFormat(ePixelFormat_R8G8B8A8); - ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == ePixelFormat_R8G8B8A8); } } } + + TEST_F(ImageProcessingTest, Test_ConvertAllAstc_Success) + { + // Compress/Decompress to all astc formats (LDR) + auto imageIdx = Image_237x177_RGB_Jpg; + IImageObjectPtr srcImage = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[imageIdx])); + QFileInfo fi(m_imagFileNameMap[imageIdx].c_str()); + AZStd::string imageName = fi.baseName().toUtf8().constData(); + for (uint32 i = 0; i < ePixelFormat_Count; i++) + { + EPixelFormat pixelFormat = (EPixelFormat)i; + if (IsASTCFormat(pixelFormat)) + { + ImageToProcess imageToProcess(srcImage); + imageToProcess.ConvertFormat(pixelFormat); + + ASSERT_TRUE(imageToProcess.Get()); + ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == pixelFormat); + ASSERT_TRUE(imageToProcess.Get()->GetWidth(0) == srcImage->GetWidth(0)); + ASSERT_TRUE(imageToProcess.Get()->GetHeight(0) == srcImage->GetHeight(0)); + + // convert back to an uncompressed format and expect it will be successful + imageToProcess.ConvertFormat(srcImage->GetPixelFormat()); + ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == srcImage->GetPixelFormat()); + + // save the image to a file so we can check the visual result + AZStd::string outputName = AZStd::string::format("ASTC_%s", imageName.c_str()); + SaveImageToFile(imageToProcess.Get(), outputName, 1); + } + } + } + + TEST_F(ImageProcessingTest, Test_ConvertHdrToAstc_Success) + { + // Compress/Decompress HDR + auto imageIdx = Image_defaultprobe_cm_1536x256_64bits_tif; + IImageObjectPtr srcImage = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[imageIdx])); + + EPixelFormat dstFormat = ePixelFormat_ASTC_4x4; + ImageToProcess imageToProcess(srcImage); + imageToProcess.ConvertFormat(ePixelFormat_ASTC_4x4); + + ASSERT_TRUE(imageToProcess.Get()); + ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == dstFormat); + ASSERT_TRUE(imageToProcess.Get()->GetWidth(0) == srcImage->GetWidth(0)); + ASSERT_TRUE(imageToProcess.Get()->GetHeight(0) == srcImage->GetHeight(0)); + + //convert back to an uncompressed format and expect it will be successful + imageToProcess.ConvertFormat(srcImage->GetPixelFormat()); + ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == srcImage->GetPixelFormat()); + + //save the image to a file so we can check the visual result + SaveImageToFile(imageToProcess.Get(), "ASTC_HDR", 1); + } + + TEST_F(ImageProcessingTest, Test_AstcNormalPreset_Success) + { + // Normal.preset which uses ASTC as output format + // This test compress a normal texture and its mipmaps + + auto outcome = BuilderSettingManager::Instance()->LoadConfigFromFolder(m_defaultSettingFolder); + ASSERT_TRUE(outcome.IsSuccess()); + + AZStd::string inputFile; + AZStd::vector outProducts; + + inputFile = m_imagFileNameMap[Image_1024x1024_normal_tiff]; + IImageObjectPtr srcImage = IImageObjectPtr(LoadImageFromFile(inputFile)); + + ImageConvertProcess* process = CreateImageConvertProcess(inputFile, m_outputFolder, "ios", outProducts, m_context.get()); + + const PresetSettings* preset = &process->GetInputDesc()->m_presetSetting; + + if (process != nullptr) + { + process->ProcessAll(); + + //get process result + ASSERT_TRUE(process->IsSucceed()); + auto outputImage = process->GetOutputImage(); + ASSERT_TRUE(outputImage->GetPixelFormat() == preset->m_pixelFormat); + ASSERT_TRUE(outputImage->GetWidth(0) == srcImage->GetWidth(0)); + ASSERT_TRUE(outputImage->GetHeight(0) == srcImage->GetHeight(0)); + + SaveImageToFile(outputImage, "ASTC_Normal", 10); + + delete process; + } + } TEST_F(ImageProcessingTest, DISABLED_TestImageFilter) { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_normal.tiff b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_normal.tiff new file mode 100644 index 0000000000..8120514ddc --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_normal.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:120aaf43057b07fb3c784264eb48b899cc612f30917d316fe843bb839220dc22 +size 203062 diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/512x512_RGB_N.tga b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/512x512_RGB_N.tga deleted file mode 100644 index d47f00912a..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/512x512_RGB_N.tga +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:646a9a9035cc3f4dfd57babc0055710d2f5bb8aee0a792f2b65d69b4fd6a94b3 -size 786450 diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake b/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake index 55ccdf1d89..ba9e9f29b7 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake @@ -114,6 +114,8 @@ set(FILES ../External/CubeMapGen/CImageSurface.cpp ../External/CubeMapGen/CImageSurface.h ../External/CubeMapGen/VectorMacros.h + Source/Compressors/ASTCCompressor.cpp + Source/Compressors/ASTCCompressor.h Source/Compressors/Compressor.h Source/Compressors/Compressor.cpp Source/Compressors/CTSquisher.h diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset index 3a5122f18e..e0d043cf20 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset @@ -39,7 +39,7 @@ "_bc", "_diffuse" ], - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "DiscardAlpha": true, "IsPowerOf2": true, "MipMapSetting": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset index 692ef99b1c..fda5a9cc52 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset @@ -36,7 +36,7 @@ "_bc", "_diffuse" ], - "PixelFormat": "ETC2a1", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset index 4ebe773f0e..8c196530e9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset @@ -36,7 +36,7 @@ "_bc", "_diffuse" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset index 6049ef5bd4..346f0f8cac 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset @@ -36,7 +36,7 @@ "_bc", "_diffuse" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset index 56dec20f3e..638e31f838 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset @@ -28,7 +28,7 @@ "_amb", "_ambientocclusion" ], - "PixelFormat": "EAC_R11" + "PixelFormat": "ASTC_4x4" }, "ios": { "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}", @@ -41,7 +41,7 @@ "_amb", "_ambientocclusion" ], - "PixelFormat": "EAC_R11" + "PixelFormat": "ASTC_4x4" }, "mac": { "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset index 2280a06302..8d1105cdfc 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset @@ -15,14 +15,14 @@ "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", "Name": "CloudShadows", "DestColor": "Linear", - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true }, "ios": { "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", "Name": "CloudShadows", "DestColor": "Linear", - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true }, "mac": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset index f1e43e74b1..628cd673e9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset @@ -24,13 +24,13 @@ "FileMasks": [ "_decal" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" }, // Decal Texture Arrays need all mips available immediately for packing. - "NumberResidentMips": 255 + "NumberResidentMips": 255 }, "ios": { "UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset index 991692c5cc..5bfea9a376 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset @@ -26,7 +26,7 @@ "FileMasks": [ "_detail" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset index 520e4ae193..8f21ad005b 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset @@ -45,7 +45,7 @@ "_ht", "_h" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "DiscardAlpha": true, "IsPowerOf2": true, "SizeReduceLevel": 3, @@ -70,7 +70,7 @@ "_ht", "_h" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "DiscardAlpha": true, "IsPowerOf2": true, "MipMapSetting": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset index ffb16482fd..8f6c846e82 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset @@ -29,7 +29,7 @@ "_em", "_emit" ], - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "DiscardAlpha": true }, "ios": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset index f06682be42..c71ada1269 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset @@ -26,7 +26,7 @@ "FileMasks": [ "_mask" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" @@ -40,7 +40,7 @@ "FileMasks": [ "_mask" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset index 9f4b5bf68d..d277785151 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset @@ -12,7 +12,7 @@ "android": { "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", "Name": "LensOptics", - "PixelFormat": "ETC2" + "PixelFormat": "ASTC_4x4" }, "ios": { "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset index ede264a78e..4de95427d6 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset @@ -18,7 +18,7 @@ "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", "Name": "LightProjector", "DestColor": "Linear", - "PixelFormat": "EAC_RG11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" @@ -28,7 +28,7 @@ "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", "Name": "LightProjector", "DestColor": "Linear", - "PixelFormat": "EAC_RG11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset index 9370de063d..79dda1977e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset @@ -19,7 +19,7 @@ "UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}", "Name": "Minimap", "SuppressEngineReduce": true, - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "SizeReduceLevel": 1, "MipMapSetting": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset index 459cd5b1fb..b02227b454 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset @@ -18,7 +18,7 @@ "UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}", "Name": "MuzzleFlash", "SuppressEngineReduce": true, - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset index bbd7fd5db9..8b66b30f28 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset @@ -44,7 +44,7 @@ "_msk", "_blend" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" @@ -67,7 +67,7 @@ "_msk", "_blend" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset index 1844e0186e..e9ea34060b 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset @@ -57,7 +57,7 @@ "_roughness", "_rough" ], - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset index e51cc7122b..e868a44096 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset @@ -22,7 +22,7 @@ "FileMasks": [ "_spec" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset index 07cc39c955..e52b616f48 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset @@ -26,7 +26,7 @@ "_spec", "_refl" ], - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset index 46a32ce5d4..191425bb92 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset @@ -19,7 +19,7 @@ "SourceColor": "Linear", "DestColor": "Linear", "SuppressEngineReduce": true, - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true }, "ios": { @@ -28,7 +28,7 @@ "SourceColor": "Linear", "DestColor": "Linear", "SuppressEngineReduce": true, - "PixelFormat": "PVRTC4", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true }, "mac": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset index 0ba70d2ca3..9b1a9c7c45 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset @@ -18,7 +18,7 @@ "SourceColor": "Linear", "DestColor": "Linear", "SuppressEngineReduce": true, - "PixelFormat": "ETC2" + "PixelFormat": "ASTC_4x4" }, "ios": { "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", @@ -26,7 +26,7 @@ "SourceColor": "Linear", "DestColor": "Linear", "SuppressEngineReduce": true, - "PixelFormat": "PVRTC4" + "PixelFormat": "ASTC_4x4" }, "mac": { "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset index 84a70935f1..8b12a08465 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset @@ -21,7 +21,7 @@ "Name": "Terrain_Albedo", "SourceColor": "Linear", "DestColor": "Linear", - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "HighPassMip": 5, "MipMapSetting": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset index 1d83737ef9..d24531858f 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset @@ -20,7 +20,7 @@ "Name": "Terrain_Albedo_HighPassed", "SourceColor": "Linear", "DestColor": "Linear", - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset index 6f70e8f14f..13334de700 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset @@ -17,7 +17,7 @@ "UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}", "Name": "UserInterface_Compressed", "SuppressEngineReduce": true, - "PixelFormat": "ETC2" + "PixelFormat": "ASTC_6x6" }, "ios": { "UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/gem.json b/Gems/Atom/Asset/ImageProcessingAtom/gem.json index 45d96f7168..1424841256 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/gem.json +++ b/Gems/Atom/Asset/ImageProcessingAtom/gem.json @@ -8,7 +8,11 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI", + "Atom_RHI", + "Atom" + ] } diff --git a/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Android/Vulkan/AzslcHeader.azsli b/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Android/Vulkan/AzslcHeader.azsli index 54c9430f87..0cab4d7a38 100644 --- a/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Android/Vulkan/AzslcHeader.azsli +++ b/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Android/Vulkan/AzslcHeader.azsli @@ -16,6 +16,11 @@ static const float4 s_AzslDebugColor = float4(165.0 / 255.0, 30.0 / 255.0, 36.0 / 255.0, 1); -// Uniform limitation need to be taken into consideration for mobile devices +// Uniform limitation need to be taken into consideration for mobile devices. This would help alleviate device lost errors if the constant buffer +// size overshoots the device's memory constraints. An example is a large number of constant buffers associated with mesh instances that would result in device lost, +// but otherwise OK on PC. We can separate out the number of instances for the different platforms with this define. // [ATOM-14949] -#define AZ_TRAIT_CONSTANT_BUFFER_LIMITATIONS 1 \ No newline at end of file +#define AZ_TRAIT_CONSTANT_BUFFER_LIMITATIONS 1 + +// Different constant buffer alignment on platforms +#define AZ_TRAIT_CONSTANT_BUFFER_ALIGNMENT 16 diff --git a/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Windows/Vulkan/AzslcHeader.azsli b/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Windows/Vulkan/AzslcHeader.azsli index b620c0d696..58395ef6a4 100644 --- a/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Windows/Vulkan/AzslcHeader.azsli +++ b/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Windows/Vulkan/AzslcHeader.azsli @@ -12,3 +12,7 @@ */ static const float4 s_AzslDebugColor = float4(165.0 / 255.0, 30.0 / 255.0, 36.0 / 255.0, 1); + + +// Different constant buffer alignment on platforms +#define AZ_TRAIT_CONSTANT_BUFFER_ALIGNMENT 128 \ No newline at end of file diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp index eb91d9e866..e9ac18a432 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp @@ -37,6 +37,7 @@ #include #include "ShaderPlatformInterfaceRequest.h" +#include "ShaderBuilder_Traits_Platform.h" #include "AtomShaderConfig.h" #include "SrgLayoutUtility.h" @@ -456,8 +457,9 @@ namespace AZ const uint32_t rhiUniqueIndex, const AZStd::string& platformIdentifier, const AZStd::string& shaderJsonPath, const uint32_t supervariantIndex, RPI::ShaderAssetSubId shaderAssetSubId) { - // platform id from identifier - AzFramework::PlatformId platformId = AzFramework::PlatformId::PC; + // Define a fallback platform ID based on the current host platform + AzFramework::PlatformId platformId = AZ_TRAIT_ATOM_FALLBACK_ASSET_HOST_PLATFORM; + if (platformIdentifier == "pc") { platformId = AzFramework::PlatformId::PC; @@ -478,6 +480,10 @@ namespace AZ { platformId = AzFramework::PlatformId::IOS; } + else if (platformIdentifier == "server") + { + platformId = AzFramework::PlatformId::SERVER; + } uint32_t assetSubId = RPI::ShaderAsset::MakeProductAssetSubId(rhiUniqueIndex, supervariantIndex, aznumeric_cast(shaderAssetSubId)); auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(shaderJsonPath, assetSubId); diff --git a/Gems/Atom/Asset/Shader/Code/Source/Platform/Android/ShaderBuilder_Traits_Android.h b/Gems/Atom/Asset/Shader/Code/Source/Platform/Android/ShaderBuilder_Traits_Android.h index f8d93059f3..86afcd201c 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Platform/Android/ShaderBuilder_Traits_Android.h +++ b/Gems/Atom/Asset/Shader/Code/Source/Platform/Android/ShaderBuilder_Traits_Android.h @@ -8,3 +8,4 @@ #pragma once #define AZ_TRAIT_ATOM_SHADERBUILDER_AZSLC UNUSED_TRAIT +#define AZ_TRAIT_ATOM_FALLBACK_ASSET_HOST_PLATFORM UNUSED_TRAIT diff --git a/Gems/Atom/Asset/Shader/Code/Source/Platform/Linux/ShaderBuilder_Traits_Linux.h b/Gems/Atom/Asset/Shader/Code/Source/Platform/Linux/ShaderBuilder_Traits_Linux.h index efa5a3e9ea..54a0a1fd5d 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Platform/Linux/ShaderBuilder_Traits_Linux.h +++ b/Gems/Atom/Asset/Shader/Code/Source/Platform/Linux/ShaderBuilder_Traits_Linux.h @@ -8,4 +8,4 @@ #pragma once #define AZ_TRAIT_ATOM_SHADERBUILDER_AZSLC "azslc" - +#define AZ_TRAIT_ATOM_FALLBACK_ASSET_HOST_PLATFORM AzFramework::PlatformId::LINUX_ID diff --git a/Gems/Atom/Asset/Shader/Code/Source/Platform/Mac/ShaderBuilder_Traits_Mac.h b/Gems/Atom/Asset/Shader/Code/Source/Platform/Mac/ShaderBuilder_Traits_Mac.h index d47967a559..7b93324711 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Platform/Mac/ShaderBuilder_Traits_Mac.h +++ b/Gems/Atom/Asset/Shader/Code/Source/Platform/Mac/ShaderBuilder_Traits_Mac.h @@ -8,3 +8,4 @@ #pragma once #define AZ_TRAIT_ATOM_SHADERBUILDER_AZSLC "azslc" +#define AZ_TRAIT_ATOM_FALLBACK_ASSET_HOST_PLATFORM AzFramework::PlatformId::MAC_ID diff --git a/Gems/Atom/Asset/Shader/Code/Source/Platform/Windows/ShaderBuilder_Traits_Windows.h b/Gems/Atom/Asset/Shader/Code/Source/Platform/Windows/ShaderBuilder_Traits_Windows.h index 3645897fa2..d6dd19fbfb 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Platform/Windows/ShaderBuilder_Traits_Windows.h +++ b/Gems/Atom/Asset/Shader/Code/Source/Platform/Windows/ShaderBuilder_Traits_Windows.h @@ -8,3 +8,4 @@ #pragma once #define AZ_TRAIT_ATOM_SHADERBUILDER_AZSLC "azslc.exe" +#define AZ_TRAIT_ATOM_FALLBACK_ASSET_HOST_PLATFORM AzFramework::PlatformId::PC diff --git a/Gems/Atom/Asset/Shader/Code/Source/Platform/iOS/ShaderBuilder_Traits_iOS.h b/Gems/Atom/Asset/Shader/Code/Source/Platform/iOS/ShaderBuilder_Traits_iOS.h index f8d93059f3..86afcd201c 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Platform/iOS/ShaderBuilder_Traits_iOS.h +++ b/Gems/Atom/Asset/Shader/Code/Source/Platform/iOS/ShaderBuilder_Traits_iOS.h @@ -8,3 +8,4 @@ #pragma once #define AZ_TRAIT_ATOM_SHADERBUILDER_AZSLC UNUSED_TRAIT +#define AZ_TRAIT_ATOM_FALLBACK_ASSET_HOST_PLATFORM UNUSED_TRAIT diff --git a/Gems/Atom/Asset/Shader/gem.json b/Gems/Atom/Asset/Shader/gem.json index eadf34acff..6d5e9f4dbe 100644 --- a/Gems/Atom/Asset/Shader/gem.json +++ b/Gems/Atom/Asset/Shader/gem.json @@ -8,7 +8,10 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI", + "Atom_RPI" + ] } diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index ddd88a1f52..8a6b7db1ca 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -175,7 +176,10 @@ namespace AZ m_isAssetCatalogLoaded = true; - RPI::RPISystemInterface::Get()->InitializeSystemAssets(); + if (!RPI::RPISystemInterface::Get()->IsInitialized()) + { + RPI::RPISystemInterface::Get()->InitializeSystemAssets(); + } if (!RPI::RPISystemInterface::Get()->IsInitialized()) { @@ -303,6 +307,11 @@ namespace AZ RPI::RenderPipelineDescriptor renderPipelineDescriptor = *RPI::GetDataFromAnyAsset(pipelineAsset); renderPipelineDescriptor.m_name = AZStd::string::format("%s_%i", renderPipelineDescriptor.m_name.c_str(), viewportContext->GetId()); + // Make sure non-msaa super variant is used for non-msaa pipeline + bool isNonMsaaPipeline = (renderPipelineDescriptor.m_renderSettings.m_multisampleState.m_samples == 1); + const char* supervariantName = isNonMsaaPipeline ? AZ::RPI::NoMsaaSupervariantName : ""; + AZ::RPI::ShaderSystemInterface::Get()->SetSupervariantName(AZ::Name(supervariantName)); + if (!scene->GetRenderPipeline(AZ::Name(renderPipelineDescriptor.m_name))) { RPI::RenderPipelinePtr renderPipeline = RPI::RenderPipeline::CreateRenderPipelineForWindow(renderPipelineDescriptor, *viewportContext->GetWindowContext().get()); diff --git a/Gems/Atom/Bootstrap/gem.json b/Gems/Atom/Bootstrap/gem.json index d5f287eb7f..5e98a1887d 100644 --- a/Gems/Atom/Bootstrap/gem.json +++ b/Gems/Atom/Bootstrap/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/Atom/Component/DebugCamera/gem.json b/Gems/Atom/Component/DebugCamera/gem.json index cb2c597b07..586cb37058 100644 --- a/Gems/Atom/Component/DebugCamera/gem.json +++ b/Gems/Atom/Component/DebugCamera/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli index 2f84aaf5b2..4a66c52bf2 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli @@ -104,6 +104,7 @@ option bool o_layer3_enabled; enum class DebugDrawMode { None, BlendMask, Displacement, FinalBlendWeights }; option DebugDrawMode o_debugDrawMode; +// If you modify this enum, you must update the BlendSourceUsesDisplacement function in StandardMultilayerPBR_Displacement.lua enum class LayerBlendSource { BlendMaskTexture, BlendMaskVertexColors, Displacement, Displacement_With_BlendMaskTexture, Displacement_With_BlendMaskVertexColors, Fallback }; option LayerBlendSource o_layerBlendSource; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua index 8fa010f7bd..a032479d19 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua @@ -88,7 +88,7 @@ end -- @return a table with two values {min,max}. Negative values are below the surface and positive values are above the surface. function CalcOverallHeightRange(context) - local heightMinMax = {nil, nil} + local heightMinMax = {} local function GetMergedHeightRange(heightMinMax, offset, factor) top = offset @@ -138,7 +138,8 @@ function CalcOverallHeightRange(context) if(enableLayer3) then GetMergedHeightRange(heightMinMax, offsetLayer3, factorLayer3) end else - heightMinMax = {0,0} + heightMinMax[0] = 0 + heightMinMax[1] = 0 end return heightMinMax diff --git a/Gems/Atom/Feature/Common/Assets/Passes/HDRColorGrading.pass b/Gems/Atom/Feature/Common/Assets/Passes/HDRColorGrading.pass new file mode 100644 index 0000000000..adcd43ce83 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/HDRColorGrading.pass @@ -0,0 +1,215 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "HDRColorGradingTemplate", + "PassClass": "HDRColorGradingPass", + "Slots": [ + { + "Name": "Input", + "SlotType": "Input", + "ShaderInputName": "m_framebuffer", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "Output", + "SlotType": "Output", + "ScopeAttachmentUsage": "RenderTarget", + "LoadStoreAction": { + "LoadAction": "DontCare" + } + } + ], + "ImageAttachments": [ + { + "Name": "ColorGradingOutput", + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "Input" + } + }, + "FormatSource": { + "Pass": "This", + "Attachment": "Input" + } + } + ], + "Connections": [ + { + "LocalSlot": "Output", + "AttachmentRef": { + "Pass": "This", + "Attachment": "ColorGradingOutput" + } + } + ], + "FallbackConnections": [ + { + "Input": "Input", + "Output": "Output" + } + ], + "PassData": { + "$type": "FullscreenTrianglePassData", + "ShaderAsset": { + "FilePath": "Shaders/PostProcessing/HDRColorGrading.shader" + }, + "ShaderDataMappings": { + "FloatMappings": [ + { + "Name": "m_colorGradingExposure", + "Value": 0.0 // unconstrained, log2 stops + }, + { + "Name": "m_colorGradingContrast", + "Value": 0.0 // -100 ... 100 + }, + { + "Name": "m_colorGradingHueShift", + "Value": 0.0 // 0 ... 1, can wrap + }, + { + "Name": "m_colorGradingPreSaturation", + "Value": 1.0 // -100 ... 100 + }, + { + "Name": "m_colorFilterIntensity", + "Value": 1.0 // unconstrained, log2 stops + }, + { + "Name": "m_colorFilterMultiply", + "Value": 0.0 // modulate, 0 ... 1 + }, + { + "Name": "m_whiteBalanceKelvin", + "Value": 6600.0 // 1000.0f ... 40000.0f kelvin + }, + { + "Name": "m_whiteBalanceTint", + "Value": 0.0 // -100 ... 100 + }, + { + "Name": "m_splitToneBalance", + "Value": 0.0 // -1 ... 1 + }, + { + "Name": "m_splitToneWeight", + "Value": 0.0 // 0 ... 1 + }, + { + "Name": "m_colorGradingPostSaturation", + "Value": 1.0 // -100 ... 100 + }, + { + "Name": "m_smhShadowsStart", + "Value": 0.0 // 0 ... 1 + }, + { + "Name": "m_smhShadowsEnd", + "Value": 0.3 // 0 ... 1 + }, + { + "Name": "m_smhHighlightsStart", + "Value": 0.55 // 0 ... 1 + }, + { + "Name": "m_smhHighlightsEnd", + "Value": 1.0 // 0 ... 1 + }, + { + "Name": "m_smhWeight", + "Value": 0.0 // 0 ... 1 + } + ], + // The colors defined here are expected to be in linear rgb color space. + // These are converted to ACEScg color space within the HDRColorGrading.azsl shader + "ColorMappings": [ + { + "Name": "m_colorFilterSwatch", + "Value": [ + 1.0, + 0.5, + 0.5, + 1.0 + ] + }, + { + "Name": "m_splitToneShadowsColor", + "Value": [ + 1.0, + 0.1, + 0.1, + 1.0 + ] + }, + { + "Name": "m_splitToneHighlightsColor", + "Value": [ + 0.1, + 1.0, + 0.1, + 1.0 + ] + }, + { + "Name": "m_smhShadowsColor", + "Value": [ + 1.0, + 0.25, + 0.25, + 1.0 + ] + }, + { + "Name": "m_smhMidtonesColor", + "Value": [ + 0.1, + 0.1, + 1.0, + 1.0 + ] + }, + { + "Name": "m_smhHighlightsColor", + "Value": [ + 1.0, + 0.0, + 1.0, + 1.0 + ] + } + ], + "Float3Mappings": [ + { + "Name": "m_channelMixingRed", + "Value": [ + 1.0, + 0.0, + 0.0 + ] + }, + { + "Name": "m_channelMixingGreen", + "Value": [ + 0.0, + 1.0, + 0.0 + ] + }, + { + "Name": "m_channelMixingBlue", + "Value": [ + 0.0, + 0.0, + 1.0 + ] + } + ] + } + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass index ff55ebc200..eec3634045 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass @@ -92,8 +92,8 @@ ] }, { - "Name": "LookModificationTransformPass", - "TemplateName": "LookModificationTransformTemplate", + "Name": "HDRColorGradingPass", + "TemplateName": "HDRColorGradingTemplate", "Enabled": true, "Connections": [ { @@ -102,6 +102,20 @@ "Pass": "Parent", "Attachment": "LightingInput" } + } + ] + }, + { + "Name": "LookModificationTransformPass", + "TemplateName": "LookModificationTransformTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "HDRColorGradingPass", + "Attachment": "Output" + } }, { "LocalSlot": "EyeAdaptationDataInput", diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset index 8b7d6a8438..4e38717d31 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset +++ b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset @@ -499,7 +499,11 @@ { "Name": "KawaseShadowBlurTemplate", "Path": "Passes/KawaseShadowBlur.pass" - } + }, + { + "Name": "HDRColorGradingTemplate", + "Path": "Passes/HDRColorGrading.pass" + } ] } } diff --git a/Gems/Atom/Feature/Common/Assets/Scripts/material_find_overrides_demo.lua b/Gems/Atom/Feature/Common/Assets/Scripts/material_find_overrides_demo.lua index 41df35e355..4a1b8c8f06 100644 --- a/Gems/Atom/Feature/Common/Assets/Scripts/material_find_overrides_demo.lua +++ b/Gems/Atom/Feature/Common/Assets/Scripts/material_find_overrides_demo.lua @@ -22,6 +22,7 @@ local FindMaterialAssignmentTest = "materials/presets/macbeth/12_orange_yellow_srgb.tif.streamingimage", "materials/presets/macbeth/17_magenta_srgb.tif.streamingimage" }, + MaterialSlotFilter = "" }, } @@ -50,18 +51,6 @@ function FindMaterialAssignmentTest:OnActivate() self.colors = {} self.lerpDirs = {} - self.assignmentIds = - { - MaterialComponentRequestBus.Event.FindMaterialAssignmentId(self.entityId, -1, "lambert"), - } - - for index = 1, #self.assignmentIds do - local id = self.assignmentIds[index] - if (id ~= nil) then - self.colors[index] = randomColor() - self.lerpDirs[index] = randomDir() - end - end self.tickBusHandler = TickBus.Connect(self); end @@ -144,10 +133,33 @@ function FindMaterialAssignmentTest:lerpColors(deltaTime) end function FindMaterialAssignmentTest:OnTick(deltaTime, timePoint) + + + if(nil == self.assignmentIds) then + + local originalAssignments = MaterialComponentRequestBus.Event.GetOriginalMaterialAssignments(self.entityId) + if(nil == originalAssignments or #originalAssignments <= 1) then -- There is always 1 entry for the default assignment; a loaded model will have at least 2 assignments + return + end + + self.assignmentIds = + { + MaterialComponentRequestBus.Event.FindMaterialAssignmentId(self.entityId, -1, self.Properties.MaterialSlotFilter), + } + + for index = 1, #self.assignmentIds do + local id = self.assignmentIds[index] + if (id ~= nil) then + self.colors[index] = randomColor() + self.lerpDirs[index] = randomDir() + end + end + end + self.timer = self.timer + deltaTime self.totalTime = self.totalTime + deltaTime self:lerpColors(deltaTime) - + if (self.timer > self.timeUpdate and self.totalTime < self.totalTimeMax) then self.timer = self.timer - self.timeUpdate self:UpdateProperties() @@ -155,6 +167,7 @@ function FindMaterialAssignmentTest:OnTick(deltaTime, timePoint) self:ClearProperties() self.tickBusHandler:Disconnect(self); end + end -return FindMaterialAssignmentTest \ No newline at end of file +return FindMaterialAssignmentTest diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/KelvinToRgb.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/KelvinToRgb.azsli new file mode 100644 index 0000000000..aa57f0f4a1 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/KelvinToRgb.azsli @@ -0,0 +1,75 @@ +/* + * 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) AND Creative Commons 3.0 + * + */ + +// Ref: https://www.shadertoy.com/view/lsSXW1 +// ported by Renaud Bédard (@renaudbedard) from original code from Tanner Helland +// http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ + +// color space functions translated from HLSL versions on Chilli Ant (by Ian Taylor) +// http://www.chilliant.com/rgb2hsv.html + +// licensed and released under Creative Commons 3.0 Attribution +// https://creativecommons.org/licenses/by/3.0/ + +float3 HueToRgb(float hue) +{ + return saturate(float3(abs(hue * 6.0f - 3.0f) - 1.0f, + 2.0f - abs(hue * 6.0f - 2.0f), + 2.0f - abs(hue * 6.0f - 4.0f))); +} + +float3 RgbToHcv(float3 rgb) +{ + // Based on work by Sam Hocevar and Emil Persson + const float4 p = (rgb.g < rgb.b) ? float4(rgb.bg, -1.0f, 2.0f/3.0f) : float4(rgb.gb, 0.0f, -1.0f/3.0f); + const float4 q1 = (rgb.r < p.x) ? float4(p.xyw, rgb.r) : float4(rgb.r, p.yzx); + const float c = q1.x - min(q1.w, q1.y); + const float h = abs((q1.w - q1.y) / (6.0f * c + 0.000001f ) + q1.z); + return float3(h, c, q1.x); +} + +float3 RgbToHsl(float3 rgb) +{ + rgb.xyz = max(rgb.xyz, 0.000001f); + const float3 hcv = RgbToHcv(rgb); + const float L = hcv.z - hcv.y * 0.5f; + const float S = hcv.y / (1.0f - abs(L * 2.0f - 1.0f) + 0.000001f); + return float3(hcv.x, S, L); +} + +float3 HslToRgb(float3 hsl) +{ + const float3 rgb = HueToRgb(hsl.x); + const float c = (1.0f - abs(2.0f * hsl.z - 1.0f)) * hsl.y; + return (rgb - 0.5f) * c + hsl.z; +} + +// Color temperature +float3 KelvinToRgb(float kelvin) +{ + float3 ret; + kelvin = clamp(kelvin, 1000.0f, 40000.0f) / 100.0f; + if(kelvin <= 66.0f) + { + ret.r = 1.0f; + ret.g = saturate(0.39008157876901960784f * log(kelvin) - 0.63184144378862745098f); + } + else + { + float t = max(kelvin - 60.0f, 0.0f); + ret.r = saturate(1.29293618606274509804f * pow(t, -0.1332047592f)); + ret.g = saturate(1.12989086089529411765f * pow(t, -0.0755148492f)); + } + if(kelvin >= 66.0f) + ret.b = 1.0f; + else if(kelvin < 19.0f) + ret.b = 0.0f; + else + ret.b = saturate(0.54320678911019607843f * log(kelvin - 10.0f) - 1.19625408914f); + return ret; +} diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/PSstyleColorBlends_NonSeparable.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/PSstyleColorBlends_NonSeparable.azsli new file mode 100644 index 0000000000..9c2a5bc306 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/PSstyleColorBlends_NonSeparable.azsli @@ -0,0 +1,177 @@ +/* + * 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 + * + */ + +/* +------------------------------------------------------------------------------ + Public Domain +------------------------------------------------------------------------------ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +Source: https://www.ryanjuckett.com/photoshop-blend-modes-in-hlsl/ +*/ +//****************************************************************************** +//****************************************************************************** +float Color_GetLuminosity(float3 c) +{ + return 0.3*c.r + 0.59*c.g + 0.11*c.b; +} + +//****************************************************************************** +//****************************************************************************** +float3 Color_SetLuminosity(float3 c, float lum) +{ + float d = lum - Color_GetLuminosity(c); + c.rgb += float3(d,d,d); + + // clip back into legal range + lum = Color_GetLuminosity(c); + float cMin = min(c.r, min(c.g, c.b)); + float cMax = max(c.r, max(c.g, c.b)); + + if(cMin < 0) + c = lerp(float3(lum,lum,lum), c, lum / (lum - cMin)); + + if(cMax > 1) + c = lerp(float3(lum,lum,lum), c, (1 - lum) / (cMax - lum)); + + return c; +} + +//****************************************************************************** +//****************************************************************************** +float Color_GetSaturation(float3 c) +{ + return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b)); +} + +//****************************************************************************** +// Set saturation if color components are sorted in ascending order. +//****************************************************************************** +float3 Color_SetSaturation_MinMidMax(float3 cSorted, float s) +{ + if(cSorted.z > cSorted.x) + { + cSorted.y = (((cSorted.y - cSorted.x) * s) / (cSorted.z - cSorted.x)); + cSorted.z = s; + } + else + { + cSorted.y = 0; + cSorted.z = 0; + } + + cSorted.x = 0; + + return cSorted; +} + +//****************************************************************************** +//****************************************************************************** +float3 Color_SetSaturation(float3 c, float s) +{ + if (c.r <= c.g && c.r <= c.b) + { + if (c.g <= c.b) + c.rgb = Color_SetSaturation_MinMidMax(c.rgb, s); + else + c.rbg = Color_SetSaturation_MinMidMax(c.rbg, s); + } + else if (c.g <= c.r && c.g <= c.b) + { + if (c.r <= c.b) + c.grb = Color_SetSaturation_MinMidMax(c.grb, s); + else + c.gbr = Color_SetSaturation_MinMidMax(c.gbr, s); + } + else + { + if (c.r <= c.g) + c.brg = Color_SetSaturation_MinMidMax(c.brg, s); + else + c.bgr = Color_SetSaturation_MinMidMax(c.bgr, s); + } + + return c; +} + +//****************************************************************************** +// Creates a color with the hue of the blend color and the saturation and +// luminosity of the base color. +//****************************************************************************** +float3 BlendMode_Hue(float3 base, float3 blend) +{ + return Color_SetLuminosity(Color_SetSaturation(blend, Color_GetSaturation(base)), Color_GetLuminosity(base)); +} + +//****************************************************************************** +// Creates a color with the saturation of the blend color and the hue and +// luminosity of the base color. +//****************************************************************************** +float3 BlendMode_Saturation(float3 base, float3 blend) +{ + return Color_SetLuminosity(Color_SetSaturation(base, Color_GetSaturation(blend)), Color_GetLuminosity(base)); +} + +//****************************************************************************** +// Creates a color with the hue and saturation of the blend color and the +// luminosity of the base color. +//****************************************************************************** +float3 BlendMode_Color(float3 base, float3 blend) +{ + return Color_SetLuminosity(blend, Color_GetLuminosity(base)); +} + +//****************************************************************************** +// Creates a color with the luminosity of the blend color and the hue and +// saturation of the base color. +//****************************************************************************** +float3 BlendMode_Luminosity(float3 base, float3 blend) +{ + return Color_SetLuminosity(base, Color_GetLuminosity(blend)); +} + +//****************************************************************************** +// Compares the total of all channel values for the blend and base color and +// displays the lower value color. +//****************************************************************************** +float3 BlendMode_DarkerColor(float3 base, float3 blend) +{ + return Color_GetLuminosity(base) <= Color_GetLuminosity(blend) ? base : blend; +} + +//****************************************************************************** +// Compares the total of all channel values for the blend and base color and +// displays the higher value color. +//****************************************************************************** +float3 BlendMode_LighterColor(float3 base, float3 blend) +{ + return Color_GetLuminosity(base) > Color_GetLuminosity(blend) ? base : blend; +} diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/PSstyleColorBlends_Separable.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/PSstyleColorBlends_Separable.azsli new file mode 100644 index 0000000000..f64df30231 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/3rdParty/Features/PostProcessing/PSstyleColorBlends_Separable.azsli @@ -0,0 +1,306 @@ +/* + * 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 + * + */ + +/* +------------------------------------------------------------------------------ + Public Domain +------------------------------------------------------------------------------ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +Source: https://www.ryanjuckett.com/photoshop-blend-modes-in-hlsl/ +*/ +//****************************************************************************** +// Selects the blend color, ignoring the base. +//****************************************************************************** +float3 BlendMode_Normal(float3 base, float3 blend) +{ + return blend; +} + +//****************************************************************************** +// Looks at the color information in each channel and selects the base or blend +// color—whichever is darker—as the result color. +//****************************************************************************** +float3 BlendMode_Darken(float3 base, float3 blend) +{ + return min(base, blend); +} + +//****************************************************************************** +// Looks at the color information in each channel and multiplies the base color +// by the blend color. +//****************************************************************************** +float3 BlendMode_Multiply(float3 base, float3 blend) +{ + return base*blend; +} + +//****************************************************************************** +// Looks at the color information in each channel and darkens the base color to +// reflect the blend color by increasing the contrast between the two. +//****************************************************************************** +float BlendMode_ColorBurn(float base, float blend) +{ + return blend > 0 ? 1 - min(1, (1-base) / blend) : 0; +} + +float3 BlendMode_ColorBurn(float3 base, float3 blend) +{ + return float3( BlendMode_ColorBurn(base.r, blend.r), + BlendMode_ColorBurn(base.g, blend.g), + BlendMode_ColorBurn(base.b, blend.b) ); +} + +//****************************************************************************** +// Looks at the color information in each channel and darkens the base color to +// reflect the blend color by decreasing the brightness. +//****************************************************************************** +float BlendMode_LinearBurn(float base, float blend) +{ + return max(0, base + blend - 1); +} + +float3 BlendMode_LinearBurn(float3 base, float3 blend) +{ + return float3( BlendMode_LinearBurn(base.r, blend.r), + BlendMode_LinearBurn(base.g, blend.g), + BlendMode_LinearBurn(base.b, blend.b) ); +} + +//****************************************************************************** +// Looks at the color information in each channel and selects the base or blend +// color—whichever is lighter—as the result color. +//****************************************************************************** +float3 BlendMode_Lighten(float3 base, float3 blend) +{ + return max(base, blend); +} + +//****************************************************************************** +// Looks at each channel’s color information and multiplies the inverse of the +// blend and base colors. +//****************************************************************************** +float3 BlendMode_Screen(float3 base, float3 blend) +{ + return base + blend - base*blend; +} + +//****************************************************************************** +// Looks at the color information in each channel and brightens the base color +// to reflect the blend color by decreasing contrast between the two. +//****************************************************************************** +float BlendMode_ColorDodge(float base, float blend) +{ + return blend < 1 ? min(1, base / (1-blend)) : 1; +} + +float3 BlendMode_ColorDodge(float3 base, float3 blend) +{ + return float3( BlendMode_ColorDodge(base.r, blend.r), + BlendMode_ColorDodge(base.g, blend.g), + BlendMode_ColorDodge(base.b, blend.b) ); +} + +//****************************************************************************** +// Looks at the color information in each channel and brightens the base color +// to reflect the blend color by decreasing contrast between the two. +//****************************************************************************** +float BlendMode_LinearDodge(float base, float blend) +{ + return min(1, base + blend); +} + +float3 BlendMode_LinearDodge(float3 base, float3 blend) +{ + return float3( BlendMode_LinearDodge(base.r, blend.r), + BlendMode_LinearDodge(base.g, blend.g), + BlendMode_LinearDodge(base.b, blend.b) ); +} + +//****************************************************************************** +// Multiplies or screens the colors, depending on the base color. +//****************************************************************************** +float BlendMode_Overlay(float base, float blend) +{ + return (base <= 0.5) ? 2*base*blend : 1 - 2*(1-base)*(1-blend); +} + +float3 BlendMode_Overlay(float3 base, float3 blend) +{ + return float3( BlendMode_Overlay(base.r, blend.r), + BlendMode_Overlay(base.g, blend.g), + BlendMode_Overlay(base.b, blend.b) ); +} + +//****************************************************************************** +// Darkens or lightens the colors, depending on the blend color. +//****************************************************************************** +float BlendMode_SoftLight(float base, float blend) +{ + if (blend <= 0.5) + { + return base - (1-2*blend)*base*(1-base); + } + else + { + float d = (base <= 0.25) ? ((16*base-12)*base+4)*base : sqrt(base); + return base + (2*blend-1)*(d-base); + } +} + +float3 BlendMode_SoftLight(float3 base, float3 blend) +{ + return float3( BlendMode_SoftLight(base.r, blend.r), + BlendMode_SoftLight(base.g, blend.g), + BlendMode_SoftLight(base.b, blend.b) ); +} + +//****************************************************************************** +// Multiplies or screens the colors, depending on the blend color. +//****************************************************************************** +float BlendMode_HardLight(float base, float blend) +{ + return (blend <= 0.5) ? 2*base*blend : 1 - 2*(1-base)*(1-blend); +} + +float3 BlendMode_HardLight(float3 base, float3 blend) +{ + return float3( BlendMode_HardLight(base.r, blend.r), + BlendMode_HardLight(base.g, blend.g), + BlendMode_HardLight(base.b, blend.b) ); +} + +//****************************************************************************** +// Burns or dodges the colors by increasing or decreasing the contrast, +// depending on the blend color. +//****************************************************************************** +float BlendMode_VividLight(float base, float blend) +{ + return (blend <= 0.5) ? BlendMode_ColorBurn(base,2*blend) : BlendMode_ColorDodge(base,2*(blend-0.5)); +} + +float3 BlendMode_VividLight(float3 base, float3 blend) +{ + return float3( BlendMode_VividLight(base.r, blend.r), + BlendMode_VividLight(base.g, blend.g), + BlendMode_VividLight(base.b, blend.b) ); +} + +//****************************************************************************** +// Burns or dodges the colors by decreasing or increasing the brightness, +// depending on the blend color. +//****************************************************************************** +float BlendMode_LinearLight(float base, float blend) +{ + return (blend <= 0.5) ? BlendMode_LinearBurn(base,2*blend) : BlendMode_LinearDodge(base,2*(blend-0.5)); +} + +float3 BlendMode_LinearLight(float3 base, float3 blend) +{ + return float3( BlendMode_LinearLight(base.r, blend.r), + BlendMode_LinearLight(base.g, blend.g), + BlendMode_LinearLight(base.b, blend.b) ); +} + +//****************************************************************************** +// Replaces the colors, depending on the blend color. +//****************************************************************************** +float BlendMode_PinLight(float base, float blend) +{ + return (blend <= 0.5) ? min(base,2*blend) : max(base,2*(blend-0.5)); +} + +float3 BlendMode_PinLight(float3 base, float3 blend) +{ + return float3( BlendMode_PinLight(base.r, blend.r), + BlendMode_PinLight(base.g, blend.g), + BlendMode_PinLight(base.b, blend.b) ); +} + +//****************************************************************************** +// Adds the red, green and blue channel values of the blend color to the RGB +// values of the base color. If the resulting sum for a channel is 255 or +// greater, it receives a value of 255; if less than 255, a value of 0. +//****************************************************************************** +float BlendMode_HardMix(float base, float blend) +{ + return (base + blend >= 1.0) ? 1.0 : 0.0; +} + +float3 BlendMode_HardMix(float3 base, float3 blend) +{ + return float3( BlendMode_HardMix(base.r, blend.r), + BlendMode_HardMix(base.g, blend.g), + BlendMode_HardMix(base.b, blend.b) ); +} + +//****************************************************************************** +// Looks at the color information in each channel and subtracts either the +// blend color from the base color or the base color from the blend color, +// depending on which has the greater brightness value. +//****************************************************************************** +float3 BlendMode_Difference(float3 base, float3 blend) +{ + return abs(base-blend); +} + +//****************************************************************************** +// Creates an effect similar to but lower in contrast than the Difference mode. +//****************************************************************************** +float3 BlendMode_Exclusion(float3 base, float3 blend) +{ + return base + blend - 2*base*blend; +} + +//****************************************************************************** +// Looks at the color information in each channel and subtracts the blend color +// from the base color. +//****************************************************************************** +float3 BlendMode_Subtract(float3 base, float3 blend) +{ + return max(0, base - blend); +} + +//****************************************************************************** +// Looks at the color information in each channel and divides the blend color +// from the base color. +//****************************************************************************** +float BlendMode_Divide(float base, float blend) +{ + return blend > 0 ? min(1, base / blend) : 1; +} + +float3 BlendMode_Divide(float3 base, float3 blend) +{ + return float3( BlendMode_Divide(base.r, blend.r), + BlendMode_Divide(base.g, blend.g), + BlendMode_Divide(base.b, blend.b) ); +} diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCcToAcesCg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCcToAcesCg.azsli new file mode 100644 index 0000000000..e84b18c550 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCcToAcesCg.azsli @@ -0,0 +1,80 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: (MIT OR Apache-2.0) AND LicenseRef-ACES + * + */ + +/* +License Terms for Academy Color Encoding System Components + +Academy Color Encoding System (ACES) software and tools are provided by the + Academy under the following terms and conditions: A worldwide, royalty-free, + non-exclusive right to copy, modify, create derivatives, and use, in source and + binary forms, is hereby granted, subject to acceptance of this license. + +Copyright © 2015 Academy of Motion Picture Arts and Sciences (A.M.P.A.S.). +Portions contributed by others as indicated. All rights reserved. + +Performance of any of the aforementioned acts indicates acceptance to be bound + by the following terms and conditions: + +* Copies of source code, in whole or in part, must retain the above copyright + notice, this list of conditions and the Disclaimer of Warranty. +* Use in binary form must retain the above copyright notice, this list of + conditions and the Disclaimer of Warranty in the documentation and/or other + materials provided with the distribution. +* Nothing in this license shall be deemed to grant any rights to trademarks, + copyrights, patents, trade secrets or any other intellectual property of + A.M.P.A.S. or any contributors, except as expressly stated herein. +* Neither the name "A.M.P.A.S." nor the name of any other contributors to this + software may be used to endorse or promote products derivative of or based on + this software without express prior written permission of A.M.P.A.S. or the + contributors, as appropriate. + +This license shall be construed pursuant to the laws of the State of California, +and any disputes related thereto shall be subject to the jurisdiction of the + courts therein. + +Disclaimer of Warranty: THIS SOFTWARE IS PROVIDED BY A.M.P.A.S. AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, +AND NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL A.M.P.A.S., OR ANY +CONTRIBUTORS OR DISTRIBUTORS, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, RESITUTIONARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//////////////////////////////////////////////////////////////////////////////// +WITHOUT LIMITING THE GENERALITY OF THE FOREGOING, THE ACADEMY SPECIFICALLY +DISCLAIMS ANY REPRESENTATIONS OR WARRANTIES WHATSOEVER RELATED TO PATENT OR +OTHER INTELLECTUAL PROPERTY RIGHTS IN THE ACADEMY COLOR ENCODING SYSTEM, OR +APPLICATIONS THEREOF, HELD BY PARTIES OTHER THAN A.M.P.A.S.,WHETHER DISCLOSED OR +UNDISCLOSED. +*/ + +#pragma once + +static const float HALF_MAX = 65504.0f; + +float AcesCcToLinear(float value) +{ + if (value < -0.3013698630) // (9.72-15)/17.52 + return (pow( 2., value*17.52-9.72) - pow( 2.,-16.))*2.0; + else if (value < (log2(HALF_MAX)+9.72)/17.52) + return pow( 2., value*17.52-9.72); + else // (value >= (log2(HALF_MAX)+9.72)/17.52) + return HALF_MAX; +} + +float3 AcesCcToAcesCg(float3 color) +{ + return float3( + AcesCcToLinear(color.r), + AcesCcToLinear(color.g), + AcesCcToLinear(color.b)); +} diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCgToAcesCc.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCgToAcesCc.azsli new file mode 100644 index 0000000000..f784a76990 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCgToAcesCc.azsli @@ -0,0 +1,79 @@ +/* + * 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: (MIT OR Apache-2.0) AND LicenseRef-ACES + * + */ + +/* +License Terms for Academy Color Encoding System Components + +Academy Color Encoding System (ACES) software and tools are provided by the + Academy under the following terms and conditions: A worldwide, royalty-free, + non-exclusive right to copy, modify, create derivatives, and use, in source and + binary forms, is hereby granted, subject to acceptance of this license. + +Copyright © 2015 Academy of Motion Picture Arts and Sciences (A.M.P.A.S.). +Portions contributed by others as indicated. All rights reserved. + +Performance of any of the aforementioned acts indicates acceptance to be bound + by the following terms and conditions: + +* Copies of source code, in whole or in part, must retain the above copyright + notice, this list of conditions and the Disclaimer of Warranty. +* Use in binary form must retain the above copyright notice, this list of + conditions and the Disclaimer of Warranty in the documentation and/or other + materials provided with the distribution. +* Nothing in this license shall be deemed to grant any rights to trademarks, + copyrights, patents, trade secrets or any other intellectual property of + A.M.P.A.S. or any contributors, except as expressly stated herein. +* Neither the name "A.M.P.A.S." nor the name of any other contributors to this + software may be used to endorse or promote products derivative of or based on + this software without express prior written permission of A.M.P.A.S. or the + contributors, as appropriate. + +This license shall be construed pursuant to the laws of the State of California, +and any disputes related thereto shall be subject to the jurisdiction of the + courts therein. + +Disclaimer of Warranty: THIS SOFTWARE IS PROVIDED BY A.M.P.A.S. AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, +AND NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL A.M.P.A.S., OR ANY +CONTRIBUTORS OR DISTRIBUTORS, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, RESITUTIONARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//////////////////////////////////////////////////////////////////////////////// +WITHOUT LIMITING THE GENERALITY OF THE FOREGOING, THE ACADEMY SPECIFICALLY +DISCLAIMS ANY REPRESENTATIONS OR WARRANTIES WHATSOEVER RELATED TO PATENT OR +OTHER INTELLECTUAL PROPERTY RIGHTS IN THE ACADEMY COLOR ENCODING SYSTEM, OR +APPLICATIONS THEREOF, HELD BY PARTIES OTHER THAN A.M.P.A.S.,WHETHER DISCLOSED OR +UNDISCLOSED. +*/ + +#pragma once + +float LinearToAcesCc(float value) +{ + if (value <= 0) + return -0.3584474886; // =(log2( pow(2.,-16.))+9.72)/17.52 + else if (value < pow(2.,-15.)) + return (log2( pow(2.,-16.) + value * 0.5) + 9.72) / 17.52; + else // (value >= pow(2.,-15)) + return (log2(value) + 9.72) / 17.52; +} + +float3 AcesCgToAcesCc(float3 color) +{ + return float3( + LinearToAcesCc(color.r), + LinearToAcesCc(color.g), + LinearToAcesCc(color.b) + ); +} diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/TransformColor.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/TransformColor.azsli index e298445c7c..c9d1b7d979 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/TransformColor.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ColorManagement/TransformColor.azsli @@ -8,11 +8,14 @@ #pragma once +#include #include "GeneratedTransforms/LinearSrgb_To_AcesCg.azsli" #include "GeneratedTransforms/AcesCg_To_LinearSrgb.azsli" #include "GeneratedTransforms/LinearSrgb_To_Srgb.azsli" #include "GeneratedTransforms/Srgb_To_LinearSrgb.azsli" #include "GeneratedTransforms/Aces_To_AcesCg.azsli" +#include "GeneratedTransforms/AcesCcToAcesCg.azsli" +#include "GeneratedTransforms/AcesCgToAcesCc.azsli" #include "GeneratedTransforms/CalculateLuminance_LinearSrgb.azsli" #include "GeneratedTransforms/CalculateLuminance_AcesCg.azsli" @@ -20,6 +23,7 @@ enum class ColorSpaceId { SRGB = 0, LinearSRGB, + ACEScc, ACEScg, ACES2065, XYZ, @@ -51,15 +55,27 @@ float3 TransformColor(in float3 color, ColorSpaceId fromColorSpace, ColorSpaceId color = AcesCg_To_LinearSrgb(color); color = LinearSrgb_To_Srgb(color); } + else if (fromColorSpace == ColorSpaceId::ACEScg && toColorSpace == ColorSpaceId::LinearSRGB) + { + color = AcesCg_To_LinearSrgb(color); + } + else if (fromColorSpace == ColorSpaceId::ACEScg && toColorSpace == ColorSpaceId::ACEScc) + { + color = AcesCgToAcesCc(color); + } else if (fromColorSpace == ColorSpaceId::ACES2065 && toColorSpace == ColorSpaceId::ACEScg) { color = Aces_To_AcesCg(color); } + else if (fromColorSpace == ColorSpaceId::ACEScc && toColorSpace == ColorSpaceId::ACEScg) + { + color = AcesCcToAcesCg(color); + } else { color = float3(1, 0, 1); } - + return color; } @@ -75,6 +91,7 @@ float CalculateLuminance(in float3 color, ColorSpaceId colorSpace) luminance = CalculateLuminance_AcesCg(color); break; case ColorSpaceId::SRGB: + case ColorSpaceId::ACEScc: case ColorSpaceId::ACES2065: case ColorSpaceId::XYZ: case ColorSpaceId::Invalid: @@ -83,3 +100,29 @@ float CalculateLuminance(in float3 color, ColorSpaceId colorSpace) return luminance; } + +float RotateHue(float hue, float low, float hi) +{ + return (hue < low) + ? hue + hi + : (hue > hi) + ? hue - hi + : hue; +} + +float3 RgbToHsv(float3 color) +{ + const float4 k = float4(0.0f, -1.0f / 3.0f, 2.0f / 3.0f, -1.0f); + const float4 p = lerp(float4(color.bg, k.wz), float4(color.gb, k.xy), step(color.b, color.g)); + const float4 q = lerp(float4(p.xyw, color.r), float4(color.r, p.yzx), step(p.x, color.r)); + const float d = q.x - min(q.w, q.y); + const float e = EPSILON; + return float3(abs(q.z + (q.w - q.y) / (6.0f * d + e)), d / (q.x + e), q.x); +} + +float3 HsvToRgb(float3 color) +{ + const float4 k = float4(1.0f, 2.0f / 3.0f, 1.0f / 3.0f, 3.0f); + const float3 p = abs(frac(color.xxx + k.xyz) * 6.0f - k.www); + return color.z * lerp(k.xxx, saturate(p - k.xxx), color.y); +} diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli index ccaf5cf5d0..7a2d2ee428 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli @@ -40,11 +40,10 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject //! Reflection Probe (smallest probe volume that overlaps the object position) struct ReflectionProbeData { - float3 m_aabbPos; - float3 m_outerAabbMin; - float3 m_outerAabbMax; - float3 m_innerAabbMin; - float3 m_innerAabbMax; + row_major float3x4 m_modelToWorld; + row_major float3x4 m_modelToWorldInverse; // does not include extents + float3 m_outerObbHalfLengths; + float3 m_innerObbHalfLengths; float m_padding; bool m_useReflectionProbe; bool m_useParallaxCorrection; @@ -52,4 +51,32 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject ReflectionProbeData m_reflectionProbeData; TextureCube m_reflectionProbeCubeMap; + + float4x4 GetReflectionProbeWorldMatrix() + { + float4x4 modelToWorld = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorld[0] = m_reflectionProbeData.m_modelToWorld[0]; + modelToWorld[1] = m_reflectionProbeData.m_modelToWorld[1]; + modelToWorld[2] = m_reflectionProbeData.m_modelToWorld[2]; + return modelToWorld; + } + + float4x4 GetReflectionProbeWorldMatrixInverse() + { + float4x4 modelToWorldInverse = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorldInverse[0] = m_reflectionProbeData.m_modelToWorldInverse[0]; + modelToWorldInverse[1] = m_reflectionProbeData.m_modelToWorldInverse[1]; + modelToWorldInverse[2] = m_reflectionProbeData.m_modelToWorldInverse[2]; + return modelToWorldInverse; + } } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/LightingUtils.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/LightingUtils.azsli index 503095020c..3248fc83eb 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/LightingUtils.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/LightingUtils.azsli @@ -50,10 +50,10 @@ float GetRoughnessMip(float roughness) return roughness * maxRoughnessMip; } -// compute parallax corrected reflection vector +// compute parallax corrected reflection vector, AABB version // we do this by finding the intersection with the volume and adjusting the reflection vector for the surface position // https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ -float3 ApplyParallaxCorrection(float3 aabbMin, float3 aabbMax, float3 aabbPos, float3 positionWS, float3 reflectDir) +float3 ApplyParallaxCorrectionAABB(float3 aabbMin, float3 aabbMax, float3 aabbPos, float3 positionWS, float3 reflectDir) { float3 rcpReflectDir = 1.0f / reflectDir; float3 intersectA = (aabbMax - positionWS) * rcpReflectDir; @@ -63,3 +63,10 @@ float3 ApplyParallaxCorrection(float3 aabbMin, float3 aabbMax, float3 aabbPos, f float3 intersectPos = reflectDir * distance + positionWS; return (intersectPos - aabbPos); } + +// compute parallax corrected reflection vector, OBB version +float3 ApplyParallaxCorrectionOBB(float4x4 obbTransformInverse, float3 obbHalfExtents, float3 positionWS, float3 reflectDir) +{ + float4 p = mul(obbTransformInverse, float4(positionWS, 1.0f)); + return ApplyParallaxCorrectionAABB(-obbHalfExtents, obbHalfExtents, float3(0.0f, 0.0f, 0.0f), p, reflectDir); +} diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli index 5a0e150dbb..d33a307dfe 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli @@ -48,10 +48,9 @@ float3 GetIblSpecular( { if (ObjectSrg::m_reflectionProbeData.m_useParallaxCorrection) { - reflectDir = ApplyParallaxCorrection( - ObjectSrg::m_reflectionProbeData.m_outerAabbMin, - ObjectSrg::m_reflectionProbeData.m_outerAabbMax, - ObjectSrg::m_reflectionProbeData.m_aabbPos, + reflectDir = ApplyParallaxCorrectionOBB( + ObjectSrg::GetReflectionProbeWorldMatrixInverse(), + ObjectSrg::m_reflectionProbeData.m_outerObbHalfLengths, position, reflectDir); } @@ -60,11 +59,10 @@ float3 GetIblSpecular( probeSpecular *= (specularF0 * brdf.x + brdf.y); // compute blend amount based on world position in the reflection probe volume - float blendAmount = ComputeLerpBetweenInnerOuterAABBs( - ObjectSrg::m_reflectionProbeData.m_innerAabbMin, - ObjectSrg::m_reflectionProbeData.m_innerAabbMax, - ObjectSrg::m_reflectionProbeData.m_outerAabbMax, - ObjectSrg::m_reflectionProbeData.m_aabbPos, + float blendAmount = ComputeLerpBetweenInnerOuterOBBs( + ObjectSrg::GetReflectionProbeWorldMatrixInverse(), + ObjectSrg::m_reflectionProbeData.m_innerObbHalfLengths, + ObjectSrg::m_reflectionProbeData.m_outerObbHalfLengths, position); outSpecular = lerp(outSpecular, probeSpecular, blendAmount); diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.azsl new file mode 100644 index 0000000000..cfa149f0d5 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.azsl @@ -0,0 +1,203 @@ +/* + * 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 <3rdParty/Features/PostProcessing/PSstyleColorBlends_Separable.azsli> +#include <3rdParty/Features/PostProcessing/PSstyleColorBlends_NonSeparable.azsli> +#include <3rdParty/Features/PostProcessing/KelvinToRgb.azsli> + +static const float FloatEpsilon = 1.192092896e-07; // 1.0 + FloatEpsilon != 1.0, smallest positive float +static const float FloatMin = FLOAT_32_MIN; // Min float number that is positive +static const float FloatMax = FLOAT_32_MAX; // Max float number representable + +static const float AcesCcMidGrey = 0.4135884; + +ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback +{ + // get the framebuffer + Texture2D m_framebuffer; + + // framebuffer sampler + Sampler LinearSampler + { + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; + + float m_colorGradingExposure; + float m_colorGradingContrast; + float m_colorGradingHueShift; + float m_colorGradingPreSaturation; + float m_colorFilterIntensity; + float m_colorFilterMultiply; + float m_whiteBalanceKelvin; + float m_whiteBalanceTint; + float m_splitToneBalance; + float m_splitToneWeight; + float m_colorGradingPostSaturation; + float m_smhShadowsStart; + float m_smhShadowsEnd; + float m_smhHighlightsStart; + float m_smhHighlightsEnd; + float m_smhWeight; + + float3 m_channelMixingRed; + float3 m_channelMixingGreen; + float3 m_channelMixingBlue; + + float4 m_colorFilterSwatch; + float4 m_splitToneShadowsColor; + float4 m_splitToneHighlightsColor; + + float4 m_smhShadowsColor; + float4 m_smhMidtonesColor; + float4 m_smhHighlightsColor; +} + +float SaturateWithEpsilon(float value) +{ + return clamp(value, FloatEpsilon, 1.0f); +} + +// Below are the color grading functions. These expect the frame color to be in ACEScg space. +// Note that some functions may have some quirks in their implementation and is subject to change. +float3 ColorGradePostExposure (float3 frameColor, float exposure) +{ + frameColor *= pow(2.0f, exposure); + return frameColor; +} + +// The contrast equation is performed in ACEScc (logarithmic) color space. +float3 ColorGradingContrast (float3 frameColor, float midgrey, float amount) +{ + const float contrastAdjustment = amount * 0.01f + 1.0f; + frameColor = TransformColor(frameColor.rgb, ColorSpaceId::ACEScg, ColorSpaceId::ACEScc); + frameColor = (frameColor - midgrey) * contrastAdjustment + midgrey; + return frameColor = TransformColor(frameColor.rgb, ColorSpaceId::ACEScc, ColorSpaceId::ACEScg); +} + +// The swatchColor param expects a linear RGB value. +float3 ColorGradeColorFilter (float3 frameColor, float3 swatchColor, float alpha) +{ + swatchColor = TransformColor(swatchColor, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); + swatchColor *= pow(2.0f, PassSrg::m_colorFilterIntensity); + const float3 frameAdjust = frameColor * swatchColor; + return frameColor = lerp(frameColor, frameAdjust, alpha); +} + +float3 ColorGradeHueShift (float3 frameColor, float amount) +{ + float3 frameHsv = RgbToHsv(frameColor); + const float hue = frameHsv.x + amount; + frameHsv.x = RotateHue(hue, 0.0, 1.0); + return HsvToRgb(frameHsv); +} + +float3 ColorGradeSaturation (float3 frameColor, float control) +{ + const float vLuminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg); + return (frameColor - vLuminance) * control + vLuminance; +} + +float3 ColorGradeKelvinColorTemp(float3 frameColor, float kelvin) +{ + const float3 kColor = TransformColor(KelvinToRgb(kelvin), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); + const float luminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg); + const float3 resHsl = RgbToHsl(frameColor.rgb * kColor.rgb); // Apply Kelvin color and convert to HSL + return HslToRgb(float3(resHsl.xy, luminance)); // Preserve luminance +} + +// pow(f, e) won't work if f is negative, or may cause inf/NAN. +float3 NoNanPow(float3 base, float3 power) +{ + return pow(max(abs(base), float3(FloatEpsilon, FloatEpsilon, FloatEpsilon)), power); +} + +float3 ColorGradeSplitTone (float3 frameColor, float balance, float weight) +{ + float3 frameSplitTone = NoNanPow(frameColor, 1.0 / 2.2); + const float t = SaturateWithEpsilon(CalculateLuminance(SaturateWithEpsilon(frameSplitTone), ColorSpaceId::ACEScg) + balance); + const float3 shadows = lerp(0.5, PassSrg::m_splitToneShadowsColor.rgb, 1.0 - t); + const float3 highlights = lerp(0.5, PassSrg::m_splitToneHighlightsColor.rgb, t); + frameSplitTone = BlendMode_SoftLight(frameSplitTone, shadows); + frameSplitTone = BlendMode_SoftLight(frameSplitTone, highlights); + frameSplitTone = NoNanPow(frameSplitTone, 2.2); + return lerp(frameColor.rgb, frameSplitTone.rgb, weight); +} + +float3 ColorGradeChannelMixer (float3 frameColor) +{ + return mul(float3x3(PassSrg::m_channelMixingRed.rgb, + PassSrg::m_channelMixingGreen.rgb, + PassSrg::m_channelMixingBlue.rgb), + frameColor); +} + +float3 ColorGradeShadowsMidtonesHighlights (float3 frameColor, float shadowsStart, float shadowsEnd, + float highlightsStart, float highlightsEnd, float weight, + float4 shadowsColor, float4 midtonesColor, float4 highlightsColor) +{ + const float3 shadowsColorACEScg = TransformColor(shadowsColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); + const float3 midtonesColorACEScg = TransformColor(midtonesColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); + const float3 highlightsColorACEScg = TransformColor(highlightsColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); + + const float cLuminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg); + const float shadowsWeight = 1.0 - smoothstep(shadowsStart, shadowsEnd, cLuminance); + const float highlightsWeight = smoothstep(highlightsStart, highlightsEnd, cLuminance); + const float midtonesWeight = 1.0 - shadowsWeight - highlightsWeight; + + const float3 frameSmh = frameColor * shadowsColorACEScg * shadowsWeight + + frameColor * midtonesColorACEScg * midtonesWeight + + frameColor * highlightsColorACEScg * highlightsWeight; + return lerp(frameColor.rgb, frameSmh.rgb, weight); +} + +float3 ColorGrade (float3 frameColor) +{ + frameColor = ColorGradePostExposure(frameColor, PassSrg::m_colorGradingExposure); + frameColor = ColorGradeKelvinColorTemp(frameColor, PassSrg::m_whiteBalanceKelvin); + frameColor = ColorGradingContrast(frameColor, AcesCcMidGrey, PassSrg::m_colorGradingContrast); + frameColor = ColorGradeColorFilter(frameColor, PassSrg::m_colorFilterSwatch.rgb, + PassSrg::m_colorFilterMultiply); + frameColor = max(frameColor, 0.0); + frameColor = ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPreSaturation); + frameColor = ColorGradeSplitTone(frameColor, PassSrg::m_splitToneBalance, PassSrg::m_splitToneWeight); + frameColor = ColorGradeChannelMixer(frameColor); + frameColor = max(frameColor, 0.0); + frameColor = ColorGradeShadowsMidtonesHighlights(frameColor, PassSrg::m_smhShadowsStart, PassSrg::m_smhShadowsEnd, + PassSrg::m_smhHighlightsStart, PassSrg::m_smhHighlightsEnd, PassSrg::m_smhWeight, + PassSrg::m_smhShadowsColor, PassSrg::m_smhMidtonesColor, PassSrg::m_smhHighlightsColor); + frameColor = ColorGradeHueShift(frameColor, PassSrg::m_colorGradingHueShift); + frameColor = ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPostSaturation); + return frameColor.rgb; +} + +PSOutput MainPS(VSOutput IN) +{ + PSOutput OUT; + + // Fetch the pixel color from the input texture + float3 frameColor = PassSrg::m_framebuffer.Sample(PassSrg::LinearSampler, IN.m_texCoord).rgb; + + OUT.m_color.rgb = ColorGrade(frameColor); + OUT.m_color.w = 1; + + return OUT; +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.shader new file mode 100644 index 0000000000..f76f6708b7 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.shader @@ -0,0 +1,22 @@ +{ + "Source" : "HDRColorGrading", + + "DepthStencilState" : { + "Depth" : { "Enable" : false } + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.azsl index 095576caaf..0fca4d03c6 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.azsl @@ -63,7 +63,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // make sure the pixel belongs to this probe volume // this is necessary since it could have the correct stencil value but actually reside // in another volume that's in between the camera and the volume we're rendering - if (!AabbContainsPoint(ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, positionWS)) + if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_outerObbHalfLengths, positionWS)) { discard; } @@ -71,11 +71,15 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // determine blend based on position with respect to the inner and outer AABBs // if it's inside the inner AABB it blends at 100%, otherwise it's the percentage of the distance between the inner/outer AABB float blendWeight = 1.0f; - if (!AabbContainsPoint(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, positionWS)) + if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, positionWS)) { // not inside the inner AABB, so it's in between the inner and outer AABBs // compute blend amount based on the distance to the outer AABB - blendWeight = ComputeLerpBetweenInnerOuterAABBs(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS); + blendWeight = ComputeLerpBetweenInnerOuterOBBs( + ObjectSrg::GetWorldMatrixInverse(), + ObjectSrg::m_innerObbHalfLengths, + ObjectSrg::m_outerObbHalfLengths, + positionWS); } // write the blend weight (additive) at this position for the probe volume diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderCommon.azsli index f172035177..ce4684b150 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderCommon.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderCommon.azsli @@ -12,12 +12,12 @@ #include // compute final probe specular using the probe cubemap and the roughness, normals, and specularF0 for the surface -bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float3 aabbMin, float3 aabbMax, uint sampleIndex, out float3 specular) +bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float4x4 obbTransformInverse, float3 outerObbHalfLengths, uint sampleIndex, out float3 specular) { // make sure the pixel belongs to this probe volume // this is necessary since it could have the correct stencil value but actually reside // in another volume that's in between the camera and the volume we're rendering - if (!AabbContainsPoint(aabbMin, aabbMax, positionWS)) + if (!ObbContainsPoint(obbTransformInverse, outerObbHalfLengths, positionWS)) { return false; } @@ -47,7 +47,11 @@ bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float3 aabbMin float3 localReflectDir = reflectDir; if (ObjectSrg::m_useParallaxCorrection) { - localReflectDir = ApplyParallaxCorrection(ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS, reflectDir); + localReflectDir = ApplyParallaxCorrectionOBB( + ObjectSrg::GetWorldMatrixInverse(), + ObjectSrg::m_outerObbHalfLengths, + positionWS, + reflectDir); } // sample reflection cubemap with the appropriate roughness mip diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl index 209605be03..a0734caf02 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl @@ -75,7 +75,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // compute specular using the probe cubemap and the roughness, normals, and specularF0 for the surface float3 specular = float3(0.0f, 0.0f, 0.0f); - if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, sampleIndex, specular)) + if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, sampleIndex, specular)) { discard; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli index 92ecd82dee..8151ed2fd5 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli @@ -13,12 +13,9 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject { row_major float3x4 m_modelToWorld; - - float3 m_aabbPos; - float3 m_outerAabbMin; - float3 m_outerAabbMax; - float3 m_innerAabbMin; - float3 m_innerAabbMax; + row_major float3x4 m_modelToWorldInverse; // does not include extents + float3 m_outerObbHalfLengths; + float3 m_innerObbHalfLengths; bool m_useParallaxCorrection; TextureCube m_reflectionCubeMap; @@ -35,4 +32,18 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject modelToWorld[2] = ObjectSrg::m_modelToWorld[2]; return modelToWorld; } + + float4x4 GetWorldMatrixInverse() + { + float4x4 modelToWorldInverse = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorldInverse[0] = ObjectSrg::m_modelToWorldInverse[0]; + modelToWorldInverse[1] = ObjectSrg::m_modelToWorldInverse[1]; + modelToWorldInverse[2] = ObjectSrg::m_modelToWorldInverse[2]; + return modelToWorldInverse; + } } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl index be94a53588..ac97172f1f 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl @@ -77,7 +77,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // compute specular using the probe cubemap and the roughness, normals, and specularF0 for the surface float3 specular = float3(0.0f, 0.0f, 0.0f); - if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, sampleIndex, specular)) + if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_outerObbHalfLengths, sampleIndex, specular)) { discard; } @@ -85,13 +85,17 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // determine blend based on position with respect to the inner and outer AABBs // if it's inside the inner AABB it blends at 100%, otherwise it's the percentage of the distance between the inner/outer AABB float blendWeight = 1.0f; - if (!AabbContainsPoint(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, positionWS)) + if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, positionWS)) { // not inside the inner AABB, so it's in between the inner and outer AABBs // compute blend amount based on the distance to the outer AABB - blendWeight = ComputeLerpBetweenInnerOuterAABBs(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS); + blendWeight = ComputeLerpBetweenInnerOuterOBBs( + ObjectSrg::GetWorldMatrixInverse(), + ObjectSrg::m_innerObbHalfLengths, + ObjectSrg::m_outerObbHalfLengths, + positionWS); } - + // retrieve the blend weight of all probes at this location float blendWeightAllProbes = PassSrg::m_blendWeight.Load(IN.m_position.xy, sampleIndex).r; diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index 8435625833..c8f99cdb24 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -86,6 +86,7 @@ set(FILES Passes/CascadedShadowmaps.pass Passes/CheckerboardResolveColor.pass Passes/CheckerboardResolveDepth.pass + Passes/HDRColorGrading.pass Passes/ContrastAdaptiveSharpening.pass Passes/ConvertToAcescg.pass Passes/DebugOverlayParent.pass @@ -221,6 +222,8 @@ set(FILES ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/LinearSrgb_To_AcesCg.azsli ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/LinearSrgb_To_Srgb.azsli ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/Srgb_To_LinearSrgb.azsli + ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCcToAcesCg.azsli + ShaderLib/Atom/Features/ColorManagement/GeneratedTransforms/AcesCgToAcesCc.azsli ShaderLib/Atom/Features/CoreLights/PhotometricValue.azsli ShaderLib/Atom/Features/Decals/DecalTextureUtil.azsli ShaderLib/Atom/Features/LightCulling/LightCullingShared.azsli @@ -286,6 +289,9 @@ set(FILES ShaderLib/Atom/Features/Shadow/Shadow.azsli ShaderLib/Atom/Features/Shadow/ShadowmapAtlasLib.azsli ShaderLib/Atom/Features/Vertex/VertexHelper.azsli + ShaderLib/3rdParty/Features/PostProcessing/KelvinToRgb.azsli + ShaderLib/3rdParty/Features/PostProcessing/PSstyleColorBlends_NonSeparable.azsli + ShaderLib/3rdParty/Features/PostProcessing/PSstyleColorBlends_Separable.azsli ShaderResourceGroups/SceneSrg.azsli ShaderResourceGroups/SceneSrgAll.azsli ShaderResourceGroups/ViewSrg.azsli @@ -392,6 +398,8 @@ set(FILES Shaders/PostProcessing/FastDepthAwareBlurVer.shader Shaders/PostProcessing/FullscreenCopy.azsl Shaders/PostProcessing/FullscreenCopy.shader + Shaders/PostProcessing/HDRColorGrading.azsl + Shaders/PostProcessing/HDRColorGrading.shader Shaders/PostProcessing/LookModificationTransform.azsl Shaders/PostProcessing/LookModificationTransform.shader Shaders/PostProcessing/LuminanceHeatmap.azsl diff --git a/Gems/Atom/Feature/Common/Code/CMakeLists.txt b/Gems/Atom/Feature/Common/Code/CMakeLists.txt index b8c414dcc6..9c4ffe5b5e 100644 --- a/Gems/Atom/Feature/Common/Code/CMakeLists.txt +++ b/Gems/Atom/Feature/Common/Code/CMakeLists.txt @@ -40,6 +40,8 @@ ly_add_target( Gem::Atom_Feature_Common.Public Gem::ImGui.imguilib #3rdParty::lux_core # AZ_TRAIT_LUXCORE_SUPPORTED is disabled in every platform, Issue #3915 will remove + RUNTIME_DEPENDENCIES + Gem::ImGui.imguilib ) ly_add_target( diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ParamMacros/StartParamFunctionsOverrideImpl.inl b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ParamMacros/StartParamFunctionsOverrideImpl.inl new file mode 100644 index 0000000000..37a853c94c --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ParamMacros/StartParamFunctionsOverrideImpl.inl @@ -0,0 +1,19 @@ +/* + * 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 + * + */ + +// Auto-generates override function declarations for getters and setters of specified parameters and overrides + +#define AZ_GFX_COMMON_PARAM(ValueType, Name, MemberName, DefaultValue) \ + ValueType Get##Name() const override { return MemberName; } \ + void Set##Name(ValueType val) override { MemberName = val; } \ + +#define AZ_GFX_COMMON_OVERRIDE(ValueType, Name, MemberName, OverrideValueType) \ + OverrideValueType Get##Name##Override() const override { return MemberName##Override; } \ + void Set##Name##Override(OverrideValueType val) override { MemberName##Override = val; } \ + +#include diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl new file mode 100644 index 0000000000..241ba3b5de --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl @@ -0,0 +1,37 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +// Macros below are of the form: +// PARAM(NAME, MEMBER_NAME, DEFAULT_VALUE, ...) + +AZ_GFX_BOOL_PARAM(Enabled, m_enabled, false) +AZ_GFX_FLOAT_PARAM(ColorGradingExposure, m_colorGradingExposure, 0.0) +AZ_GFX_FLOAT_PARAM(ColorGradingContrast, m_colorGradingContrast, 0.0) +AZ_GFX_FLOAT_PARAM(ColorGradingHueShift, m_colorGradingHueShift, 0.0) +AZ_GFX_FLOAT_PARAM(ColorGradingPreSaturation, m_colorGradingPreSaturation, 1.0) +AZ_GFX_FLOAT_PARAM(ColorGradingFilterIntensity, m_colorGradingFilterIntensity, 1.0) +AZ_GFX_FLOAT_PARAM(ColorGradingFilterMultiply, m_colorGradingFilterMultiply, 0.0) +AZ_GFX_FLOAT_PARAM(ColorGradingPostSaturation, m_colorGradingPostSaturation, 1.0) +AZ_GFX_FLOAT_PARAM(WhiteBalanceKelvin, m_whiteBalanceKelvin, 6600.0) +AZ_GFX_FLOAT_PARAM(WhiteBalanceTint, m_whiteBalanceTint, 0.0) +AZ_GFX_FLOAT_PARAM(SplitToneBalance, m_splitToneBalance, 0.0) +AZ_GFX_FLOAT_PARAM(SplitToneWeight, m_splitToneWeight, 0.0) +AZ_GFX_FLOAT_PARAM(SmhShadowsStart, m_smhShadowsStart, 0.0) +AZ_GFX_FLOAT_PARAM(SmhShadowsEnd, m_smhShadowsEnd, 0.3) +AZ_GFX_FLOAT_PARAM(SmhHighlightsStart, m_smhHighlightsStart, 0.55) +AZ_GFX_FLOAT_PARAM(SmhHighlightsEnd, m_smhHighlightsEnd, 1.0) +AZ_GFX_FLOAT_PARAM(SmhWeight, m_smhWeight, 0.0) +AZ_GFX_VEC3_PARAM(ChannelMixingRed, m_channelMixingRed, AZ::Vector3(1.0f, 0.0f, 0.0f)) +AZ_GFX_VEC3_PARAM(ChannelMixingGreen, m_channelMixingGreen, AZ::Vector3(0.0f, 1.0f, 0.0f)) +AZ_GFX_VEC3_PARAM(ChannelMixingBlue, m_channelMixingBlue, AZ::Vector3(0.0f, 0.f, 1.0f)) +AZ_GFX_VEC3_PARAM(ColorFilterSwatch, m_colorFilterSwatch, AZ::Vector3(1.0f, 0.5f, 0.5f)) +AZ_GFX_VEC3_PARAM(SplitToneShadowsColor, m_splitToneShadowsColor, AZ::Vector3(1.0f, 0.5f, 0.5f)) +AZ_GFX_VEC3_PARAM(SplitToneHighlightsColor, m_splitToneHighlightsColor, AZ::Vector3(0.1f, 1.0f, 0.1f)) +AZ_GFX_VEC3_PARAM(SmhShadowsColor, m_smhShadowsColor, AZ::Vector3(1.0f, 0.25f, 0.25f)) +AZ_GFX_VEC3_PARAM(SmhMidtonesColor, m_smhMidtonesColor, AZ::Vector3(0.1f, 0.1f, 1.0f)) +AZ_GFX_VEC3_PARAM(SmhHighlightsColor, m_smhHighlightsColor, AZ::Vector3(1.0f, 0.0f, 1.0f)) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingSettingsInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingSettingsInterface.h new file mode 100644 index 0000000000..019df206eb --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingSettingsInterface.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class HDRColorGradingSettingsInterface + { + public: + AZ_RTTI(AZ::Render::HDRColorGradingSettingsInterface, "{CB5ADF78-27DE-438C-A991-1E5433046A42}"); + + // Auto-gen virtual getter and setter functions... +#include +#include +#include + + virtual void OnConfigChanged() = 0; + }; + } +} diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettings.inl b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettings.inl index 0259f07525..8409732f34 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettings.inl +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettings.inl @@ -25,3 +25,4 @@ POST_PROCESS_MEMBER(ExposureControlSettings, m_exposureControlSettings) POST_PROCESS_MEMBER(SsaoSettings, m_ssaoSettings) POST_PROCESS_MEMBER(LookModificationSettings, m_lookModificationSettings) POST_PROCESS_MEMBER(DeferredFogSettings, m_deferredFogSettings) +POST_PROCESS_MEMBER(HDRColorGradingSettings, m_hdrColorGradingSettings) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettingsInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettingsInterface.h index 12c0017aad..905aa9f2ca 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettingsInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettingsInterface.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp index 214643505a..29db7d6673 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp @@ -9,8 +9,6 @@ #include "AuxGeomDrawQueue.h" -#include - #include #include @@ -565,7 +563,7 @@ namespace AZ AuxGeomBufferData* AuxGeomDrawQueue::Commit() { - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "AuxGeomDrawQueue: Commit"); + AZ_PROFILE_SCOPE(AzRender, "AuxGeomDrawQueue: Commit"); // get a mutually exclusive lock and then switch to the next buffer, returning a pointer to the current buffer (before the switch) // grab the lock @@ -585,7 +583,7 @@ namespace AZ void AuxGeomDrawQueue::ClearCurrentBufferData() { - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "AuxGeomDrawQueue: ClearCurrentBufferData"); + AZ_PROFILE_SCOPE(AzRender, "AuxGeomDrawQueue: ClearCurrentBufferData"); // no need for mutex here, this function is only called from a function holding a lock AuxGeomBufferData& data = m_buffers[m_currentBufferIndex]; @@ -649,7 +647,7 @@ namespace AZ AZ::u8 width, int32_t viewProjOverrideIndex) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "AuxGeomDrawQueue: DrawPrimitiveWithSharedVerticesCommon"); // grab a mutex lock for the rest of this function so that a commit cannot happen during it and // other threads can't add geometry during it @@ -720,8 +718,7 @@ namespace AZ AZ::u8 width, int32_t viewProjOverrideIndex) { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "AuxGeomDrawQueue: DrawPrimitiveWithSharedVerticesCommon"); + AZ_PROFILE_SCOPE(AzRender, "AuxGeomDrawQueue: DrawPrimitiveWithSharedVerticesCommon"); AZ_Assert(indexCount >= verticesPerPrimitiveType && (indexCount % verticesPerPrimitiveType == 0), "Index count must be at least %d and must be a multiple of %d", diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomFeatureProcessor.cpp index feeb1dedcc..a4720ad131 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomFeatureProcessor.cpp @@ -12,7 +12,6 @@ #include "DynamicPrimitiveProcessor.h" #include "FixedShapeProcessor.h" -#include #include #include @@ -80,7 +79,7 @@ namespace AZ void AuxGeomFeatureProcessor::Render(const FeatureProcessor::RenderPacket& fpPacket) { - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "AuxGeomFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(AzRender, "AuxGeomFeatureProcessor: Render"); // Get the scene data and switch buffers so that other threads can continue to queue requests AuxGeomBufferData* bufferData = static_cast(m_sceneDrawQueue.get())->Commit(); diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/DynamicPrimitiveProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/DynamicPrimitiveProcessor.cpp index f35809f148..ae9e55ac8d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/DynamicPrimitiveProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/DynamicPrimitiveProcessor.cpp @@ -9,7 +9,6 @@ #include "DynamicPrimitiveProcessor.h" #include "AuxGeomDrawProcessorShared.h" -#include #include #include #include @@ -21,6 +20,8 @@ #include #include +#include + namespace AZ { namespace Render @@ -70,7 +71,7 @@ namespace AZ void DynamicPrimitiveProcessor::PrepareFrame() { - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "DynamicPrimitiveProcessor: PrepareFrame"); + AZ_PROFILE_SCOPE(AzRender, "DynamicPrimitiveProcessor: PrepareFrame"); m_drawPackets.clear(); m_processSrgs.clear(); @@ -88,7 +89,7 @@ namespace AZ void DynamicPrimitiveProcessor::ProcessDynamicPrimitives(const AuxGeomBufferData* bufferData, const RPI::FeatureProcessor::RenderPacket& fpPacket) { - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "DynamicPrimitiveProcessor: ProcessDynamicPrimitives"); + AZ_PROFILE_SCOPE(AzRender, "DynamicPrimitiveProcessor: ProcessDynamicPrimitives"); RHI::DrawPacketBuilder drawPacketBuilder; const DynamicPrimitiveData& srcPrimitives = bufferData->m_primitiveData; diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/FixedShapeProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/FixedShapeProcessor.cpp index 0045311bef..c2ee397b4c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/FixedShapeProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/FixedShapeProcessor.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include @@ -108,8 +107,8 @@ namespace AZ } void FixedShapeProcessor::PrepareFrame() - { - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "FixedShapeProcessor: PrepareFrame"); + { + AZ_PROFILE_SCOPE(AzRender, "FixedShapeProcessor: PrepareFrame"); m_processSrgs.clear(); m_drawPackets.clear(); @@ -127,8 +126,7 @@ namespace AZ void FixedShapeProcessor::ProcessObjects(const AuxGeomBufferData* bufferData, const RPI::FeatureProcessor::RenderPacket& fpPacket) { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "FixedShapeProcessor: ProcessObjects"); + AZ_PROFILE_SCOPE(AzRender, "FixedShapeProcessor: ProcessObjects"); RHI::DrawPacketBuilder drawPacketBuilder; diff --git a/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardPass.h b/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardPass.h index 3ef4e5f851..5db9a7e487 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardPass.h @@ -35,7 +35,7 @@ namespace AZ protected: // Pass overrides... - void FrameBeginInternal(FramePrepareParams params); + void FrameBeginInternal(FramePrepareParams params) override; void BuildInternal() override; void FrameEndInternal() override; diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index de03c08c6f..db49fba96f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -222,6 +223,7 @@ namespace AZ passSystem->AddPassCreator(Name("LightCullingRemapPass"), &LightCullingRemap::Create); passSystem->AddPassCreator(Name("LightCullingTilePreparePass"), &LightCullingTilePreparePass::Create); passSystem->AddPassCreator(Name("BlendColorGradingLutsPass"), &BlendColorGradingLutsPass::Create); + passSystem->AddPassCreator(Name("HDRColorGradingPass"), &HDRColorGradingPass::Create); passSystem->AddPassCreator(Name("LookModificationCompositePass"), &LookModificationCompositePass::Create); passSystem->AddPassCreator(Name("LookModificationTransformPass"), &LookModificationPass::Create); passSystem->AddPassCreator(Name("SMAAEdgeDetectionPass"), &SMAAEdgeDetectionPass::Create); diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/CapsuleLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/CapsuleLightFeatureProcessor.cpp index 683295cf5b..bbea462ac8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/CapsuleLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/CapsuleLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -102,7 +101,7 @@ namespace AZ void CapsuleLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "CapsuleLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "CapsuleLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -114,7 +113,7 @@ namespace AZ void CapsuleLightFeatureProcessor::Render(const CapsuleLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "CapsuleLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "CapsuleLightFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index c235f78595..42cca0e57c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -196,7 +195,7 @@ namespace AZ void DirectionalLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket&) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "DirectionalLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "DirectionalLightFeatureProcessor: Simulate"); if (m_shadowingLightHandle.IsValid()) { @@ -293,7 +292,7 @@ namespace AZ void DirectionalLightFeatureProcessor::Render(const FeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "DirectionalLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "DirectionalLightFeatureProcessor: Render"); if (m_shadowingLightHandle.IsValid()) { @@ -1232,7 +1231,7 @@ namespace AZ void DirectionalLightFeatureProcessor::SetFilterParameterToPass(LightHandle handle, const RPI::View* cameraView) { - AZ_ATOM_PROFILE_FUNCTION("DirectionalLightFeatureProcessor", "DirectionalLightFeatureProcessor::SetFilterParameterToPass"); + AZ_PROFILE_SCOPE(RPI, "DirectionalLightFeatureProcessor::SetFilterParameterToPass"); if (handle != m_shadowingLightHandle) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp index dfbeea0ffe..26e1757a5a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include -#include #include #include @@ -123,7 +122,7 @@ namespace AZ void DiskLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "DiskLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "DiskLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -135,7 +134,7 @@ namespace AZ void DiskLightFeatureProcessor::Render(const DiskLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "DiskLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "DiskLightFeatureProcessor: Simulate"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp index af440e5040..d3b5646e0b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -119,7 +118,7 @@ namespace AZ void PointLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "PointLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "PointLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -131,7 +130,7 @@ namespace AZ void PointLightFeatureProcessor::Render(const PointLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "PointLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "PointLightFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PolygonLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PolygonLightFeatureProcessor.cpp index 089adf4621..f3c05eeeec 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PolygonLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PolygonLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include -#include #include #include @@ -132,7 +131,7 @@ namespace AZ::Render void PolygonLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "PolygonLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "PolygonLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -153,7 +152,7 @@ namespace AZ::Render void PolygonLightFeatureProcessor::Render(const PolygonLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "PolygonLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "PolygonLightFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/QuadLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/QuadLightFeatureProcessor.cpp index e22174225d..787e150646 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/QuadLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/QuadLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include -#include #include #include @@ -107,7 +106,7 @@ namespace AZ void QuadLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "QuadLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "QuadLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -119,7 +118,7 @@ namespace AZ void QuadLightFeatureProcessor::Render(const QuadLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "QuadLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "QuadLightFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimplePointLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimplePointLightFeatureProcessor.cpp index b8bbb03335..bc13d3d508 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimplePointLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimplePointLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -102,7 +101,7 @@ namespace AZ void SimplePointLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "SimplePointLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "SimplePointLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -114,7 +113,7 @@ namespace AZ void SimplePointLightFeatureProcessor::Render(const SimplePointLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "SimplePointLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "SimplePointLightFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimpleSpotLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimpleSpotLightFeatureProcessor.cpp index d4776faec6..49b7f7d12c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimpleSpotLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimpleSpotLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -102,7 +101,7 @@ namespace AZ void SimpleSpotLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "SimpleSpotLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "SimpleSpotLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -114,7 +113,7 @@ namespace AZ void SimpleSpotLightFeatureProcessor::Render(const SimpleSpotLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "SimpleSpotLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "SimpleSpotLightFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp index 4954ffc01c..f00c902a73 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp @@ -10,7 +10,6 @@ #include -#include #include #include @@ -107,7 +106,7 @@ namespace AZ void DecalFeatureProcessor::Simulate(const RPI::FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "DecalFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "DecalFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -131,7 +130,7 @@ namespace AZ void DecalFeatureProcessor::Render(const RPI::FeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "DecalFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "DecalFeatureProcessor: Render"); AZStd::array_view> baseMaps = GetImagesFromDecalData<1>(); AZStd::array_view> opacityMaps = GetImagesFromDecalData<2>(); diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp index 4ecfd7fc09..c0b8f3315a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp @@ -145,7 +145,7 @@ namespace AZ void DecalTextureArrayFeatureProcessor::Simulate(const RPI::FeatureProcessor::SimulatePacket& packet) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "DecalTextureArrayFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -158,7 +158,7 @@ namespace AZ void DecalTextureArrayFeatureProcessor::Render(const RPI::FeatureProcessor::RenderPacket& packet) { // Note that decals are rendered as part of the forward shading pipeline. We only need to bind the decal buffers/textures in here. - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "DecalTextureArrayFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { @@ -294,7 +294,7 @@ namespace AZ void DecalTextureArrayFeatureProcessor::SetDecalMaterial(const DecalHandle handle, const AZ::Data::AssetId material) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "DecalTextureArrayFeatureProcessor: SetDecalMaterial"); if (handle.IsNull()) { AZ_Warning("DecalTextureArrayFeatureProcessor", false, "Invalid handle passed to DecalTextureArrayFeatureProcessor::SetDecalMaterial()."); @@ -364,7 +364,7 @@ namespace AZ void DecalTextureArrayFeatureProcessor::OnAssetReady(const Data::Asset asset) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "DecalTextureArrayFeatureProcessor: OnAssetReady"); const Data::AssetId& assetId = asset->GetId(); const RPI::MaterialAsset* materialAsset = asset.GetAs(); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp index 543851da0f..d79240d4f1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp @@ -111,7 +111,7 @@ namespace AZ void DiffuseProbeGridFeatureProcessor::Simulate([[maybe_unused]] const FeatureProcessor::SimulatePacket& packet) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "DiffuseProbeGridFeatureProcessor: Simulate"); // update pipeline states if (m_needUpdatePipelineStates) diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp index b6ca1191dd..0ff8d2c165 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp @@ -15,7 +15,6 @@ #include -#include #include #include @@ -577,8 +576,7 @@ namespace AZ void ImGuiPass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("Pass", "ImGuiPass: Execute"); + AZ_PROFILE_SCOPE(AzRender, "ImGuiPass: BuildCommandListInternal"); context.GetCommandList()->SetViewport(m_viewportState); @@ -607,8 +605,7 @@ namespace AZ uint32_t ImGuiPass::UpdateImGuiResources() { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("Pass", "ImGuiPass: UpdateImGuiResources"); + AZ_PROFILE_SCOPE(AzRender, "ImGuiPass: UpdateImGuiResources"); auto imguiContextScope = ImguiContextScope(m_imguiContext); diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.h b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.h index 0bde3edb4f..018d2d46b7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.h @@ -77,7 +77,7 @@ namespace AZ void RenderImguiDrawData(const ImDrawData& drawData); // TickBus::Handler overrides... - void OnTick(float deltaTime, AZ::ScriptTimePoint timePoint); + void OnTick(float deltaTime, AZ::ScriptTimePoint timePoint) override; // AzFramework::InputTextEventListener overrides... bool OnInputTextEventFiltered(const AZStd::string& textUTF8) override; diff --git a/Gems/Atom/Feature/Common/Code/Source/ImageBasedLights/ImageBasedLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ImageBasedLights/ImageBasedLightFeatureProcessor.cpp index 7c41379a14..6a2eda8f22 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImageBasedLights/ImageBasedLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ImageBasedLights/ImageBasedLightFeatureProcessor.cpp @@ -11,8 +11,6 @@ #include #include -#include - #include namespace AZ @@ -50,7 +48,7 @@ namespace AZ void ImageBasedLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "ImageBasedLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "ImageBasedLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); m_sceneSrg->SetImage(m_specularEnvMapIndex, m_specular); diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/DrawListFunctor.h b/Gems/Atom/Feature/Common/Code/Source/Material/DrawListFunctor.h index eb4bb0ab00..a21fb963e9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/DrawListFunctor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Material/DrawListFunctor.h @@ -26,6 +26,7 @@ namespace AZ static void Reflect(ReflectContext* context); + using RPI::MaterialFunctor::Process; void Process(RuntimeContext& context) override; private: diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/DrawListFunctorSourceData.h b/Gems/Atom/Feature/Common/Code/Source/Material/DrawListFunctorSourceData.h index 58df14a812..ba5e174101 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/DrawListFunctorSourceData.h +++ b/Gems/Atom/Feature/Common/Code/Source/Material/DrawListFunctorSourceData.h @@ -27,6 +27,7 @@ namespace AZ static void Reflect(ReflectContext* context); + using RPI::MaterialFunctorSourceData::CreateFunctor; FunctorResult CreateFunctor(const RuntimeContext& context) const override; private: diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctor.h b/Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctor.h index 10be4a0c47..e412687947 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctor.h @@ -26,6 +26,7 @@ namespace AZ static void Reflect(ReflectContext* context); + using RPI::MaterialFunctor::Process; void Process(RuntimeContext& context) override; private: diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctorSourceData.h b/Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctorSourceData.h index c4f07c549d..9d074551c3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctorSourceData.h +++ b/Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctorSourceData.h @@ -25,6 +25,7 @@ namespace AZ static void Reflect(AZ::ReflectContext* context); + using AZ::RPI::MaterialFunctorSourceData::CreateFunctor; FunctorResult CreateFunctor(const RuntimeContext& context) const override; private: diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/Transform2DFunctor.h b/Gems/Atom/Feature/Common/Code/Source/Material/Transform2DFunctor.h index bee24d90b7..2ef5af6913 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/Transform2DFunctor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Material/Transform2DFunctor.h @@ -34,6 +34,7 @@ namespace AZ static void Reflect(ReflectContext* context); + using RPI::MaterialFunctor::Process; void Process(RuntimeContext& context) override; private: diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/Transform2DFunctorSourceData.h b/Gems/Atom/Feature/Common/Code/Source/Material/Transform2DFunctorSourceData.h index ecac071f94..db7e65fedc 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/Transform2DFunctorSourceData.h +++ b/Gems/Atom/Feature/Common/Code/Source/Material/Transform2DFunctorSourceData.h @@ -25,6 +25,7 @@ namespace AZ static void Reflect(AZ::ReflectContext* context); + using AZ::RPI::MaterialFunctorSourceData::CreateFunctor; FunctorResult CreateFunctor(const RuntimeContext& context) const override; private: diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 8ab324cdf7..3fcda71998 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -8,7 +8,6 @@ #include -#include #include #include #include @@ -75,8 +74,7 @@ namespace AZ void MeshFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("RPI", "MeshFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "MeshFeatureProcessor: Simulate"); AZ_UNUSED(packet); AZStd::concurrency_check_scope scopeCheck(m_meshDataChecker); @@ -87,7 +85,6 @@ namespace AZ { const auto jobLambda = [&]() -> void { - AZ_PROFILE_SCOPE(AzRender, "MeshFP::Simulate() Lambda"); for (auto meshDataIter = iteratorRange.first; meshDataIter != iteratorRange.second; ++meshDataIter) { if (!meshDataIter->m_model) @@ -149,7 +146,7 @@ namespace AZ const MeshHandleDescriptor& descriptor, const MaterialAssignmentMap& materials) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "MeshFeatureProcessor: AcquireMesh"); // don't need to check the concurrency during emplace() because the StableDynamicArray won't move the other elements during insertion MeshHandle meshDataHandle = m_meshData.emplace(); @@ -952,7 +949,7 @@ namespace AZ subMeshes.push_back(subMesh); } - rayTracingFeatureProcessor->SetMesh(m_objectId, subMeshes); + rayTracingFeatureProcessor->SetMesh(m_objectId, m_model->GetModelAsset()->GetId(), subMeshes); } void MeshDataInstance::SetSortKey(RHI::DrawItemSortKey sortKey) @@ -984,7 +981,7 @@ namespace AZ void MeshDataInstance::UpdateDrawPackets(bool forceUpdate /*= false*/) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "MeshDataInstance:: UpdateDrawPackets"); for (auto& drawPacketList : m_drawPacketListsByLod) { for (auto& drawPacket : drawPacketList) @@ -999,7 +996,7 @@ namespace AZ void MeshDataInstance::BuildCullable() { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "MeshDataInstance: BuildCullable"); AZ_Assert(m_cullableNeedsRebuild, "This function only needs to be called if the cullable to be rebuilt"); AZ_Assert(m_model, "The model has not finished loading yet"); @@ -1076,7 +1073,7 @@ namespace AZ void MeshDataInstance::UpdateCullBounds(const TransformServiceFeatureProcessor* transformService) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "MeshDataInstance: UpdateCullBounds"); AZ_Assert(m_cullBoundsNeedsUpdate, "This function only needs to be called if the culling bounds need to be rebuilt"); AZ_Assert(m_model, "The model has not finished loading yet"); @@ -1112,20 +1109,17 @@ namespace AZ if (reflectionProbeFeatureProcessor && (m_descriptor.m_useForwardPassIblSpecular || m_hasForwardPassIblSpecularMaterial)) { // retrieve probe constant indices - AZ::RHI::ShaderInputConstantIndex posConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_aabbPos")); - AZ_Error("MeshDataInstance", posConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); + AZ::RHI::ShaderInputConstantIndex modelToWorldConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_modelToWorld")); + AZ_Error("MeshDataInstance", modelToWorldConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); - AZ::RHI::ShaderInputConstantIndex outerAabbMinConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerAabbMin")); - AZ_Error("MeshDataInstance", outerAabbMinConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); + AZ::RHI::ShaderInputConstantIndex modelToWorldInverseConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_modelToWorldInverse")); + AZ_Error("MeshDataInstance", modelToWorldInverseConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); - AZ::RHI::ShaderInputConstantIndex outerAabbMaxConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerAabbMax")); - AZ_Error("MeshDataInstance", outerAabbMaxConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); + AZ::RHI::ShaderInputConstantIndex outerObbHalfLengthsConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerObbHalfLengths")); + AZ_Error("MeshDataInstance", outerObbHalfLengthsConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); - AZ::RHI::ShaderInputConstantIndex innerAabbMinConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerAabbMin")); - AZ_Error("MeshDataInstance", innerAabbMinConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); - - AZ::RHI::ShaderInputConstantIndex innerAabbMaxConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerAabbMax")); - AZ_Error("MeshDataInstance", innerAabbMaxConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); + AZ::RHI::ShaderInputConstantIndex innerObbHalfLengthsConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerObbHalfLengths")); + AZ_Error("MeshDataInstance", innerObbHalfLengthsConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); AZ::RHI::ShaderInputConstantIndex useReflectionProbeConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_useReflectionProbe")); AZ_Error("MeshDataInstance", useReflectionProbeConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); @@ -1147,11 +1141,10 @@ namespace AZ if (!reflectionProbes.empty() && reflectionProbes[0]) { - m_shaderResourceGroup->SetConstant(posConstantIndex, reflectionProbes[0]->GetPosition()); - m_shaderResourceGroup->SetConstant(outerAabbMinConstantIndex, reflectionProbes[0]->GetOuterAabbWs().GetMin()); - m_shaderResourceGroup->SetConstant(outerAabbMaxConstantIndex, reflectionProbes[0]->GetOuterAabbWs().GetMax()); - m_shaderResourceGroup->SetConstant(innerAabbMinConstantIndex, reflectionProbes[0]->GetInnerAabbWs().GetMin()); - m_shaderResourceGroup->SetConstant(innerAabbMaxConstantIndex, reflectionProbes[0]->GetInnerAabbWs().GetMax()); + m_shaderResourceGroup->SetConstant(modelToWorldConstantIndex, reflectionProbes[0]->GetTransform()); + m_shaderResourceGroup->SetConstant(modelToWorldInverseConstantIndex, Matrix3x4::CreateFromTransform(reflectionProbes[0]->GetTransform()).GetInverseFull()); + m_shaderResourceGroup->SetConstant(outerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetOuterObbWs().GetHalfLengths()); + m_shaderResourceGroup->SetConstant(innerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetInnerObbWs().GetHalfLengths()); m_shaderResourceGroup->SetConstant(useReflectionProbeConstantIndex, true); m_shaderResourceGroup->SetConstant(useParallaxCorrectionConstantIndex, reflectionProbes[0]->GetUseParallaxCorrection()); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/Bloom/BloomSettings.h b/Gems/Atom/Feature/Common/Code/Source/PostProcess/Bloom/BloomSettings.h index 1c245732aa..a433305d92 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/Bloom/BloomSettings.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/Bloom/BloomSettings.h @@ -47,7 +47,7 @@ namespace AZ void ApplySettingsTo(BloomSettings* target, float alpha) const; // Generate getters and setters. -#include +#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ColorGrading/HDRColorGradingSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ColorGrading/HDRColorGradingSettings.cpp new file mode 100644 index 0000000000..4542b53a15 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ColorGrading/HDRColorGradingSettings.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#include + +#include +#include + +namespace AZ +{ + namespace Render + { + HDRColorGradingSettings::HDRColorGradingSettings(PostProcessFeatureProcessor* featureProcessor) + : PostProcessBase(featureProcessor) + { + } + + void HDRColorGradingSettings::OnConfigChanged() + { + m_parentSettings->OnConfigChanged(); + } + + void HDRColorGradingSettings::ApplySettingsTo(HDRColorGradingSettings* target, [[maybe_unused]] float alpha) const + { + AZ_Assert(target != nullptr, "HDRColorGradingSettings::ApplySettingsTo called with nullptr as argument."); + + if (GetEnabled()) + { + target->m_enabled = m_enabled; + +#define AZ_GFX_BOOL_PARAM(NAME, MEMBER_NAME, DefaultValue) ; +#define AZ_GFX_FLOAT_PARAM(NAME, MEMBER_NAME, DefaultValue) \ + { \ + target->Set##NAME(AZ::Lerp(target->MEMBER_NAME, MEMBER_NAME, alpha)); \ + } + +#define AZ_GFX_VEC3_PARAM(NAME, MEMBER_NAME, DefaultValue) \ + { \ + target->MEMBER_NAME.Set(AZ::Lerp(target->MEMBER_NAME.GetX(), MEMBER_NAME.GetX(), alpha), \ + AZ::Lerp(target->MEMBER_NAME.GetY(), MEMBER_NAME.GetY(), alpha), \ + AZ::Lerp(target->MEMBER_NAME.GetZ(), MEMBER_NAME.GetZ(), alpha)); \ + } + +#include +#include + } + } + + void HDRColorGradingSettings::Simulate([[maybe_unused]] float deltaTime) + { + } + } // namespace Render +} // namespace AZ + + diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ColorGrading/HDRColorGradingSettings.h b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ColorGrading/HDRColorGradingSettings.h new file mode 100644 index 0000000000..2358919c28 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ColorGrading/HDRColorGradingSettings.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include + +#include + +namespace AZ +{ + namespace Render + { + class PostProcessSettings; + + class HDRColorGradingSettings final + : public HDRColorGradingSettingsInterface + , public PostProcessBase + { + friend class PostProcessSettings; + friend class PostProcessFeatureProcessor; + + public: + AZ_RTTI(HDRColorGradingSettings, "{EA8C05D4-66D0-4141-8D4D-68E5D764C2ED}", HDRColorGradingSettingsInterface, PostProcessBase); + AZ_CLASS_ALLOCATOR(HDRColorGradingSettings, SystemAllocator, 0); + + HDRColorGradingSettings(PostProcessFeatureProcessor* featureProcessor); + ~HDRColorGradingSettings() = default; + + void OnConfigChanged() override; + + void ApplySettingsTo(HDRColorGradingSettings* target, float alpha) const; + + // Generate all getters and override setters. + // Declare non-override setters, which will be defined in the .cpp +#define AZ_GFX_COMMON_PARAM(ValueType, Name, MemberName, DefaultValue) \ + ValueType Get##Name() const override { return MemberName; } \ + void Set##Name(ValueType val) override \ + { \ + MemberName = val; \ + } \ + +#define AZ_GFX_COMMON_OVERRIDE(ValueType, Name, MemberName, OverrideValueType) \ + OverrideValueType Get##Name##Override() const override { return MemberName##Override; } \ + void Set##Name##Override(OverrideValueType val) override { MemberName##Override = val; } \ + +#include +#include +#include + + private: + // Generate members... +#include +#include +#include + + void Simulate(float deltaTime); + + PostProcessSettings* m_parentSettings = nullptr; + }; + } +} diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessFeatureProcessor.cpp index 35294d7403..a9d8d5105f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessFeatureProcessor.cpp @@ -8,8 +8,6 @@ #include -#include - #include #include #include @@ -49,7 +47,7 @@ namespace AZ void PostProcessFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "PostProcessFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "PostProcessFeatureProcessor: Simulate"); AZ_UNUSED(packet); UpdateTime(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessSettings.h b/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessSettings.h index 2bb1eee277..c2a32e6cee 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessSettings.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessSettings.h @@ -17,6 +17,7 @@ #include #include #include +#include #include namespace AZ @@ -52,7 +53,7 @@ namespace AZ #undef POST_PROCESS_MEMBER // Auto-gen getter and setter functions for post process members... -#include +#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/Ssao/SsaoSettings.h b/Gems/Atom/Feature/Common/Code/Source/PostProcess/Ssao/SsaoSettings.h index f49b8151b1..4b3eb6e535 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/Ssao/SsaoSettings.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/Ssao/SsaoSettings.h @@ -47,7 +47,7 @@ namespace AZ void ApplySettingsTo(SsaoSettings* target, float alpha) const; // Generate getters and setters. -#include +#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.cpp new file mode 100644 index 0000000000..62fff6b6fe --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include +#include +#include + + namespace AZ +{ + namespace Render + { + RPI::Ptr HDRColorGradingPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew HDRColorGradingPass(descriptor); + return AZStd::move(pass); + } + + HDRColorGradingPass::HDRColorGradingPass(const RPI::PassDescriptor& descriptor) + : AZ::RPI::FullscreenTrianglePass(descriptor) + { + } + + void HDRColorGradingPass::InitializeInternal() + { + FullscreenTrianglePass::InitializeInternal(); + + m_colorGradingExposureIndex.Reset(); + m_colorGradingContrastIndex.Reset(); + m_colorGradingHueShiftIndex.Reset(); + m_colorGradingPreSaturationIndex.Reset(); + m_colorFilterIntensityIndex.Reset(); + m_colorFilterMultiplyIndex.Reset(); + m_whiteBalanceKelvinIndex.Reset(); + m_whiteBalanceTintIndex.Reset(); + m_splitToneBalanceIndex.Reset(); + m_splitToneWeightIndex.Reset(); + m_colorGradingPostSaturationIndex.Reset(); + m_smhShadowsStartIndex.Reset(); + m_smhShadowsEndIndex.Reset(); + m_smhHighlightsStartIndex.Reset(); + m_smhHighlightsEndIndex.Reset(); + m_smhWeightIndex.Reset(); + + m_channelMixingRedIndex.Reset(); + m_channelMixingGreenIndex.Reset(); + m_channelMixingBlueIndex.Reset(); + + m_colorFilterSwatchIndex.Reset(); + m_splitToneShadowsColorIndex.Reset(); + m_splitToneHighlightsColorIndex.Reset(); + m_smhShadowsColorIndex.Reset(); + m_smhMidtonesColorIndex.Reset(); + m_smhHighlightsColorIndex.Reset(); + } + + void HDRColorGradingPass::FrameBeginInternal(FramePrepareParams params) + { + SetSrgConstants(); + + FullscreenTrianglePass::FrameBeginInternal(params); + } + + bool HDRColorGradingPass::IsEnabled() const + { + const auto* colorGradingSettings = GetHDRColorGradingSettings(); + return colorGradingSettings ? colorGradingSettings->GetEnabled() : false; + } + + void HDRColorGradingPass::SetSrgConstants() + { + const HDRColorGradingSettings* settings = GetHDRColorGradingSettings(); + if (settings) + { + m_shaderResourceGroup->SetConstant(m_colorGradingExposureIndex, settings->GetColorGradingExposure()); + m_shaderResourceGroup->SetConstant(m_colorGradingContrastIndex, settings->GetColorGradingContrast()); + m_shaderResourceGroup->SetConstant(m_colorGradingHueShiftIndex, settings->GetColorGradingHueShift()); + m_shaderResourceGroup->SetConstant(m_colorGradingPreSaturationIndex, settings->GetColorGradingPreSaturation()); + m_shaderResourceGroup->SetConstant(m_colorFilterIntensityIndex, settings->GetColorGradingFilterIntensity()); + m_shaderResourceGroup->SetConstant(m_colorFilterMultiplyIndex, settings->GetColorGradingFilterMultiply()); + m_shaderResourceGroup->SetConstant(m_whiteBalanceKelvinIndex, settings->GetWhiteBalanceKelvin()); + m_shaderResourceGroup->SetConstant(m_whiteBalanceTintIndex, settings->GetWhiteBalanceTint()); + m_shaderResourceGroup->SetConstant(m_splitToneBalanceIndex, settings->GetSplitToneBalance()); + m_shaderResourceGroup->SetConstant(m_splitToneWeightIndex, settings->GetSplitToneWeight()); + m_shaderResourceGroup->SetConstant(m_colorGradingPostSaturationIndex, settings->GetColorGradingPostSaturation()); + m_shaderResourceGroup->SetConstant(m_smhShadowsStartIndex, settings->GetSmhShadowsStart()); + m_shaderResourceGroup->SetConstant(m_smhShadowsEndIndex, settings->GetSmhShadowsEnd()); + m_shaderResourceGroup->SetConstant(m_smhHighlightsStartIndex, settings->GetSmhHighlightsStart()); + m_shaderResourceGroup->SetConstant(m_smhHighlightsEndIndex, settings->GetSmhHighlightsEnd()); + m_shaderResourceGroup->SetConstant(m_smhWeightIndex, settings->GetSmhWeight()); + + m_shaderResourceGroup->SetConstant(m_channelMixingRedIndex, settings->GetChannelMixingRed()); + m_shaderResourceGroup->SetConstant(m_channelMixingGreenIndex, settings->GetChannelMixingGreen()); + m_shaderResourceGroup->SetConstant(m_channelMixingBlueIndex, settings->GetChannelMixingBlue()); + + m_shaderResourceGroup->SetConstant(m_colorFilterSwatchIndex, AZ::Vector4(settings->GetColorFilterSwatch())); + m_shaderResourceGroup->SetConstant(m_splitToneShadowsColorIndex, AZ::Vector4(settings->GetSplitToneShadowsColor())); + m_shaderResourceGroup->SetConstant(m_splitToneHighlightsColorIndex, AZ::Vector4(settings->GetSplitToneHighlightsColor())); + m_shaderResourceGroup->SetConstant(m_smhShadowsColorIndex, AZ::Vector4(settings->GetSmhShadowsColor())); + m_shaderResourceGroup->SetConstant(m_smhMidtonesColorIndex, AZ::Vector4(settings->GetSmhMidtonesColor())); + m_shaderResourceGroup->SetConstant(m_smhHighlightsColorIndex, AZ::Vector4(settings->GetSmhHighlightsColor())); + } + } + + const AZ::Render::HDRColorGradingSettings* HDRColorGradingPass::GetHDRColorGradingSettings() const + { + RPI::Scene* scene = GetScene(); + if (scene) + { + PostProcessFeatureProcessor* fp = scene->GetFeatureProcessor(); + AZ::RPI::ViewPtr view = scene->GetDefaultRenderPipeline()->GetDefaultView(); + if (fp) + { + PostProcessSettings* postProcessSettings = fp->GetLevelSettingsFromView(view); + if (postProcessSettings) + { + const HDRColorGradingSettings* colorGradingSettings = postProcessSettings->GetHDRColorGradingSettings(); + if (colorGradingSettings != nullptr && colorGradingSettings->GetEnabled()) + { + return postProcessSettings->GetHDRColorGradingSettings(); + } + } + } + } + return nullptr; + } + } +} diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h new file mode 100644 index 0000000000..e5eaf7db26 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + /** + * The color grading pass. + */ + class HDRColorGradingPass + : public AZ::RPI::FullscreenTrianglePass + //TODO: , public PostProcessingShaderOptionBase + { + public: + AZ_RTTI(HDRColorGradingPass, "{E68E31A1-DB24-4AFF-A029-456A8B74C03C}", AZ::RPI::FullscreenTrianglePass); + AZ_CLASS_ALLOCATOR(HDRColorGradingPass, SystemAllocator, 0); + + virtual ~HDRColorGradingPass() = default; + + //! Creates a ColorGradingPass + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + protected: + HDRColorGradingPass(const RPI::PassDescriptor& descriptor); + + //! Pass behavior overrides + void InitializeInternal() override; + void FrameBeginInternal(FramePrepareParams params) override; + bool IsEnabled() const override; + + private: + const HDRColorGradingSettings* GetHDRColorGradingSettings() const; + void SetSrgConstants(); + + RHI::ShaderInputNameIndex m_colorGradingExposureIndex = "m_colorGradingExposure"; + RHI::ShaderInputNameIndex m_colorGradingContrastIndex = "m_colorGradingContrast"; + RHI::ShaderInputNameIndex m_colorGradingHueShiftIndex = "m_colorGradingHueShift"; + RHI::ShaderInputNameIndex m_colorGradingPreSaturationIndex = "m_colorGradingPreSaturation"; + RHI::ShaderInputNameIndex m_colorFilterIntensityIndex = "m_colorFilterIntensity"; + RHI::ShaderInputNameIndex m_colorFilterMultiplyIndex = "m_colorFilterMultiply"; + RHI::ShaderInputNameIndex m_whiteBalanceKelvinIndex = "m_whiteBalanceKelvin"; + RHI::ShaderInputNameIndex m_whiteBalanceTintIndex = "m_whiteBalanceTint"; + RHI::ShaderInputNameIndex m_splitToneBalanceIndex = "m_splitToneBalance"; + RHI::ShaderInputNameIndex m_splitToneWeightIndex = "m_splitToneWeight"; + RHI::ShaderInputNameIndex m_colorGradingPostSaturationIndex = "m_colorGradingPostSaturation"; + RHI::ShaderInputNameIndex m_smhShadowsStartIndex = "m_smhShadowsStart"; + RHI::ShaderInputNameIndex m_smhShadowsEndIndex = "m_smhShadowsEnd"; + RHI::ShaderInputNameIndex m_smhHighlightsStartIndex = "m_smhHighlightsStart"; + RHI::ShaderInputNameIndex m_smhHighlightsEndIndex = "m_smhHighlightsEnd"; + RHI::ShaderInputNameIndex m_smhWeightIndex = "m_smhWeight"; + + RHI::ShaderInputNameIndex m_channelMixingRedIndex = "m_channelMixingRed"; + RHI::ShaderInputNameIndex m_channelMixingGreenIndex = "m_channelMixingGreen"; + RHI::ShaderInputNameIndex m_channelMixingBlueIndex = "m_channelMixingBlue"; + + RHI::ShaderInputNameIndex m_colorFilterSwatchIndex = "m_colorFilterSwatch"; + RHI::ShaderInputNameIndex m_splitToneShadowsColorIndex = "m_splitToneShadowsColor"; + RHI::ShaderInputNameIndex m_splitToneHighlightsColorIndex = "m_splitToneHighlightsColor"; + RHI::ShaderInputNameIndex m_smhShadowsColorIndex = "m_smhShadowsColor"; + RHI::ShaderInputNameIndex m_smhMidtonesColorIndex = "m_smhMidtonesColor"; + RHI::ShaderInputNameIndex m_smhHighlightsColorIndex = "m_smhHighlightsColor"; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp index 56d413e84f..eef0c51e95 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp @@ -16,8 +16,6 @@ #include -#include - #include #include #include @@ -161,7 +159,7 @@ namespace AZ void SMAAFeatureProcessor::Render([[maybe_unused]] const SMAAFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "SMAAFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "SMAAFeatureProcessor: Render"); UpdateConvertToPerceptualPass(); UpdateEdgeDetectionPass(); diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp index 920326e081..a476b5b839 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp @@ -8,7 +8,6 @@ #include "ProfilingCaptureSystemComponent.h" -#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h index 1846767139..9f8a8a90c6 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h @@ -12,7 +12,6 @@ #include #include -#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp index ea89f64b73..042e934eb0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -79,7 +78,7 @@ namespace AZ AZ_Assert(m_rayTracingMaterialSrg, "Failed to create RayTracingMaterialSrg"); } - void RayTracingFeatureProcessor::SetMesh(const ObjectId objectId, const SubMeshVector& subMeshes) + void RayTracingFeatureProcessor::SetMesh(const ObjectId objectId, const AZ::Data::AssetId& assetId, const SubMeshVector& subMeshes) { if (!m_rayTracingEnabled) { @@ -89,10 +88,13 @@ namespace AZ RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); uint32_t objectIndex = objectId.GetIndex(); + // lock the mutex to protect the mesh and BLAS lists + AZStd::unique_lock lock(m_mutex); + MeshMap::iterator itMesh = m_meshes.find(objectIndex); if (itMesh == m_meshes.end()) { - m_meshes.insert(AZStd::make_pair(objectIndex, Mesh{ subMeshes })); + m_meshes.insert(AZStd::make_pair(objectIndex, Mesh{ assetId, subMeshes })); } else { @@ -102,11 +104,31 @@ namespace AZ m_meshes[objectIndex].m_subMeshes = subMeshes; } - // create the BLAS buffers for each sub-mesh - // Note: the buffer is just reserved here, the BLAS is built in the RayTracingAccelerationStructurePass Mesh& mesh = m_meshes[objectIndex]; - for (auto& subMesh : mesh.m_subMeshes) + + // search for an existing BLAS instance entry for this mesh using the assetId + BlasInstanceMap::iterator itMeshBlasInstance = m_blasInstanceMap.find(assetId); + if (itMeshBlasInstance == m_blasInstanceMap.end()) { + // make a new BLAS map entry for this mesh + MeshBlasInstance meshBlasInstance; + meshBlasInstance.m_count = 1; + meshBlasInstance.m_subMeshes.reserve(mesh.m_subMeshes.size()); + itMeshBlasInstance = m_blasInstanceMap.insert({ assetId, meshBlasInstance }).first; + } + else + { + itMeshBlasInstance->second.m_count++; + } + + // create the BLAS buffers for each sub-mesh, or re-use existing BLAS objects if they were already created. + // Note: all sub-meshes must either create new BLAS objects or re-use existing ones, otherwise it's an error (it's the same model in both cases) + // Note: the buffer is just reserved here, the BLAS is built in the RayTracingAccelerationStructurePass + bool blasInstanceFound = false; + for (uint32_t subMeshIndex = 0; subMeshIndex < mesh.m_subMeshes.size(); ++subMeshIndex) + { + SubMesh& subMesh = mesh.m_subMeshes[subMeshIndex]; + RHI::RayTracingBlasDescriptor blasDescriptor; blasDescriptor.Build() ->Geometry() @@ -115,11 +137,34 @@ namespace AZ ->IndexBuffer(subMesh.m_indexBufferView) ; - // create the BLAS object - subMesh.m_blas = AZ::RHI::RayTracingBlas::CreateRHIRayTracingBlas(); + // determine if we have an existing BLAS object for this subMesh + if (itMeshBlasInstance->second.m_subMeshes.size() >= subMeshIndex + 1) + { + // re-use existing BLAS + subMesh.m_blas = itMeshBlasInstance->second.m_subMeshes[subMeshIndex].m_blas; - // create the buffers from the descriptor - subMesh.m_blas->CreateBuffers(*device, &blasDescriptor, *m_bufferPools); + // keep track of the fact that we re-used a BLAS + blasInstanceFound = true; + } + else + { + AZ_Assert(blasInstanceFound == false, "Partial set of RayTracingBlas objects found for mesh"); + + // create the BLAS object + subMesh.m_blas = AZ::RHI::RayTracingBlas::CreateRHIRayTracingBlas(); + + // create the buffers from the descriptor + subMesh.m_blas->CreateBuffers(*device, &blasDescriptor, *m_bufferPools); + + // store the BLAS in the side list + itMeshBlasInstance->second.m_subMeshes.push_back({ subMesh.m_blas }); + } + } + + if (blasInstanceFound) + { + // set the mesh BLAS flag so we don't try to rebuild it in the RayTracingAccelerationStructurePass + mesh.m_blasBuilt = true; } // set initial transform @@ -140,12 +185,26 @@ namespace AZ return; } + // lock the mutex to protect the mesh and BLAS lists + AZStd::unique_lock lock(m_mutex); + MeshMap::iterator itMesh = m_meshes.find(objectId.GetIndex()); if (itMesh != m_meshes.end()) { m_subMeshCount -= aznumeric_cast(itMesh->second.m_subMeshes.size()); m_meshes.erase(itMesh); m_revision++; + + // decrement the count from the BLAS instances, and check to see if we can remove them + BlasInstanceMap::iterator itBlas = m_blasInstanceMap.find(itMesh->second.m_assetId); + if (itBlas != m_blasInstanceMap.end()) + { + itBlas->second.m_count--; + if (itBlas->second.m_count == 0) + { + m_blasInstanceMap.erase(itBlas); + } + } } m_meshInfoBufferNeedsUpdate = true; diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h index 6bd9829c7e..52bb67f547 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h @@ -116,6 +116,9 @@ namespace AZ //! Contains data for the top level mesh, including the list of sub-meshes struct Mesh { + // assetId of the model + AZ::Data::AssetId m_assetId = AZ::Data::AssetId{}; + // sub-mesh list SubMeshVector m_subMeshes; @@ -134,7 +137,7 @@ namespace AZ //! Sets ray tracing data for a mesh. //! This will cause an update to the RayTracing acceleration structure on the next frame - void SetMesh(const ObjectId objectId, const SubMeshVector& subMeshes); + void SetMesh(const ObjectId objectId, const AZ::Data::AssetId& assetId, const SubMeshVector& subMeshes); //! Removes ray tracing data for a mesh. //! This will cause an update to the RayTracing acceleration structure on the next frame @@ -220,6 +223,9 @@ namespace AZ // cached TransformServiceFeatureProcessor TransformServiceFeatureProcessor* m_transformServiceFeatureProcessor = nullptr; + // mutex for the mesh and BLAS lists + AZStd::mutex m_mutex; + // structure for data in the m_meshInfoBuffer, shaders that use the buffer must match this type struct MeshInfo { @@ -260,6 +266,21 @@ namespace AZ // flag indicating we need to update the materialInfo buffer bool m_materialInfoBufferNeedsUpdate = false; + + // side list for looking up existing BLAS objects so they can be re-used when the same mesh is added multiple times + struct SubMeshBlasInstance + { + RHI::Ptr m_blas; + }; + + struct MeshBlasInstance + { + uint32_t m_count = 0; + AZStd::vector m_subMeshes; + }; + + using BlasInstanceMap = AZStd::unordered_map; + BlasInstanceMap m_blasInstanceMap; }; } } diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp index 66c326e6e7..e86d91d387 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp @@ -138,43 +138,39 @@ namespace AZ if (m_updateSrg) { // stencil Srg - // Note: the stencil pass uses a slightly reduced inner AABB to avoid seams + // Note: the stencil pass uses a slightly reduced inner OBB to avoid seams Vector3 innerExtentsReduced = m_innerExtents - Vector3(0.1f, 0.1f, 0.1f); - Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced); + Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced); m_stencilSrg->SetConstant(m_reflectionRenderData->m_modelToWorldStencilConstantIndex, modelToWorldStencil); m_stencilSrg->Compile(); + Matrix3x4 modelToWorldInverse = Matrix3x4::CreateFromTransform(m_transform).GetInverseFull(); + // blend weight Srg - Matrix3x4 modelToWorldOuter = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_outerExtents); + Matrix3x4 modelToWorldOuter = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_outerExtents); m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldOuter); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter()); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin()); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax()); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin()); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax()); + m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse); + m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths()); + m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths()); m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection); m_blendWeightSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage); m_blendWeightSrg->Compile(); // render outer Srg m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldOuter); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter()); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin()); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax()); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin()); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax()); + m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse); + m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths()); + m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths()); m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection); m_renderOuterSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage); m_renderOuterSrg->Compile(); // render inner Srg - Matrix3x4 modelToWorldInner = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_innerExtents); + Matrix3x4 modelToWorldInner = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_innerExtents); m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldInner); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter()); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin()); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax()); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin()); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax()); + m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse); + m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths()); + m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths()); m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection); m_renderInnerSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage); m_renderInnerSrg->Compile(); @@ -244,22 +240,22 @@ namespace AZ m_outerExtents *= m_transform.GetUniformScale(); m_innerExtents *= m_transform.GetUniformScale(); - m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f); - m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f); + m_outerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_outerExtents / 2.0f); + m_innerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_innerExtents / 2.0f); m_updateSrg = true; } void ReflectionProbe::SetOuterExtents(const AZ::Vector3& outerExtents) { m_outerExtents = outerExtents * m_transform.GetUniformScale(); - m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f); + m_outerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_outerExtents / 2.0f); m_updateSrg = true; } void ReflectionProbe::SetInnerExtents(const AZ::Vector3& innerExtents) { m_innerExtents = innerExtents * m_transform.GetUniformScale(); - m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f); + m_innerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_innerExtents / 2.0f); m_updateSrg = true; } @@ -370,11 +366,27 @@ namespace AZ { // set draw list mask m_cullable.m_cullData.m_drawListMask.reset(); - m_cullable.m_cullData.m_drawListMask = - m_stencilDrawPacket->GetDrawListMask() | - m_blendWeightDrawPacket->GetDrawListMask() | - m_renderOuterDrawPacket->GetDrawListMask() | - m_renderInnerDrawPacket->GetDrawListMask(); + + // check for draw packets due certain render pipelines such as lowend render pipeline that might not have this feature enabled + if (m_stencilDrawPacket) + { + m_cullable.m_cullData.m_drawListMask |= m_stencilDrawPacket->GetDrawListMask(); + } + + if (m_blendWeightDrawPacket) + { + m_cullable.m_cullData.m_drawListMask |= m_blendWeightDrawPacket->GetDrawListMask(); + } + + if (m_renderOuterDrawPacket) + { + m_cullable.m_cullData.m_drawListMask |= m_renderOuterDrawPacket->GetDrawListMask(); + } + + if (m_renderInnerDrawPacket) + { + m_cullable.m_cullData.m_drawListMask |= m_renderInnerDrawPacket->GetDrawListMask(); + } // setup the Lod entry, using one entry for all four draw packets m_cullable.m_lodData.m_lods.clear(); @@ -394,13 +406,14 @@ namespace AZ lod.m_screenCoverageMax = 1.0f; // update cullable bounds + Aabb outerAabb = Aabb::CreateFromObb(m_outerObbWs); Vector3 center; float radius; - m_outerAabbWs.GetAsSphere(center, radius); + outerAabb.GetAsSphere(center, radius); m_cullable.m_cullData.m_boundingSphere = Sphere(center, radius); - m_cullable.m_cullData.m_boundingObb = m_outerAabbWs.GetTransformedObb(AZ::Transform::CreateIdentity()); - m_cullable.m_cullData.m_visibilityEntry.m_boundingVolume = m_outerAabbWs; + m_cullable.m_cullData.m_boundingObb = m_outerObbWs; + m_cullable.m_cullData.m_visibilityEntry.m_boundingVolume = outerAabb; m_cullable.m_cullData.m_visibilityEntry.m_userData = &m_cullable; m_cullable.m_cullData.m_visibilityEntry.m_typeFlags = AzFramework::VisibilityEntry::TYPE_RPI_Cullable; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h index bb30ce9470..5b56b70ec8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h @@ -55,15 +55,13 @@ namespace AZ RHI::DrawListTag m_renderOuterDrawListTag; RHI::DrawListTag m_renderInnerDrawListTag; - RHI::ShaderInputConstantIndex m_modelToWorldStencilConstantIndex; - RHI::ShaderInputConstantIndex m_modelToWorldRenderConstantIndex; - RHI::ShaderInputConstantIndex m_aabbPosRenderConstantIndex; - RHI::ShaderInputConstantIndex m_outerAabbMinRenderConstantIndex; - RHI::ShaderInputConstantIndex m_outerAabbMaxRenderConstantIndex; - RHI::ShaderInputConstantIndex m_innerAabbMinRenderConstantIndex; - RHI::ShaderInputConstantIndex m_innerAabbMaxRenderConstantIndex; - RHI::ShaderInputConstantIndex m_useParallaxCorrectionRenderConstantIndex; - RHI::ShaderInputImageIndex m_reflectionCubeMapRenderImageIndex; + RHI::ShaderInputNameIndex m_modelToWorldStencilConstantIndex = "m_modelToWorld"; + RHI::ShaderInputNameIndex m_modelToWorldRenderConstantIndex = "m_modelToWorld"; + RHI::ShaderInputNameIndex m_modelToWorldInverseRenderConstantIndex = "m_modelToWorldInverse"; + RHI::ShaderInputNameIndex m_outerObbHalfLengthsRenderConstantIndex = "m_outerObbHalfLengths"; + RHI::ShaderInputNameIndex m_innerObbHalfLengthsRenderConstantIndex = "m_innerObbHalfLengths"; + RHI::ShaderInputNameIndex m_useParallaxCorrectionRenderConstantIndex = "m_useParallaxCorrection"; + RHI::ShaderInputNameIndex m_reflectionCubeMapRenderImageIndex = "m_reflectionCubeMap"; }; // ReflectionProbe manages all aspects of a single probe, including rendering, visualization, and cubemap generation @@ -78,6 +76,7 @@ namespace AZ void Simulate(uint32_t probeIndex); const Vector3& GetPosition() const { return m_transform.GetTranslation(); } + const AZ::Transform& GetTransform() const { return m_transform; } void SetTransform(const AZ::Transform& transform); const AZ::Vector3& GetOuterExtents() const { return m_outerExtents; } @@ -86,8 +85,8 @@ namespace AZ const AZ::Vector3& GetInnerExtents() const { return m_innerExtents; } void SetInnerExtents(const AZ::Vector3& innerExtents); - const Aabb& GetOuterAabbWs() const { return m_outerAabbWs; } - const Aabb& GetInnerAabbWs() const { return m_innerAabbWs; } + const Obb& GetOuterObbWs() const { return m_outerObbWs; } + const Obb& GetInnerObbWs() const { return m_innerObbWs; } const Data::Instance& GetCubeMapImage() const { return m_cubeMapImage; } void SetCubeMapImage(const Data::Instance& cubeMapImage, const AZStd::string& relativePath); @@ -133,9 +132,9 @@ namespace AZ AZ::Vector3 m_outerExtents = AZ::Vector3(0.0f, 0.0f, 0.0f); AZ::Vector3 m_innerExtents = AZ::Vector3(0.0f, 0.0f, 0.0f); - // probe volume AABBs (world space), built from position and extents - Aabb m_outerAabbWs; - Aabb m_innerAabbWs; + // probe volume OBBs (world space), built from position and extents + Obb m_outerObbWs; + Obb m_innerObbWs; // cubemap Data::Instance m_cubeMapImage; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp index 341d1a0274..52d089ae0d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -77,61 +76,6 @@ namespace AZ m_reflectionRenderData.m_renderInnerSrgLayout, m_reflectionRenderData.m_renderInnerDrawListTag); - // create ShaderResourceGroups here so we can get the layout and cache the indices - // Note: the SRGs are not needed beyond this method since each probe creates its own SRGs, we are just interested in the indices - - // cache probe stencil shader indices - Data::Instance stencilSrg = RPI::ShaderResourceGroup::Create( - m_reflectionRenderData.m_stencilShader->GetAsset(), - m_reflectionRenderData.m_stencilShader->GetSupervariantIndex(), - m_reflectionRenderData.m_stencilSrgLayout->GetName()); - AZ_Error("ReflectionProbeFeatureProcessor", stencilSrg.get(), "Failed to create stencil back face shader resource group"); - - const RHI::ShaderResourceGroupLayout* stencilSrgLayout = stencilSrg->GetLayout(); - Name modelToWorldConstantName = Name("m_modelToWorld"); - m_reflectionRenderData.m_modelToWorldStencilConstantIndex = stencilSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldStencilConstantIndex.IsValid(), "Failed to find stencil shader input constant [%s]", modelToWorldConstantName.GetCStr()); - - // cache probe render shader indices - // Note: the outer and inner render shaders use the same Srg - Data::Instance renderReflectionSrg = RPI::ShaderResourceGroup::Create( - m_reflectionRenderData.m_renderOuterShader->GetAsset(), - m_reflectionRenderData.m_renderOuterShader->GetSupervariantIndex(), - m_reflectionRenderData.m_renderOuterSrgLayout->GetName()); - AZ_Error("ReflectionProbeFeatureProcessor", renderReflectionSrg.get(), "Failed to create render reflection shader resource group"); - - const RHI::ShaderResourceGroupLayout* renderReflectionSrgLayout = renderReflectionSrg->GetLayout(); - m_reflectionRenderData.m_modelToWorldRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", modelToWorldConstantName.GetCStr()); - - Name aabbPosConstantName = Name("m_aabbPos"); - m_reflectionRenderData.m_aabbPosRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(aabbPosConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_aabbPosRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", aabbPosConstantName.GetCStr()); - - Name outerAabbMinConstantName = Name("m_outerAabbMin"); - m_reflectionRenderData.m_outerAabbMinRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(outerAabbMinConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_outerAabbMinRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", outerAabbMinConstantName.GetCStr()); - - Name outerAabbMaxConstantName = Name("m_outerAabbMax"); - m_reflectionRenderData.m_outerAabbMaxRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(outerAabbMaxConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_outerAabbMaxRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", outerAabbMaxConstantName.GetCStr()); - - Name innerAabbMinConstantName = Name("m_innerAabbMin"); - m_reflectionRenderData.m_innerAabbMinRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(innerAabbMinConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_innerAabbMinRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", innerAabbMinConstantName.GetCStr()); - - Name innerAabbMaxConstantName = Name("m_innerAabbMax"); - m_reflectionRenderData.m_innerAabbMaxRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(innerAabbMaxConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_innerAabbMaxRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", innerAabbMaxConstantName.GetCStr()); - - Name useParallaxCorrectionConstantName = Name("m_useParallaxCorrection"); - m_reflectionRenderData.m_useParallaxCorrectionRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(useParallaxCorrectionConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_useParallaxCorrectionRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", useParallaxCorrectionConstantName.GetCStr()); - - Name reflectionCubeMapImageName = Name("m_reflectionCubeMap"); - m_reflectionRenderData.m_reflectionCubeMapRenderImageIndex = renderReflectionSrgLayout->FindShaderInputImageIndex(reflectionCubeMapImageName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_reflectionCubeMapRenderImageIndex.IsValid(), "Failed to find render shader input image [%s]", reflectionCubeMapImageName.GetCStr()); - EnableSceneNotification(); } @@ -154,8 +98,7 @@ namespace AZ void ReflectionProbeFeatureProcessor::Simulate([[maybe_unused]] const FeatureProcessor::SimulatePacket& packet) { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("ReflectionProbe", "ReflectionProbeFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(AzRender, "ReflectionProbeFeatureProcessor: Simulate"); // update pipeline states if (m_needUpdatePipelineStates) @@ -194,15 +137,15 @@ namespace AZ if (m_probeSortRequired) { AZ_PROFILE_SCOPE(AzRender, "Sort reflection probes"); - AZ_ATOM_PROFILE_FUNCTION("ReflectionProbe", "ReflectionProbeFeatureProcessor: Sort reflection probes"); // sort the probes by descending inner volume size, so the smallest volumes are rendered last auto sortFn = [](AZStd::shared_ptr const& probe1, AZStd::shared_ptr const& probe2) -> bool { - const Aabb& aabb1 = probe1->GetInnerAabbWs(); - const Aabb& aabb2 = probe2->GetInnerAabbWs(); - float size1 = aabb1.GetXExtent() * aabb1.GetZExtent() * aabb1.GetYExtent(); - float size2 = aabb2.GetXExtent() * aabb2.GetZExtent() * aabb2.GetYExtent(); + const Obb& obb1 = probe1->GetInnerObbWs(); + const Obb& obb2 = probe2->GetInnerObbWs(); + float size1 = obb1.GetHalfLengthX() * obb1.GetHalfLengthZ() * obb1.GetHalfLengthY(); + float size2 = obb2.GetHalfLengthX() * obb2.GetHalfLengthZ() * obb2.GetHalfLengthY(); + return (size1 > size2); }; @@ -347,7 +290,7 @@ namespace AZ // simple AABB check to find the reflection probes that contain the position for (auto& reflectionProbe : m_reflectionProbes) { - if (reflectionProbe->GetOuterAabbWs().Contains(position) + if (reflectionProbe->GetOuterObbWs().Contains(position) && reflectionProbe->GetCubeMapImage() && reflectionProbe->GetCubeMapImage()->IsInitialized()) { diff --git a/Gems/Atom/Feature/Common/Code/Source/ScreenSpace/DeferredFogSettings.h b/Gems/Atom/Feature/Common/Code/Source/ScreenSpace/DeferredFogSettings.h index 57d1dd24b5..85ced5496b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ScreenSpace/DeferredFogSettings.h +++ b/Gems/Atom/Feature/Common/Code/Source/ScreenSpace/DeferredFogSettings.h @@ -56,7 +56,7 @@ namespace AZ ~DeferredFogSettings() = default; // DeferredFogSettingsInterface overrides... - void OnSettingsChanged(); + void OnSettingsChanged() override; bool GetSettingsNeedUpdate() { return m_needUpdate; @@ -66,35 +66,35 @@ namespace AZ m_needUpdate = needUpdate; } - void SetEnabled(bool value); - virtual bool GetEnabled() const override + void SetEnabled(bool value) override; + bool GetEnabled() const override { return m_enabled; } - virtual void SetInitialized(bool isInitialized) override + void SetInitialized(bool isInitialized) override { m_isInitialized = isInitialized; } - virtual bool IsInitialized() override + bool IsInitialized() override { return m_isInitialized; } - virtual void SetUseNoiseTextureShaderOption(bool value) override + void SetUseNoiseTextureShaderOption(bool value) override { m_useNoiseTextureShaderOption = value; } - virtual bool GetUseNoiseTextureShaderOption() override + bool GetUseNoiseTextureShaderOption() override { return m_useNoiseTextureShaderOption; } - virtual void SetEnableFogLayerShaderOption(bool value) override + void SetEnableFogLayerShaderOption(bool value) override { m_enableFogLayerShaderOption = value; } - virtual bool GetEnableFogLayerShaderOption() override + bool GetEnableFogLayerShaderOption() override { return m_enableFogLayerShaderOption; } diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index 68c31bd859..c0202c7c74 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -486,7 +485,7 @@ namespace AZ::Render void ProjectedShadowFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& /*packet*/) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "ProjectedShadowFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "ProjectedShadowFeatureProcessor: Simulate"); if (m_shadowmapPassNeedsUpdate) { @@ -581,7 +580,7 @@ namespace AZ::Render void ProjectedShadowFeatureProcessor::Render(const ProjectedShadowFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "ProjectedShadowFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "ProjectedShadowFeatureProcessor: Render"); if (!m_projectedShadowmapsPasses.empty()) { diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h index f4c6cad7bf..0b266b9a40 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h @@ -48,13 +48,14 @@ namespace AZ::Render void SetFieldOfViewY(ShadowId id, float fieldOfViewYRadians) override; void SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) override; void SetShadowBias(ShadowId id, float bias) override; - void SetEsmExponent(ShadowId id, float exponent); void SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) override; void SetSofteningBoundaryWidthAngle(ShadowId id, float boundaryWidthRadians) override; void SetFilteringSampleCount(ShadowId id, uint16_t count) override; void SetShadowProperties(ShadowId id, const ProjectedShadowDescriptor& descriptor) override; const ProjectedShadowDescriptor& GetShadowProperties(ShadowId id) override; + void SetEsmExponent(ShadowId id, float exponent); + private: // GPU data stored in m_projectedShadows. diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp index 0aa72bf2ca..e0209702dc 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include @@ -69,8 +68,7 @@ namespace AZ void SkinnedMeshFeatureProcessor::Render(const FeatureProcessor::RenderPacket& packet) { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("SkinnedMesh", "SkinnedMeshFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(AzRender, "SkinnedMeshFeatureProcessor: Render"); #if 0 //[GFX_TODO][ATOM-13564] Temporarily disable skinning culling until we figure out how to hook up visibility & lod selection with skinning: //Setup the culling workgroup (it will be re-used for each view) diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp index b84d4347a7..4146a669fe 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp @@ -534,7 +534,7 @@ namespace AZ // lod0 Positions[^ ^] lod0Normals[^ ^] lod1Positions[^ ^] lod1Normals[^ ^] // lod0 subMesh0+1 Positions[^ ^^ ^] lod0 subMesh0+1 Normals[^ ^^ ^] lod1 sm0+1 pos[^ ^^ ^] lod1 sm0+1 norm[^ ^^ ^] - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "SkinnedMeshInputBuffers: CreateSkinnedMeshInstance"); AZStd::intrusive_ptr instance = aznew SkinnedMeshInstance; // Each model gets a unique, random ID, so if the same source model is used for multiple instances, multiple target models will be created. diff --git a/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp index d0150fc0c2..ca8775a073 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp @@ -12,7 +12,6 @@ #include -#include #include #include #include @@ -105,7 +104,7 @@ namespace AZ void SkyBoxFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "SkyBoxFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "SkyBoxFeatureProcessor: Simulate"); AZ_UNUSED(packet); m_sceneSrg->SetConstant(m_skyboxEnableIndex, m_enable); diff --git a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp index 6f9c0dc73a..141acbd744 100644 --- a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp @@ -8,7 +8,6 @@ #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 40188109ca..92f96a591b 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -183,6 +183,8 @@ set(FILES Source/PostProcess/PostProcessFeatureProcessor.h Source/PostProcess/PostProcessSettings.cpp Source/PostProcess/PostProcessSettings.h + Source/PostProcess/ColorGrading/HDRColorGradingSettings.h + Source/PostProcess/ColorGrading/HDRColorGradingSettings.cpp Source/PostProcess/Bloom/BloomSettings.cpp Source/PostProcess/Bloom/BloomSettings.h Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp @@ -205,6 +207,8 @@ set(FILES Source/PostProcessing/BloomCompositePass.cpp Source/PostProcessing/BloomParentPass.h Source/PostProcessing/BloomParentPass.cpp + Source/PostProcessing/HDRColorGradingPass.cpp + Source/PostProcessing/HDRColorGradingPass.h Source/PostProcessing/DepthOfFieldCompositePass.h Source/PostProcessing/DepthOfFieldCompositePass.cpp Source/PostProcessing/DepthOfFieldBokehBlurPass.h diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake index 886a9f0f22..d7af7b0108 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake @@ -39,6 +39,7 @@ set(FILES Include/Atom/Feature/ParamMacros/StartParamCopySettingsTo.inl Include/Atom/Feature/ParamMacros/StartParamFunctions.inl Include/Atom/Feature/ParamMacros/StartParamFunctionsOverride.inl + Include/Atom/Feature/ParamMacros/StartParamFunctionsOverrideImpl.inl Include/Atom/Feature/ParamMacros/StartParamFunctionsVirtual.inl Include/Atom/Feature/ParamMacros/StartParamMembers.inl Include/Atom/Feature/ParamMacros/StartParamSerializeContext.inl @@ -50,6 +51,8 @@ set(FILES Include/Atom/Feature/PostProcess/Bloom/BloomConstants.h Include/Atom/Feature/PostProcess/Bloom/BloomParams.inl Include/Atom/Feature/PostProcess/Bloom/BloomSettingsInterface.h + Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl + Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingSettingsInterface.h Include/Atom/Feature/PostProcess/DepthOfField/DepthOfFieldConstants.h Include/Atom/Feature/PostProcess/DepthOfField/DepthOfFieldParams.inl Include/Atom/Feature/PostProcess/DepthOfField/DepthOfFieldSettingsInterface.h diff --git a/Gems/Atom/Feature/Common/gem.json b/Gems/Atom/Feature/Common/gem.json index cbaa56e0d7..36a61fbbbb 100644 --- a/Gems/Atom/Feature/Common/gem.json +++ b/Gems/Atom/Feature/Common/gem.json @@ -8,7 +8,12 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI", + "Atom", + "ImGui", + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/Code/CMakeLists.txt b/Gems/Atom/RHI/Code/CMakeLists.txt index 843eeb6364..5238985feb 100644 --- a/Gems/Atom/RHI/Code/CMakeLists.txt +++ b/Gems/Atom/RHI/Code/CMakeLists.txt @@ -56,9 +56,6 @@ ly_add_target( Gem::Atom_RHI.Reflect PUBLIC ${RENDERDOC_DEPENDENCY} - COMPILE_DEFINITIONS - PUBLIC - ${RENDERDOC_DEFINE} ) ly_add_target( diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h index 36511ebccf..b6f6e0df11 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h @@ -28,25 +28,19 @@ namespace AZ size_t m_accumulatedInBytes = 0; }; - /** - * Tracks memory usage for a specific heap in the system. The data is expected to adhere to the following constraints: - * - * 1) Reserved <= Budget (unless the budget is 0). - * 2) Resident <= Reserved. - */ + //! Tracks memory usage for a specific heap in the system. The data is expected to adhere to the following constraints: + //! 1) Reserved <= Budget (unless the budget is 0). + //! 2) Resident <= Reserved. struct HeapMemoryUsage { HeapMemoryUsage() = default; HeapMemoryUsage(const HeapMemoryUsage&); HeapMemoryUsage& operator=(const HeapMemoryUsage&); - /** - * This helper reserves memory in a thread-safe fashion. If the result exceeds the budget, the reservation is safely - * reverted and false is returned. otherwise, true is returned. Only m_reservedInBytes is affected. - * - * @param sizeInBytes The amount of bytes to reserve. - * @return Whether the reservation was successful. - */ + //! This helper reserves memory in a thread-safe fashion. If the result exceeds the budget, the reservation is safely + //! reverted and false is returned. otherwise, true is returned. Only m_reservedInBytes is affected. + //! @param sizeInBytes The amount of bytes to reserve. + //! @return Whether the reservation was successful. bool TryReserveMemory(size_t sizeInBytes) { const size_t reservationInBytes = (m_reservedInBytes += sizeInBytes); @@ -60,45 +54,41 @@ namespace AZ return true; } - /** - * Helper function to validate sizes - */ + //! Helper function to validate sizes void Validate() { if (Validation::IsEnabled()) { - AZ_Assert(m_budgetInBytes >= m_reservedInBytes, "Reserved memory is larger than memory budget"); - AZ_Assert(m_reservedInBytes >= m_residentInBytes, "Resident memory is larger than reserved memory"); + AZ_Assert( + m_budgetInBytes >= m_reservedInBytes, + "Reserved memory is larger than memory budget. Memory budget %zu Reserved %zu", m_budgetInBytes, m_reservedInBytes.load()); + AZ_Assert( + m_reservedInBytes >= m_residentInBytes, + "Resident memory is larger than reserved memory. Reserved Memory %zu Resident memory %zu", m_reservedInBytes.load(), + m_residentInBytes.load()); } } - /** - * The budget for the heap in bytes. A non-zero budget means the pool will reject reservation requests - * once the budget is exceeded. A zero budget effectively disables this check. On certain platforms, - * it may be unnecessary to budget certain heaps. Other platforms may require a non-zero budget for certain - * heaps. - */ + // The budget for the heap in bytes. A non-zero budget means the pool will reject reservation requests + // once the budget is exceeded. A zero budget effectively disables this check. On certain platforms, + // it may be unnecessary to budget certain heaps. Other platforms may require a non-zero budget for certain + // heaps. size_t m_budgetInBytes = 0; - /** - * Number of bytes reserved on the heap for allocations. This value represents the allocation capacity for - * the platform. It is validated against the budget and may not exceed it. - */ + // Number of bytes reserved on the heap for allocations. This value represents the allocation capacity for + // the platform. It is validated against the budget and may not exceed it. AZStd::atomic_size_t m_reservedInBytes{ 0 }; - /** - * Number of bytes physically allocated on the heap. This may not exceed the reservation. Certain platforms - * may choose to transfer memory down the heap level hierarchy in response to memory trim events from the driver. - */ + // Number of bytes physically allocated on the heap. This may not exceed the reservation. Certain platforms + // may choose to transfer memory down the heap level hierarchy in response to memory trim events from the driver. AZStd::atomic_size_t m_residentInBytes{ 0 }; }; - /** - * Describes memory usage metrics of a resource pool. Resource pools *can* associate with a single - * device memory heap (i.e. a single GPU) and the host memory heap. Certain pools on specific platforms - * may not require one or the other. In this case, the memory usage / budget will report empty values for - * that heap type. - */ + //! + //! Describes memory usage metrics of a resource pool. Resource pools *can* associate with a single + //! device memory heap (i.e. a single GPU) and the host memory heap. Certain pools on specific platforms + //! may not require one or the other. In this case, the memory usage / budget will report empty values for + //! that heap type. struct PoolMemoryUsage { PoolMemoryUsage() = default; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h index 0bd0056a0b..dc95078f80 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h @@ -13,13 +13,12 @@ namespace AZ { namespace RHI { - /** - * A virtual address which may be relative to a base resource. This means - * 0 might be a valid address (dependent on the Allocator::Descriptor::m_addressBase value). - * To account for this, VirtualAddress::Null is used instead. Check validity of the address - * using IsValid or IsNull instead of checking for 0. VirtualAddress is initialized - * to Null, so returning the default constructor is sufficient to represent an invalid address. - */ + + //! A virtual address which may be relative to a base resource. This means + //! 0 might be a valid address (dependent on the Allocator::Descriptor::m_addressBase value). + //! To account for this, VirtualAddress::Null is used instead. Check validity of the address + //! using IsValid or IsNull instead of checking for 0. VirtualAddress is initialized + //! to Null, so returning the default constructor is sufficient to represent an invalid address. class VirtualAddress { static const VirtualAddress Null; @@ -29,13 +28,13 @@ namespace AZ static VirtualAddress CreateNull(); - /// Creates a valid address with a zero offset. + //! Creates a valid address with a zero offset. static VirtualAddress CreateZero(); - /// Creates an address from a pointer. + //! Creates an address from a pointer. static VirtualAddress CreateFromPointer(void* ptr); - /// Creates an address from an offset from a base pointer. + //! Creates an address from an offset from a base pointer. static VirtualAddress CreateFromOffset(uint64_t offset); inline bool IsValid() const @@ -51,15 +50,13 @@ namespace AZ uintptr_t m_ptr; }; - /** - * An allocator interface used for external GPU allocations. The allocator - * does not manage the host memory. Instead, the user specifies a base address - * (which may be 0, in order to allocate offsets from a base resource). The allocator - * interface also provides an API for garbage collection. If used to manage GPU resources, - * these are often deferred-released after N frames. The user may provide a garbage collection - * latency, which controls the number of GarbageCollect calls that must occur before an allocation - * is actually reclaimed. The intended use case is to garbage collect at the end of each frame. - */ + //! An allocator interface used for external GPU allocations. The allocator + //! does not manage the host memory. Instead, the user specifies a base address + //! (which may be 0, in order to allocate offsets from a base resource). The allocator + //! interface also provides an API for garbage collection. If used to manage GPU resources, + //! these are often deferred-released after N frames. The user may provide a garbage collection + //! latency, which controls the number of GarbageCollect calls that must occur before an allocation + //! is actually reclaimed. The intended use case is to garbage collect at the end of each frame. class Allocator { public: @@ -86,44 +83,42 @@ namespace AZ virtual void Shutdown() = 0; - /** - * Allocates a virtual address relative to the base address provided at initialization time. - * @param byteCount The number of bytes to allocate. - * @param byteAlignement The alignment used to align the allocation. - */ + //! Allocates a virtual address relative to the base address provided at initialization time. + //! @param byteCount The number of bytes to allocate. + //! @param byteAlignement The alignment used to align the allocation. virtual VirtualAddress Allocate(size_t byteCount, size_t byteAlignment) = 0; - /** - * Deallocates an allocation. The memory is not reclaimed until garbage collect is called. - * Depending on the garbage collection latency, it may take several garbage collection cycles - * before the memory is reclaimed. - */ + //! Deallocates an allocation. The memory is not reclaimed until garbage collect is called. + //! Depending on the garbage collection latency, it may take several garbage collection cycles + //! before the memory is reclaimed. virtual void DeAllocate(VirtualAddress offset) = 0; - /// Allocations are deferred-released until a specific number of GC cycles have occurred. This - /// is useful for allocations actively being consumed by the GPU. + //! Allocations are deferred-released until a specific number of GC cycles have occurred. This + //! is useful for allocations actively being consumed by the GPU. virtual void GarbageCollect() = 0; - /// Forces garbage collection of all allocations, regardless of the GC latency. + //! Forces garbage collection of all allocations, regardless of the GC latency. virtual void GarbageCollectForce() = 0; - /** - * Returns the number of allocations active for this allocator. This includes - * allocations that are pending garbage collection. - */ + //! Returns the number of allocations active for this allocator. This includes + //! allocations that are pending garbage collection. virtual size_t GetAllocationCount() const { return 0; } - /** - * Returns the number of bytes used by the allocator. This includes - * allocations that are pending garbage collection. - */ + //! Returns the number of bytes used by the allocator. This includes + //! allocations that are pending garbage collection. virtual size_t GetAllocatedByteCount() const { return 0; } - /// Returns the descriptor used to initialize the allocator. + //! Returns the descriptor used to initialize the allocator. virtual const Descriptor& GetDescriptor() const = 0; - /// Helper for converting agnostic VirtualAddress type to pointer type. Will convert - /// VirtualAddress::Null to nullptr. + //! Clone the current allocator to the new allocator passed in + virtual void Clone([[maybe_unused]] RHI::Allocator* newAllocator) + { + AZ_Assert(false, "Not Implemented"); + }; + + //! Helper for converting agnostic VirtualAddress type to pointer type. Will convert + //! VirtualAddress::Null to nullptr. template T* AllocateAs(size_t byteCount, size_t byteAlignment) { diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfiler.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfiler.h index 3fedc99566..70c5771b57 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfiler.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfiler.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -21,15 +22,15 @@ namespace AZ //! Structure that is used to cache a timed region into the thread's local storage. struct CachedTimeRegion { - //! Structure that the profiling macro utilizes to create statically initialized instance to create string - //! literals in static memory + //! Structure used internally for caching assumed global string pointers (ideally literals) to the marker group/region + //! NOTE: When used in a separate shared library, the library mustn't be unloaded before the CpuProfiler is shutdown. struct GroupRegionName { GroupRegionName() = delete; GroupRegionName(const char* const group, const char* const region); - - const char* const m_groupName = nullptr; - const char* const m_regionName = nullptr; + + const char* m_groupName = nullptr; + const char* m_regionName = nullptr; struct Hash { @@ -39,31 +40,16 @@ namespace AZ }; CachedTimeRegion() = default; - CachedTimeRegion(const GroupRegionName* groupRegionName); - CachedTimeRegion(const GroupRegionName* groupRegionName, uint16_t stackDepth, uint64_t startTick, uint64_t endTick); + CachedTimeRegion(const GroupRegionName& groupRegionName); + CachedTimeRegion(const GroupRegionName& groupRegionName, uint16_t stackDepth, uint64_t startTick, uint64_t endTick); - //! Pointer to the GroupRegionName static instance. - //! NOTE: When used in a separate shared library, the library mustn't be unloaded before - //! the CpuProfiler is shutdown. - const GroupRegionName* m_groupRegionName = nullptr; + GroupRegionName m_groupRegionName{nullptr, nullptr}; uint16_t m_stackDepth = 0u; AZStd::sys_time_t m_startTick = 0; AZStd::sys_time_t m_endTick = 0; }; - //! Helper class used as a RAII-style mechanism for the macros to begin and end a region. - class TimeRegion : public CachedTimeRegion - { - public: - TimeRegion() = delete; - TimeRegion(const GroupRegionName* groupRegionName); - ~TimeRegion(); - - //! End region - void EndRegion(); - }; - //! Interface class of the CpuProfiler class CpuProfiler { @@ -80,12 +66,6 @@ namespace AZ static CpuProfiler* Get(); - //! Add a new time region - virtual void BeginTimeRegion(TimeRegion& timeRegion) = 0; - - //! Ends a time region - virtual void EndTimeRegion() = 0; - //! Get the last frame's TimeRegionMap virtual const TimeRegionMap& GetTimeRegionMap() const = 0; @@ -101,38 +81,7 @@ namespace AZ virtual void SetProfilerEnabled(bool enabled) = 0; virtual bool IsProfilerEnabled() const = 0 ; - - //! Used by AZ_ATOM_PROFILE_DYNAMIC to create GroupRegionNames with known lifetimes. - virtual const CachedTimeRegion::GroupRegionName& InsertDynamicName(const char* groupName, const AZStd::string& regionName) = 0; }; } // namespace RPI } // namespace AZ - -//! Utility functions for timing a section of code and writing the timing (in cycles) to a new, named time region inside the -//! provided statistics data. - -//! Supply a group and region to the time region -#define AZ_ATOM_PROFILE_TIME_GROUP_REGION(groupName, regionName) \ - static const AZ::RHI::CachedTimeRegion::GroupRegionName AZ_JOIN(groupRegionName, __LINE__)(groupName, regionName); \ - AZ::RHI::TimeRegion AZ_JOIN(timeRegion, __LINE__)(&AZ_JOIN(groupRegionName, __LINE__)); - -//! Supply a region to the time region; "Default" will be used for the group -#define AZ_ATOM_PROFILE_TIME_REGION(regionName) \ - AZ_ATOM_PROFILE_TIME_GROUP_REGION("Default", regionName) - -//! Used to create a time region; "Default" will be used for the group, and __FUNCTION__ macro for the region -#define AZ_ATOM_PROFILE_TIME_FUNCTION() \ - AZ_ATOM_PROFILE_TIME_GROUP_REGION("Default", AZ_FUNCTION_SIGNATURE) - -//! Macro that combines the AZ_TRACE_METHOD with time profiling macro -#define AZ_ATOM_PROFILE_FUNCTION(groupName, regionName) \ - AZ_TRACE_METHOD(); \ - AZ_ATOM_PROFILE_TIME_GROUP_REGION(groupName, regionName) \ - -//! Macro that allows for region names to be submitted at runtime. Use sparingly - this acquires a lock and allocates new objects within a map. -#define AZ_ATOM_PROFILE_DYNAMIC(groupName, regionName) \ - static_assert(AZStd::is_convertible_v, "Runtime group names are not allowed, use a static string literal instead."); \ - const AZ::RHI::CachedTimeRegion::GroupRegionName& AZ_JOIN(groupRegionName, __LINE__) = \ - AZ::RHI::CpuProfiler::Get()->InsertDynamicName(groupName, regionName); \ - AZ::RHI::TimeRegion AZ_JOIN(timeRegion, __LINE__)(&AZ_JOIN(groupRegionName, __LINE__)); diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfilerImpl.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfilerImpl.h index 92b56880b7..29886625ea 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfilerImpl.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfilerImpl.h @@ -43,13 +43,13 @@ namespace AZ static constexpr uint32_t TimeRegionStackSize = 2048u; // Adds a region to the stack, gets called each time a region begins - void RegionStackPushBack(TimeRegion& timeRegion); + void RegionStackPushBack(CachedTimeRegion& timeRegion); // Pops a region from the stack, gets called each time a region ends void RegionStackPopBack(); // Add a new cached time region. If the stack is empty, flush all entries to the cached map - void AddCachedRegion(CachedTimeRegion&& timeRegionCached); + void AddCachedRegion(const CachedTimeRegion& timeRegionCached); // Tries to flush the map to the passed parameter, only if the thread's mutex is unlocked void TryFlushCachedMap(CpuProfiler::ThreadTimeRegionMap& cachedRegionMap); @@ -63,7 +63,7 @@ namespace AZ // Use fixed vectors to avoid re-allocating new elements // Keeps track of the regions that added and removed using the macro - AZStd::fixed_vector m_timeRegionStack; + AZStd::fixed_vector m_timeRegionStack; // Keeps track of regions that completed (i.e regions that was pushed and popped from the stack) // Intermediate storage point for the CachedTimeRegions, when the stack is empty, all entries will be @@ -85,7 +85,8 @@ namespace AZ //! forwards the request to profile a region to the appropriate thread. The user is able to request all //! cached regions, which are stored on a per thread frequency. class CpuProfilerImpl final - : public CpuProfiler + : public AZ::Debug::Profiler + , public CpuProfiler , public SystemTickBus::Handler { friend class CpuTimingLocalStorage; @@ -107,16 +108,17 @@ namespace AZ // m_timeRegionMap so that the next frame has up-to-date profiling data. void OnSystemTick() final override; + //! AZ::Debug::Profiler overrides... + void BeginRegion(const AZ::Debug::Budget* budget, const char* eventName) final override; + void EndRegion(const AZ::Debug::Budget* budget) final override; + //! CpuProfiler overrides... - void BeginTimeRegion(TimeRegion& timeRegion) final override; - void EndTimeRegion() final override; const TimeRegionMap& GetTimeRegionMap() const final override; bool BeginContinuousCapture() final override; bool EndContinuousCapture(AZStd::ring_buffer& flushTarget) final override; bool IsContinuousCaptureInProgress() const final override; void SetProfilerEnabled(bool enabled) final override; bool IsProfilerEnabled() const final override; - const CachedTimeRegion::GroupRegionName& InsertDynamicName(const char* groupName, const AZStd::string& regionName) final override; private: static constexpr AZStd::size_t MaxFramesToSave = 2 * 60 * 120; // 2 minutes of 120fps @@ -133,15 +135,6 @@ namespace AZ AZStd::vector, AZ::OSStdAllocator> m_registeredThreads; AZStd::mutex m_threadRegisterMutex; - // Pool for GroupRegionNames that are generated at runtime through AZ_ATOM_PROFILE_DYNAMIC. Each unique - // combination of group name and region name submitted will be stored in this pool to emulate static lifetime. - AZStd::unordered_set m_dynamicGroupRegionNamePool; - - // String pool for storing region names submitted at runtime. Each call to AZ_ATOM_PROFILE_DYNAMIC will either construct - // a string in this pool or use an already-existing entry. - AZStd::unordered_set m_regionNameStringPool; - AZStd::mutex m_dynamicNameMutex; - // Thread local storage, gets lazily allocated when a thread is created static thread_local CpuTimingLocalStorage* ms_threadLocalStorage; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h index f9df29cf74..fb4082bb25 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h @@ -139,6 +139,12 @@ namespace AZ //! Notifies after all objects currently in the platform release queue are released virtual void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) = 0; + //! Allows the back-ends to compact SRG related memory if applicable + virtual RHI::ResultCode CompactSRGMemory() + { + return RHI::ResultCode::Success; + }; + protected: DeviceFeatures m_features; DeviceLimits m_limits; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h index 992cc0e79a..71f9f1605b 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h @@ -112,6 +112,9 @@ namespace AZ //! Returns true if Pix dll is loaded static bool IsPixModuleLoaded(); + //! Returns true if Warp is enabled + static bool UsingWarpDevice(); + //! Returns the name of the Factory. virtual Name GetName() = 0; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h index 49d5cc9345..ce20f9cbe5 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h @@ -51,10 +51,11 @@ namespace AZ VirtualAddress Allocate(size_t byteCount, size_t byteAlignment) override; void DeAllocate(VirtualAddress allocation) override; void GarbageCollect() override; - void GarbageCollectForce(); + void GarbageCollectForce() override; size_t GetAllocationCount() const override; size_t GetAllocatedByteCount() const override; const Descriptor& GetDescriptor() const override; + void Clone(RHI::Allocator* newAllocator) override; ////////////////////////////////////////////////////////////////////////// private: diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/MemorySubAllocator.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/MemorySubAllocator.h index a10afd880f..3e464a6974 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/MemorySubAllocator.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/MemorySubAllocator.h @@ -7,7 +7,6 @@ */ #pragma once -#include #include #include #include @@ -157,7 +156,7 @@ namespace AZ template void MemorySubAllocator::GarbageCollect() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "MemorySubAllocator: GarbageCollect"); + AZ_PROFILE_SCOPE(RHI, "MemorySubAllocator: GarbageCollect"); for (PageContext& pageContext : m_pageContexts) { pageContext.m_allocator.GarbageCollect(); diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h index cd8a85a385..7558442ad1 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h @@ -7,8 +7,9 @@ */ #pragma once -#include #include + +#include #include #include #include @@ -173,7 +174,7 @@ namespace AZ template void ObjectCollector::Collect(bool forceFlush) { - AZ_ATOM_PROFILE_FUNCTION("DX12", "ObjectCollector: Collect"); + AZ_PROFILE_SCOPE(RHI, "ObjectCollector: Collect"); m_mutex.lock(); if (m_pendingObjects.size()) { diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/PipelineStateDescriptor.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/PipelineStateDescriptor.h index 63f0cb3822..83ab3cb584 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/PipelineStateDescriptor.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/PipelineStateDescriptor.h @@ -44,7 +44,7 @@ namespace AZ /// Returns the hash of the pipeline state descriptor contents. virtual HashValue64 GetHash() const = 0; - virtual bool operator == (const PipelineStateDescriptor& rhs) const; + bool operator == (const PipelineStateDescriptor& rhs) const; /// The pipeline layout describing the shader resource bindings. ConstPtr m_pipelineLayoutDescriptor = nullptr; @@ -77,8 +77,7 @@ namespace AZ /// Computes the hash value for this descriptor. HashValue64 GetHash() const override; - - virtual bool operator == (const PipelineStateDescriptorForDispatch& rhs) const; + bool operator == (const PipelineStateDescriptorForDispatch& rhs) const; /// The compute function containing byte code to compile. ConstPtr m_computeFunction; @@ -102,7 +101,7 @@ namespace AZ /// Computes the hash value for this descriptor. HashValue64 GetHash() const override; - virtual bool operator == (const PipelineStateDescriptorForDraw& rhs) const; + bool operator == (const PipelineStateDescriptorForDraw& rhs) const; /// [Required] The vertex function to compile. ConstPtr m_vertexFunction; @@ -135,7 +134,7 @@ namespace AZ //! Computes the hash value for this descriptor. HashValue64 GetHash() const override; - virtual bool operator == (const PipelineStateDescriptorForRayTracing& rhs) const; + bool operator == (const PipelineStateDescriptorForRayTracing& rhs) const; // The ray tracing shader byte code ConstPtr m_rayTracingFunction; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/PoolAllocator.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/PoolAllocator.h index 1392d1a59d..70a9937b1c 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/PoolAllocator.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/PoolAllocator.h @@ -51,7 +51,7 @@ namespace AZ VirtualAddress Allocate(size_t byteCount, size_t byteAlignment) override; void DeAllocate(VirtualAddress allocation) override; void GarbageCollect() override; - void GarbageCollectForce(); + void GarbageCollectForce() override; size_t GetAllocationCount() const override; size_t GetAllocatedByteCount() const override; const Descriptor& GetDescriptor() const override; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupPool.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupPool.h index 44bf80a980..a8775f8625 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupPool.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupPool.h @@ -38,7 +38,7 @@ namespace AZ ResultCode InitGroup(ShaderResourceGroup& srg); //! Returns the descriptor passed at initialization time. - const ShaderResourceGroupPoolDescriptor& GetDescriptor() const; + const ShaderResourceGroupPoolDescriptor& GetDescriptor() const override; //! Returns the SRG layout used when initializing the pool. const ShaderResourceGroupLayout* GetLayout() const; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h index 42ba2d2e25..14e9968e42 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h @@ -81,15 +81,6 @@ namespace AZ AZ_RTTI(SwapChain, "{888B64A5-D956-406F-9C33-CF6A54FC41B0}", Object); -#if defined(PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) - // On Linux platforms that uses XCB, a resize may occur in the swap chain but the command queue may still - // reference the original surface. This flag is a temporary fix to make sure the swap chain is ready to present - // We need to remove this work around with - - // [GFX TODO][GHI - 2678] - AZStd::atomic_bool m_readyToPresent { false }; -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - protected: SwapChain(); diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp index ab45d2239b..ef330e7902 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp @@ -155,11 +155,6 @@ namespace AZ { arguments += " -Zpr"; } - if (m_dxcGenerateDebugInfo) - { - arguments += " -Zi"; // Generate debug information - arguments += " -Zss"; // Compute Shader Hash considering source information - } // strip spaces at both sides AZStd::string dxcAdditionalFreeArguments = m_dxcAdditionalFreeArguments; AzFramework::StringFunc::TrimWhiteSpace(dxcAdditionalFreeArguments, true, true); diff --git a/Gems/Atom/RHI/Code/Source/RHI/AsyncWorkQueue.cpp b/Gems/Atom/RHI/Code/Source/RHI/AsyncWorkQueue.cpp index 52021fa736..a076bf3e58 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/AsyncWorkQueue.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/AsyncWorkQueue.cpp @@ -128,7 +128,7 @@ namespace AZ return; } - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncWorkQueue: WaitToFinish"); AZStd::unique_lock lock(m_waitWorkItemMutex); m_waitWorkItemCondition.wait(lock, [&]() {return HasFinishedWork(workHandle); }); diff --git a/Gems/Atom/RHI/Code/Source/RHI/BufferPool.cpp b/Gems/Atom/RHI/Code/Source/RHI/BufferPool.cpp index 9849db254e..94b0b57c37 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/BufferPool.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/BufferPool.cpp @@ -7,7 +7,6 @@ */ #include -#include #include #include @@ -163,7 +162,7 @@ namespace AZ return ResultCode::InvalidArgument; } - AZ_ATOM_PROFILE_FUNCTION("RHI", "BufferPool::OrphanBuffer"); + AZ_PROFILE_SCOPE(RHI, "BufferPool::OrphanBuffer"); return OrphanBufferInternal(buffer); } diff --git a/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp b/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp index 15621c7f2b..b50c36d3f9 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp @@ -7,9 +7,10 @@ */ #include -#include #include +#include + namespace AZ { namespace RHI @@ -22,7 +23,7 @@ namespace AZ ResultCode CommandQueue::Init(Device& device, const CommandQueueDescriptor& descriptor) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueue: Init"); #if defined (AZ_RHI_ENABLE_VALIDATION) if (IsInitialized()) @@ -83,7 +84,7 @@ namespace AZ void CommandQueue::FlushCommands() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "CommandQueue: FlushCommands"); + AZ_PROFILE_SCOPE(RHI, "CommandQueue: FlushCommands"); while (!m_isWorkQueueEmpty && !m_isQuitting) { AZStd::this_thread::yield(); diff --git a/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp b/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp index e16b89b5cd..1bc17adb22 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp @@ -27,38 +27,14 @@ namespace AZ return Interface::Get(); } - // --- TimeRegion --- - - TimeRegion::TimeRegion(const GroupRegionName* groupRegionName) : - CachedTimeRegion(groupRegionName) - { - if (CpuProfiler::Get()) - { - CpuProfiler::Get()->BeginTimeRegion(*this); - } - } - - TimeRegion::~TimeRegion() - { - EndRegion(); - } - - void TimeRegion::EndRegion() - { - if (CpuProfiler::Get()) - { - CpuProfiler::Get()->EndTimeRegion(); - } - } - // --- CachedTimeRegion --- - CachedTimeRegion::CachedTimeRegion(const GroupRegionName* groupRegionName) + CachedTimeRegion::CachedTimeRegion(const GroupRegionName& groupRegionName) { m_groupRegionName = groupRegionName; } - CachedTimeRegion::CachedTimeRegion(const GroupRegionName* groupRegionName, uint16_t stackDepth, uint64_t startTick, uint64_t endTick) + CachedTimeRegion::CachedTimeRegion(const GroupRegionName& groupRegionName, uint16_t stackDepth, uint64_t startTick, uint64_t endTick) { m_groupRegionName = groupRegionName; m_stackDepth = stackDepth; @@ -92,6 +68,7 @@ namespace AZ void CpuProfilerImpl::Init() { + Interface::Register(this); Interface::Register(this); m_initialized = true; SystemTickBus::Handler::BusConnect(); @@ -106,6 +83,7 @@ namespace AZ } // When this call is made, no more thread profiling calls can be performed anymore Interface::Unregister(this); + Interface::Unregister(this); // Wait for the remaining threads that might still be processing its profiling calls AZStd::unique_lock shutdownLock(m_shutdownMutex); @@ -121,7 +99,7 @@ namespace AZ SystemTickBus::Handler::BusDisconnect(); } - void CpuProfilerImpl::BeginTimeRegion(TimeRegion& timeRegion) + void CpuProfilerImpl::BeginRegion(const AZ::Debug::Budget* budget, const char* eventName) { // Try to lock here, the shutdownMutex will only be contested when the CpuProfiler is shutting down. if (m_shutdownMutex.try_lock_shared()) @@ -132,6 +110,7 @@ namespace AZ RegisterThreadStorage(); // Push it to the stack + CachedTimeRegion timeRegion({budget->Name(), eventName}); ms_threadLocalStorage->RegionStackPushBack(timeRegion); } @@ -139,11 +118,12 @@ namespace AZ } } - void CpuProfilerImpl::EndTimeRegion() + void CpuProfilerImpl::EndRegion([[maybe_unused]] const AZ::Debug::Budget* budget) { // Try to lock here, the shutdownMutex will only be contested when the CpuProfiler is shutting down. if (m_shutdownMutex.try_lock_shared()) { + // guard against enabling mid-marker if (m_enabled && ms_threadLocalStorage != nullptr) { ms_threadLocalStorage->RegionStackPopBack(); @@ -232,19 +212,6 @@ namespace AZ return m_enabled; } - const CachedTimeRegion::GroupRegionName& CpuProfilerImpl::InsertDynamicName(const char* groupName, const AZStd::string& regionName) - { - AZStd::scoped_lock lock(m_dynamicNameMutex); - AZ_Warning("CpuProfiler", m_regionNameStringPool.size() < MaxRegionStringPoolSize, - "Stored dynamic region names are accumulating. Consider removing a AZ_ATOM_PROFILE_DYNAMIC invocation."); - auto [regionNameItr, wasRegionInserted] = m_regionNameStringPool.insert(regionName); - - CachedTimeRegion::GroupRegionName newGroupRegionName(groupName, regionNameItr->c_str()); - auto [groupRegionNameItr, wasGroupRegionInserted] = m_dynamicGroupRegionNamePool.insert(newGroupRegionName); - - return *groupRegionNameItr; - } - void CpuProfilerImpl::OnSystemTick() { if (!m_enabled) @@ -307,7 +274,7 @@ namespace AZ m_deleteFlag = true; } - void CpuTimingLocalStorage::RegionStackPushBack(TimeRegion& timeRegion) + void CpuTimingLocalStorage::RegionStackPushBack(CachedTimeRegion& timeRegion) { // If it was (re)enabled, clear the lists first if (m_clearContainers) @@ -323,13 +290,13 @@ namespace AZ timeRegion.m_stackDepth = static_cast(m_stackLevel); AZ_Assert(m_timeRegionStack.size() < TimeRegionStackSize, "Adding too many time regions to the stack. Increase the size of TimeRegionStackSize."); - m_timeRegionStack.push_back(&timeRegion); + m_timeRegionStack.push_back(timeRegion); // Increment the stack m_stackLevel++; // Set the starting time at the end, to avoid recording the minor overhead - timeRegion.m_startTick = AZStd::GetTimeNowTicks(); + m_timeRegionStack.back().m_startTick = AZStd::GetTimeNowTicks(); } void CpuTimingLocalStorage::RegionStackPopBack() @@ -344,23 +311,23 @@ namespace AZ const AZStd::sys_time_t endRegionTime = AZStd::GetTimeNowTicks(); AZ_Assert(!m_timeRegionStack.empty(), "Trying to pop an element in the stack, but it's empty."); - TimeRegion* back = m_timeRegionStack.back(); + CachedTimeRegion back = m_timeRegionStack.back(); m_timeRegionStack.pop_back(); // Set the ending time - back->m_endTick = endRegionTime; + back.m_endTick = endRegionTime; // Decrement the stack m_stackLevel--; // Add an entry to the cached region - AddCachedRegion(CachedTimeRegion(back->m_groupRegionName, back->m_stackDepth, back->m_startTick, back->m_endTick)); + AddCachedRegion(back); } // Gets called when region ends and all data is set - void CpuTimingLocalStorage::AddCachedRegion(CachedTimeRegion&& timeRegionCached) + void CpuTimingLocalStorage::AddCachedRegion(const CachedTimeRegion& timeRegionCached) { - if (m_hitSizeLimitMap[timeRegionCached.m_groupRegionName->m_regionName]) + if (m_hitSizeLimitMap[timeRegionCached.m_groupRegionName.m_regionName]) { return; } @@ -379,12 +346,12 @@ namespace AZ // Add the cached regions to the map for (auto& cachedTimeRegion : m_cachedTimeRegions) { - const AZStd::string regionName = cachedTimeRegion.m_groupRegionName->m_regionName; + const AZStd::string regionName = cachedTimeRegion.m_groupRegionName.m_regionName; AZStd::vector& regionVec = m_cachedTimeRegionMap[regionName]; regionVec.push_back(cachedTimeRegion); if (regionVec.size() >= TimeRegionStackSize) { - m_hitSizeLimitMap[cachedTimeRegion.m_groupRegionName->m_regionName] = true; + m_hitSizeLimitMap.insert_or_assign(AZStd::move(regionName), true); } } @@ -448,8 +415,8 @@ namespace AZ CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializerEntry::CpuProfilingStatisticsSerializerEntry( const RHI::CachedTimeRegion& cachedTimeRegion, AZStd::thread_id threadId) { - m_groupName = cachedTimeRegion.m_groupRegionName->m_groupName; - m_regionName = cachedTimeRegion.m_groupRegionName->m_regionName; + m_groupName = cachedTimeRegion.m_groupRegionName.m_groupName; + m_regionName = cachedTimeRegion.m_groupRegionName.m_regionName; m_stackDepth = cachedTimeRegion.m_stackDepth; m_startTick = cachedTimeRegion.m_startTick; m_endTick = cachedTimeRegion.m_endTick; diff --git a/Gems/Atom/RHI/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Code/Source/RHI/Device.cpp index 3af09717df..2f4ca297a1 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/Device.cpp @@ -6,7 +6,6 @@ * */ -#include #include #include @@ -128,7 +127,7 @@ namespace AZ { if (ValidateIsInitialized() && ValidateIsInFrame()) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "Device: EndFrame"); + AZ_PROFILE_SCOPE(RHI, "Device: EndFrame"); EndFrameInternal(); m_isInFrame = false; return ResultCode::Success; @@ -150,7 +149,7 @@ namespace AZ { if (ValidateIsInitialized() && ValidateIsNotInFrame()) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "Device: CompileMemoryStatistics"); + AZ_PROFILE_SCOPE(RHI, "Device: CompileMemoryStatistics"); MemoryStatisticsBuilder builder; builder.Begin(memoryStatistics, reportFlags); CompileMemoryStatisticsInternal(builder); diff --git a/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp b/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp index 48b64c17c0..82b0a13c86 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp @@ -8,12 +8,12 @@ #include #include +#include #include #include #if defined(USE_RENDERDOC) || defined(USE_PIX) #include -#include #include #endif @@ -28,6 +28,8 @@ static AZStd::unique_ptr s_pixModule; static bool s_isPixGpuCaptureDllLoaded = false; #endif +static bool s_usingWarpDevice = false; + namespace AZ { namespace RHI @@ -55,6 +57,8 @@ namespace AZ Factory::Factory() { + AZStd::string preferredUserAdapterName = RHI::GetCommandLineValue("forceAdapter"); + s_usingWarpDevice = preferredUserAdapterName == "Microsoft Basic Render Driver"; #if defined(USE_RENDERDOC) // If RenderDoc is requested, we need to load the library as early as possible (before device queries/factories are made) bool enableRenderDoc = RHI::QueryCommandLineOption("enableRenderDoc"); @@ -197,5 +201,10 @@ namespace AZ return false; #endif } + + bool Factory::UsingWarpDevice() + { + return s_usingWarpDevice; + } } } diff --git a/Gems/Atom/RHI/Code/Source/RHI/Fence.cpp b/Gems/Atom/RHI/Code/Source/RHI/Fence.cpp index b35260caca..d030ff8d2b 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/Fence.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/Fence.cpp @@ -8,6 +8,8 @@ #include +#include + namespace AZ { namespace RHI @@ -81,7 +83,7 @@ namespace AZ return ResultCode::InvalidOperation; } - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "Fence: WaitOnCpu"); WaitOnCpuInternal(); return ResultCode::Success; } diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp index ff6ecb4df5..9f3d21a2f1 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -73,7 +72,7 @@ namespace AZ void FrameGraph::Clear() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraph: Clear"); + AZ_PROFILE_SCOPE(RHI, "FrameGraph: Clear"); for (Scope* scope : m_scopes) { scope->Deactivate(); @@ -126,7 +125,7 @@ namespace AZ ResultCode FrameGraph::End() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraph: End"); + AZ_PROFILE_SCOPE(RHI, "FrameGraph: End"); ResultCode resultCode = ValidateEnd(); if (resultCode != ResultCode::Success) { diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphCompiler.cpp index c4204a4a90..d06ec002e4 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphCompiler.cpp @@ -6,7 +6,6 @@ * */ -#include #include #include #include @@ -121,7 +120,7 @@ namespace AZ */ MessageOutcome FrameGraphCompiler::Compile(const FrameGraphCompileRequest& request) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: Compile"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: Compile"); MessageOutcome outcome = ValidateCompileRequest(request); if (!outcome) @@ -146,7 +145,7 @@ namespace AZ /// [Phase 4] Compile platform-specific scope data after all attachments and views have been compiled. { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: Scope Compile"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: Scope Compile"); for (Scope* scope : frameGraph.GetScopes()) { @@ -162,7 +161,7 @@ namespace AZ FrameGraph& frameGraph, FrameSchedulerCompileFlags compileFlags) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileQueueCentricScopeGraph"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileQueueCentricScopeGraph"); const bool disableAsyncQueues = CheckBitsAll(compileFlags, FrameSchedulerCompileFlags::DisableAsyncQueues); if (disableAsyncQueues) @@ -480,7 +479,7 @@ namespace AZ return; } - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileTransientAttachments"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileTransientAttachments"); ExtendTransientAttachmentAsyncQueueLifetimes(frameGraph, compileFlags); @@ -769,7 +768,7 @@ namespace AZ void FrameGraphCompiler::CompileResourceViews(const FrameGraphAttachmentDatabase& attachmentDatabase) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileResourceViews"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileResourceViews"); for (ImageFrameAttachment* imageAttachment : attachmentDatabase.GetImageAttachments()) { diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphExecuter.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphExecuter.cpp index 342e537993..6888531b67 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphExecuter.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphExecuter.cpp @@ -6,7 +6,6 @@ * */ #include -#include #include #include #include @@ -71,14 +70,13 @@ namespace AZ void FrameGraphExecuter::Begin(const FrameGraph& frameGraph) { - AZ_TRACE_METHOD(); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphExecuter: Begin"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphExecuter: Begin"); BeginInternal(frameGraph); } void FrameGraphExecuter::End() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphExecuter: End"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphExecuter: End"); AZ_Assert(m_pendingGroups.empty(), "Pending contexts in queue."); m_groups.clear(); EndInternal(); diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp index fe69f56856..5d2feb1e34 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp @@ -6,7 +6,6 @@ * */ -#include #include #include #include @@ -137,7 +136,7 @@ namespace AZ ResultCode FrameScheduler::ImportScopeProducer(ScopeProducer& scopeProducer) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: ImportScopeProducer"); if (!ValidateIsProcessing()) { @@ -171,14 +170,14 @@ namespace AZ MessageOutcome FrameScheduler::Compile(const FrameSchedulerCompileRequest& compileRequest) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: Compile"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: Compile"); PrepareProducers(); m_compileRequest = compileRequest; { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RHI", "FrameScheduler: Compile: OnFrameCompile"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: Compile: OnFrameCompile"); FrameEventBus::Broadcast(&FrameEventBus::Events::OnFrameCompile); } @@ -193,7 +192,7 @@ namespace AZ if (outcome.IsSuccess()) { { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RHI", "FrameScheduler: Compile: OnFrameCompileEnd"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: Compile: OnFrameCompileEnd"); FrameEventBus::Broadcast(&FrameEventBus::Events::OnFrameCompileEnd, *m_frameGraph); } @@ -216,8 +215,7 @@ namespace AZ void FrameScheduler::PrepareProducers() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: PrepareProducers"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: PrepareProducers"); for (ScopeProducer* scopeProducer : m_scopeProducers) { @@ -237,8 +235,7 @@ namespace AZ void FrameScheduler::CompileProducers() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: CompileProducers"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: CompileProducers"); for (ScopeProducer* scopeProducer : m_scopeProducers) { @@ -249,8 +246,7 @@ namespace AZ void FrameScheduler::CompileShaderResourceGroups() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: CompileShaderResourceGroups"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: CompileShaderResourceGroups"); // Execute all queued resource invalidations, which will mark SRG's for compilation. { @@ -286,7 +282,7 @@ namespace AZ const auto compileGroupsForIntervalLambda = [srgPool, interval]() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler : compileGroupsForIntervalLambda"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler : compileGroupsForIntervalLambda"); srgPool->CompileGroupsForInterval(interval); }; @@ -318,12 +314,16 @@ namespace AZ resourcePoolDatabase.ForEachShaderResourceGroupPool(compileAllLambda); } + + //It is possible for certain back ends to run out of SRG memory (due to fragmentation) in which case + //we try to compact and re-compile SRGs. + RHI::ResultCode resultCode = m_device->CompactSRGMemory(); + AZ_Assert(resultCode == RHI::ResultCode::Success, "SRG compaction failed and this can lead to a gpu crash."); } void FrameScheduler::BuildRayTracingShaderTables() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: BuildRayTracingShaderTables"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: BuildRayTracingShaderTables"); for (auto rayTracingShaderTable : m_rayTracingShaderTablesToBuild) { @@ -341,8 +341,7 @@ namespace AZ ResultCode FrameScheduler::BeginFrame() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: BeginFrame"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: BeginFrame"); if (!ValidateIsInitialized()) { @@ -376,8 +375,7 @@ namespace AZ ResultCode FrameScheduler::EndFrame() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: EndFrame"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: EndFrame"); if (Validation::IsEnabled()) { @@ -404,7 +402,7 @@ namespace AZ m_scopeProducerLookup.clear(); { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RHI", "FrameScheduler: EndFrame: OnFrameEnd"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: EndFrame: OnFrameEnd"); FrameEventBus::Event(m_device, &FrameEventBus::Events::OnFrameEnd); } @@ -431,8 +429,7 @@ namespace AZ void FrameScheduler::ExecuteGroupInternal(AZ::Job* parentJob, uint32_t groupIndex) { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: ExecuteGroupInternal"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: ExecuteGroupInternal"); FrameGraphExecuteGroup* executeGroup = m_frameGraphExecuter->BeginGroup(groupIndex); const uint32_t contextCount = executeGroup->GetContextCount(); @@ -474,8 +471,7 @@ namespace AZ void FrameScheduler::Execute(JobPolicy overrideJobPolicy) { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: Execute"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: Execute"); const uint32_t groupCount = m_frameGraphExecuter->GetGroupCount(); const JobPolicy platformJobPolicy = m_frameGraphExecuter->GetJobPolicy(); diff --git a/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp b/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp index c646700495..77877b0020 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp @@ -344,5 +344,17 @@ namespace AZ handle = node.m_nextFree; } } + + void FreeListAllocator::Clone(RHI::Allocator* newAllocator) + { + FreeListAllocator* newFreeListAllocator = static_cast(newAllocator); + newFreeListAllocator->m_headHandle = m_headHandle; + newFreeListAllocator->m_nodeFreeList = m_nodeFreeList; + newFreeListAllocator->m_nodes = m_nodes; + newFreeListAllocator->m_allocations = m_allocations; + newFreeListAllocator->m_garbage = m_garbage; + newFreeListAllocator->m_garbageCollectCycle = m_garbageCollectCycle; + newFreeListAllocator->m_byteCountTotal = m_byteCountTotal; + } } } diff --git a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp index 6e98456933..0c06887dd6 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp @@ -6,9 +6,10 @@ * */ -#include #include #include + +#include #include #include @@ -212,7 +213,7 @@ namespace AZ void PipelineStateCache::Compact() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "PipelineStateCache: Compact"); + AZ_PROFILE_SCOPE(RHI, "PipelineStateCache: Compact"); AZStd::unique_lock lock(m_mutex); // Merge the pending cache into the read-only cache. diff --git a/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp b/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp index a381209d2b..ad3ab119ef 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp @@ -6,12 +6,12 @@ * */ -#include #include #include #include #include +#include #include #include @@ -187,8 +187,7 @@ namespace AZ void RHISystem::FrameUpdate(FrameGraphCallback frameGraphCallback) { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "RHISystem: FrameUpdate"); + AZ_PROFILE_SCOPE(RHI, "RHISystem: FrameUpdate"); { AZ_PROFILE_SCOPE(RHI, "main per-frame work"); @@ -201,7 +200,7 @@ namespace AZ * own RHI scopes to the frame scheduler. This happens prior to the RPI pass graph registration. */ { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RHI", "RHISystem: FrameUpdate: OnFramePrepare"); + AZ_PROFILE_SCOPE(RHI, "RHISystem: FrameUpdate: OnFramePrepare"); RHISystemNotificationBus::Broadcast(&RHISystemNotificationBus::Events::OnFramePrepare, m_frameScheduler); } diff --git a/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp index 92d7a125d8..b0501d937d 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp @@ -164,12 +164,6 @@ namespace AZ m_currentImageIndex = 0; } -#if defined(PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) - // If we are presenting through the editor, the resize is triggered through the editor's window, which - // won't happen until after the surface is ready to present - m_readyToPresent.store(true); -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - return resultCode; } diff --git a/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h b/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h index 1f22599b38..63c2d69ea2 100644 --- a/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h +++ b/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h @@ -64,6 +64,12 @@ namespace AZ //! int array: Max count for descriptors AZStd::unordered_map> m_descriptorHeapLimits; + // Number of max static handles for shader visible srv/uav/cbv views + uint32_t m_numShaderVisibleCbvSrvUavStaticHandles = 2000; + + //Bool to indicate allowing compaction of shader visible srv/uav/cbv heap in case of fragmentation + bool m_allowDescriptorHeapCompaction = false; + FrameGraphExecuterData m_frameGraphExecuterData; void LoadPlatformLimitsDescriptor(const char* rhiName) override; diff --git a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/DX12_Windows.h b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/DX12_Windows.h index 85b782aa6b..2eef783932 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/DX12_Windows.h +++ b/Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/DX12_Windows.h @@ -16,7 +16,10 @@ #include #include + +AZ_PUSH_DISABLE_WARNING(4265, "-Wunknown-warning-option") // class has virtual functions, but its non-trivial destructor is not virtual; #include +AZ_POP_DISABLE_WARNING #include #include diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp index 89ae3ede46..f30fc72ace 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp @@ -255,6 +255,11 @@ namespace AZ // Compilation parameters AZStd::string params = shaderCompilerArguments.MakeAdditionalDxcCommandLineString(); + if (BuildHasDebugInfo(shaderCompilerArguments)) + { + params += " -Zi"; // Generate debug information + params += " -Zss"; // Compute Shader Hash considering source information + } // Enable half precision types when shader model >= 6.2 int shaderModelMajor = 0; @@ -281,12 +286,11 @@ namespace AZ AZStd::string symbolDatabaseFileCliArgument{" "}; // when not debug: still insert a space between 5.dxil and 7.hlsl-in if (BuildHasDebugInfo(shaderCompilerArguments)) { - // prepare .ldd filename: + // prepare .pdb filename: AZStd::string md5hex = RHI::ByteToHexString(md5); AZStd::string symbolDatabaseFilePath = dxcInputFile.c_str(); // mutate from source - AZStd::string lldFileName = md5hex // lld is like pdb but it's the default symbol database extension in dxc - + "-" + profileIt->second; // concatenate the shader profile to disambiguate vs/ps... - AzFramework::StringFunc::Path::ReplaceFullName(symbolDatabaseFilePath, lldFileName.c_str(), "lld"); + AZStd::string pdbFileName = md5hex + "-" + profileIt->second; // concatenate the shader profile to disambiguate vs/ps... + AzFramework::StringFunc::Path::ReplaceFullName(symbolDatabaseFilePath, pdbFileName.c_str(), "pdb"); // it is possible that another activated platform/profile, already exported that file. (since it's hashed on the source file) // dxc returns an error in such case. we get less surprising effets by just not mentionning an -Fd argument if (AZ::IO::SystemFile::Exists(symbolDatabaseFilePath.c_str())) diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp index 77b41d6ffc..fd9ad84511 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp @@ -19,8 +19,10 @@ namespace AZ if (SerializeContext* serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(0) + ->Version(1) ->Field("DescriptorHeapLimits", &PlatformLimitsDescriptor::m_descriptorHeapLimits) + ->Field("NumShaderVisibleCbvSrvUavStaticHandles", &PlatformLimitsDescriptor::m_numShaderVisibleCbvSrvUavStaticHandles) + ->Field("AllowDescriptorHeapCompaction", &PlatformLimitsDescriptor::m_allowDescriptorHeapCompaction) ->Field("FrameGraphExecuterData", &PlatformLimitsDescriptor::m_frameGraphExecuterData) ; } @@ -54,7 +56,7 @@ namespace AZ // Map default value must be initialized after attempting to serialize (and result in failure). // Otherwise, serialization won't overwrite the default values. m_descriptorHeapLimits = AZStd::unordered_map>({ - { AZStd::string("DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV"), { 1000000, 1000000 } }, + { AZStd::string("DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV"), { 100000, 1000000 } }, { AZStd::string("DESCRIPTOR_HEAP_TYPE_SAMPLER"), { 2048, 2048 } }, { AZStd::string("DESCRIPTOR_HEAP_TYPE_RTV"), { 2048, 0 } }, { AZStd::string("DESCRIPTOR_HEAP_TYPE_DSV"), { 2048, 0 } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/AsyncUploadQueue.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/AsyncUploadQueue.cpp index 29b210e17e..cb9dac3864 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/AsyncUploadQueue.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/AsyncUploadQueue.cpp @@ -196,7 +196,7 @@ namespace AZ AsyncUploadQueue::FramePacket* AsyncUploadQueue::BeginFramePacket() { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: BeginFramePacket"); AZ_Assert(!m_recordingFrame, "The previous frame packet isn't ended"); FramePacket* framePacket = &m_framePackets[m_frameIndex]; @@ -212,7 +212,7 @@ namespace AZ void AsyncUploadQueue::EndFramePacket(ID3D12CommandQueue* commandQueue) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: EndFramePacket"); AZ_Assert(m_recordingFrame, "The frame packet wasn't started. You need to call StartFramePacket first."); AssertSuccess(m_commandList->Close()); @@ -229,7 +229,7 @@ namespace AZ // [GFX TODO][ATOM-4205] Stage/Upload 3D streaming images more efficiently. uint64_t AsyncUploadQueue::QueueUpload(const RHI::StreamingImageExpandRequest& request, uint32_t residentMip) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: QueueUpload"); uint64_t fenceValue = m_uploadFence.Increment(); @@ -475,7 +475,7 @@ namespace AZ void AsyncUploadQueue::WaitForUpload(uint64_t fenceValue) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: WaitForUpload"); if (!IsUploadFinished(fenceValue)) { @@ -489,7 +489,7 @@ namespace AZ void AsyncUploadQueue::ProcessCallbacks(uint64_t fenceValue) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: ProcessCallbacks"); AZStd::lock_guard lock(m_callbackMutex); while (m_callbacks.size() > 0 && m_callbacks.front().second <= fenceValue) { diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h index 443ab3e73a..1472cdc80e 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h @@ -438,7 +438,7 @@ namespace AZ switch (pipelineType) { case RHI::PipelineStateType::Draw: - if (binding.m_resourceTable.IsValid()) + if (binding.m_resourceTable.IsValid() && compiledData.m_gpuViewsDescriptorHandle.ptr) { GetCommandList()->SetGraphicsRootDescriptorTable(binding.m_resourceTable.GetIndex(), compiledData.m_gpuViewsDescriptorHandle); } @@ -448,14 +448,15 @@ namespace AZ GetCommandList()->SetGraphicsRootConstantBufferView(binding.m_constantBuffer.GetIndex(), compiledData.m_gpuConstantAddress); } - if (binding.m_samplerTable.IsValid()) + if (binding.m_samplerTable.IsValid() && compiledData.m_gpuSamplersDescriptorHandle.ptr) { GetCommandList()->SetGraphicsRootDescriptorTable(binding.m_samplerTable.GetIndex(), compiledData.m_gpuSamplersDescriptorHandle); } for (uint32_t unboundedArrayIndex = 0; unboundedArrayIndex < ShaderResourceGroupCompiledData::MaxUnboundedArrays; ++unboundedArrayIndex) { - if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid()) + if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid() && + compiledData.m_gpuUnboundedArraysDescriptorHandles[unboundedArrayIndex].ptr) { GetCommandList()->SetGraphicsRootDescriptorTable( binding.m_unboundedArrayResourceTables[unboundedArrayIndex].GetIndex(), @@ -465,7 +466,7 @@ namespace AZ break; case RHI::PipelineStateType::Dispatch: - if (binding.m_resourceTable.IsValid()) + if (binding.m_resourceTable.IsValid() && compiledData.m_gpuViewsDescriptorHandle.ptr) { GetCommandList()->SetComputeRootDescriptorTable(binding.m_resourceTable.GetIndex(), compiledData.m_gpuViewsDescriptorHandle); } @@ -475,14 +476,15 @@ namespace AZ GetCommandList()->SetComputeRootConstantBufferView(binding.m_constantBuffer.GetIndex(), compiledData.m_gpuConstantAddress); } - if (binding.m_samplerTable.IsValid()) + if (binding.m_samplerTable.IsValid() && compiledData.m_gpuSamplersDescriptorHandle.ptr) { GetCommandList()->SetComputeRootDescriptorTable(binding.m_samplerTable.GetIndex(), compiledData.m_gpuSamplersDescriptorHandle); } for (uint32_t unboundedArrayIndex = 0; unboundedArrayIndex < ShaderResourceGroupCompiledData::MaxUnboundedArrays; ++unboundedArrayIndex) { - if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid()) + if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid() && + compiledData.m_gpuUnboundedArraysDescriptorHandles[unboundedArrayIndex].ptr) { GetCommandList()->SetComputeRootDescriptorTable( binding.m_unboundedArrayResourceTables[unboundedArrayIndex].GetIndex(), diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp index c5c71a9f43..9733045179 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp @@ -33,7 +33,7 @@ namespace AZ void CommandListBase::Reset(ID3D12CommandAllocator* commandAllocator) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandListBase: Reset"); AZ_Assert(m_queuedBarriers.empty(), "Unflushed barriers in command list."); m_commandList->Reset(commandAllocator, nullptr); @@ -50,7 +50,7 @@ namespace AZ void CommandListBase::SetNameInternal(const AZStd::string_view& name) { - AZStd::wstring wname; + AZStd::fixed_wstring<256> wname; AZStd::to_wstring(wname, name.data()); GetCommandList()->SetName(wname.data()); } @@ -95,7 +95,7 @@ namespace AZ { if (m_queuedBarriers.size()) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandListBase: FlushBarriers"); m_commandList->ResourceBarrier((UINT)m_queuedBarriers.size(), m_queuedBarriers.data()); m_queuedBarriers.clear(); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListPool.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListPool.cpp index c31fe8ada8..20021e71ba 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListPool.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListPool.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include namespace AZ @@ -175,7 +174,7 @@ namespace AZ void CommandListAllocator::Collect() { - AZ_ATOM_PROFILE_FUNCTION("DX12", "CommandListAllocator: Collect"); + AZ_PROFILE_SCOPE(RHI, "CommandListAllocator: Collect(DX12)"); for (uint32_t queueIdx = 0; queueIdx < RHI::HardwareQueueClassCount; ++queueIdx) { m_commandListSubAllocators[queueIdx].ForEach([](Internal::CommandListSubAllocator& commandListSubAllocator) diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueue.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueue.cpp index 9d58217f01..0cde6aeb09 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueue.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueue.cpp @@ -195,7 +195,7 @@ namespace AZ void CommandQueue::UpdateTileMappings(CommandList& commandList) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueue: UpdateTileMappings"); for (const CommandList::TileMapRequest& request : commandList.GetTileMapRequests()) { const uint32_t tileCount = request.m_sourceRegionSize.NumTiles; @@ -229,7 +229,7 @@ namespace AZ void CommandQueue::WaitForIdle() { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueue: WaitForIdle"); Fence fence; fence.Init(m_device.get(), RHI::FenceState::Reset); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp index 96d621df59..012784d3fc 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include + #include #include #include @@ -39,7 +39,7 @@ namespace AZ { Device& device = static_cast(deviceBase); m_currentFrameIndex = 0; - m_frameFences.resize(RHI::Limits::Device::FrameCountMax - 1); + m_frameFences.resize(RHI::Limits::Device::FrameCountMax); for (FenceSet& fences : m_frameFences) { fences.Init(device.GetDevice(), RHI::FenceState::Signaled); @@ -101,7 +101,7 @@ namespace AZ void CommandQueueContext::WaitForIdle() { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueueContext: WaitForIdle"); for (uint32_t hardwareQueueIdx = 0; hardwareQueueIdx < RHI::HardwareQueueClassCount; ++hardwareQueueIdx) { if (m_commandQueues[hardwareQueueIdx]) @@ -113,7 +113,7 @@ namespace AZ void CommandQueueContext::Begin() { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueueContext: Begin"); { AZ_PROFILE_SCOPE(RHI, "Clearing Command Queue Timers"); @@ -131,8 +131,7 @@ namespace AZ void CommandQueueContext::End() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("DX12", "CommandQueueContext: End"); + AZ_PROFILE_SCOPE(RHI, "CommandQueueContext: End"); QueueGpuSignals(m_frameFences[m_currentFrameIndex]); @@ -146,7 +145,6 @@ namespace AZ { AZ_PROFILE_SCOPE(RHI, "Wait and Reset Fence"); - AZ_ATOM_PROFILE_TIME_GROUP_REGION("DX12", "CommandQueueContext: Wait on Fences"); FenceEvent event("FrameFence"); m_frameFences[m_currentFrameIndex].Wait(event); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp index 942b514b97..1b0df5846c 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp @@ -10,8 +10,9 @@ #include #include #include -#include +#include #include +#include namespace AZ { @@ -41,7 +42,7 @@ namespace AZ for (D3D12_SRV_DIMENSION dimension : validSRVDimensions) { - DescriptorHandle srvDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + DescriptorHandle srvDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); D3D12_SHADER_RESOURCE_VIEW_DESC desc = {}; desc.Format = DXGI_FORMAT_R32_UINT; @@ -63,7 +64,7 @@ namespace AZ for (D3D12_UAV_DIMENSION dimension : UAVDimensions) { - DescriptorHandle uavDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + DescriptorHandle uavDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); D3D12_UNORDERED_ACCESS_VIEW_DESC desc = {}; desc.Format = DXGI_FORMAT_R32_UINT; @@ -76,14 +77,14 @@ namespace AZ void DescriptorContext::CreateNullDescriptorsCBV() { D3D12_CONSTANT_BUFFER_VIEW_DESC constantBufferDesc = {}; - DescriptorHandle cbvDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + DescriptorHandle cbvDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); m_device->CreateConstantBufferView(&constantBufferDesc, GetCpuPlatformHandle(cbvDescriptorHandle)); m_nullDescriptorCBV = cbvDescriptorHandle; } void DescriptorContext::CreateNullDescriptorsSampler() { - m_nullSamplerDescriptor = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + m_nullSamplerDescriptor = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); D3D12_SAMPLER_DESC samplerDesc = {}; samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; @@ -103,7 +104,7 @@ namespace AZ AZ_Assert(platformLimitsDescriptor.get(), "Platform limits information is missing"); m_platformLimitsDescriptor = platformLimitsDescriptor; - + m_allowDescriptorHeapCompaction = m_platformLimitsDescriptor->m_allowDescriptorHeapCompaction; for (const auto& itr : platformLimitsDescriptor->m_descriptorHeapLimits) { for (uint32_t shaderVisibleIdx = 0; shaderVisibleIdx < PlatformLimitsDescriptor::NumHeapFlags; ++shaderVisibleIdx) @@ -115,11 +116,33 @@ namespace AZ if (descriptorCountMax) { - GetPool(static_cast(heapTypeIdx.value()), shaderVisibleIdx).Init(m_device.get(), type, flags, descriptorCountMax); + if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flags)) + { + //Init the two heaps to help support compaction after fragmentation + m_shaderVisibleCbvSrvUavPools[0].Init( + m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, + descriptorCountMax, platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles); + + m_shaderVisibleCbvSrvUavPools[1].Init( + m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, + descriptorCountMax, platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles); + } + else + { + GetPool(static_cast(heapTypeIdx.value()), shaderVisibleIdx).Init(m_device.get(), type, flags, descriptorCountMax, descriptorCountMax); + } } } } - + + if (m_allowDescriptorHeapCompaction) + { + m_backupStaticHandles.Init( + m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, + platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles, + platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles); + } + CreateNullDescriptors(); } @@ -130,7 +153,7 @@ namespace AZ { if (constantBufferView.IsNull()) { - constantBufferView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + constantBufferView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(constantBufferView); @@ -146,7 +169,7 @@ namespace AZ { if (shaderResourceView.IsNull()) { - shaderResourceView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + shaderResourceView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(shaderResourceView); @@ -166,7 +189,7 @@ namespace AZ { if (unorderedAccessView.IsNull()) { - unorderedAccessView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + unorderedAccessView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE unorderedAccessDescriptor = GetCpuPlatformHandle(unorderedAccessView); @@ -177,7 +200,24 @@ namespace AZ // Copy the UAV descriptor into the GPU-visible version for clearing. if (unorderedAccessViewClear.IsNull()) { - unorderedAccessViewClear = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1).GetOffset(); + unorderedAccessViewClear = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1); + + if (unorderedAccessViewClear.IsNull()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory for static handles. Please consider increasing the value of NumShaderVisibleCbvSrvUavStaticHandles" + "within platformlimits.azasset file for dx12."); + return; + } + + if (m_allowDescriptorHeapCompaction) + { + //We make a copy of static handles in case we need to compact and recreate the shader visible heap + m_device->CopyDescriptorsSimple( + 1, m_backupStaticHandles.GetCpuPlatformHandle(unorderedAccessViewClear), unorderedAccessDescriptor, + unorderedAccessViewClear.m_type); + } } CopyDescriptor(unorderedAccessViewClear, unorderedAccessView); } @@ -189,7 +229,7 @@ namespace AZ { if (shaderResourceView.IsNull()) { - shaderResourceView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + shaderResourceView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(shaderResourceView); @@ -206,7 +246,7 @@ namespace AZ { if (unorderedAccessView.IsNull()) { - unorderedAccessView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + unorderedAccessView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE unorderedAccessDescriptor = GetCpuPlatformHandle(unorderedAccessView); @@ -217,7 +257,24 @@ namespace AZ // Copy the UAV descriptor into the GPU-visible version for clearing. if (unorderedAccessViewClear.IsNull()) { - unorderedAccessViewClear = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1).GetOffset(); + unorderedAccessViewClear = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1); + + if (unorderedAccessViewClear.IsNull()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory for static handles. Please consider increasing the value of " + "NumShaderVisibleCbvSrvUavStaticHandles within platformlimits.azasset file for dx12."); + return; + } + + if (m_allowDescriptorHeapCompaction) + { + // We make a copy of static handles in case we need to compact and recreate the shader visible heap + m_device->CopyDescriptorsSimple( + 1, m_backupStaticHandles.GetCpuPlatformHandle(unorderedAccessViewClear), unorderedAccessDescriptor, + unorderedAccessViewClear.m_type); + } } CopyDescriptor(unorderedAccessViewClear, unorderedAccessView); } @@ -229,7 +286,7 @@ namespace AZ { if (renderTargetView.IsNull()) { - renderTargetView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + renderTargetView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor = GetCpuPlatformHandle(renderTargetView); @@ -246,13 +303,13 @@ namespace AZ { if (depthStencilView.IsNull()) { - depthStencilView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + depthStencilView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE depthStencilDescriptor = GetCpuPlatformHandle(depthStencilView); if (depthStencilReadView.IsNull()) { - depthStencilReadView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + depthStencilReadView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE depthStencilReadDescriptor = GetCpuPlatformHandle(depthStencilReadView); @@ -275,7 +332,7 @@ namespace AZ { if (samplerHandle.IsNull()) { - samplerHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + samplerHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_SAMPLER_DESC samplerDesc; @@ -287,17 +344,49 @@ namespace AZ { if (!descriptorHandle.IsNull()) { - ReleaseDescriptorTable(DescriptorTable(descriptorHandle, 1)); + GetPool(descriptorHandle.m_type, descriptorHandle.m_flags).ReleaseHandle(descriptorHandle); } } DescriptorTable DescriptorContext::CreateDescriptorTable( - D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, - uint32_t descriptorCount) + D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, uint32_t descriptorCount, ShaderResourceGroup* srg) { - return Allocate(descriptorHeapType, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, descriptorCount); + if (m_allowDescriptorHeapCompaction && !m_compactionInProgress) + { + // Track active SRGs in case we need to compact the shader visible cbv_srv_uav heap + AZStd::scoped_lock lock{ m_srgMapMutex }; + auto iter = m_srgAllocations.find(srg); + if (iter == m_srgAllocations.end()) + { + m_srgAllocations.emplace(srg, 1); + } + else + { + m_srgAllocations[srg]++; + } + } + + return GetPool(descriptorHeapType, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE).AllocateTable(descriptorCount); } + void DescriptorContext::ReleaseDescriptorTable(DescriptorTable table, ShaderResourceGroup* srg) + { + if (m_allowDescriptorHeapCompaction && !m_compactionInProgress) + { + //Track active SRGs in case we need to compact the shader visible cbv_srv_uav heap + AZStd::scoped_lock lock{ m_srgMapMutex }; + auto iter = m_srgAllocations.find(srg); + AZ_Assert(iter != m_srgAllocations.end(), "Srg entry not found"); + m_srgAllocations[srg]--; + if (m_srgAllocations[srg] == 0) + { + m_srgAllocations.erase(srg); + } + } + + GetPool(table.GetType(), table.GetFlags()).ReleaseTable(table); + } + void DescriptorContext::UpdateDescriptorTableRange( DescriptorTable gpuDestinationTable, const DescriptorHandle* cpuSourceDescriptors, @@ -314,14 +403,12 @@ namespace AZ } // Resolve destination descriptor to platform handle. - D3D12_CPU_DESCRIPTOR_HANDLE gpuDestinationHandle = GetCpuPlatformHandle(gpuDestinationTable.GetOffset()); + D3D12_CPU_DESCRIPTOR_HANDLE gpuDestinationHandle = GetCpuPlatformHandleForTable(gpuDestinationTable); // An array of descriptor sizes for each range. We just want N ranges with 1 descriptor each. AZStd::vector rangeCounts(DescriptorCount, 1); - /** - * We are gathering N source descriptors into a contiguous destination table. - */ + //We are gathering N source descriptors into a contiguous destination table. m_device->CopyDescriptors( 1, // Number of destination ranges. &gpuDestinationHandle, // Destination range array. @@ -341,7 +428,7 @@ namespace AZ void DescriptorContext::GarbageCollect() { - AZ_ATOM_PROFILE_FUNCTION("DX12", "DescriptorContext: GarbageCollect"); + AZ_PROFILE_SCOPE(RHI, "DescriptorContext: GarbageCollect(DX12)"); for (const auto& itr : m_platformLimitsDescriptor->m_descriptorHeapLimits) { for (uint32_t shaderVisibleIdx = 0; shaderVisibleIdx < PlatformLimitsDescriptor::NumHeapFlags; ++shaderVisibleIdx) @@ -354,19 +441,24 @@ namespace AZ } } } + + if (m_allowDescriptorHeapCompaction) + { + m_backupStaticHandles.GarbageCollect(); + } } - DescriptorTable DescriptorContext::Allocate( + DescriptorTable DescriptorContext::AllocateTable( D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count) { - return GetPool(type, flags).Allocate(count); + return GetPool(type, flags).AllocateTable(count); } - void DescriptorContext::ReleaseDescriptorTable(DescriptorTable table) + DescriptorHandle DescriptorContext::AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count) { - GetPool(table.GetType(), table.GetFlags()).Release(table); + return GetPool(type, flags).AllocateHandle(count); } D3D12_CPU_DESCRIPTOR_HANDLE DescriptorContext::GetCpuPlatformHandle(DescriptorHandle handle) const @@ -379,6 +471,16 @@ namespace AZ return GetPool(handle.m_type, handle.m_flags).GetGpuPlatformHandle(handle); } + D3D12_CPU_DESCRIPTOR_HANDLE DescriptorContext::GetCpuPlatformHandleForTable(DescriptorTable descTable) const + { + return GetPool(descTable.GetOffset().m_type, descTable.GetOffset().m_flags).GetCpuPlatformHandleForTable(descTable); + } + + D3D12_GPU_DESCRIPTOR_HANDLE DescriptorContext::GetGpuPlatformHandleForTable(DescriptorTable descTable) const + { + return GetPool(descTable.GetOffset().m_type, descTable.GetOffset().m_flags).GetGpuPlatformHandleForTable(descTable); + } + DescriptorHandle DescriptorContext::GetNullHandleSRV(D3D12_SRV_DIMENSION dimension) const { auto iter = m_nullDescriptorsSRV.find(dimension); @@ -432,14 +534,88 @@ namespace AZ { AZ_Assert(type < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES, "Trying to get pool with invalid type: [%d]", type); AZ_Assert(flag < NumHeapFlags, "Trying to get pool with invalid flag: [%d]", flag); - return m_pools[type][flag]; + + if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flag)) + { + return m_shaderVisibleCbvSrvUavPools[m_currentHeapIndex]; + } + else + { + return m_pools[type][flag]; + } } const DescriptorPool& DescriptorContext::GetPool(uint32_t type, uint32_t flag) const { AZ_Assert(type < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES, "Trying to get pool with invalid type: [%d]", type); AZ_Assert(flag < NumHeapFlags, "Trying to get pool with invalid flag: [%d]", flag); - return m_pools[type][flag]; + if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flag)) + { + return m_shaderVisibleCbvSrvUavPools[m_currentHeapIndex]; + } + else + { + return m_pools[type][flag]; + } + } + + RHI::ResultCode DescriptorContext::CompactDescriptorHeap() + { + //Check if heap compaction is enabled by the user. Since there is an overhead associated with heap compaction it is not enabled by default + if(!m_allowDescriptorHeapCompaction) + { + AZ_Assert( + false, + "Descriptor heap Compaction not allowed. Please consider increasing number of handles allowed for the second value" + "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV or enabling AllowDescriptorHeapCompaction within platformlimits.azasset file for dx12."); + return RHI::ResultCode::OutOfMemory; + } + + //We need to ping-pong between two heaps as we cannot compact the active heap without updating it and that is not allowed as + //we need to keep that gpu memory untouched until GPU is finished consuming which can take up to 3 frames. + m_compactionInProgress = true; + DescriptorPool& srcPool = GetPool(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); + + //Update the currently active heap index + m_currentHeapIndex = !m_currentHeapIndex; + DescriptorPool& destPool = GetPool(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); + + //Copy over all the static handles first + for (size_t i = 0; i < m_platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles; i++) + { + DescriptorHandle srcHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, static_cast(i)); + DescriptorHandle destHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, static_cast(i)); + m_device->CopyDescriptorsSimple(1, destPool.GetCpuPlatformHandle(destHandle), m_backupStaticHandles.GetCpuPlatformHandle(srcHandle), destHandle.m_type); + } + + //Clone the allocator of the source pool into the destination pool + srcPool.CloneAllocator(destPool.GetAllocator()); + + { + //The mutex is here 'just in case' Compaction is called from more than one thread. + AZStd::scoped_lock lock{ m_srgMapMutex }; + //Re-update all the descriptor tables associated with active SRGs + for (const auto& [srg, numAllocations] : m_srgAllocations) + { + RHI::ResultCode resultCode = static_cast(srg->GetPool())->UpdateDescriptorTableAfterCompaction(*srg, srg->GetData()); + if (resultCode != RHI::ResultCode::Success) + { + return resultCode; + } + } + } + + //Clear the allocator of the source pool + srcPool.ClearAllocator(); + + m_compactionInProgress = false; + + return RHI::ResultCode::Success; + } + + bool DescriptorContext::IsShaderVisibleCbvSrvUavHeap(uint32_t type, uint32_t flag) const + { + return type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV && flag == D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; } } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h index d4b6479fb2..88d2781329 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h @@ -15,6 +15,8 @@ #include #include #include +#include +#include namespace AZ { @@ -82,12 +84,14 @@ namespace AZ //! Creates a GPU-visible descriptor table. //! @param descriptorHeapType The descriptor heap to allocate from. //! @param descriptorCount The number of descriptors to allocate. + //! @param srg Shader resource group with which the descriptor table is associated with DescriptorTable CreateDescriptorTable( - D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, - uint32_t descriptorCount); - - void ReleaseDescriptorTable(DescriptorTable descriptorTable); + D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, uint32_t descriptorCount, ShaderResourceGroup* srg); + //! Releases a GPU-visible descriptor table. + //! @param descriptorHeapType The descriptor heap to allocate from. + //! @param srg Shader resource group with which the descriptor table is associated with + void ReleaseDescriptorTable(DescriptorTable descriptorTable, ShaderResourceGroup* srg); //! Performs a gather of disjoint CPU-side descriptors and copies to a contiguous GPU-side descriptor table. //! @param gpuDestinationTable The destination descriptor table that the descriptors will be uploaded to. @@ -110,6 +114,8 @@ namespace AZ D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandle(DescriptorHandle handle) const; D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandle(DescriptorHandle handle) const; + D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable descTable) const; + D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable descTable) const; void SetDescriptorHeaps(ID3D12GraphicsCommandList* commandList) const; @@ -117,6 +123,12 @@ namespace AZ ID3D12DeviceX* GetDevice(); + //! Since we are only allowed one shader visible CbvSrvUav heap of a limited size in certain hardware, it is possible that + //! it can get fragmented by constant alloc/de-alloc of descriptor tables related to direct views or unbounded resource views within a SRG. We use two + //! heaps to ping pong during compaction as fragmentation can occur many times. It copies static handles directly and for all the + //! dynamic handles we re-update the new heap by copying over the handles from the 'non-shader visible' heap. + RHI::ResultCode CompactDescriptorHeap(); + private: void CopyDescriptor(DescriptorHandle dst, DescriptorHandle src); @@ -129,10 +141,13 @@ namespace AZ DescriptorPool& GetPool(uint32_t type, uint32_t flag); const DescriptorPool& GetPool(uint32_t type, uint32_t flag) const; - DescriptorTable Allocate( - D3D12_DESCRIPTOR_HEAP_TYPE type, - D3D12_DESCRIPTOR_HEAP_FLAGS flags, - uint32_t count); + //! Allocates a Descriptor table which describes a contiguous range of descriptor handles + DescriptorTable AllocateTable(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count); + + //! Allocates a single descriptor handle + DescriptorHandle AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count); + + bool IsShaderVisibleCbvSrvUavHeap(uint32_t type, uint32_t flag) const; static const uint32_t NumHeapFlags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE + 1; static const uint32_t s_descriptorCountMax[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES][NumHeapFlags]; @@ -147,6 +162,25 @@ namespace AZ DescriptorHandle m_nullSamplerDescriptor; RHI::ConstPtr m_platformLimitsDescriptor; + + // Use 2 heaps below in order to ping-pong between shader visible CbvSrvUav heap when one of them fragments and run out of memory. + static const uint32_t MaxShaderVisibleCbvSrvUavHeaps = 2; + DescriptorPoolShaderVisibleCbvSrvUav m_shaderVisibleCbvSrvUavPools[MaxShaderVisibleCbvSrvUavHeaps]; + //This pool stores a copy of static handles that can later be used to recreate the compacted shader visible CbvSrvUav heap. + DescriptorPool m_backupStaticHandles; + + //Boolean to dictate when compaction was in progress + bool m_compactionInProgress = false; + + //Boolean to dictate if we should support compaction for shader visible CbvSrvUav heap + bool m_allowDescriptorHeapCompaction = false; + + //Map to store active SRGs and the number of associated descriptor tables. This is used to recreate the new compacted heap when we switch heaps + AZStd::unordered_map m_srgAllocations; + AZStd::mutex m_srgMapMutex; + + //Index that holds the currently active shader visible CbvSrvUav heap + uint32_t m_currentHeapIndex = 0; }; } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp index 7a6d96a519..96a3979f41 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp @@ -18,34 +18,38 @@ namespace AZ ID3D12DeviceX* device, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, - uint32_t descriptorCount) + uint32_t descriptorCountForHeap, + uint32_t descriptorCountForAllocator) { - m_Desc.Type = type; - m_Desc.Flags = flags; - m_Desc.NumDescriptors = descriptorCount; - m_Desc.NodeMask = 1; + m_desc.Type = type; + m_desc.Flags = flags; + m_desc.NumDescriptors = descriptorCountForHeap; + m_desc.NodeMask = 1; ID3D12DescriptorHeap* heap; - DX12::AssertSuccess(device->CreateDescriptorHeap(&m_Desc, IID_GRAPHICS_PPV_ARGS(&heap))); + DX12::AssertSuccess(device->CreateDescriptorHeap(&m_desc, IID_GRAPHICS_PPV_ARGS(&heap))); heap->SetName(L"DescriptorHeap"); - m_DescriptorHeap.Attach(heap); - m_Stride = device->GetDescriptorHandleIncrementSize(m_Desc.Type); + m_descriptorHeap.Attach(heap); + m_stride = device->GetDescriptorHandleIncrementSize(m_desc.Type); - m_CpuStart = heap->GetCPUDescriptorHandleForHeapStart(); - m_GpuStart = {}; - - if (RHI::CheckBitsAny(flags, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)) - { - m_GpuStart = heap->GetGPUDescriptorHandleForHeapStart(); - } + m_cpuStart = heap->GetCPUDescriptorHandleForHeapStart(); + m_gpuStart = {}; const bool isGpuVisible = RHI::CheckBitsAll(flags, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); + if (isGpuVisible) + { + m_gpuStart = heap->GetGPUDescriptorHandleForHeapStart(); + } + if (isGpuVisible) { RHI::FreeListAllocator::Descriptor descriptor; descriptor.m_alignmentInBytes = 1; - descriptor.m_capacityInBytes = descriptorCount; + + //It is possible for descriptorCountForAllocator to not match descriptorCountForHeap for DescriptorPoolShaderVisibleCbvSrvUav + //heaps in which case descriptorCountForAllocator defines the number of static handles + descriptor.m_capacityInBytes = aznumeric_cast(descriptorCountForAllocator); descriptor.m_garbageCollectLatency = RHI::Limits::Device::FrameCountMax; RHI::FreeListAllocator* allocator = aznew RHI::FreeListAllocator(); @@ -56,10 +60,11 @@ namespace AZ { // Non-shader-visible heaps don't require contiguous descriptors. Therefore, we can allocate // them using a block allocator. + RHI::PoolAllocator::Descriptor descriptor; descriptor.m_alignmentInBytes = 1; descriptor.m_elementSize = 1; - descriptor.m_capacityInBytes = descriptorCount; + descriptor.m_capacityInBytes = aznumeric_cast(descriptorCountForAllocator); descriptor.m_garbageCollectLatency = 0; RHI::PoolAllocator* allocator = aznew RHI::PoolAllocator(); @@ -68,7 +73,7 @@ namespace AZ } } - DescriptorTable DescriptorPool::Allocate(uint32_t count) + DescriptorHandle DescriptorPool::AllocateHandle(uint32_t count) { RHI::VirtualAddress address; { @@ -78,24 +83,34 @@ namespace AZ if (address.IsValid()) { - DescriptorHandle handle(m_Desc.Type, m_Desc.Flags, static_cast(address.m_ptr)); - return DescriptorTable(handle, static_cast(count)); + DescriptorHandle handle(m_desc.Type, m_desc.Flags, static_cast(address.m_ptr)); + return handle; } else { - return DescriptorTable{}; + return DescriptorHandle{}; } } - void DescriptorPool::Release(DescriptorTable table) + void DescriptorPool::ReleaseHandle(DescriptorHandle handle) { - if (table.IsNull()) + if (handle.IsNull()) { return; } AZStd::lock_guard lock(m_mutex); - m_allocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(table.GetOffset().m_index)); + m_allocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(handle.m_index)); + } + + DescriptorTable DescriptorPool::AllocateTable(uint32_t count) + { + return DescriptorTable(AllocateHandle(count), static_cast(count)); + } + + void DescriptorPool::ReleaseTable(DescriptorTable table) + { + ReleaseHandle(table.GetOffset()); } void DescriptorPool::GarbageCollect() @@ -106,20 +121,134 @@ namespace AZ ID3D12DescriptorHeap* DescriptorPool::GetPlatformHeap() const { - return m_DescriptorHeap.Get(); + return m_descriptorHeap.Get(); } D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPool::GetCpuPlatformHandle(DescriptorHandle handle) const { AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); - return D3D12_CPU_DESCRIPTOR_HANDLE{ m_CpuStart.ptr + handle.m_index * m_Stride }; + return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + handle.m_index * m_stride }; } D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPool::GetGpuPlatformHandle(DescriptorHandle handle) const { AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible"); AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); - return D3D12_GPU_DESCRIPTOR_HANDLE{ m_GpuStart.ptr + handle.m_index * m_Stride }; + return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (handle.m_index * m_stride) }; + } + + D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPool::GetCpuPlatformHandleForTable(DescriptorTable descTable) const + { + DescriptorHandle handle = descTable.GetOffset(); + AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); + return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + handle.m_index * m_stride }; + } + + D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPool::GetGpuPlatformHandleForTable(DescriptorTable descTable) const + { + DescriptorHandle handle = descTable.GetOffset(); + AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible"); + AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); + return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (handle.m_index * m_stride) }; + } + + void DescriptorPool::CloneAllocator(RHI::Allocator* newAllocator) + { + m_allocator->Clone(newAllocator); + } + + void DescriptorPool::ClearAllocator() + { + AZ_Assert(m_gpuStart.ptr, "Clearing the allocator is only supported for the gpu visible heap as only this heap can be compacted"); + static_cast(m_allocator.get()) + ->Init(static_cast(m_allocator.get())->GetDescriptor()); + } + + RHI::Allocator* DescriptorPool::GetAllocator() const + { + return m_allocator.get(); + } + + void DescriptorPoolShaderVisibleCbvSrvUav::Init( + ID3D12DeviceX* device, + D3D12_DESCRIPTOR_HEAP_TYPE type, + D3D12_DESCRIPTOR_HEAP_FLAGS flags, + uint32_t descriptorCount, + uint32_t staticHandlesCount) + { + //This pool manages two allocators. The allocator in the base class manages static handles + Base::Init(device, type, flags, descriptorCount, staticHandlesCount); + + //This allocator manages dynamic handles associated with descriptor tables. This allows us to + //reconstruct the full heap in a compact manner if it ever fragments. + RHI::FreeListAllocator::Descriptor descriptor; + descriptor.m_alignmentInBytes = 1; + descriptor.m_capacityInBytes = aznumeric_cast(descriptorCount - staticHandlesCount); + descriptor.m_garbageCollectLatency = RHI::Limits::Device::FrameCountMax; + + RHI::FreeListAllocator* allocator = aznew RHI::FreeListAllocator(); + allocator->Init(descriptor); + m_unboundedArrayAllocator.reset(allocator); + + //Cache the starting point of the dynamic section of the heap + m_startingHandleIndex = staticHandlesCount; + } + + DescriptorTable DescriptorPoolShaderVisibleCbvSrvUav::AllocateTable(uint32_t count) + { + RHI::VirtualAddress address; + { + AZStd::lock_guard lock(m_mutex); + address = m_unboundedArrayAllocator->Allocate(count, 1); + } + + if (address.IsValid()) + { + DescriptorHandle handle(m_desc.Type, m_desc.Flags, static_cast(address.m_ptr)); + return DescriptorTable(handle, static_cast(count)); + } + else + { + return DescriptorTable{}; + } + } + + void DescriptorPoolShaderVisibleCbvSrvUav::ReleaseTable(DescriptorTable table) + { + if (table.IsNull()) + { + return; + } + + AZStd::lock_guard lock(m_mutex); + m_unboundedArrayAllocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(table.GetOffset().m_index)); + } + + void DescriptorPoolShaderVisibleCbvSrvUav::GarbageCollect() + { + Base::GarbageCollect(); + m_unboundedArrayAllocator->GarbageCollect(); + } + + D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPoolShaderVisibleCbvSrvUav::GetCpuPlatformHandleForTable(DescriptorTable descTable) const + { + DescriptorHandle handle = descTable.GetOffset(); + AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); + return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + (m_startingHandleIndex * m_stride) + (handle.m_index * m_stride) }; + } + + D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPoolShaderVisibleCbvSrvUav::GetGpuPlatformHandleForTable(DescriptorTable descTable) const + { + DescriptorHandle handle = descTable.GetOffset(); + AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible"); + AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); + return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (m_startingHandleIndex * m_stride) + (handle.m_index * m_stride) }; + } + + void DescriptorPoolShaderVisibleCbvSrvUav::ClearAllocator() + { + Base::ClearAllocator(); + static_cast(m_unboundedArrayAllocator.get())->Init(static_cast(m_unboundedArrayAllocator.get())->GetDescriptor()); } } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h index cd90d1aba3..2237841ea7 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h @@ -18,37 +18,91 @@ namespace AZ { namespace DX12 { + //! This class defines a Descriptor pool which manages all the descriptors used for binding resources class DescriptorPool { public: DescriptorPool() = default; + virtual ~DescriptorPool() = default; - void Init( + //! Initialize the native heap as well as init the allocators tracking the memory for descriptor handles + virtual void Init( ID3D12DeviceX* device, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, - uint32_t descriptorCount); + uint32_t descriptorCountForHeap, + uint32_t descriptorCountForAllocator); ID3D12DescriptorHeap* GetPlatformHeap() const; - DescriptorTable Allocate(uint32_t count = 1); + //! Allocate a Descriptor handles + DescriptorHandle AllocateHandle(uint32_t count = 1); + //! Release a descriptor handle + void ReleaseHandle(DescriptorHandle table); + //! Allocate a range contiguous handles (i.e Descriptor table) + virtual DescriptorTable AllocateTable(uint32_t count = 1); + //! Release a range contiguous handles (i.e Descriptor table) + virtual void ReleaseTable(DescriptorTable table); + //! Garbage collection for freed handles or tables + virtual void GarbageCollect(); + //Get native pointers from the heap + virtual D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable handle) const; + virtual D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable handle) const; + //Clear the tracking allocator + virtual void ClearAllocator(); - void Release(DescriptorTable table); - - void GarbageCollect(); D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandle(DescriptorHandle handle) const; D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandle(DescriptorHandle handle) const; - private: - D3D12_CPU_DESCRIPTOR_HANDLE m_CpuStart = {}; - D3D12_GPU_DESCRIPTOR_HANDLE m_GpuStart = {}; - D3D12_CPU_DESCRIPTOR_HANDLE m_NullDescriptor = {}; - uint32_t m_Stride = 0; - D3D12_DESCRIPTOR_HEAP_DESC m_Desc; - Microsoft::WRL::ComPtr m_DescriptorHeap; + //Clone the tracking allocator + void CloneAllocator(RHI::Allocator* newAllocator); + RHI::Allocator* GetAllocator() const; + + protected: + D3D12_DESCRIPTOR_HEAP_DESC m_desc; AZStd::mutex m_mutex; + D3D12_CPU_DESCRIPTOR_HANDLE m_cpuStart = {}; + D3D12_GPU_DESCRIPTOR_HANDLE m_gpuStart = {}; + uint32_t m_stride = 0; + private: + + // Native heap + Microsoft::WRL::ComPtr m_descriptorHeap; + + // Allocator used to manage the whole native heap. In the case of DescriptorPoolShaderVisibleCbvSrvUav this allocator + // is used to manage the part of the heap that only manages static handles. AZStd::unique_ptr m_allocator; }; + + //! A specialized pool that specifically handles Descriptor tables for Cbv/Srv/Uav views and allows for Compaction + //! Specifically this pool handles the dynamic part of the heap + class DescriptorPoolShaderVisibleCbvSrvUav : public DescriptorPool + { + using Base = DescriptorPool; + + public: + void Init( + ID3D12DeviceX* device, + D3D12_DESCRIPTOR_HEAP_TYPE type, + D3D12_DESCRIPTOR_HEAP_FLAGS flags, + uint32_t descriptorCount, + uint32_t staticHandlesCount); + + DescriptorTable AllocateTable(uint32_t count = 1) override; + void ReleaseTable(DescriptorTable table) override; + void GarbageCollect() override; + + D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable handle) const override; + D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable handle) const override; + void ClearAllocator() override; + + private: + + // A separate allocator that handles descriptor tables which are dynamic in nature and may fragment and require compaction + AZStd::unique_ptr m_unboundedArrayAllocator; + //Starting index of the dynamic part of the heap + uint32_t m_startingHandleIndex = 0; + }; } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp index 82e4aba57c..6f614499d2 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp @@ -625,5 +625,20 @@ namespace AZ { return m_isAftermathInitialized; } + + RHI::ResultCode Device::CompactSRGMemory() + { + if (m_isDescriptorHeapCompactionNeeded) + { + m_isDescriptorHeapCompactionNeeded = false; + return m_descriptorContext->CompactDescriptorHeap(); + } + return RHI::ResultCode::Success; + } + + void Device::DescriptorHeapCompactionNeeded() + { + m_isDescriptorHeapCompactionNeeded = true; + } } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h index 7004900ec5..40e26db891 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h @@ -98,39 +98,27 @@ namespace AZ D3D12_RESOURCE_STATES initialState, ImageTileLayout& imageTilingInfo); - /** - * Queues a DX12 COM object for release (by taking a reference) after the current frame has flushed - * through the GPU. - */ + //! Queues a DX12 COM object for release (by taking a reference) after the current frame has flushed + //! through the GPU. void QueueForRelease(RHI::Ptr dx12Object); - /** - * Queues the backing Memory instance of a MemoryView for release (by taking a reference) after the - * current frame has flushed through the GPU. The reference on the MemoryView itself is not released. - */ + //! Queues the backing Memory instance of a MemoryView for release (by taking a reference) after the + //! current frame has flushed through the GPU. The reference on the MemoryView itself is not released. void QueueForRelease(const MemoryView& memoryView); - /** - * Allocates host memory from the internal frame allocator that is suitable for staging - * uploads to the GPU for the current frame. The memory is valid for the lifetime of - * the frame and is automatically reclaimed after the frame has completed on the GPU. - */ + //! Allocates host memory from the internal frame allocator that is suitable for staging + //! uploads to the GPU for the current frame. The memory is valid for the lifetime of + //! the frame and is automatically reclaimed after the frame has completed on the GPU. MemoryView AcquireStagingMemory(size_t size, size_t alignment); - /** - * Acquires a pipeline layout from the internal cache. - */ + //! Acquires a pipeline layout from the internal cache. RHI::ConstPtr AcquirePipelineLayout(const RHI::PipelineLayoutDescriptor& descriptor); - /** - * Acquires a new command list for the frame given the hardware queue class. The command list is - * automatically reclaimed after the current frame has flushed through the GPU. - */ + //! Acquires a new command list for the frame given the hardware queue class. The command list is + //! automatically reclaimed after the current frame has flushed through the GPU. CommandList* AcquireCommandList(RHI::HardwareQueueClass hardwareQueueClass); - /** - * Acquires a sampler from the internal cache. - */ + //! Acquires a sampler from the internal cache. RHI::ConstPtr AcquireSampler(const RHI::SamplerState& state); const PhysicalDevice& GetPhysicalDevice() const; @@ -146,6 +134,10 @@ namespace AZ AsyncUploadQueue& GetAsyncUploadQueue(); bool IsAftermathInitialized() const; + + //! Indicate that we need to compact the shader visible srv/uav/cbv shader visible heap. + void DescriptorHeapCompactionNeeded(); + private: Device(); @@ -167,6 +159,7 @@ namespace AZ RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::ImageDescriptor & descriptor) override; RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::BufferDescriptor & descriptor) override; void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) override; + RHI::ResultCode CompactSRGMemory() override; ////////////////////////////////////////////////////////////////////////// RHI::ResultCode InitSubPlatform(RHI::PhysicalDevice& physicalDevice); @@ -198,6 +191,9 @@ namespace AZ AZStd::mutex m_samplerCacheMutex; bool m_isAftermathInitialized = false; + + // Boolean used to compact the view specific shader visible heap + bool m_isDescriptorHeapCompactionNeeded = false; }; } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp index 7eaafc3705..4ff7d581a4 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -204,7 +203,7 @@ namespace AZ RHI::MessageOutcome FrameGraphCompiler::CompileInternal(const RHI::FrameGraphCompileRequest& request) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileInternal(DX12)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileInternal(DX12)"); RHI::FrameGraph& frameGraph = *request.m_frameGraph; @@ -373,7 +372,7 @@ namespace AZ void FrameGraphCompiler::CompileResourceBarriers(Scope* rootScope, const RHI::FrameGraphAttachmentDatabase& attachmentDatabase) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileResourceBarriers(DX12)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileResourceBarriers(DX12)"); for (RHI::BufferFrameAttachment* bufferFrameAttachment : attachmentDatabase.GetBufferAttachments()) { @@ -394,7 +393,7 @@ namespace AZ ResourceTransitionLoggerNull logger(bufferFrameAttachment.GetId()); #endif - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileBufferBarriers(DX12)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileBufferBarriers(DX12)"); Buffer& buffer = static_cast(*bufferFrameAttachment.GetBuffer()); RHI::BufferScopeAttachment* scopeAttachment = bufferFrameAttachment.GetFirstScopeAttachment(); @@ -469,7 +468,7 @@ namespace AZ ResourceTransitionLoggerNull logger(imageFrameAttachment.GetId()); #endif - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileImageBarriers (DX12)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileImageBarriers (DX12)"); Image& image = static_cast(*imageFrameAttachment.GetImage()); RHI::ImageScopeAttachment* scopeAttachment = imageFrameAttachment.GetFirstScopeAttachment(); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp index 66b3f9623a..63625fde72 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp @@ -49,9 +49,11 @@ namespace AZ AZStd::array_view bytes; bool shouldCreateLibFromSerializedData = true; - if (RHI::Factory::Get().IsRenderDocModuleLoaded() || RHI::Factory::Get().IsPixModuleLoaded()) + if (RHI::Factory::Get().IsRenderDocModuleLoaded() || + RHI::Factory::Get().IsPixModuleLoaded() || + RHI::Factory::Get().UsingWarpDevice()) { - // CreatePipelineLibrary api does not function properly if Renderdoc or Pix is enabled + // CreatePipelineLibrary api does not function properly if Renderdoc, Pix or Warp is enabled shouldCreateLibFromSerializedData = false; } @@ -215,9 +217,11 @@ namespace AZ RHI::ResultCode PipelineLibrary::MergeIntoInternal([[maybe_unused]] AZStd::array_view pipelineLibraries) { - if (RHI::Factory::Get().IsRenderDocModuleLoaded() || RHI::Factory::Get().IsPixModuleLoaded()) + if (RHI::Factory::Get().IsRenderDocModuleLoaded() || + RHI::Factory::Get().IsPixModuleLoaded() || + RHI::Factory::Get().UsingWarpDevice()) { - // StorePipeline api does not function properly if RenderDoc or Pix is enabled + // StorePipeline api does not function properly if RenderDoc, Pix or Warp is enabled return RHI::ResultCode::Fail; } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h index 4c686fe525..0a7185ccd5 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h @@ -51,6 +51,7 @@ namespace AZ ShaderResourceGroup() = default; friend class ShaderResourceGroupPool; + friend class DescriptorContext; /// The current index into the compiled data array. uint32_t m_compiledDataIndex = 0; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp index d51fdd3495..bd648cf535 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp @@ -132,33 +132,17 @@ namespace AZ compiledData.m_cpuConstantAddress = cpuAddress + m_constantBufferSize * i; } } - - if (m_viewsDescriptorTableSize) - { - group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize); - - if (!group.m_viewsDescriptorTable.IsValid()) - { - AZ_Error("ShaderResourceGroupPool", false, "Descriptor context failed to allocate view descriptor table. Try increasing the limits specified in platformlimits.azasset file for dx12"); - return RHI::ResultCode::OutOfMemory; - } - - for (uint32_t i = 0; i < copyCount; ++i) - { - const DescriptorHandle descriptorHandle = group.m_viewsDescriptorTable.GetOffset() + m_viewsDescriptorTableSize * i; - - ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i]; - compiledData.m_gpuViewsDescriptorHandle = m_descriptorContext->GetGpuPlatformHandle(descriptorHandle); - } - } - + if (m_samplersDescriptorTableSize) { - group.m_samplersDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, m_samplersDescriptorTableRingSize); + group.m_samplersDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, m_samplersDescriptorTableRingSize, &group); if (!group.m_samplersDescriptorTable.IsValid()) { - AZ_Error("ShaderResourceGroupPool", false, "Descriptor context failed to allocate sampler descriptor table. Try increasing the limits specified in platformlimits.azasset file for dx12."); + AZ_Error( + "ShaderResourceGroupPool", false, + "Descriptor context failed to allocate sampler descriptor table. Please consider increasing number of handles " + "allowed for the second value of DESCRIPTOR_HEAP_TYPE_SAMPLER within platformlimits.azasset file for dx12."); return RHI::ResultCode::OutOfMemory; } @@ -167,7 +151,7 @@ namespace AZ const DescriptorHandle descriptorHandle = group.m_samplersDescriptorTable.GetOffset() + m_samplersDescriptorTableSize * i; ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i]; - compiledData.m_gpuSamplersDescriptorHandle = m_descriptorContext->GetGpuPlatformHandle(descriptorHandle); + compiledData.m_gpuSamplersDescriptorHandle = m_descriptorContext->GetGpuPlatformHandleForTable(DescriptorTable(descriptorHandle, static_cast(m_samplersDescriptorTableSize))); } } @@ -186,19 +170,25 @@ namespace AZ if (m_viewsDescriptorTableSize) { - m_descriptorContext->ReleaseDescriptorTable(group.m_viewsDescriptorTable); + if (group.m_viewsDescriptorTable.IsValid()) + { + m_descriptorContext->ReleaseDescriptorTable(group.m_viewsDescriptorTable, &group); + } } if (m_samplersDescriptorTableSize) { - m_descriptorContext->ReleaseDescriptorTable(group.m_samplersDescriptorTable); + if (group.m_viewsDescriptorTable.IsValid()) + { + m_descriptorContext->ReleaseDescriptorTable(group.m_samplersDescriptorTable, &group); + } } for (uint32_t unboundedArrayindex = 0; unboundedArrayindex < (ShaderResourceGroupCompiledData::MaxUnboundedArrays * RHI::Limits::Device::FrameCountMax); ++unboundedArrayindex) { if (group.m_unboundedDescriptorTables[unboundedArrayindex].IsValid()) { - m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[unboundedArrayindex]); + m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[unboundedArrayindex], &group); } } @@ -213,6 +203,7 @@ namespace AZ const RHI::ShaderResourceGroupData& groupData) { ShaderResourceGroup& group = static_cast(groupBase); + auto& device = static_cast(GetDevice()); group.m_compiledDataIndex = (group.m_compiledDataIndex + 1) % RHI::Limits::Device::FrameCountMax; if (m_constantBufferSize) @@ -222,6 +213,22 @@ namespace AZ if (m_viewsDescriptorTableSize) { + //Lazy initialization for cbv/srv/uav Descriptor Tables + if (!group.m_viewsDescriptorTable.IsValid()) + { + group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize, &group); + + if (!group.m_viewsDescriptorTable.IsValid()) + { + //We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that. + device.DescriptorHeapCompactionNeeded(); + return RHI::ResultCode::Success; + } + + CacheGpuHandlesForViews(group); + } + const DescriptorTable descriptorTable( group.m_viewsDescriptorTable.GetOffset() + group.m_compiledDataIndex * m_viewsDescriptorTableSize, static_cast(m_viewsDescriptorTableSize)); @@ -246,6 +253,18 @@ namespace AZ return RHI::ResultCode::Success; } + void ShaderResourceGroupPool::CacheGpuHandlesForViews(ShaderResourceGroup& group) + { + for (uint32_t i = 0; i < RHI::Limits::Device::FrameCountMax; ++i) + { + const DescriptorHandle descriptorHandle = group.m_viewsDescriptorTable.GetOffset() + m_viewsDescriptorTableSize * i; + + ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i]; + compiledData.m_gpuViewsDescriptorHandle = m_descriptorContext->GetGpuPlatformHandleForTable( + DescriptorTable(descriptorHandle, static_cast(m_viewsDescriptorTableSize))); + } + } + void ShaderResourceGroupPool::UpdateViewsDescriptorTable(DescriptorTable descriptorTable, const RHI::ShaderResourceGroupData& groupData) { const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout(); @@ -261,27 +280,27 @@ namespace AZ AZStd::vector descriptorHandles; switch (descriptorRangeType) { - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - { - descriptorHandles = GetSRVsFromImageViews< RHI::BufferView, BufferView> (bufferViews, D3D12_SRV_DIMENSION_BUFFER); - break; + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + { + descriptorHandles = GetSRVsFromImageViews< RHI::BufferView, BufferView> (bufferViews, D3D12_SRV_DIMENSION_BUFFER); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + { + descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: + { + descriptorHandles = GetCBVsFromBufferViews(bufferViews); + break; + } + default: + AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); + break; } - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - { - descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER); - break; - } - case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: - { - descriptorHandles = GetCBVsFromBufferViews(bufferViews); - break; - } - default: - AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); - break; - } - UpdateDescriptorTableRange(descriptorTable, descriptorHandles, bufferInputIndex); + UpdateDescriptorTableRange(descriptorTable, descriptorHandles, bufferInputIndex); ++shaderInputIndex; } @@ -297,23 +316,24 @@ namespace AZ AZStd::vector descriptorHandles; switch (descriptorRangeType) { - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - { - descriptorHandles = GetSRVsFromImageViews(imageViews, ConvertSRVDimension(shaderInputImage.m_type)); - break; - } - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - { - descriptorHandles = GetUAVsFromImageViews(imageViews, ConvertUAVDimension(shaderInputImage.m_type)); - break; - } - default: + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + { + descriptorHandles = + GetSRVsFromImageViews(imageViews, ConvertSRVDimension(shaderInputImage.m_type)); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + { + descriptorHandles = + GetUAVsFromImageViews(imageViews, ConvertUAVDimension(shaderInputImage.m_type)); + break; + } + default: AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); break; } UpdateDescriptorTableRange(descriptorTable, descriptorHandles, imageInputIndex); - ++shaderInputIndex; } } @@ -334,7 +354,7 @@ namespace AZ void ShaderResourceGroupPool::UpdateUnboundedArrayDescriptorTables(ShaderResourceGroup& group, const RHI::ShaderResourceGroupData& groupData) { const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout(); - + auto& device = static_cast(GetDevice()); uint32_t shaderInputIndex = 0; // process buffer unbounded arrays @@ -350,50 +370,30 @@ namespace AZ { if (group.m_unboundedDescriptorTables[tableIndex].IsValid()) { - m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex]); + m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex], &group); group.m_unboundedDescriptorTables[tableIndex] = DescriptorTable{}; } if (!bufferViews.empty()) { - group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(bufferViews.size())); - AZ_Assert(group.m_unboundedDescriptorTables[tableIndex].IsValid(), "Descriptor context failed to allocate unbounded array descriptor table, most likely out of memory."); + group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(bufferViews.size()), &group); + + if (!group.m_unboundedDescriptorTables[tableIndex].IsValid()) + { + // We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that. + device.DescriptorHeapCompactionNeeded(); + return; + } ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex]; - compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandle(group.m_unboundedDescriptorTables[tableIndex].GetOffset()); + compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]); } } - - ++shaderInputIndex; - - if (bufferViews.empty()) - { - // we don't need to update descriptors since the buffer list is empty - continue; - } - - D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputBufferAccess(shaderInputBufferUnboundedArray.m_access); - - AZStd::vector descriptorHandles; - switch (descriptorRangeType) - { - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - { - descriptorHandles = GetSRVsFromImageViews(bufferViews, D3D12_SRV_DIMENSION_BUFFER); - break; - } - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - { - descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER); - break; - } - default: - AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); - break; - } - + const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(bufferViews.size())); - m_descriptorContext->UpdateDescriptorTableRange(descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + UpdateUnboundedBuffersDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputBufferUnboundedArray.m_access); + ++shaderInputIndex; } // process image unbounded arrays @@ -407,55 +407,223 @@ namespace AZ // resize the descriptor table allocation if necessary if (group.m_unboundedDescriptorTables[tableIndex].GetSize() != imageViews.size()) { + if (group.m_unboundedDescriptorTables[tableIndex].IsValid()) { - m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex]); + m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex], &group); group.m_unboundedDescriptorTables[tableIndex] = DescriptorTable{}; } if (!imageViews.empty()) { - group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(imageViews.size())); - AZ_Assert(group.m_unboundedDescriptorTables[tableIndex].IsValid(), "Descriptor context failed to allocate unbounded array descriptor table, most likely out of memory."); + group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(imageViews.size()), &group); + + if (!group.m_unboundedDescriptorTables[tableIndex].IsValid()) + { + // We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that + device.DescriptorHeapCompactionNeeded(); + return; + } ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex]; - compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandle(group.m_unboundedDescriptorTables[tableIndex].GetOffset()); + compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]); } } - ++shaderInputIndex; - - if (imageViews.empty()) - { - // we don't need to update descriptors since the image list is empty - continue; - } - - D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputImageAccess(shaderInputImageUnboundedArray.m_access); - - AZStd::vector descriptorHandles; - switch (descriptorRangeType) - { - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - { - descriptorHandles = GetSRVsFromImageViews(imageViews, ConvertSRVDimension(shaderInputImageUnboundedArray.m_type)); - break; - } - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - { - descriptorHandles = GetUAVsFromImageViews(imageViews, ConvertUAVDimension(shaderInputImageUnboundedArray.m_type)); - break; - } - default: - AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); - break; - } - const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(imageViews.size())); - m_descriptorContext->UpdateDescriptorTableRange(descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + UpdateUnboundedImagesDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputImageUnboundedArray.m_access, shaderInputImageUnboundedArray.m_type); + ++shaderInputIndex; } } + void ShaderResourceGroupPool::UpdateUnboundedBuffersDescTable( + DescriptorTable descriptorTable, + const RHI::ShaderResourceGroupData& groupData, + uint32_t shaderInputIndex, + RHI::ShaderInputBufferAccess bufferAccess) + { + const RHI::ShaderInputBufferUnboundedArrayIndex bufferUnboundedArrayInputIndex(shaderInputIndex); + AZStd::array_view> bufferViews = + groupData.GetBufferViewUnboundedArray(bufferUnboundedArrayInputIndex); + + if (bufferViews.empty()) + { + // we don't need to update descriptors since the buffer list is empty + return; + } + + D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputBufferAccess(bufferAccess); + + AZStd::vector descriptorHandles; + switch (descriptorRangeType) + { + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + { + descriptorHandles = GetSRVsFromImageViews(bufferViews, D3D12_SRV_DIMENSION_BUFFER); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + { + descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER); + break; + } + default: + AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); + break; + } + + m_descriptorContext->UpdateDescriptorTableRange( + descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + } + + void ShaderResourceGroupPool::UpdateUnboundedImagesDescTable( + DescriptorTable descriptorTable, + const RHI::ShaderResourceGroupData& groupData, + uint32_t shaderInputIndex, + RHI::ShaderInputImageAccess imageAccess, + RHI::ShaderInputImageType imageType) + { + const RHI::ShaderInputImageUnboundedArrayIndex imageUnboundedArrayInputIndex(shaderInputIndex); + AZStd::array_view> imageViews = + groupData.GetImageViewUnboundedArray(imageUnboundedArrayInputIndex); + + if (imageViews.empty()) + { + // we don't need to update descriptors since the image list is empty + return; + } + + D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputImageAccess(imageAccess); + + AZStd::vector descriptorHandles; + switch (descriptorRangeType) + { + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + { + descriptorHandles = GetSRVsFromImageViews(imageViews, ConvertSRVDimension(imageType)); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + { + descriptorHandles = GetUAVsFromImageViews(imageViews, ConvertUAVDimension(imageType)); + break; + } + default: + AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); + break; + } + + m_descriptorContext->UpdateDescriptorTableRange( + descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + } + + RHI::ResultCode ShaderResourceGroupPool::UpdateDescriptorTableAfterCompaction( + RHI::ShaderResourceGroup& groupBase, const RHI::ShaderResourceGroupData& groupData) + { + // Since we are trying to compact we will re-create all the descriptor tables and re-update them all + ShaderResourceGroup& group = static_cast(groupBase); + + if (m_viewsDescriptorTableSize) + { + group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize, &group); + + if (!group.m_viewsDescriptorTable.IsValid()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value" + "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12."); + return RHI::ResultCode::OutOfMemory; + } + + CacheGpuHandlesForViews(group); + + const DescriptorTable descriptorTable( + group.m_viewsDescriptorTable.GetOffset() + group.m_compiledDataIndex * m_viewsDescriptorTableSize, + static_cast(m_viewsDescriptorTableSize)); + + UpdateViewsDescriptorTable(descriptorTable, groupData); + } + + if (m_unboundedArrayCount) + { + //Reset all the old descriptor tables as the previous heap is gone. + for (uint32_t unboundedArrayindex = 0; unboundedArrayindex < (ShaderResourceGroupCompiledData::MaxUnboundedArrays * RHI::Limits::Device::FrameCountMax); ++unboundedArrayindex) + { + group.m_unboundedDescriptorTables[unboundedArrayindex] = DescriptorTable{}; + } + + const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout(); + uint32_t shaderInputIndex = 0; + + // process buffer unbounded arrays + for (const RHI::ShaderInputBufferUnboundedArrayDescriptor& shaderInputBufferUnboundedArray : groupLayout.GetShaderInputListForBufferUnboundedArrays()) + { + const RHI::ShaderInputBufferUnboundedArrayIndex bufferUnboundedArrayInputIndex(shaderInputIndex); + AZStd::array_view> bufferViews = groupData.GetBufferViewUnboundedArray(bufferUnboundedArrayInputIndex); + + uint32_t tableIndex = shaderInputIndex * RHI::Limits::Device::FrameCountMax + group.m_compiledDataIndex; + if (!bufferViews.empty()) + { + group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(bufferViews.size()), &group); + + if (!group.m_unboundedDescriptorTables[tableIndex].IsValid()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value" + "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12."); + return RHI::ResultCode::OutOfMemory; + } + + ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex]; + compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]); + + const DescriptorTable descriptorTable( + group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(bufferViews.size())); + UpdateUnboundedBuffersDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputBufferUnboundedArray.m_access); + } + shaderInputIndex++; + } + + // process image unbounded arrays + for (const RHI::ShaderInputImageUnboundedArrayDescriptor& shaderInputImageUnboundedArray : + groupLayout.GetShaderInputListForImageUnboundedArrays()) + { + const RHI::ShaderInputImageUnboundedArrayIndex imageUnboundedArrayInputIndex(shaderInputIndex); + AZStd::array_view> imageViews = + groupData.GetImageViewUnboundedArray(imageUnboundedArrayInputIndex); + + uint32_t tableIndex = shaderInputIndex * RHI::Limits::Device::FrameCountMax + group.m_compiledDataIndex; + if (!imageViews.empty()) + { + group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(imageViews.size()), &group); + + if (!group.m_unboundedDescriptorTables[tableIndex].IsValid()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value" + "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12."); + return RHI::ResultCode::OutOfMemory; + } + + ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex]; + compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]); + + const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(imageViews.size())); + UpdateUnboundedImagesDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputImageUnboundedArray.m_access, shaderInputImageUnboundedArray.m_type); + } + shaderInputIndex++; + } + } + return RHI::ResultCode::Success; + } + void ShaderResourceGroupPool::OnFrameEnd() { m_constantAllocator.GarbageCollect(); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h index bbe7e6596d..e1b2145097 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h @@ -30,6 +30,9 @@ namespace AZ static RHI::Ptr Create(); + //! Re-Update the descriptor tables for all the cbv/srv/uav views (direct and via unbounded array) + RHI::ResultCode UpdateDescriptorTableAfterCompaction(RHI::ShaderResourceGroup& groupBase, const RHI::ShaderResourceGroupData& groupData); + private: ShaderResourceGroupPool() = default; @@ -51,6 +54,21 @@ namespace AZ void UpdateSamplersDescriptorTable(DescriptorTable descriptorTable, const RHI::ShaderResourceGroupData& groupData); void UpdateUnboundedArrayDescriptorTables(ShaderResourceGroup& group, const RHI::ShaderResourceGroupData& groupData); + //! Update all the buffer views for the unbounded array + void UpdateUnboundedBuffersDescTable( + DescriptorTable descriptorTable, + const RHI::ShaderResourceGroupData& groupData, + uint32_t shaderInputIndex, + RHI::ShaderInputBufferAccess bufferAccess); + + //! Update all the image views for the unbounded array + void UpdateUnboundedImagesDescTable( + DescriptorTable descriptorTable, + const RHI::ShaderResourceGroupData& groupData, + uint32_t shaderInputIndex, + RHI::ShaderInputImageAccess imageAccess, + RHI::ShaderInputImageType imageType); + void UpdateDescriptorTableRange( DescriptorTable descriptorTable, const AZStd::vector& descriptors, @@ -66,6 +84,9 @@ namespace AZ RHI::ShaderInputSamplerIndex samplerIndex, AZStd::array_view samplerStates); + //Cache all the gpu handles for the Descriptor tables related to all the views + void CacheGpuHandlesForViews(ShaderResourceGroup& group); + DescriptorTable GetBufferTable(DescriptorTable descriptorTable, RHI::ShaderInputBufferIndex bufferIndex) const; DescriptorTable GetBufferTableUnbounded(DescriptorTable descriptorTable, RHI::ShaderInputBufferIndex bufferIndex) const; DescriptorTable GetImageTable(DescriptorTable descriptorTable, RHI::ShaderInputImageIndex imageIndex) const; @@ -79,7 +100,6 @@ namespace AZ AZStd::vector GetCBVsFromBufferViews(const AZStd::array_view>& bufferViews); - AZStd::mutex m_constantAllocatorMutex; MemoryPoolSubAllocator m_constantAllocator; DescriptorContext* m_descriptorContext = nullptr; uint32_t m_constantBufferSize = 0; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/StagingMemoryAllocator.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/StagingMemoryAllocator.cpp index 1a32f06fa2..337b25a793 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/StagingMemoryAllocator.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/StagingMemoryAllocator.cpp @@ -55,7 +55,7 @@ namespace AZ void StagingMemoryAllocator::GarbageCollect() { - AZ_ATOM_PROFILE_FUNCTION("DX12", "StagingMemoryAllocator: GarbageCollect"); + AZ_PROFILE_SCOPE(RHI, "StagingMemoryAllocator: GarbageCollect(DX12)"); m_mediumBlockAllocators.ForEach([](MemoryLinearSubAllocator& subAllocator) { subAllocator.GarbageCollect(); diff --git a/Gems/Atom/RHI/DX12/gem.json b/Gems/Atom/RHI/DX12/gem.json index df0ae12cc4..eb876a1b9f 100644 --- a/Gems/Atom/RHI/DX12/gem.json +++ b/Gems/Atom/RHI/DX12/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp index 7f0c535383..e4c651c2b1 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp @@ -7,7 +7,6 @@ */ #include -#include #include #include #include @@ -79,8 +78,8 @@ namespace AZ void CommandQueueContext::End() { - AZ_PROFILE_FUNCTION(RHI); - + AZ_PROFILE_SCOPE(RHI, "CommandQueueContext: End"); + QueueGpuSignals(m_frameFences[m_currentFrameIndex]); for (uint32_t hardwareQueueIdx = 0; hardwareQueueIdx < RHI::HardwareQueueClassCount; ++hardwareQueueIdx) { @@ -92,7 +91,6 @@ namespace AZ { AZ_PROFILE_SCOPE(RHI, "Wait and Reset Fence"); - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RHI", "CommandQueueContext: Wait on Fences"); //Synchronize the CPU with the GPU by waiting on the fence until signalled by the GPU. CPU can only go upto //RHI::Limits::Device::FrameCountMax frames ahead of the GPU diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/FrameGraphCompiler.cpp index 520e417f6b..9ee000e166 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/FrameGraphCompiler.cpp @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include + #include #include #include @@ -33,7 +33,7 @@ namespace AZ RHI::MessageOutcome FrameGraphCompiler::CompileInternal(const RHI::FrameGraphCompileRequest& request) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileInternal(Metal)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileInternal(Metal)"); RHI::FrameGraph& frameGraph = *request.m_frameGraph; if (!RHI::CheckBitsAny(request.m_compileFlags, RHI::FrameSchedulerCompileFlags::DisableAsyncQueues)) { diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Scope.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/Scope.cpp index 7e4d510dfa..c680684efd 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Scope.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Scope.cpp @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include + #include #include #include diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp index 90ba04c3db..d05e1a00a3 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp @@ -6,10 +6,10 @@ * */ +#include #include #include #include -#include #include #include #include @@ -192,7 +192,7 @@ namespace AZ id SwapChain::RequestDrawable(bool isFrameCaptureEnabled) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "SwapChain::RequestDrawable"); + AZ_PROFILE_SCOPE(RHI, "SwapChain::RequestDrawable"); m_metalView.metalLayer.framebufferOnly = !isFrameCaptureEnabled; const uint32_t currentImageIndex = GetCurrentImageIndex(); if(m_drawables[currentImageIndex]) diff --git a/Gems/Atom/RHI/Metal/gem.json b/Gems/Atom/RHI/Metal/gem.json index 497e0149b6..8da6bfabec 100644 --- a/Gems/Atom/RHI/Metal/gem.json +++ b/Gems/Atom/RHI/Metal/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/Null/Code/Source/RHI.Builders/ShaderPlatformInterface.h b/Gems/Atom/RHI/Null/Code/Source/RHI.Builders/ShaderPlatformInterface.h index 0a03211ca6..a9176ac750 100644 --- a/Gems/Atom/RHI/Null/Code/Source/RHI.Builders/ShaderPlatformInterface.h +++ b/Gems/Atom/RHI/Null/Code/Source/RHI.Builders/ShaderPlatformInterface.h @@ -34,7 +34,7 @@ namespace AZ const AssetBuilderSDK::PlatformInfo& platform, const AZStd::string& shaderSourcePath, const AZStd::string& functionName, RHI::ShaderHardwareStage shaderStage, const AZStd::string& tempFolderPath, StageDescriptor& outputDescriptor, const RHI::ShaderCompilerArguments& shaderCompilerArguments) const override; - AZStd::string GetAzslCompilerWarningParameters(const RHI::ShaderCompilerArguments& shaderCompilerArguments) const; + AZStd::string GetAzslCompilerWarningParameters(const RHI::ShaderCompilerArguments& shaderCompilerArguments) const override; bool BuildHasDebugInfo(const RHI::ShaderCompilerArguments& shaderCompilerArguments) const override; const char* GetAzslHeader(const AssetBuilderSDK::PlatformInfo& platform) const override; bool BuildPipelineLayoutDescriptor( diff --git a/Gems/Atom/RHI/Null/gem.json b/Gems/Atom/RHI/Null/gem.json index aa2e08501a..0870efaae7 100644 --- a/Gems/Atom/RHI/Null/gem.json +++ b/Gems/Atom/RHI/Null/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/Registry/Platform/Windows/PlatformLimits.setreg b/Gems/Atom/RHI/Registry/Platform/Windows/PlatformLimits.setreg index 240f8b041c..22b7d1bf29 100644 --- a/Gems/Atom/RHI/Registry/Platform/Windows/PlatformLimits.setreg +++ b/Gems/Atom/RHI/Registry/Platform/Windows/PlatformLimits.setreg @@ -21,11 +21,13 @@ "$type": "AZ::DX12::PlatformLimitsDescriptor", "DescriptorHeapLimits": { - "DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [1000000, 1000000], + "DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [100000, 1000000], "DESCRIPTOR_HEAP_TYPE_SAMPLER": [2048, 2048], "DESCRIPTOR_HEAP_TYPE_RTV": [2048, 0], "DESCRIPTOR_HEAP_TYPE_DSV": [2048, 0] - } + }, + "NumShaderVisibleCbvSrvUavStaticHandles": 2000, + "AllowDescriptorHeapCompaction": false }, "vulkan": { diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp index a818599b0d..7d70d0245f 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp @@ -10,6 +10,10 @@ #include #include +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB +#include +#endif + namespace AZ { namespace Vulkan @@ -21,7 +25,7 @@ namespace AZ #if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB xcb_connection_t* xcb_connection = nullptr; - if (auto xcbConnectionManager = AzFramework::LinuxXcbConnectionManagerInterface::Get(); + if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get(); xcbConnectionManager != nullptr) { xcb_connection = xcbConnectionManager->GetXcbConnection(); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/AsyncUploadQueue.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/AsyncUploadQueue.cpp index 7a8499ab9d..2c3958cb65 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/AsyncUploadQueue.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/AsyncUploadQueue.cpp @@ -451,7 +451,7 @@ namespace AZ AsyncUploadQueue::FramePacket* AsyncUploadQueue::BeginFramePacket(Queue* queue) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: BeginFramePacket"); AZ_Assert(!m_recordingFrame, "The previous frame packet isn't ended."); auto& device = static_cast(GetDevice()); @@ -471,7 +471,7 @@ namespace AZ void AsyncUploadQueue::EndFramePacket(Queue* queue, Semaphore* semaphoreToSignal /*=nullptr*/) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: EndFramePacket"); AZ_Assert(m_recordingFrame, "The frame packet wasn't started. You need to call StartFramePacket first."); m_commandList->EndCommandBuffer(); @@ -636,7 +636,7 @@ namespace AZ void AsyncUploadQueue::ProcessCallback(const RHI::AsyncWorkHandle& handle) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: ProcessCallback"); AZStd::unique_lock lock(m_callbackListMutex); auto findIter = m_callbackList.find(handle); if (findIter != m_callbackList.end()) diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp index e4d8212228..c712099172 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp @@ -42,15 +42,6 @@ namespace AZ void CommandQueue::ExecuteWork(const RHI::ExecuteWorkRequest& rhiRequest) { -#if defined(PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) - for (RHI::SwapChain* swapChain : rhiRequest.m_swapChainsToPresent) - { - if (!swapChain->m_readyToPresent) - { - return; - } - } -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB const ExecuteWorkRequest& request = static_cast(rhiRequest); QueueCommand([=](void* queue) { diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp index 39f0ac9d58..ec7311cd6e 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -42,7 +41,7 @@ namespace AZ void CommandQueueContext::End() { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueueContext: End"); for (auto& commandQueue : m_commandQueues) { @@ -55,7 +54,6 @@ namespace AZ { AZ_PROFILE_SCOPE(RHI, "Wait on Fences"); - AZ_ATOM_PROFILE_FUNCTION("RHI", "CommandQueueContext: Wait on Fences"); FencesPerQueue& nextFences = m_frameFences[m_currentFrameIndex]; for (auto& fence : nextFences) @@ -79,7 +77,7 @@ namespace AZ void CommandQueueContext::WaitForIdle() { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueueContext: WaitForIdle"); for (auto& commandQueue : m_commandQueues) { commandQueue->WaitForIdle(); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/DescriptorSet.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/DescriptorSet.cpp index c192b49a48..d70d4a01b5 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/DescriptorSet.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/DescriptorSet.cpp @@ -466,7 +466,7 @@ namespace AZ bool DescriptorSet::IsNullDescriptorInfo(const VkDescriptorImageInfo& descriptorInfo) { - return descriptorInfo.imageView == VK_NULL_HANDLE; + return (descriptorInfo.imageView == VK_NULL_HANDLE && descriptorInfo.sampler == VK_NULL_HANDLE); } bool DescriptorSet::IsNullDescriptorInfo(const VkBufferView& descriptorInfo) diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp index 4f800f114b..35bc966d1d 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp @@ -235,8 +235,6 @@ namespace AZ //Load device features now that we have loaded all extension info physicalDevice.LoadSupportedFeatures(); - - InitFeaturesAndLimits(physicalDevice); return RHI::ResultCode::Success; } @@ -247,6 +245,8 @@ namespace AZ RHI::ResultCode result = m_commandQueueContext.Init(*this, commandQueueContextDescriptor); RETURN_RESULT_IF_UNSUCCESSFUL(result); + InitFeaturesAndLimits(static_cast(GetPhysicalDevice())); + // Initialize member variables. ReleaseQueue::Descriptor releaseQueueDescriptor; releaseQueueDescriptor.m_collectLatency = m_descriptor.m_frameCountMax - 1; diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphCompiler.cpp index 372ebc273d..e153ad6645 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphCompiler.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -45,7 +44,7 @@ namespace AZ RHI::MessageOutcome FrameGraphCompiler::CompileInternal(const RHI::FrameGraphCompileRequest& request) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileInternal(Vulkan)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileInternal(Vulkan)"); AZ_Assert(request.m_frameGraph, "FrameGraph is null."); RHI::FrameGraph& frameGraph = *request.m_frameGraph; @@ -89,7 +88,7 @@ namespace AZ void FrameGraphCompiler::CompileResourceBarriers(const RHI::FrameGraphAttachmentDatabase& attachmentDatabase) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileResourceBarriers(Vulkan)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileResourceBarriers(Vulkan)"); for (RHI::BufferFrameAttachment* bufferFrameAttachment : attachmentDatabase.GetBufferAttachments()) { diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/MemoryTypeAllocator.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/MemoryTypeAllocator.h index c628cb61e0..8fd083d4b9 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/MemoryTypeAllocator.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/MemoryTypeAllocator.h @@ -38,7 +38,7 @@ namespace AZ void Init(const Descriptor& descriptor); - void Shutdown(); + void Shutdown() override; void GarbageCollect(); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Scope.h b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Scope.h index 86140e2c7c..dad356cab6 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Scope.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Scope.h @@ -142,7 +142,7 @@ namespace AZ ////////////////////////////////////////////////////////////////////////// // FrameEventBus::Handler - void OnFrameCompileEnd(RHI::FrameGraph& frameGraph); + void OnFrameCompileEnd(RHI::FrameGraph& frameGraph) override; ////////////////////////////////////////////////////////////////////////// // Returns if a barrier can be converted to an implicit subpass barrier. diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp index e1d3e8c060..7040defca8 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp @@ -128,17 +128,6 @@ namespace AZ nativeDimensions->m_imageFormat = ConvertFormat(m_surfaceFormat.format); } -#if defined(PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) - // When launching in game mode, the surface will be ready at this point, meaning that after - // intialization, this swap chain is ready to present - AZ::ApplicationTypeQuery appType; - ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType); - if (appType.IsGame()) - { - m_readyToPresent.store(true); - } -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - SetName(GetName()); return result; } diff --git a/Gems/Atom/RHI/Vulkan/gem.json b/Gems/Atom/RHI/Vulkan/gem.json index 2b43de4ab1..81e8dd22ea 100644 --- a/Gems/Atom/RHI/Vulkan/gem.json +++ b/Gems/Atom/RHI/Vulkan/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/gem.json b/Gems/Atom/RHI/gem.json index 015ee64994..5f916e5224 100644 --- a/Gems/Atom/RHI/gem.json +++ b/Gems/Atom/RHI/gem.json @@ -8,7 +8,13 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI_DX12", + "Atom_RHI_Metal", + "Atom_RHI_Vulkan", + "Atom_RHI_Null", + "Atom_Feature_Common" + ] } diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli index a94e01e11b..b004623c4e 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli @@ -116,6 +116,22 @@ float ComputeLerpBetweenInnerOuterAABBs(float3 innerAabbMin, float3 innerAabbMax return totalDistance > 0.0f ? saturate(shortestDistance / totalDistance) : 1.0f; } +// returns true if the Obb contains the specified point +bool ObbContainsPoint(float4x4 obbTransformInverse, float3 obbHalfExtents, float3 testPoint) +{ + // get the position in Obb local space, force to positive quadrant with abs() + float4 p = abs(mul(obbTransformInverse, float4(testPoint, 1.0f))); + return AabbContainsPoint(-obbHalfExtents, obbHalfExtents, p); +} + +// computes [0..1] percentage of a point that's in between the inner and outer OBBs +float ComputeLerpBetweenInnerOuterOBBs(float3x4 obbTransformInverse, float3 innerObbHalfExtents, float3 outerObbHalfExtents, float3 position) +{ + // get the position in Obb local space, force to positive quadrant with abs() + float3 p = abs(mul(obbTransformInverse, float4(position, 1.0f))); + return ComputeLerpBetweenInnerOuterAABBs(-innerObbHalfExtents, innerObbHalfExtents, outerObbHalfExtents, float3(0.0f, 0.0f, 0.0f), p); +} + // ---------- Normal Encoding ----------- // Encode/Decode functions for Signed Octahedron normals diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Material/Material.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Material/Material.h index 59c5d571fc..3976ef989b 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Material/Material.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Material/Material.h @@ -74,6 +74,7 @@ namespace AZ MaterialPropertyIndex FindPropertyIndex(const Name& name) const; //! Sets the value of a material property. The template data type must match the property's data type. + //! @return true if property value was changed template bool SetPropertyValue(MaterialPropertyIndex index, const Type& value); @@ -81,12 +82,15 @@ namespace AZ template const Type& GetPropertyValue(MaterialPropertyIndex index) const; - //! Gets flags indicating which properties have been modified. - const MaterialPropertyFlags& GetPropertyDirtyFlags() const; - + //! Sets the value of a material property. The @value data type must match the property's data type. + //! @return true if property value was changed bool SetPropertyValue(MaterialPropertyIndex index, const MaterialPropertyValue& value); + const MaterialPropertyValue& GetPropertyValue(MaterialPropertyIndex index) const; const AZStd::vector& GetPropertyValues() const; + + //! Gets flags indicating which properties have been modified. + const MaterialPropertyFlags& GetPropertyDirtyFlags() const; //! Gets the material properties layout. RHI::ConstPtr GetMaterialPropertiesLayout() const; @@ -111,6 +115,12 @@ namespace AZ //! @param return the number of shader options that were updated, or Failure if the material owns the indicated shader option. AZ::Outcome SetSystemShaderOption(const Name& shaderOptionName, RPI::ShaderOptionValue value); + //! Override the material's default PSO handling setting. + //! This is normally used in tools like Asset Processor or Material Editor to allow changes that impact + //! Pipeline State Objects which is not allowed at runtime. See MaterialPropertyPsoHandling for more details. + //! Do not set this in the shipping runtime unless you know what you are doing. + void SetPsoHandlingOverride(MaterialPropertyPsoHandling psoHandlingOverride); + const RHI::ShaderResourceGroup* GetRHIShaderResourceGroup() const; const Data::Asset& GetAsset() const; @@ -189,6 +199,10 @@ namespace AZ //! Records the m_currentChangeId when the material was last compiled. ChangeId m_compiledChangeId = DEFAULT_CHANGE_ID; + + bool m_isInitializing = false; + + MaterialPropertyPsoHandling m_psoHandling = MaterialPropertyPsoHandling::Warning; }; } // namespace RPI diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h index 5429dc13c0..c31f353adb 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h @@ -53,6 +53,8 @@ namespace AZ //! Construct filter with only pass name. PassHierarchyFilter(const Name& passName); + virtual ~PassHierarchyFilter() = default; + //! Construct filter with pass name and its parents' names in the order of the hierarchy //! This means k-th element is always an ancestor of the (k-1)-th element. //! And the last element is the pass name. diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/LuaMaterialFunctor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/LuaMaterialFunctor.h index 026e9f7f18..5f4dce40e5 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/LuaMaterialFunctor.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/LuaMaterialFunctor.h @@ -83,14 +83,19 @@ namespace AZ AZ_TYPE_INFO(AZ::RPI::LuaMaterialFunctorCommonContext, "{2CCCB9A9-AD4F-447C-B587-E7A91CEA8088}"); explicit LuaMaterialFunctorCommonContext(MaterialFunctor::RuntimeContext* runtimeContextImpl, + const MaterialPropertyFlags* materialPropertyDependencies, const AZStd::string& propertyNamePrefix, const AZStd::string& srgNamePrefix, const AZStd::string& optionsNamePrefix); explicit LuaMaterialFunctorCommonContext(MaterialFunctor::EditorContext* editorContextImpl, + const MaterialPropertyFlags* materialPropertyDependencies, const AZStd::string& propertyNamePrefix, const AZStd::string& srgNamePrefix, const AZStd::string& optionsNamePrefix); + + //! Returns false if PSO changes are not allowed, and may report errors or warnings + bool CheckPsoChangesAllowed(); protected: @@ -100,6 +105,12 @@ namespace AZ MaterialPropertyIndex GetMaterialPropertyIndex(const char* name, const char* functionName) const; const MaterialPropertyValue& GetMaterialPropertyValue(MaterialPropertyIndex propertyIndex) const; + + MaterialPropertyPsoHandling GetMaterialPropertyPsoHandling() const; + + RHI::ConstPtr GetMaterialPropertiesLayout() const; + + AZStd::string GetMaterialPropertyDependenciesString() const; // These are prefix strings that will be applied to every name lookup in the lua functor. // This allows the lua script to be reused in different contexts. @@ -112,6 +123,8 @@ namespace AZ // Only one of these will be valid MaterialFunctor::RuntimeContext* m_runtimeContextImpl = nullptr; MaterialFunctor::EditorContext* m_editorContextImpl = nullptr; + const MaterialPropertyFlags* m_materialPropertyDependencies = nullptr; + bool m_psoChangesReported = false; //!< errors/warnings about PSO changes will only be reported once per execution of the functor }; //! Wraps RHI::RenderStates for LuaMaterialFunctor access @@ -241,7 +254,11 @@ namespace AZ static void Reflect(BehaviorContext* behaviorContext); - explicit LuaMaterialFunctorShaderItem(ShaderCollection::Item* shaderItem) : m_shaderItem(shaderItem) {} + LuaMaterialFunctorShaderItem() : + m_context(nullptr), m_shaderItem(nullptr) {} + + explicit LuaMaterialFunctorShaderItem(LuaMaterialFunctorCommonContext* context, ShaderCollection::Item* shaderItem) : + m_context(context), m_shaderItem(shaderItem) {} LuaMaterialFunctorRenderStates GetRenderStatesOverride(); void SetEnabled(bool enable); @@ -253,6 +270,7 @@ namespace AZ private: void SetShaderOptionValue(const Name& name, AZStd::function setValueCommand); + LuaMaterialFunctorCommonContext* m_context = nullptr; ShaderCollection::Item* m_shaderItem = nullptr; }; @@ -265,6 +283,7 @@ namespace AZ static void Reflect(BehaviorContext* behaviorContext); explicit LuaMaterialFunctorRuntimeContext(MaterialFunctor::RuntimeContext* runtimeContextImpl, + const MaterialPropertyFlags* materialPropertyDependencies, const AZStd::string& propertyNamePrefix, const AZStd::string& srgNamePrefix, const AZStd::string& optionsNamePrefix); @@ -304,6 +323,7 @@ namespace AZ static void Reflect(BehaviorContext* behaviorContext); explicit LuaMaterialFunctorEditorContext(MaterialFunctor::EditorContext* editorContextImpl, + const MaterialPropertyFlags* materialPropertyDependencies, const AZStd::string& propertyNamePrefix, const AZStd::string& srgNamePrefix, const AZStd::string& optionsNamePrefix); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h index 52af56ba0e..6ca3bca652 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h @@ -19,6 +19,11 @@ #include +namespace UnitTest +{ + class MaterialTests; +} + namespace AZ { class ReflectContext; @@ -40,6 +45,7 @@ namespace AZ friend class MaterialAssetCreator; friend class MaterialAssetHandler; friend class MaterialAssetCreatorCommon; + friend class UnitTest::MaterialTests; public: AZ_RTTI(MaterialAsset, "{522C7BE0-501D-463E-92C6-15184A2B7AD8}", AZ::Data::AssetData); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h index 682183f70c..09d10bf538 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h @@ -28,6 +28,24 @@ namespace AZ class MaterialPropertiesLayout; using MaterialPropertyFlags = AZStd::bitset; + + //! Indicates how the material system should respond to any material property changes that + //! impact Pipeline State Object configuration. This is significant because some platforms + //! require that PSOs be pre-compiled and shipped with the game. + enum class MaterialPropertyPsoHandling + { + //! PSO-impacting property changes are not allowed, are ignored, and will report an error. + //! This should be used at runtime. It is recommended to do this on all platforms, not just the restricted ones, + //! to encourage best-practices. However, if a game project is not shipping on any restricted platforms, + //! then the team could decide to allow PSO changes. + Error, + + //! PSO-impacting property changes are allowed, but produce a warning message. + Warning, + + //! PSO-impacting property changes are allowed. This can be used during asset processing, in developer tools, or on platforms that don't restrict PSO changes. + Allowed + }; //! MaterialFunctor objects provide custom logic and calculations to configure shaders, render states, //! editor metadata, and more. @@ -81,6 +99,8 @@ namespace AZ const MaterialPropertyValue& GetMaterialPropertyValue(const MaterialPropertyIndex& index) const; const MaterialPropertiesLayout* GetMaterialPropertiesLayout() const { return m_materialPropertiesLayout.get(); } + + MaterialPropertyPsoHandling GetMaterialPropertyPsoHandling() const { return m_psoHandling; } //! Set the value of a shader option //! @param shaderIndex the index of a shader in the material's ShaderCollection @@ -126,16 +146,18 @@ namespace AZ RHI::ConstPtr materialPropertiesLayout, ShaderCollection* shaderCollection, ShaderResourceGroup* shaderResourceGroup, - const MaterialPropertyFlags* materialPropertyDependencies + const MaterialPropertyFlags* materialPropertyDependencies, + MaterialPropertyPsoHandling psoHandling ); private: bool SetShaderOptionValue(ShaderCollection::Item& shaderItem, ShaderOptionIndex optionIndex, ShaderOptionValue value); const AZStd::vector& m_materialPropertyValues; RHI::ConstPtr m_materialPropertiesLayout; - ShaderCollection* m_shaderCollection; - ShaderResourceGroup* m_shaderResourceGroup; + ShaderCollection* m_shaderCollection; + ShaderResourceGroup* m_shaderResourceGroup; const MaterialPropertyFlags* m_materialPropertyDependencies = nullptr; + MaterialPropertyPsoHandling m_psoHandling = MaterialPropertyPsoHandling::Error; }; class EditorContext @@ -144,7 +166,7 @@ namespace AZ public: const MaterialPropertyDynamicMetadata* GetMaterialPropertyMetadata(const Name& propertyName) const; const MaterialPropertyDynamicMetadata* GetMaterialPropertyMetadata(const MaterialPropertyIndex& index) const; - + const MaterialPropertyGroupDynamicMetadata* GetMaterialPropertyGroupMetadata(const Name& propertyName) const; //! Get the property value. The type must be one of those in MaterialPropertyValue. @@ -158,6 +180,8 @@ namespace AZ const MaterialPropertyValue& GetMaterialPropertyValue(const MaterialPropertyIndex& index) const; const MaterialPropertiesLayout* GetMaterialPropertiesLayout() const { return m_materialPropertiesLayout.get(); } + + MaterialPropertyPsoHandling GetMaterialPropertyPsoHandling() const { return MaterialPropertyPsoHandling::Allowed; } //! Set the visibility dynamic metadata of a material property. bool SetMaterialPropertyVisibility(const Name& propertyName, MaterialPropertyVisibility visibility); @@ -177,7 +201,7 @@ namespace AZ bool SetMaterialPropertySoftMaxValue(const Name& propertyName, const MaterialPropertyValue& max); bool SetMaterialPropertySoftMaxValue(const MaterialPropertyIndex& index, const MaterialPropertyValue& max); - + bool SetMaterialPropertyGroupVisibility(const Name& propertyGroupName, MaterialPropertyGroupVisibility visibility); // [GFX TODO][ATOM-4168] Replace the workaround for unlink-able RPI.Public classes in MaterialFunctor diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantAsset.h index 1462490884..66bbd7b188 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantAsset.h @@ -40,7 +40,7 @@ namespace AZ //! by ShaderVariantAssetBuilder this is 1+. static uint32_t MakeAssetProductSubId( uint32_t rhiApiUniqueIndex, uint32_t supervariantIndex, ShaderVariantStableId variantStableId, - uint32_t subProductType = ShaderVariantAssetSubProductType); + uint32_t subProductType = 0); ShaderVariantAsset() = default; ~ShaderVariantAsset() = default; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp index 5ddeb08ef8..ef0678046c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace AZ { @@ -46,7 +47,7 @@ namespace AZ { AssetBuilderSDK::AssetBuilderDesc materialBuilderDescriptor; materialBuilderDescriptor.m_name = JobKey; - materialBuilderDescriptor.m_version = 107; // ATOM-14918 + materialBuilderDescriptor.m_version = 108; // Set materialtype dependency to OrderOnce materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.material", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.materialtype", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_busId = azrtti_typeid(); @@ -66,21 +67,19 @@ namespace AZ //! Adds all relevant dependencies for a referenced source file, considering that the path might be relative to the original file location or a full asset path. //! This will usually include multiple source dependencies and a single job dependency, but will include only source dependencies if the file is not found. //! Note the AssetBuilderSDK::JobDependency::m_platformIdentifier will not be set by this function. The calling code must set this value before passing back - //! to the AssetBuilderSDK::CreateJobsResponse. - void AddPossibleDependencies( - AZStd::string_view currentFilePath, AZStd::string_view referencedParentPath, - AZStd::vector& sourceFileDependencies, - const char* jobKey, AZStd::vector& jobDependencies) + //! to the AssetBuilderSDK::CreateJobsResponse. If isOrderedOnceForMaterialTypes is true and the dependency is a materialtype file, the job dependency type + //! will be set to JobDependencyType::OrderOnce. + void AddPossibleDependencies(AZStd::string_view currentFilePath, + AZStd::string_view referencedParentPath, + const char* jobKey, + AZStd::vector& jobDependencies, + bool isOrderedOnceForMaterialTypes = false) { bool dependencyFileFound = false; AZStd::vector possibleDependencies = RPI::AssetUtils::GetPossibleDepenencyPaths(currentFilePath, referencedParentPath); for (auto& file : possibleDependencies) { - AssetBuilderSDK::SourceFileDependency sourceFileDependency; - sourceFileDependency.m_sourceFileDependencyPath = file; - sourceFileDependencies.push_back(sourceFileDependency); - // The first path found is the highest priority, and will have a job dependency, as this is the one // the builder will actually use if (!dependencyFileFound) @@ -93,8 +92,11 @@ namespace AZ { AssetBuilderSDK::JobDependency jobDependency; jobDependency.m_jobKey = jobKey; - jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order; jobDependency.m_sourceFile.m_sourceFileDependencyPath = file; + + const bool isMaterialTypeFile = AzFramework::StringFunc::Path::IsExtension(file.c_str(), MaterialTypeSourceData::Extension); + jobDependency.m_type = (isMaterialTypeFile && isOrderedOnceForMaterialTypes) ? AssetBuilderSDK::JobDependencyType::OrderOnce : AssetBuilderSDK::JobDependencyType::Order; + jobDependencies.push_back(jobDependency); } } @@ -173,8 +175,9 @@ namespace AZ for (auto& shader : materialTypeSourceData.GetValue().m_shaderCollection) { - AddPossibleDependencies(request.m_sourceFile, shader.m_shaderFilePath, - response.m_sourceFileDependencyList, "Shader Asset", + AddPossibleDependencies(request.m_sourceFile, + shader.m_shaderFilePath, + "Shader Asset", outputJobDescriptor.m_jobDependencyList); } @@ -184,9 +187,10 @@ namespace AZ for (const MaterialFunctorSourceData::AssetDependency& dependency : dependencies) { - AddPossibleDependencies(request.m_sourceFile, dependency.m_sourceFilePath, - response.m_sourceFileDependencyList, - dependency.m_jobKey.c_str(), outputJobDescriptor.m_jobDependencyList); + AddPossibleDependencies(request.m_sourceFile, + dependency.m_sourceFilePath, + dependency.m_jobKey.c_str(), + outputJobDescriptor.m_jobDependencyList); } } } @@ -219,11 +223,24 @@ namespace AZ parentMaterialPath = materialTypePath; } + // If includeMaterialPropertyNames is false, then a job dependency is needed so the material builder can validate MaterialAsset properties + // against the MaterialTypeAsset at asset build time. + // If includeMaterialPropertyNames is true, the material properties will be validated at runtime when the material is loaded, so the job dependency + // is needed only for first-time processing to set up the initial MaterialAsset. This speeds up AP processing time when a materialtype file + // is edited (e.g. 10s when editing StandardPBR.materialtype on AtomTest project from 45s). + bool includeMaterialPropertyNames = true; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Get(includeMaterialPropertyNames, "/O3DE/Atom/RPI/MaterialBuilder/IncludeMaterialPropertyNames"); + } + // Register dependency on the parent material source file so we can load it and use it's data to build this variant material. // Note, we don't need a direct dependency on the material type because the parent material will depend on it. - AddPossibleDependencies(request.m_sourceFile, parentMaterialPath, - response.m_sourceFileDependencyList, - JobKey, outputJobDescriptor.m_jobDependencyList); + AddPossibleDependencies(request.m_sourceFile, + parentMaterialPath, + JobKey, + outputJobDescriptor.m_jobDependencyList, + includeMaterialPropertyNames); } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp index cf7e53c596..c83761cf8e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp @@ -44,7 +44,7 @@ namespace AZ if (auto* serialize = azrtti_cast(context)) { serialize->Class() - ->Version(4) + ->Version(5) // Set materialtype dependency to OrderOnce ->Attribute(Edit::Attributes::SystemComponentTags, AZStd::vector({ AssetBuilderSDK::ComponentTags::AssetBuilder })); } } @@ -78,10 +78,7 @@ namespace AZ AZStd::string materialTypePath; RPI::MaterialConverterBus::BroadcastResult(materialTypePath, &RPI::MaterialConverterBus::Events::GetMaterialTypePath); - bool includeMaterialPropertyNames = true; - RPI::MaterialConverterBus::BroadcastResult(includeMaterialPropertyNames, &RPI::MaterialConverterBus::Events::ShouldIncludeMaterialPropertyNames); - // TODO: Use includeMaterialPropertyNames to break materialtype dependency on fbx files. Materialasset's dependency on materialtypeasset will need to be decoupled first - if (conversionEnabled && !materialTypePath.empty() /*&& !includeMaterialPropertyNames*/) + if (conversionEnabled && !materialTypePath.empty()) { AssetBuilderSDK::SourceFileDependency materialTypeSource; materialTypeSource.m_sourceFileDependencyPath = materialTypePath; @@ -90,7 +87,15 @@ namespace AZ jobDependency.m_jobKey = "Atom Material Builder"; jobDependency.m_sourceFile = materialTypeSource; jobDependency.m_platformIdentifier = platformIdentifier; - jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order; + + // If includeMaterialPropertyNames is false, then a job dependency is needed so the material builder can validate + // MaterialAsset properties against the MaterialTypeAsset at asset build time. If includeMaterialPropertyNames is true, the + // material properties will be validated at runtime when the material is loaded, so the job dependency is needed only for + // first-time processing to set up the initial MaterialAsset. This speeds up AP processing time when a materialtype file is + // edited (e.g. 10s when editing StandardPBR.materialtype on AtomTest project from 45s). + bool includeMaterialPropertyNames = true; + RPI::MaterialConverterBus::BroadcastResult(includeMaterialPropertyNames, &RPI::MaterialConverterBus::Events::ShouldIncludeMaterialPropertyNames); + jobDependency.m_type = includeMaterialPropertyNames ? AssetBuilderSDK::JobDependencyType::OrderOnce : AssetBuilderSDK::JobDependencyType::Order; jobDependencyList.push_back(jobDependency); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index bedf4fe989..348f0aa57a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -14,8 +14,6 @@ #include #include -#include - #include #include #include @@ -299,7 +297,7 @@ namespace AZ //work function void Process() override { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "AddObjectsToViewJob: Process"); const View::UsageFlags viewFlags = m_jobData->m_view->GetUsageFlags(); const RHI::DrawListMask drawListMask = m_jobData->m_view->GetDrawListMask(); @@ -645,7 +643,7 @@ namespace AZ uint32_t AddLodDataToView(const Vector3& pos, const Cullable::LodData& lodData, RPI::View& view) { #ifdef AZ_CULL_PROFILE_DETAILED - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "AddLodDataToView"); #endif const Matrix4x4& viewToClip = view.GetViewToClipMatrix(); @@ -725,17 +723,27 @@ namespace AZ void CullingScene::BeginCulling(const AZStd::vector& views) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "CullingScene: BeginCulling"); + AZ_PROFILE_SCOPE(RPI, "CullingScene: BeginCulling"); m_cullDataConcurrencyCheck.soft_lock(); m_debugCtx.ResetCullStats(); m_debugCtx.m_numCullablesInScene = GetNumCullables(); + AZ::JobCompletion beginCullingCompletion; for (auto& view : views) { - view->BeginCulling(); + const auto cullingLambda = [&view]() + { + view->BeginCulling(); + }; + + AZ::Job* cullingJob = AZ::CreateJobFunction(AZStd::move(cullingLambda), true, nullptr); + cullingJob->SetDependent(&beginCullingCompletion); + cullingJob->Start(); } + beginCullingCompletion.StartAndWaitForCompletion(); + AuxGeomDrawPtr auxGeom; if (m_debugCtx.m_debugDraw) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQuerySystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQuerySystem.cpp index 257bb689ee..0143c8ee2d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQuerySystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQuerySystem.cpp @@ -7,7 +7,6 @@ */ #include -#include #include #include #include @@ -75,7 +74,7 @@ namespace AZ void GpuQuerySystem::Update() { - AZ_ATOM_PROFILE_FUNCTION("RPI", "GpuQuerySystem: Update"); + AZ_PROFILE_SCOPE(RPI, "GpuQuerySystem: Update"); for (auto& queryPool : m_queryPoolArray) { if (queryPool) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp index d4a55d1c29..f201e8414c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include @@ -34,6 +33,8 @@ #include #include +AZ_DECLARE_BUDGET(RPI); + namespace AZ { namespace RPI @@ -171,7 +172,7 @@ namespace AZ void ImageSystem::Update() { - AZ_ATOM_PROFILE_FUNCTION("RPI", "ImageSystem: Update"); + AZ_PROFILE_SCOPE(RPI, "ImageSystem: Update"); AZStd::lock_guard lock(m_activeStreamingPoolMutex); for (StreamingImagePool* imagePool : m_activeStreamingPools) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp index 3b82114a69..d44ed40c7b 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp @@ -20,6 +20,7 @@ #include #include +#include namespace AZ { @@ -58,6 +59,8 @@ namespace AZ { AZ_TRACE_METHOD(); + ScopedValue isInitializing(&m_isInitializing, true, false); + m_materialAsset = { &materialAsset, AZ::Data::AssetLoadBehavior::PreLoad }; // Cache off pointers to some key data structures from the material type... @@ -97,9 +100,15 @@ namespace AZ ShaderReloadNotificationBus::MultiHandler::BusConnect(shaderItem.GetShaderAsset().GetId()); } + // If this Init() is actually a re-initialize, we need to re-apply any overridden property values + // after loading the property values from the asset, so we save that data here. MaterialPropertyFlags prevOverrideFlags = m_propertyOverrideFlags; AZStd::vector prevPropertyValues = m_propertyValues; + // The property values are cleared to their default state to ensure that SetPropertyValue() does not early-return + // when called below. This is important when Init() is actually a re-initialize. + m_propertyValues.clear(); + // Initialize the shader runtime data like shader constant buffers and shader variants by applying the // material's property values. This will feed through the normal runtime material value-change data flow, which may // include custom property change handlers provided by the material type. @@ -198,6 +207,11 @@ namespace AZ return AZ::Success(appliedCount); } + void Material::SetPsoHandlingOverride(MaterialPropertyPsoHandling psoHandlingOverride) + { + m_psoHandling = psoHandlingOverride; + } + const RHI::ShaderResourceGroup* Material::GetRHIShaderResourceGroup() const { return m_rhiShaderResourceGroup; @@ -310,6 +324,16 @@ namespace AZ if (NeedsCompile() && CanCompile()) { + // On some platforms, PipelineStateObjects must be pre-compiled and shipped with the game; they cannot be compiled at runtime. So at some + // point the material system needs to be smart about when it allows PSO changes and when it doesn't. There is a task scheduled to + // thoroughly address this in 2022, but for now we just report a warning to alert users who are using the engine in a way that might + // not be supported for much longer. PSO changes should only be allowed in developer tools (though we could also expose a way for users to + // enable dynamic PSO changes if their project only targets platforms that support this). + // PSO modifications are allowed during initialization because that's using the stored asset data, which the asset system can + // access to pre-compile the necessary PSOs. + MaterialPropertyPsoHandling psoHandling = m_isInitializing ? MaterialPropertyPsoHandling::Allowed : m_psoHandling; + + AZ_PROFILE_BEGIN(RPI, "Material::Compile() Processing Functors"); for (const Ptr& functor : m_materialAsset->GetMaterialFunctors()) { @@ -325,7 +349,8 @@ namespace AZ m_layout, &m_shaderCollection, m_shaderResourceGroup.get(), - &materialPropertyDependencies + &materialPropertyDependencies, + psoHandling ); @@ -484,6 +509,13 @@ namespace AZ } MaterialPropertyValue& savedPropertyValue = m_propertyValues[index.GetIndex()]; + + // If the property value didn't actually change, don't waste time running functors and compiling the changes. + if (savedPropertyValue == value) + { + return false; + } + savedPropertyValue = value; m_propertyDirtyFlags.set(index.GetIndex()); m_propertyOverrideFlags.set(index.GetIndex()); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp index 6fd313e27f..50da470ec4 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp @@ -42,7 +42,7 @@ namespace AZ Data::Instance Model::CreateInternal(const Data::Asset& modelAsset) { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "Model: CreateInternal"); Data::Instance model = aznew Model(); const RHI::ResultCode resultCode = model->Init(modelAsset); @@ -56,7 +56,7 @@ namespace AZ RHI::ResultCode Model::Init(const Data::Asset& modelAsset) { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "Model: Init"); m_lods.resize(modelAsset->GetLodAssets().size()); @@ -128,7 +128,7 @@ namespace AZ bool Model::LocalRayIntersection(const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, float& distanceNormalized, AZ::Vector3& normal) const { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "Model: LocalRayIntersection"); if (!GetModelAsset()) { @@ -171,7 +171,7 @@ namespace AZ float& distanceNormalized, AZ::Vector3& normal) const { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "Model: RayIntersection"); const AZ::Vector3 clampedScale = nonUniformScale.GetMax(AZ::Vector3(AZ::MinTransformScale)); const AZ::Transform inverseTM = modelTransform.GetInverse(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp index ef9521d23c..a1651defbc 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp @@ -27,7 +27,7 @@ namespace AZ ModelLodIndex SelectLod(const View* view, const Vector3& position, const Model& model, ModelLodIndex lodOverride) { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "ModelLodUtils: SelectLod"); ModelLodIndex lodIndex; if (model.GetLodCount() == 1) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp index a8feeb1047..f4f51f97b7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp @@ -19,7 +19,6 @@ #include -#include #include #include @@ -169,7 +168,7 @@ namespace AZ void PassSystem::RemovePasses() { m_state = PassSystemState::RemovingPasses; - AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: RemovePasses"); + AZ_PROFILE_SCOPE(RPI, "PassSystem: RemovePasses"); if (!m_removePassList.empty()) { @@ -189,8 +188,7 @@ namespace AZ void PassSystem::BuildPasses() { m_state = PassSystemState::BuildingPasses; - AZ_PROFILE_FUNCTION(RPI); - AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: BuildPassAttachments"); + AZ_PROFILE_SCOPE(RPI, "PassSystem: BuildPasses"); m_passHierarchyChanged = m_passHierarchyChanged || !m_buildPassList.empty(); @@ -239,8 +237,7 @@ namespace AZ void PassSystem::InitializePasses() { m_state = PassSystemState::InitializingPasses; - AZ_PROFILE_FUNCTION(RPI); - AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: BuildPassAttachments"); + AZ_PROFILE_SCOPE(RPI, "PassSystem: InitializePasses"); m_passHierarchyChanged = m_passHierarchyChanged || !m_initializePassList.empty(); @@ -277,7 +274,6 @@ namespace AZ void PassSystem::Validate() { m_state = PassSystemState::ValidatingPasses; - AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: Validate"); if (PassValidation::IsEnabled()) { @@ -286,7 +282,7 @@ namespace AZ return; } - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "PassSystem: Validate"); PassValidationResults validationResults; m_rootPass->Validate(validationResults); @@ -298,7 +294,7 @@ namespace AZ void PassSystem::ProcessQueuedChanges() { - AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: ProcessQueuedChanges"); + AZ_PROFILE_SCOPE(RPI, "PassSystem: ProcessQueuedChanges"); RemovePasses(); BuildPasses(); InitializePasses(); @@ -307,8 +303,7 @@ namespace AZ void PassSystem::FrameUpdate(RHI::FrameGraphBuilder& frameGraphBuilder) { - AZ_PROFILE_FUNCTION(RPI); - AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: FrameUpdate"); + AZ_PROFILE_SCOPE(RPI, "PassSystem: FrameUpdate"); ResetFrameStatistics(); ProcessQueuedChanges(); @@ -317,14 +312,14 @@ namespace AZ Pass::FramePrepareParams params{ &frameGraphBuilder }; { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Pass: FrameBegin"); + AZ_PROFILE_SCOPE(RPI, "Pass: FrameBegin"); m_rootPass->FrameBegin(params); } } void PassSystem::FrameEnd() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "PassSystem: FrameEnd"); + AZ_PROFILE_SCOPE(RHI, "PassSystem: FrameEnd"); m_state = PassSystemState::FrameEnd; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp index 4c923d4a1a..d9f98c11d3 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp @@ -216,7 +216,7 @@ namespace AZ void RasterPass::CompileResources(const RHI::FrameGraphCompileContext& context) { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "RasterPass: CompileResources"); if (m_shaderResourceGroup == nullptr) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp index 500bf21628..5df1c655d6 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp @@ -233,7 +233,7 @@ namespace AZ void RPISystem::OnSystemTick() { - AZ_ATOM_PROFILE_FUNCTION("RPI", "RPISystem: OnSystemTick"); + AZ_PROFILE_SCOPE(RPI, "RPISystem: OnSystemTick"); // Image system update is using system tick but not game tick so it can stream images in background even game is pausing m_imageSystem.Update(); @@ -245,7 +245,7 @@ namespace AZ { return; } - AZ_ATOM_PROFILE_FUNCTION("RPI", "RPISystem: SimulationTick"); + AZ_PROFILE_SCOPE(RPI, "RPISystem: SimulationTick"); AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit); @@ -273,8 +273,7 @@ namespace AZ return; } - AZ_PROFILE_FUNCTION(RPI); - AZ_ATOM_PROFILE_FUNCTION("RPI", "RPISystem: RenderTick"); + AZ_PROFILE_SCOPE(RPI, "RPISystem: RenderTick"); // Query system update is to increment the frame count m_querySystem.Update(); @@ -301,7 +300,7 @@ namespace AZ }); { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "RPISystem: FrameEnd"); + AZ_PROFILE_SCOPE(RPI, "RPISystem: FrameEnd"); m_dynamicDraw.FrameEnd(); m_passSystem.FrameEnd(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp index 7f0ab9c8aa..409a084e4f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp @@ -397,7 +397,7 @@ namespace AZ void RenderPipeline::OnStartFrame() { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame"); m_lastRenderStartTime = m_lastRenderRequestTime; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 9fa49f7f2a..a7ecf089c0 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -6,8 +6,6 @@ * */ -#include - #include #include #include @@ -350,7 +348,7 @@ namespace AZ void Scene::Simulate([[maybe_unused]] const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: Simulate"); + AZ_PROFILE_SCOPE(RPI, "Scene: Simulate"); m_simulationTime = tickInfo.m_currentGameTime; @@ -389,7 +387,7 @@ namespace AZ { if (completionJob) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: WaitAndCleanCompletionJob"); + AZ_PROFILE_SCOPE(RPI, "Scene: WaitAndCleanCompletionJob"); //[GFX TODO]: the completion job should start earlier and wait for completion here completionJob->StartAndWaitForCompletion(); delete completionJob; @@ -422,11 +420,10 @@ namespace AZ void Scene::PrepareRender([[maybe_unused]]const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: PrepareRender"); + AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender"); { AZ_PROFILE_SCOPE(RPI, "WaitForSimulationCompletion"); - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "WaitForSimulationCompletion"); WaitAndCleanCompletionJob(m_simulationCompletion); } @@ -435,7 +432,7 @@ namespace AZ // Get active pipelines which need to be rendered and notify them of an impending frame. AZStd::vector activePipelines; { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Scene: OnPrepareFrame"); + AZ_PROFILE_SCOPE(RPI, "Scene: OnPrepareFrame"); for (auto& pipeline : m_pipelines) { pipeline->OnPrepareFrame(); @@ -449,7 +446,7 @@ namespace AZ // Get active pipelines which need to be rendered and notify them frame started for (const auto& pipeline : activePipelines) { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Scene: OnStartFrame"); + AZ_PROFILE_SCOPE(RPI, "Scene: OnStartFrame"); pipeline->OnStartFrame(); } @@ -468,7 +465,7 @@ namespace AZ { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Setup Views"); + AZ_PROFILE_SCOPE(RPI, "Setup Views"); // Collect persistent views from all pipelines to be rendered AZStd::map persistentViews; @@ -506,8 +503,7 @@ namespace AZ } { - AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets"); - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "CollectDrawPackets"); + AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets"); AZ::JobCompletion* collectDrawPacketsCompletion = aznew AZ::JobCompletion(); // Launch FeatureProcessor::Render() jobs @@ -550,14 +546,13 @@ namespace AZ // Add dynamic draw data for all the views if (m_dynamicDrawSystem) { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "DynamicDraw SubmitDrawData"); + AZ_PROFILE_SCOPE(RPI, "DynamicDraw SubmitDrawData"); m_dynamicDrawSystem->SubmitDrawData(this, m_renderPacket.m_views); } } { AZ_PROFILE_BEGIN(RPI, "FinalizeDrawLists"); - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "FinalizeDrawLists"); if (jobPolicy == RHI::JobPolicy::Serial) { for (auto& view : m_renderPacket.m_views) @@ -586,14 +581,14 @@ namespace AZ } { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Scene OnEndPrepareRender"); + AZ_PROFILE_SCOPE(RPI, "Scene OnEndPrepareRender"); SceneNotificationBus::Event(GetId(), &SceneNotification::OnEndPrepareRender); } } void Scene::OnFrameEnd() { - AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: OnFrameEnd"); + AZ_PROFILE_SCOPE(RPI, "Scene: OnFrameEnd"); bool didRender = false; for (auto& pipeline : m_pipelines) { @@ -730,7 +725,7 @@ namespace AZ void Scene::RebuildPipelineStatesLookup() { - AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: RebuildPipelineStatesLookup"); + AZ_PROFILE_SCOPE(RPI, "Scene: RebuildPipelineStatesLookup"); m_pipelineStatesLookup.clear(); AZStd::queue parents; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp index 9e633077e7..528d32e217 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp @@ -113,7 +113,7 @@ namespace AZ return; } - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "ShaderMetricsSystem: RequestShaderVariant"); AZStd::lock_guard lock(m_metricsMutex); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index bdb3ea8899..c2356cea45 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -239,7 +239,7 @@ namespace AZ void View::FinalizeDrawLists() { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "View: FinalizeDrawLists"); m_drawListContext.FinalizeLists(); if (m_passesByDrawList) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp index bb80a1b8d2..feeeff36cf 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp @@ -57,7 +57,7 @@ namespace AZ // Only allocate buffer if initial data is not empty if (initialData != nullptr && initialDataSize > 0) { - bufferAsset->m_buffer.resize(descriptor.m_byteCount); + bufferAsset->m_buffer.resize_no_construct(descriptor.m_byteCount); memcpy(bufferAsset->m_buffer.data(), initialData, initialDataSize); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp index 77902093f0..9338d52cb2 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp @@ -128,7 +128,7 @@ namespace AZ if (m_scriptStatus == ScriptStatus::Ready) { - LuaMaterialFunctorRuntimeContext luaContext{&context, m_propertyNamePrefix, m_srgNamePrefix, m_optionsNamePrefix}; + LuaMaterialFunctorRuntimeContext luaContext{&context, &GetMaterialPropertyDependencies(), m_propertyNamePrefix, m_srgNamePrefix, m_optionsNamePrefix}; AZ::ScriptDataContext call; if (m_scriptContext->Call("Process", call)) { @@ -146,7 +146,7 @@ namespace AZ if (m_scriptStatus == ScriptStatus::Ready) { - LuaMaterialFunctorEditorContext luaContext{&context, m_propertyNamePrefix, m_srgNamePrefix, m_optionsNamePrefix}; + LuaMaterialFunctorEditorContext luaContext{&context, &GetMaterialPropertyDependencies(), m_propertyNamePrefix, m_srgNamePrefix, m_optionsNamePrefix}; AZ::ScriptDataContext call; if (m_scriptContext->Call("ProcessEditor", call)) { @@ -157,10 +157,12 @@ namespace AZ } LuaMaterialFunctorCommonContext::LuaMaterialFunctorCommonContext(MaterialFunctor::RuntimeContext* runtimeContextImpl, + const MaterialPropertyFlags* materialPropertyDependencies, const AZStd::string& propertyNamePrefix, const AZStd::string& srgNamePrefix, const AZStd::string& optionsNamePrefix) : m_runtimeContextImpl(runtimeContextImpl) + , m_materialPropertyDependencies(materialPropertyDependencies) , m_propertyNamePrefix(propertyNamePrefix) , m_srgNamePrefix(srgNamePrefix) , m_optionsNamePrefix(optionsNamePrefix) @@ -168,34 +170,96 @@ namespace AZ } LuaMaterialFunctorCommonContext::LuaMaterialFunctorCommonContext(MaterialFunctor::EditorContext* editorContextImpl, + const MaterialPropertyFlags* materialPropertyDependencies, const AZStd::string& propertyNamePrefix, const AZStd::string& srgNamePrefix, const AZStd::string& optionsNamePrefix) : m_editorContextImpl(editorContextImpl) + , m_materialPropertyDependencies(materialPropertyDependencies) , m_propertyNamePrefix(propertyNamePrefix) , m_srgNamePrefix(srgNamePrefix) , m_optionsNamePrefix(optionsNamePrefix) { } + + MaterialPropertyPsoHandling LuaMaterialFunctorCommonContext::GetMaterialPropertyPsoHandling() const + { + if (m_runtimeContextImpl) + { + return m_runtimeContextImpl->GetMaterialPropertyPsoHandling(); + } + else + { + return m_editorContextImpl->GetMaterialPropertyPsoHandling(); + } + } + + RHI::ConstPtr LuaMaterialFunctorCommonContext::GetMaterialPropertiesLayout() const + { + if (m_runtimeContextImpl) + { + return m_runtimeContextImpl->GetMaterialPropertiesLayout(); + } + else + { + return m_editorContextImpl->GetMaterialPropertiesLayout(); + } + } + + AZStd::string LuaMaterialFunctorCommonContext::GetMaterialPropertyDependenciesString() const + { + AZStd::vector propertyList; + for (size_t i = 0; i < m_materialPropertyDependencies->size(); ++i) + { + if ((*m_materialPropertyDependencies)[i]) + { + propertyList.push_back(GetMaterialPropertiesLayout()->GetPropertyDescriptor(MaterialPropertyIndex{i})->GetName().GetStringView()); + } + } + + AZStd::string propertyListString; + AzFramework::StringFunc::Join(propertyListString, propertyList.begin(), propertyList.end(), ", "); + + return propertyListString; + } + + bool LuaMaterialFunctorCommonContext::CheckPsoChangesAllowed() + { + if (GetMaterialPropertyPsoHandling() == MaterialPropertyPsoHandling::Error) + { + if (!m_psoChangesReported) + { + LuaMaterialFunctorUtilities::Script_Error( + AZStd::string::format( + "The following material properties must not be changed at runtime because they impact Pipeline State Objects: %s", GetMaterialPropertyDependenciesString().c_str())); + + m_psoChangesReported = true; + } + + return false; + } + else if (GetMaterialPropertyPsoHandling() == MaterialPropertyPsoHandling::Warning) + { + if (!m_psoChangesReported) + { + LuaMaterialFunctorUtilities::Script_Warning( + AZStd::string::format( + "The following material properties should not be changed at runtime because they impact Pipeline State Objects: %s", GetMaterialPropertyDependenciesString().c_str())); + + m_psoChangesReported = true; + } + } + + return true; + } MaterialPropertyIndex LuaMaterialFunctorCommonContext::GetMaterialPropertyIndex(const char* name, const char* functionName) const { MaterialPropertyIndex propertyIndex; Name propertyFullName{m_propertyNamePrefix + name}; - - if (m_runtimeContextImpl) - { - propertyIndex = m_runtimeContextImpl->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyFullName); - } - else if (m_editorContextImpl) - { - propertyIndex = m_editorContextImpl->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyFullName); - } - else - { - AZ_Assert(false, "Context not initialized properly"); - } + + propertyIndex = GetMaterialPropertiesLayout()->FindPropertyIndex(propertyFullName); if (!propertyIndex.IsValid()) { @@ -297,10 +361,11 @@ namespace AZ } LuaMaterialFunctorRuntimeContext::LuaMaterialFunctorRuntimeContext(MaterialFunctor::RuntimeContext* runtimeContextImpl, + const MaterialPropertyFlags* materialPropertyDependencies, const AZStd::string& propertyNamePrefix, const AZStd::string& srgNamePrefix, const AZStd::string& optionsNamePrefix) - : LuaMaterialFunctorCommonContext(runtimeContextImpl, propertyNamePrefix, srgNamePrefix, optionsNamePrefix) + : LuaMaterialFunctorCommonContext(runtimeContextImpl, materialPropertyDependencies, propertyNamePrefix, srgNamePrefix, optionsNamePrefix) , m_runtimeContextImpl(runtimeContextImpl) { } @@ -331,7 +396,7 @@ namespace AZ if (!shaderItem.MaterialOwnsShaderOption(optionIndex)) { - LuaMaterialFunctorUtilities::Script_Error(AZStd::string::format("Shader option '%s' is not owned by this material.", fullOptionName.GetCStr()).c_str()); + LuaMaterialFunctorUtilities::Script_Error(AZStd::string::format("Shader option '%s' is not owned by this material.", fullOptionName.GetCStr())); break; } @@ -398,12 +463,12 @@ namespace AZ { if (index < GetShaderCount()) { - return LuaMaterialFunctorShaderItem{&(*m_runtimeContextImpl->m_shaderCollection)[index]}; + return LuaMaterialFunctorShaderItem{this, &(*m_runtimeContextImpl->m_shaderCollection)[index]}; } else { LuaMaterialFunctorUtilities::Script_Error(AZStd::string::format("GetShader(%zu) is invalid.", index)); - return LuaMaterialFunctorShaderItem{nullptr}; + return {}; } } @@ -412,13 +477,13 @@ namespace AZ const AZ::Name tag{shaderTag}; if (m_runtimeContextImpl->m_shaderCollection->HasShaderTag(tag)) { - return LuaMaterialFunctorShaderItem{&(*m_runtimeContextImpl->m_shaderCollection)[tag]}; + return LuaMaterialFunctorShaderItem{this, &(*m_runtimeContextImpl->m_shaderCollection)[tag]}; } else { LuaMaterialFunctorUtilities::Script_Error(AZStd::string::format( "GetShaderByTag('%s') is invalid: Could not find a shader with the tag '%s'.", tag.GetCStr(), tag.GetCStr())); - return LuaMaterialFunctorShaderItem{nullptr}; + return {}; } } @@ -459,10 +524,11 @@ namespace AZ } LuaMaterialFunctorEditorContext::LuaMaterialFunctorEditorContext(MaterialFunctor::EditorContext* editorContextImpl, + const MaterialPropertyFlags* materialPropertyDependencies, const AZStd::string& propertyNamePrefix, const AZStd::string& srgNamePrefix, const AZStd::string& optionsNamePrefix) - : LuaMaterialFunctorCommonContext(editorContextImpl, propertyNamePrefix, srgNamePrefix, optionsNamePrefix) + : LuaMaterialFunctorCommonContext(editorContextImpl, materialPropertyDependencies, propertyNamePrefix, srgNamePrefix, optionsNamePrefix) , m_editorContextImpl(editorContextImpl) { } @@ -595,7 +661,7 @@ namespace AZ LuaMaterialFunctorRenderStates LuaMaterialFunctorShaderItem::GetRenderStatesOverride() { - if (m_shaderItem) + if (m_context->CheckPsoChangesAllowed() && m_shaderItem) { return LuaMaterialFunctorRenderStates{m_shaderItem->GetRenderStatesOverlay()}; } @@ -638,8 +704,7 @@ namespace AZ { LuaMaterialFunctorUtilities::Script_Error( AZStd::string::format( - "Shader option '%s' is not owned by the shader '%s'.", name.GetCStr(), m_shaderItem->GetShaderTag().GetCStr()) - .c_str()); + "Shader option '%s' is not owned by the shader '%s'.", name.GetCStr(), m_shaderItem->GetShaderTag().GetCStr())); return; } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp index d08fa3e58e..0f70d0af35 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp @@ -35,13 +35,15 @@ namespace AZ RHI::ConstPtr materialPropertiesLayout, ShaderCollection* shaderCollection, ShaderResourceGroup* shaderResourceGroup, - const MaterialPropertyFlags* materialPropertyDependencies + const MaterialPropertyFlags* materialPropertyDependencies, + MaterialPropertyPsoHandling psoHandling ) : m_materialPropertyValues(propertyValues) , m_materialPropertiesLayout(materialPropertiesLayout) , m_shaderCollection(shaderCollection) , m_shaderResourceGroup(shaderResourceGroup) , m_materialPropertyDependencies(materialPropertyDependencies) + , m_psoHandling(psoHandling) {} bool MaterialFunctor::RuntimeContext::SetShaderOptionValue(ShaderCollection::Item& shaderItem, ShaderOptionIndex optionIndex, ShaderOptionValue value) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyValue.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyValue.cpp index 849e6cfffb..13ecea52dc 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyValue.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyValue.cpp @@ -109,11 +109,20 @@ namespace AZ { result.m_value = AZStd::any_cast(value); } + else if (value.is()) + { + result.m_value = Data::Asset( + AZStd::any_cast(value), azrtti_typeid()); + } + else if (value.is>()) + { + result.m_value = Data::Asset( + AZStd::any_cast>(value).GetId(), azrtti_typeid()); + } else if (value.is>()) { result.m_value = Data::Asset( - AZStd::any_cast>(value).GetId(), - azrtti_typeid()); + AZStd::any_cast>(value).GetId(), azrtti_typeid()); } else if (value.is>()) { @@ -129,7 +138,8 @@ namespace AZ } else { - AZ_Warning("MaterialPropertyValue", false, "Cannot convert any to variant. Type in any is: %s.", + AZ_Warning( + "MaterialPropertyValue", false, "Cannot convert any to variant. Type in any is: %s.", value.get_type_info().m_id.ToString().data()); } @@ -187,5 +197,5 @@ namespace AZ return result; } - } -} + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp index 180f6a5d99..7b3b6b7a28 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp @@ -22,7 +22,7 @@ namespace AZ : public SerializeContext::IEventHandler { //! Called right before we start reading from the instance pointed by classPtr. - virtual void OnReadBegin(void* classPtr) + void OnReadBegin(void* classPtr) override { ShaderCollection::Item* shaderVariantReference = reinterpret_cast(classPtr); shaderVariantReference->m_shaderVariantId = shaderVariantReference->m_shaderOptionGroup.GetShaderVariantId(); diff --git a/Gems/Atom/RPI/Code/Tests/Common/RHI/Stubs.h b/Gems/Atom/RPI/Code/Tests/Common/RHI/Stubs.h index c3768c1ce6..2ed2875cad 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/RHI/Stubs.h +++ b/Gems/Atom/RPI/Code/Tests/Common/RHI/Stubs.h @@ -69,8 +69,8 @@ namespace UnitTest void FillFormatsCapabilitiesInternal([[maybe_unused]] FormatCapabilitiesList& formatsCapabilities) override {} AZ::RHI::ResultCode InitializeLimits() override { return AZ::RHI::ResultCode::Success; } void PreShutdown() override {} - AZ::RHI::ResourceMemoryRequirements GetResourceMemoryRequirements([[maybe_unused]] const AZ::RHI::ImageDescriptor& descriptor) { return AZ::RHI::ResourceMemoryRequirements{}; }; - AZ::RHI::ResourceMemoryRequirements GetResourceMemoryRequirements([[maybe_unused]] const AZ::RHI::BufferDescriptor& descriptor) { return AZ::RHI::ResourceMemoryRequirements{}; }; + AZ::RHI::ResourceMemoryRequirements GetResourceMemoryRequirements([[maybe_unused]] const AZ::RHI::ImageDescriptor& descriptor) override { return AZ::RHI::ResourceMemoryRequirements{}; }; + AZ::RHI::ResourceMemoryRequirements GetResourceMemoryRequirements([[maybe_unused]] const AZ::RHI::BufferDescriptor& descriptor) override { return AZ::RHI::ResourceMemoryRequirements{}; }; void ObjectCollectionNotify(AZ::RHI::ObjectCollectorNotifyFunction notifyFunction) override {} }; @@ -228,12 +228,12 @@ namespace UnitTest AZ_CLASS_ALLOCATOR(Fence, AZ::SystemAllocator, 0); private: - virtual AZ::RHI::ResultCode InitInternal(AZ::RHI::Device&, AZ::RHI::FenceState) override { return AZ::RHI::ResultCode::Success; } - virtual void ShutdownInternal() override {} - virtual void SignalOnCpuInternal() override {} - virtual void WaitOnCpuInternal() const override {}; - virtual void ResetInternal() override {} - virtual AZ::RHI::FenceState GetFenceStateInternal() const override { return AZ::RHI::FenceState::Reset; } + AZ::RHI::ResultCode InitInternal(AZ::RHI::Device&, AZ::RHI::FenceState) override { return AZ::RHI::ResultCode::Success; } + void ShutdownInternal() override {} + void SignalOnCpuInternal() override {} + void WaitOnCpuInternal() const override {}; + void ResetInternal() override {} + AZ::RHI::FenceState GetFenceStateInternal() const override { return AZ::RHI::FenceState::Reset; } }; class ShaderResourceGroupPool @@ -276,7 +276,7 @@ namespace UnitTest AZ_CLASS_ALLOCATOR(ShaderStageFunction, AZ::SystemAllocator, 0); private: - virtual AZ::RHI::ResultCode FinalizeInternal() { return AZ::RHI::ResultCode::Success; } + AZ::RHI::ResultCode FinalizeInternal() override { return AZ::RHI::ResultCode::Success; } }; class PipelineState diff --git a/Gems/Atom/RPI/Code/Tests/Common/SerializeTester.h b/Gems/Atom/RPI/Code/Tests/Common/SerializeTester.h index 8422756837..df21d13fc0 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/SerializeTester.h +++ b/Gems/Atom/RPI/Code/Tests/Common/SerializeTester.h @@ -23,6 +23,7 @@ namespace UnitTest : m_serializeContext{serializeContext} , m_outStream{&m_buffer} {} + virtual ~SerializeTester() = default; // Serializes an object out to a the internal stream. Resets the stream with each call. virtual void SerializeOut(T* object, AZ::DataStream::StreamType streamType = AZ::DataStream::ST_XML); @@ -76,7 +77,7 @@ namespace UnitTest m_assetHandler = AZ::Data::AssetManager::Instance().GetHandler(AssetDataT::RTTI_Type()); } - ~AssetTester() = default; + virtual ~AssetTester() = default; void SerializeOut(AZ::Data::Asset assetToSave) { diff --git a/Gems/Atom/RPI/Code/Tests/Image/StreamingImageTests.cpp b/Gems/Atom/RPI/Code/Tests/Image/StreamingImageTests.cpp index 345637bfe5..f7476c0bc4 100644 --- a/Gems/Atom/RPI/Code/Tests/Image/StreamingImageTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Image/StreamingImageTests.cpp @@ -40,10 +40,9 @@ namespace AZ : public UnitTest::AssetTester { public: - StreamingImageAssetTester() - { + StreamingImageAssetTester() = default; + ~StreamingImageAssetTester() override = default; - } void SetAssetReady(Data::Asset& asset) override { asset->SetReady(); @@ -54,7 +53,8 @@ namespace AZ : public UnitTest::AssetTester { public: - ImageMipChainAssetTester() {} + ImageMipChainAssetTester() = default; + ~ImageMipChainAssetTester() override = default; void SetAssetReady(Data::Asset& asset) override { diff --git a/Gems/Atom/RPI/Code/Tests/Material/LuaMaterialFunctorTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/LuaMaterialFunctorTests.cpp index 99efa38c3a..37d0930d97 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/LuaMaterialFunctorTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/LuaMaterialFunctorTests.cpp @@ -1039,6 +1039,42 @@ namespace UnitTest drawListTagRegistry->ReleaseTag(tag); } + + TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_PsoChangesNotAllowed_Error) + { + using namespace AZ::RPI; + + const char* functorScript = + R"( + function GetMaterialPropertyDependencies() + return {"general.MyBool"} + end + + function GetShaderOptionDependencies() + return {} + end + + function Process(context) + local boolValue = context:GetMaterialPropertyValue_bool("general.MyBool") + if(boolValue) then + context:GetShader(0):GetRenderStatesOverride():SetFillMode(FillMode_Wireframe) + else + context:GetShader(0):GetRenderStatesOverride():ClearFillMode() + end + end + )"; + + TestMaterialData testData; + testData.Setup(MaterialPropertyDataType::Bool, "general.MyBool", functorScript); + + testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); + + ErrorMessageFinder errorMessageFinder; + + errorMessageFinder.AddExpectedErrorMessage("not be changed at runtime because they impact Pipeline State Objects: general.MyBool"); + EXPECT_TRUE(testData.GetMaterial()->Compile()); + errorMessageFinder.CheckExpectedErrorsFound(); + } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_MultisampleCustomPositionCountIndex_Error) { @@ -1067,6 +1103,7 @@ namespace UnitTest TestMaterialData testData; testData.Setup(MaterialPropertyDataType::Bool, "general.MyBool", functorScript); + testData.GetMaterial()->SetPsoHandlingOverride(AZ::RPI::MaterialPropertyPsoHandling::Allowed); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); ErrorMessageFinder errorMessageFinder; @@ -1107,7 +1144,8 @@ namespace UnitTest errorMessageFinder.AddExpectedErrorMessage("ClearMultisampleCustomPosition(18,...) index is out of range. Must be less than 16."); testData.Setup(MaterialPropertyDataType::Bool, "general.MyBool", functorScript); errorMessageFinder.CheckExpectedErrorsFound(); - + + testData.GetMaterial()->SetPsoHandlingOverride(AZ::RPI::MaterialPropertyPsoHandling::Allowed); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); errorMessageFinder.AddExpectedErrorMessage("SetMultisampleCustomPosition(17,...) index is out of range. Must be less than 16."); @@ -1146,7 +1184,8 @@ namespace UnitTest errorMessageFinder.AddExpectedErrorMessage("ClearBlendEnabled(10,...) index is out of range. Must be less than 8."); testData.Setup(MaterialPropertyDataType::Bool, "general.MyBool", functorScript); errorMessageFinder.CheckExpectedErrorsFound(); - + + testData.GetMaterial()->SetPsoHandlingOverride(AZ::RPI::MaterialPropertyPsoHandling::Allowed); testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true}); errorMessageFinder.AddExpectedErrorMessage("SetBlendEnabled(9,...) index is out of range. Must be less than 8."); diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialFunctorTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialFunctorTests.cpp index 0c84484508..ff5d24ff9a 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialFunctorTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialFunctorTests.cpp @@ -41,6 +41,7 @@ namespace UnitTest { } + using MaterialFunctor::Process; void Process(MaterialFunctor::RuntimeContext& context) override { m_processResult = context.SetShaderOptionValue(0, m_shaderOptionIndex, m_shaderOptionValue); @@ -65,6 +66,7 @@ namespace UnitTest public: MOCK_METHOD0(ProcessCalled, void()); + using MaterialFunctor::Process; void Process(RuntimeContext& context) override { ProcessCalled(); @@ -87,6 +89,7 @@ namespace UnitTest : public MaterialFunctorSourceData { public: + using MaterialFunctorSourceData::CreateFunctor; FunctorResult CreateFunctor(const RuntimeContext& context) const override { Ptr functor = aznew PropertyDependencyTestFunctor; @@ -165,7 +168,8 @@ namespace UnitTest materialTypeAsset->GetMaterialPropertiesLayout(), &shaderCollectionCopy, unusedSrg, - &testFunctorSetOptionA.GetMaterialPropertyDependencies() + &testFunctorSetOptionA.GetMaterialPropertyDependencies(), + AZ::RPI::MaterialPropertyPsoHandling::Allowed }; testFunctorSetOptionA.Process(runtimeContext); EXPECT_TRUE(testFunctorSetOptionA.GetProcessResult()); @@ -181,7 +185,8 @@ namespace UnitTest materialTypeAsset->GetMaterialPropertiesLayout(), &shaderCollectionCopy, unusedSrg, - &testFunctorSetOptionB.GetMaterialPropertyDependencies() + &testFunctorSetOptionB.GetMaterialPropertyDependencies(), + AZ::RPI::MaterialPropertyPsoHandling::Allowed }; testFunctorSetOptionB.Process(runtimeContext); EXPECT_TRUE(testFunctorSetOptionB.GetProcessResult()); @@ -198,7 +203,8 @@ namespace UnitTest materialTypeAsset->GetMaterialPropertiesLayout(), &shaderCollectionCopy, unusedSrg, - &testFunctorSetOptionC.GetMaterialPropertyDependencies() + &testFunctorSetOptionC.GetMaterialPropertyDependencies(), + AZ::RPI::MaterialPropertyPsoHandling::Allowed }; testFunctorSetOptionC.Process(runtimeContext); EXPECT_FALSE(testFunctorSetOptionC.GetProcessResult()); @@ -213,7 +219,8 @@ namespace UnitTest materialTypeAsset->GetMaterialPropertiesLayout(), &shaderCollectionCopy, unusedSrg, - &testFunctorSetOptionInvalid.GetMaterialPropertyDependencies() + &testFunctorSetOptionInvalid.GetMaterialPropertyDependencies(), + AZ::RPI::MaterialPropertyPsoHandling::Allowed }; testFunctorSetOptionInvalid.Process(runtimeContext); EXPECT_FALSE(testFunctorSetOptionInvalid.GetProcessResult()); diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp index 5386af1cfa..ce842d385d 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp @@ -21,14 +21,14 @@ namespace JsonSerializationTests public JsonSerializerConformityTestDescriptor { public: - void Reflect(AZStd::unique_ptr& context) + void Reflect(AZStd::unique_ptr& context) override { AZ::RPI::MaterialTypeSourceData::Reflect(context.get()); AZ::RPI::MaterialPropertyDescriptor::Reflect(context.get()); AZ::RPI::ReflectMaterialDynamicMetadata(context.get()); } - void Reflect(AZStd::unique_ptr& context) + void Reflect(AZStd::unique_ptr& context) override { AZ::RPI::MaterialTypeSourceData::Reflect(context.get()); } diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertyValueSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertyValueSourceDataTests.cpp index 231558bb99..5dde21ece1 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertyValueSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertyValueSourceDataTests.cpp @@ -142,6 +142,7 @@ namespace UnitTest AZStd::string m_propertyName; MaterialPropertyValueSourceData m_propertyValue; + using MaterialFunctorSourceData::CreateFunctor; FunctorResult CreateFunctor(const RuntimeContext& context) const override { Ptr functor = aznew ValueFunctor; diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTests.cpp index d82a17e1f3..f202747bf6 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTests.cpp @@ -182,6 +182,13 @@ namespace UnitTest EXPECT_EQ(srgData.GetImageView(srgData.FindShaderInputImageIndex(Name{ "m_image" }), 0), m_testImage->GetImageView()); EXPECT_EQ(srgData.GetConstant(srgData.FindShaderInputConstantIndex(Name{ "m_enum" })), 2u); } + + //! Provides write access to private material asset property values, primarily for simulating + //! MaterialAsset hot reload. + MaterialPropertyValue& AccessMaterialAssetPropertyValue(Data::Asset materialAsset, Name propertyName) + { + return materialAsset->m_propertyValues[materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyName).GetIndex()]; + } }; TEST_F(MaterialTests, TestCreateVsFindOrCreate) @@ -313,6 +320,30 @@ namespace UnitTest EXPECT_EQ(srgData.GetConstant(srgData.FindShaderInputConstantIndex(Name{ "m_uint" })), 42u); } + TEST_F(MaterialTests, TestSetPropertyValueWhenValueIsUnchanged) + { + Data::Instance material = Material::FindOrCreate(m_testMaterialAsset); + + EXPECT_TRUE(material->SetPropertyValue(material->FindPropertyIndex(Name{ "MyFloat" }), 2.5f)); + + ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName()); + EXPECT_TRUE(material->Compile()); + + // Taint the SRG so we can check whether it was set by the SetPropertyValue() calls below. + const RHI::ShaderResourceGroup* srg = material->GetRHIShaderResourceGroup(); + const RHI::ShaderResourceGroupData& srgData = srg->GetData(); + const_cast(&srgData)->SetConstant(m_testMaterialSrgLayout->FindShaderInputConstantIndex(Name{"m_float"}), 0.0f); + + // Set the properties to the same values as before + EXPECT_FALSE(material->SetPropertyValue(material->FindPropertyIndex(Name{ "MyFloat" }), 2.5f)); + + ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName()); + EXPECT_FALSE(material->Compile()); + + // Make sure the SRG is still tainted, because the SetPropertyValue() functions weren't processed + EXPECT_EQ(srgData.GetConstant(srgData.FindShaderInputConstantIndex(Name{ "m_float" })), 0.0f); + } + TEST_F(MaterialTests, TestImageNotProvided) { Data::Asset materialAssetWithEmptyImage; @@ -785,4 +816,45 @@ namespace UnitTest EXPECT_EQ((float)inputColor.GetElement(i), (float)colorFromMaterial.GetElement(i)); } } + + TEST_F(MaterialTests, TestReinitializeForHotReload) + { + Data::Instance material = Material::FindOrCreate(m_testMaterialAsset); + const RHI::ShaderResourceGroupData* srgData = &material->GetRHIShaderResourceGroup()->GetData(); + ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName()); + + // Check the default property value + EXPECT_EQ(material->GetPropertyValue(material->FindPropertyIndex(Name{ "MyFloat" })), 1.5f); + EXPECT_EQ(srgData->GetConstant(srgData->FindShaderInputConstantIndex(Name{ "m_float" })), 1.5f); + EXPECT_EQ(material->GetPropertyValue(material->FindPropertyIndex(Name{ "MyInt" })), -2); + EXPECT_EQ(srgData->GetConstant(srgData->FindShaderInputConstantIndex(Name{ "m_int" })), -2); + + // Override a property value + EXPECT_TRUE(material->SetPropertyValue(material->FindPropertyIndex(Name{ "MyFloat" }), 5.5f)); + + // Apply the changes + EXPECT_TRUE(material->Compile()); + ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName()); + + // Check the updated values with one overridden + EXPECT_EQ(material->GetPropertyValue(material->FindPropertyIndex(Name{ "MyFloat" })), 5.5f); + EXPECT_EQ(srgData->GetConstant(srgData->FindShaderInputConstantIndex(Name{ "m_float" })), 5.5f); + EXPECT_EQ(material->GetPropertyValue(material->FindPropertyIndex(Name{ "MyInt" })), -2); + EXPECT_EQ(srgData->GetConstant(srgData->FindShaderInputConstantIndex(Name{ "m_int" })), -2); + + // Pretend there was a hot-reload with new default values + AccessMaterialAssetPropertyValue(m_testMaterialAsset, Name{"MyFloat"}) = 0.5f; + AccessMaterialAssetPropertyValue(m_testMaterialAsset, Name{"MyInt"}) = -7; + AZ::Data::AssetBus::Event(m_testMaterialAsset.GetId(), &AZ::Data::AssetBus::Handler::OnAssetReloaded, m_testMaterialAsset); + srgData = &material->GetRHIShaderResourceGroup()->GetData(); + ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName()); + + // Make sure the override values are still there + EXPECT_EQ(srgData->GetConstant(srgData->FindShaderInputConstantIndex(Name{ "m_float" })), 5.5f); + EXPECT_EQ(material->GetPropertyValue(material->FindPropertyIndex(Name{ "MyFloat" })), 5.5f); + + // Make sure the new default value is applied where it was not overridden + EXPECT_EQ(material->GetPropertyValue(material->FindPropertyIndex(Name{ "MyInt" })), -7); + EXPECT_EQ(srgData->GetConstant(srgData->FindShaderInputConstantIndex(Name{ "m_int" })), -7); + } } diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp index e11a049af2..b9774c84d9 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp @@ -47,6 +47,7 @@ namespace UnitTest ; } + using AZ::RPI::MaterialFunctor::Process; void Process(AZ::RPI::MaterialFunctor::RuntimeContext& context) override { // This code isn't actually called in the unit test, but we include it here just to demonstrate what a real functor might look like. @@ -74,6 +75,7 @@ namespace UnitTest ; } + using AZ::RPI::MaterialFunctor::Process; void Process(AZ::RPI::MaterialFunctor::RuntimeContext& context) override { // This code isn't actually called in the unit test, but we include it here just to demonstrate what a real functor might look like. diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp index 5edf09b67d..179dc7c966 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp @@ -70,6 +70,7 @@ namespace UnitTest } } + using AZ::RPI::MaterialFunctor::Process; void Process(AZ::RPI::MaterialFunctor::RuntimeContext& context) override { // This code isn't actually called in the unit test, but we include it here just to demonstrate what a real functor might look like. @@ -110,6 +111,7 @@ namespace UnitTest AZStd::string m_floatPropertyInputId; AZStd::string m_float3ShaderSettingOutputId; + using MaterialFunctorSourceData::CreateFunctor; FunctorResult CreateFunctor(const RuntimeContext& context) const override { Ptr functor = aznew Splat3Functor; @@ -138,6 +140,7 @@ namespace UnitTest } } + using AZ::RPI::MaterialFunctor::Process; void Process(AZ::RPI::MaterialFunctor::RuntimeContext& context) override { // This code isn't actually called in the unit test, but we include it here just to demonstrate what a real functor might look like. @@ -174,6 +177,7 @@ namespace UnitTest m_shaderIndex{shaderIndex} {} + using MaterialFunctorSourceData::CreateFunctor; FunctorResult CreateFunctor(const RuntimeContext& context) const override { Ptr functor = aznew EnableShaderFunctor; @@ -200,6 +204,7 @@ namespace UnitTest } } + using AZ::RPI::MaterialFunctor::Process; void Process(AZ::RPI::MaterialFunctor::RuntimeContext& context) override { // This code isn't actually called in the unit test, but we include it here just to demonstrate what a real functor might look like. @@ -232,6 +237,7 @@ namespace UnitTest return options; } + using MaterialFunctorSourceData::CreateFunctor; FunctorResult CreateFunctor([[maybe_unused]] const RuntimeContext& context) const override { Ptr functor = aznew SetShaderOptionFunctor; diff --git a/Gems/Atom/RPI/gem.json b/Gems/Atom/RPI/gem.json index f30a1c3201..b5a6fd5a1a 100644 --- a/Gems/Atom/RPI/gem.json +++ b/Gems/Atom/RPI/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h index ec16653540..900dc3535c 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h @@ -24,6 +24,12 @@ namespace AtomToolsFramework static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; typedef AZ::Uuid BusIdType; + //! Add heading widget above scroll area + virtual void AddHeading(QWidget* headingWidget) = 0; + + //! Clear heading widgets + virtual void ClearHeading() = 0; + //! Clear all inspector groups and content virtual void Reset() = 0; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h index 3d4e252967..adcd94ca10 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h @@ -41,6 +41,10 @@ namespace AtomToolsFramework ~InspectorWidget() override; // InspectorRequestBus::Handler overrides... + void AddHeading(QWidget* headingWidget) override; + + void ClearHeading() override; + void Reset() override; void AddGroupsBegin() override; @@ -77,7 +81,6 @@ namespace AtomToolsFramework virtual void OnHeaderClicked(const AZStd::string& groupNameId, QMouseEvent* event); private: - QVBoxLayout* m_layout = nullptr; QScopedPointer m_ui; struct GroupWidgetPair diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h index 42fe9a01c9..4956fbc1dc 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h @@ -116,11 +116,18 @@ namespace AtomToolsFramework // ModularViewportCameraControllerRequestBus overrides ... void InterpolateToTransform(const AZ::Transform& worldFromLocal, float lookAtDistance) override; AZStd::optional LookAtAfterInterpolation() const override; + AZ::Transform GetReferenceFrame() const override; + void SetReferenceFrame(const AZ::Transform& worldFromLocal) override; + void ClearReferenceFrame() override; private: // AzFramework::ViewportDebugDisplayEventBus overrides ... void DisplayViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + //! Update the reference frame after a change has been made to the camera + //! view without updating the internal camera via user input. + void RefreshReferenceFrame(); + //! The current mode the camera controller is in. enum class CameraMode { @@ -139,6 +146,8 @@ namespace AtomToolsFramework AzFramework::Camera m_camera; //!< The current camera state (pitch/yaw/position/look-distance). AzFramework::Camera m_targetCamera; //!< The target (next) camera state that m_camera is catching up to. + AzFramework::Camera m_previousCamera; //!< The state of the camera from the previous frame. + AZStd::optional m_storedCamera; //!< A potentially stored camera for when a custom reference frame is set. AzFramework::CameraSystem m_cameraSystem; //!< The camera system responsible for managing all CameraInputs. AzFramework::CameraProps m_cameraProps; //!< Camera properties to control rotate and translate smoothness. CameraControllerPriorityFn m_priorityFn; //!< Controls at what priority the camera controller should respond to events. @@ -147,6 +156,7 @@ namespace AtomToolsFramework CameraMode m_cameraMode = CameraMode::Control; //!< The current mode the camera is operating in. AZStd::optional m_lookAtAfterInterpolation; //!< The look at point after an interpolation has finished. //!< Will be cleared when the view changes (camera looks away). + AZ::Transform m_referenceFrameOverride = AZ::Transform::CreateIdentity(); //!< //! Flag to prevent circular updates of the camera transform (while the viewport transform is being updated internally). bool m_updatingTransformInternally = false; //! Listen for camera view changes outside of the camera controller. @@ -154,4 +164,17 @@ namespace AtomToolsFramework //! The current instance of the modular camera viewport context. AZStd::unique_ptr m_modularCameraViewportContext; }; + + //! Placeholder implementation for ModularCameraViewportContext (useful for verifying the interface). + class PlaceholderModularCameraViewportContextImpl : public AtomToolsFramework::ModularCameraViewportContext + { + public: + AZ::Transform GetCameraTransform() const override; + void SetCameraTransform(const AZ::Transform& transform) override; + void ConnectViewMatrixChangedHandler(AZ::RPI::ViewportContext::MatrixChangedEvent::Handler& handler) override; + + private: + AZ::Transform m_cameraTransform = AZ::Transform::CreateIdentity(); + AZ::RPI::ViewportContext::MatrixChangedEvent m_viewMatrixChangedEvent; + }; } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h index ae4dc5dd25..388d24164a 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h @@ -35,6 +35,16 @@ namespace AtomToolsFramework //! Look at point after an interpolation has finished and no translation has occurred. virtual AZStd::optional LookAtAfterInterpolation() const = 0; + //! Return the current reference frame. + //! @note If a reference frame has not been set or a frame has been cleared, this is just the identity. + virtual AZ::Transform GetReferenceFrame() const = 0; + + //! Set a new reference frame other than the identity for the camera controller. + virtual void SetReferenceFrame(const AZ::Transform& worldFromLocal) = 0; + + //! Clear the current reference frame to restore the identity. + virtual void ClearReferenceFrame() = 0; + protected: ~ModularViewportCameraControllerRequests() = default; }; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h index 60ddf33cf7..318a49027a 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -118,7 +118,7 @@ namespace AtomToolsFramework void ToggleFullScreenState() override; float GetDpiScaleFactor() const override; uint32_t GetSyncInterval() const override; - uint32_t GetDisplayRefreshRate() const; + uint32_t GetDisplayRefreshRate() const override; protected: // AzFramework::InputChannelEventListener ... @@ -159,8 +159,6 @@ namespace AtomToolsFramework AZ::RPI::ViewPtr m_defaultCamera; // Our viewport-local aux geom pipeline for supplemental rendering. AZ::RPI::AuxGeomDrawPtr m_auxGeom; - // Used to keep track of a pending resize event to avoid initialization before window activate. - bool m_windowResizedEvent = false; // Tracks whether the cursor is currently over our viewport, used for mouse input event book-keeping. bool m_mouseOver = false; // The last recorded mouse position, in local viewport screen coordinates. diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index 36e45bb7ca..13b9a2ba27 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -87,6 +87,7 @@ namespace AtomToolsFramework AtomToolsApplication ::~AtomToolsApplication() { + m_styleManager.reset(); AtomToolsMainWindowNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); @@ -174,12 +175,14 @@ namespace AtomToolsFramework AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml"); - AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets(); + if (!AZ::RPI::RPISystemInterface::Get()->IsInitialized()) + { + AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets(); + } LoadSettings(); AtomToolsMainWindowNotificationBus::Handler::BusConnect(); - AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::CreateMainWindow); auto editorPythonEventsInterface = AZ::Interface::Get(); @@ -206,6 +209,7 @@ namespace AtomToolsFramework { // before modules are unloaded, destroy UI to free up any assets it cached AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow); + m_styleManager.reset(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect(); @@ -461,6 +465,7 @@ namespace AtomToolsFramework void AtomToolsApplication::Stop() { AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow); + m_styleManager.reset(); UnloadSettings(); Base::Stop(); @@ -468,7 +473,7 @@ namespace AtomToolsFramework void AtomToolsApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const { - appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Game; + appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Tool; } void AtomToolsApplication::OnTraceMessage([[maybe_unused]] AZStd::string_view message) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp index 56f9538452..e1f8573bb3 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp @@ -38,7 +38,7 @@ namespace AtomToolsFramework m_propertyEditor->Setup(context, instanceNotificationHandler, false); m_propertyEditor->AddInstance(instance, instanceClassId, nullptr, instanceToCompare); m_propertyEditor->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - m_propertyEditor->QueueInvalidation(AzToolsFramework::PropertyModificationRefreshLevel::Refresh_EntireTree); + m_propertyEditor->InvalidateAll(); m_layout->addWidget(m_propertyEditor); setLayout(m_layout); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp index fe3af1d0ef..5eda93ca59 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp @@ -29,30 +29,40 @@ namespace AtomToolsFramework { } + void InspectorWidget::AddHeading(QWidget* headingWidget) + { + headingWidget->setParent(m_ui->m_headingSection); + m_ui->m_headingSectionLayout->addWidget(headingWidget); + } + + void InspectorWidget::ClearHeading() + { + qDeleteAll(m_ui->m_headingSection->findChildren(QString(), Qt::FindDirectChildrenOnly)); + qDeleteAll(m_ui->m_headingSectionLayout->children()); + } + void InspectorWidget::Reset() { - qDeleteAll(m_ui->m_propertyContent->children()); - m_layout = new QVBoxLayout(m_ui->m_propertyContent); - m_layout->setContentsMargins(0, 0, 0, 0); - m_layout->setSpacing(0); + qDeleteAll(m_ui->m_groupContents->findChildren(QString(), Qt::FindDirectChildrenOnly)); + qDeleteAll(m_ui->m_groupContentsLayout->children()); m_groups.clear(); } void InspectorWidget::AddGroupsBegin() { - setUpdatesEnabled(false); + setVisible(false); Reset(); } void InspectorWidget::AddGroupsEnd() { - m_layout->addStretch(); + m_ui->m_groupContentsLayout->addStretch(); // Scroll to top whenever there is new content - m_ui->m_propertyScrollArea->verticalScrollBar()->setValue(m_ui->m_propertyScrollArea->verticalScrollBar()->minimum()); + m_ui->m_groupScrollArea->verticalScrollBar()->setValue(m_ui->m_groupScrollArea->verticalScrollBar()->minimum()); - setUpdatesEnabled(true); + setVisible(true); } void InspectorWidget::AddGroup( @@ -61,14 +71,14 @@ namespace AtomToolsFramework const AZStd::string& groupDescription, QWidget* groupWidget) { - InspectorGroupHeaderWidget* groupHeader = new InspectorGroupHeaderWidget(m_ui->m_propertyContent); + InspectorGroupHeaderWidget* groupHeader = new InspectorGroupHeaderWidget(m_ui->m_groupContents); groupHeader->setText(groupDisplayName.c_str()); groupHeader->setToolTip(groupDescription.c_str()); - m_layout->addWidget(groupHeader); + m_ui->m_groupContentsLayout->addWidget(groupHeader); groupWidget->setObjectName(groupNameId.c_str()); - groupWidget->setParent(m_ui->m_propertyContent); - m_layout->addWidget(groupWidget); + groupWidget->setParent(m_ui->m_groupContents); + m_ui->m_groupContentsLayout->addWidget(groupWidget); m_groups[groupNameId] = {groupHeader, groupWidget}; @@ -101,28 +111,18 @@ namespace AtomToolsFramework bool InspectorWidget::IsGroupVisible(const AZStd::string& groupNameId) const { auto groupItr = m_groups.find(groupNameId); - if (groupItr != m_groups.end()) - { - return groupItr->second.m_header->isVisible(); - } - - return false; + return groupItr != m_groups.end() ? groupItr->second.m_header->isVisible() : false; } bool InspectorWidget::IsGroupHidden(const AZStd::string& groupNameId) const { auto groupItr = m_groups.find(groupNameId); - if (groupItr != m_groups.end()) - { - return groupItr->second.m_header->isHidden(); - } - - return false; + return groupItr != m_groups.end() ? groupItr->second.m_header->isHidden() : false; } void InspectorWidget::RefreshGroup(const AZStd::string& groupNameId) { - for (auto groupWidget : m_ui->m_propertyContent->findChildren(groupNameId.c_str())) + for (auto groupWidget : m_ui->m_groupContents->findChildren(groupNameId.c_str())) { groupWidget->Refresh(); } @@ -130,7 +130,7 @@ namespace AtomToolsFramework void InspectorWidget::RebuildGroup(const AZStd::string& groupNameId) { - for (auto groupWidget : m_ui->m_propertyContent->findChildren(groupNameId.c_str())) + for (auto groupWidget : m_ui->m_groupContents->findChildren(groupNameId.c_str())) { groupWidget->Rebuild(); } @@ -138,7 +138,7 @@ namespace AtomToolsFramework void InspectorWidget::RefreshAll() { - for (auto groupWidget : m_ui->m_propertyContent->findChildren()) + for (auto groupWidget : m_ui->m_groupContents->findChildren()) { groupWidget->Refresh(); } @@ -146,7 +146,7 @@ namespace AtomToolsFramework void InspectorWidget::RebuildAll() { - for (auto groupWidget : m_ui->m_propertyContent->findChildren()) + for (auto groupWidget : m_ui->m_groupContents->findChildren()) { groupWidget->Rebuild(); } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.ui b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.ui index 54976db849..13f9c9e41c 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.ui +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.ui @@ -6,20 +6,14 @@ 0 0 - 693 - 798 + 685 + 775 - - - 0 - 0 - - Inspector - + 0 @@ -36,50 +30,103 @@ 0 - - - Qt::ScrollBarAsNeeded - - - true - - - - - 0 - 0 - 691 - 796 - + + + + 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::StyledPanel + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 - - QFrame::Raised + + 0 - - - - + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::ScrollBarAsNeeded + + + true + + + + + 0 + 0 + 683 + 763 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp index 4419e0c49f..bcc9a08f77 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp @@ -30,6 +30,18 @@ namespace AtomToolsFramework ""); AZ_CVAR(float, ed_cameraSystemOrbitPointSize, 0.1f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); + AZ::Transform TransformFromMatrix4x4(const AZ::Matrix4x4& matrix) + { + const auto rotation = AZ::Matrix3x3::CreateFromMatrix4x4(matrix); + const auto translation = matrix.GetTranslation(); + return AZ::Transform::CreateFromMatrix3x3AndTranslation(rotation, translation); + } + + AZ::Matrix4x4 Matrix4x4FromTransform(const AZ::Transform& transform) + { + return AZ::Matrix4x4::CreateFromQuaternionAndTranslation(transform.GetRotation(), transform.GetTranslation()); + } + // debug void DrawPreviewAxis(AzFramework::DebugDisplayRequests& display, const AZ::Transform& transform, const float axisLength) { @@ -167,11 +179,19 @@ namespace AtomToolsFramework controller->SetupCameraControllerPriority(m_priorityFn); controller->SetupCameraControllerViewportContext(m_modularCameraViewportContext); - auto handleCameraChange = [this](const AZ::Matrix4x4&) + auto handleCameraChange = [this]([[maybe_unused]] const AZ::Matrix4x4& cameraView) { // ignore these updates if the camera is being updated internally if (!m_updatingTransformInternally) { + if (m_storedCamera.has_value()) + { + // if an external change occurs ensure we update the stored reference frame if one is set + RefreshReferenceFrame(); + return; + } + + m_previousCamera = m_targetCamera; UpdateCameraFromTransform(m_targetCamera, m_modularCameraViewportContext->GetCameraTransform()); m_camera = m_targetCamera; } @@ -231,7 +251,7 @@ namespace AtomToolsFramework } } - m_modularCameraViewportContext->SetCameraTransform(m_camera.Transform()); + m_modularCameraViewportContext->SetCameraTransform(m_referenceFrameOverride * m_camera.Transform()); } else if (m_cameraMode == CameraMode::Animation) { @@ -240,6 +260,8 @@ namespace AtomToolsFramework return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); }; + m_cameraAnimation.m_time = AZ::GetClamp(m_cameraAnimation.m_time + event.m_deltaTime.count(), 0.0f, 1.0f); + const auto& [transformStart, transformEnd, animationTime] = m_cameraAnimation; const float transitionTime = smootherStepFn(animationTime); @@ -253,14 +275,13 @@ namespace AtomToolsFramework m_camera.m_lookAt = current.GetTranslation(); m_targetCamera = m_camera; + m_modularCameraViewportContext->SetCameraTransform(current); + if (animationTime >= 1.0f) { m_cameraMode = CameraMode::Control; + RefreshReferenceFrame(); } - - m_cameraAnimation.m_time = AZ::GetClamp(animationTime + event.m_deltaTime.count(), 0.0f, 1.0f); - - m_modularCameraViewportContext->SetCameraTransform(current); } m_updatingTransformInternally = false; @@ -280,7 +301,7 @@ namespace AtomToolsFramework void ModularViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal, const float lookAtDistance) { m_cameraMode = CameraMode::Animation; - m_cameraAnimation = CameraAnimation{ m_camera.Transform(), worldFromLocal, 0.0f }; + m_cameraAnimation = CameraAnimation{ m_referenceFrameOverride * m_camera.Transform(), worldFromLocal, 0.0f }; m_lookAtAfterInterpolation = worldFromLocal.GetTranslation() + worldFromLocal.GetBasisY() * lookAtDistance; } @@ -288,4 +309,59 @@ namespace AtomToolsFramework { return m_lookAtAfterInterpolation; } + + AZ::Transform ModularViewportCameraControllerInstance::GetReferenceFrame() const + { + return m_referenceFrameOverride; + } + + void ModularViewportCameraControllerInstance::SetReferenceFrame(const AZ::Transform& worldFromLocal) + { + if (!m_storedCamera.has_value()) + { + m_storedCamera = m_previousCamera; + } + + m_referenceFrameOverride = worldFromLocal; + m_targetCamera.m_pitch = 0.0f; + m_targetCamera.m_yaw = 0.0f; + m_targetCamera.m_lookAt = AZ::Vector3::CreateZero(); + m_targetCamera.m_lookDist = 0.0f; + m_camera = m_targetCamera; + } + + void ModularViewportCameraControllerInstance::ClearReferenceFrame() + { + m_referenceFrameOverride = AZ::Transform::CreateIdentity(); + + if (m_storedCamera.has_value()) + { + m_targetCamera = m_storedCamera.value(); + m_camera = m_targetCamera; + } + + m_storedCamera.reset(); + } + + void ModularViewportCameraControllerInstance::RefreshReferenceFrame() + { + m_referenceFrameOverride = m_modularCameraViewportContext->GetCameraTransform() * m_camera.Transform().GetInverse(); + } + + AZ::Transform PlaceholderModularCameraViewportContextImpl::GetCameraTransform() const + { + return m_cameraTransform; + } + + void PlaceholderModularCameraViewportContextImpl::SetCameraTransform(const AZ::Transform& transform) + { + m_cameraTransform = transform; + m_viewMatrixChangedEvent.Signal(AzFramework::CameraViewFromCameraTransform(Matrix4x4FromTransform(transform))); + } + + void PlaceholderModularCameraViewportContextImpl::ConnectViewMatrixChangedHandler( + AZ::RPI::ViewportContext::MatrixChangedEvent::Handler& handler) + { + handler.Connect(m_viewMatrixChangedEvent); + } } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index 4446e11231..2a15041e20 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -254,28 +254,11 @@ namespace AtomToolsFramework void RenderViewportWidget::resizeEvent([[maybe_unused]] QResizeEvent* event) { - // We need to wait until the window is activated, so the underlying surface - // has been created and has the correct size. - if (windowHandle()->isActive()) - { - SendWindowResizeEvent(); - } - else - { - m_windowResizedEvent = true; - } + SendWindowResizeEvent(); } bool RenderViewportWidget::event(QEvent* event) { - // Check if we have a pending resize event. - // At this point the surface has been created and has - // the proper dimensions. - if (event->type() == QEvent::WindowActivate && m_windowResizedEvent) - { - SendWindowResizeEvent(); - } - return QWidget::event(event); } @@ -372,7 +355,6 @@ namespace AtomToolsFramework AzFramework::WindowNotificationBus::Event( GetNativeWindowHandle(), &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height()); - m_windowResizedEvent = false; } void RenderViewportWidget::NotifyUpdateRefreshRate() diff --git a/Gems/Atom/Tools/AtomToolsFramework/gem.json b/Gems/Atom/Tools/AtomToolsFramework/gem.json index 5cea71c5cc..2b0380bdae 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/gem.json +++ b/Gems/Atom/Tools/AtomToolsFramework/gem.json @@ -8,7 +8,11 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI", + "Atom_RHI", + "Atom_Bootstrap" + ] } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt b/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt index 6230217ac2..d585e81162 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt @@ -93,12 +93,14 @@ ly_add_target( AUTOMOC FILES_CMAKE materialeditor_files.cmake - Source/Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + PLATFORM_INCLUDE_FILES + ${pal_source_dir}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake INCLUDE_DIRECTORIES PRIVATE . Source - Source/Platform/${PAL_PLATFORM_NAME} + ${pal_source_dir} PUBLIC Include BUILD_DEPENDENCIES @@ -108,8 +110,14 @@ ly_add_target( Gem::MaterialEditor.Window Gem::MaterialEditor.Viewport Gem::MaterialEditor.Document + RUNTIME_DEPENDENCIES + Gem::AtomToolsFramework.Editor + Gem::EditorPythonBindings.Editor + Gem::ImageProcessingAtom.Editor ) +ly_set_gem_variant_to_load(TARGETS MaterialEditor VARIANTS Tools) + # Add a 'builders' alias to allow the MaterialEditor root gem path to be added to the generated # cmake_dependencies..assetprocessor.setreg to allow the asset scan folder for it to be added ly_create_alias(NAME MaterialEditor.Builders NAMESPACE Gem) @@ -118,26 +126,6 @@ ly_create_alias(NAME MaterialEditor.Builders NAMESPACE Gem) # Editor opens up the MaterialEditor ly_add_dependencies(Editor Gem::MaterialEditor) -ly_add_target_files( - TARGETS - MaterialEditor - FILES - ${CMAKE_CURRENT_LIST_DIR}/../MaterialEditor.xml - OUTPUT_SUBDIRECTORY - Gems/Atom/Tools/MaterialEditor -) - -ly_add_target_dependencies( - TARGETS - MaterialEditor - DEPENDENCIES_FILES - tool_dependencies.cmake - Source/Platform/${PAL_PLATFORM_NAME}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake - # The Material Editor needs the LyShine "Tools" gem variant for the custom LyShine pass - DEPENDENT_TARGETS - Gem::LyShine.Tools -) - # Inject the project path into the MaterialEditor VS debugger command arguments if the build system being invoked # in a project centric view if(NOT PROJECT_NAME STREQUAL "O3DE") diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index 11834beb73..d032f35e38 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -763,6 +763,10 @@ namespace MaterialEditor return false; } + // Pipeline State Object changes are always allowed in the material editor because it only runs on developer systems + // where such changes are supported at runtime. + m_materialInstance->SetPsoHandlingOverride(AZ::RPI::MaterialPropertyPsoHandling::Allowed); + // Populate the property map from a combination of source data and assets // Assets must still be used for now because they contain the final accumulated value after all other materials // in the hierarchy are applied diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp deleted file mode 100644 index 14b5fee1fe..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include - -namespace Platform -{ - void LoadPluginDependencies() - { - AZ_Warning("Material Editor", false, "LoadPluginDependencies() function is not implemented"); - } - - void ProcessInput(void* message) - { - AZ_Warning("Material Editor", false, "ProcessInput() function is not implemented"); - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - AZ_Warning("Material Editor", false, "GetWindowHandle() function is not implemented"); - AZ_UNUSED(winId); - return nullptr; - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - AZ_Warning("Material Editor", false, "GetClientAreaSize() function is not implemented"); - AZ_UNUSED(window); - return AzFramework::WindowSize{1,1}; - } -} diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake index c09abb0dd2..2417ad1b55 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -9,5 +9,4 @@ set(FILES MaterialEditor_Traits_Platform.h MaterialEditor_Traits_Linux.h - MaterialEditor_Linux.cpp ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/tool_dependencies_linux.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/tool_dependencies_linux.cmake index 5bf4d7cb7e..b2885100e9 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/tool_dependencies_linux.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/tool_dependencies_linux.cmake @@ -6,5 +6,5 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp deleted file mode 100644 index 14b5fee1fe..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include - -namespace Platform -{ - void LoadPluginDependencies() - { - AZ_Warning("Material Editor", false, "LoadPluginDependencies() function is not implemented"); - } - - void ProcessInput(void* message) - { - AZ_Warning("Material Editor", false, "ProcessInput() function is not implemented"); - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - AZ_Warning("Material Editor", false, "GetWindowHandle() function is not implemented"); - AZ_UNUSED(winId); - return nullptr; - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - AZ_Warning("Material Editor", false, "GetClientAreaSize() function is not implemented"); - AZ_UNUSED(window); - return AzFramework::WindowSize{1,1}; - } -} diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/platform_mac_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/platform_mac_files.cmake index ec56cc1c4e..7275f82047 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/platform_mac_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/platform_mac_files.cmake @@ -9,5 +9,4 @@ set(FILES MaterialEditor_Traits_Platform.h MaterialEditor_Traits_Mac.h - MaterialEditor_Mac.cpp ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/tool_dependencies_mac.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/tool_dependencies_mac.cmake index 5bf4d7cb7e..b2885100e9 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/tool_dependencies_mac.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/tool_dependencies_mac.cmake @@ -6,5 +6,5 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp deleted file mode 100644 index 65c135315b..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include -#include -#include - -namespace Platform -{ - void ProcessInput(void* message) - { - MSG* msg = (MSG*)message; - - // Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system, - // but only while in game mode so we don't accumulate raw input events before we start actually - // ticking the input devices, otherwise the queued events will get sent when entering game mode. - if (msg->message == WM_INPUT) - { - UINT rawInputSize; - const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER); - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, NULL, &rawInputSize, rawInputHeaderSize); - - LPBYTE rawInputBytes = new BYTE[rawInputSize]; - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize); - - RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes; - - AzFramework::RawInputNotificationBusWindows::Broadcast( - &AzFramework::RawInputNotificationBusWindows::Events::OnRawInputEvent, *rawInput); - } - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - return reinterpret_cast(winId); - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - RECT r; - if (GetWindowRect(reinterpret_cast(window), &r)) - { - return AzFramework::WindowSize{aznumeric_cast(r.right - r.left), aznumeric_cast(r.bottom - r.top)}; - } - else - { - AZ_Assert(false, "Failed to get dimensions for window"); - return AzFramework::WindowSize{}; - } - } -} diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/platform_windows_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/platform_windows_files.cmake index c104a2b8be..608cb36a28 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/platform_windows_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -9,6 +9,5 @@ set(FILES MaterialEditor_Traits_Platform.h MaterialEditor_Traits_Windows.h - MaterialEditor_Windows.cpp MaterialEditor.rc ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/tool_dependencies_windows.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/tool_dependencies_windows.cmake index 374438983f..e1e811ff67 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/tool_dependencies_windows.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/tool_dependencies_windows.cmake @@ -6,6 +6,6 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES Gem::QtForPython.Editor ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp index 36498d6d08..7edff11174 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp @@ -231,12 +231,10 @@ namespace MaterialEditor MaterialViewportNotificationBus::Handler::BusConnect(); AZ::TickBus::Handler::BusConnect(); AZ::TransformNotificationBus::MultiHandler::BusConnect(m_cameraEntity->GetId()); - AzFramework::WindowSystemRequestBus::Handler::BusConnect(); } MaterialViewportRenderer::~MaterialViewportRenderer() { - AzFramework::WindowSystemRequestBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::MultiHandler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect(); @@ -287,11 +285,6 @@ namespace MaterialEditor return m_viewportController; } - AzFramework::NativeWindowHandle MaterialViewportRenderer::GetDefaultWindowHandle() - { - return (m_windowContext) ? m_windowContext->GetWindowHandle() : nullptr; - } - void MaterialViewportRenderer::OnDocumentOpened(const AZ::Uuid& documentId) { AZ::Data::Instance materialInstance; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h index 8a3e0ca4ff..c6380ddc00 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h @@ -16,7 +16,6 @@ #include #include #include -#include #include namespace AZ @@ -46,7 +45,6 @@ namespace MaterialEditor , public AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler , public MaterialViewportNotificationBus::Handler , public AZ::TransformNotificationBus::MultiHandler - , public AzFramework::WindowSystemRequestBus::Handler { public: AZ_CLASS_ALLOCATOR(MaterialViewportRenderer, AZ::SystemAllocator, 0); @@ -81,9 +79,6 @@ namespace MaterialEditor // AZ::TransformNotificationBus::MultiHandler overrides... void OnTransformChanged(const AZ::Transform&, const AZ::Transform&) override; - // AzFramework::WindowSystemRequestBus::Handler overrides ... - AzFramework::NativeWindowHandle GetDefaultWindowHandle() override; - using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle; AZ::Data::Instance m_swapChainPass; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp index c78951ed45..0322fa194e 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp @@ -13,24 +13,16 @@ #include #include -#include -#include +#include +#include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include #include -#include "Source/Viewport/ui_MaterialViewportWidget.h" +#include "Viewport/ui_MaterialViewportWidget.h" AZ_POP_DISABLE_WARNING -#include #include -namespace Platform -{ - void ProcessInput(void* message); -} - - namespace MaterialEditor { @@ -40,11 +32,6 @@ namespace MaterialEditor { m_ui->setupUi(this); - if (auto dispatcher = QAbstractEventDispatcher::instance()) - { - dispatcher->installNativeEventFilter(this); - } - // The viewport context created by AtomToolsFramework::RenderViewportWidget has no name. // Systems like frame capturing and post FX expect there to be a context with DefaultViewportContextName auto viewportContextManager = AZ::Interface::Get(); @@ -54,13 +41,4 @@ namespace MaterialEditor m_renderer = AZStd::make_unique(GetViewportContext()->GetWindowContext()); GetControllerList()->Add(m_renderer->GetController()); } - - // This is a temporary fix to get input working in Qt window, otherwise it wont receive input events - // This will later be handled on the QApplication subclass level - bool MaterialViewportWidget::nativeEventFilter(const QByteArray& /*eventType*/, void* message, long* /*result*/) - { - Platform::ProcessInput(message); - - return false; - } } // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h index 438c7b9a9e..2a71dcc1df 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h @@ -8,12 +8,10 @@ #pragma once #if !defined(Q_MOC_RUN) -#include #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT #include -#include AZ_POP_DISABLE_WARNING #endif @@ -38,14 +36,11 @@ namespace MaterialEditor class MaterialViewportWidget : public AtomToolsFramework::RenderViewportWidget - , public QAbstractNativeEventFilter { public: MaterialViewportWidget(QWidget* parent = nullptr); QScopedPointer m_ui; AZStd::unique_ptr m_renderer; - - bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; }; } // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake index 2b554a852e..d4c4364ba7 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake @@ -10,5 +10,4 @@ set(FILES Source/main.cpp Source/MaterialEditorApplication.cpp Source/MaterialEditorApplication.h - tool_dependencies.cmake ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/tool_dependencies.cmake b/Gems/Atom/Tools/MaterialEditor/Code/tool_dependencies.cmake deleted file mode 100644 index 8803be3852..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/tool_dependencies.cmake +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -set(GEM_DEPENDENCIES - Gem::Atom_RHI_Null.Private - Gem::Atom_RHI_DX12.Private - Gem::Atom_RHI_Vulkan.Private - Gem::Atom_RHI.Private - Gem::Atom_Component_DebugCamera - Gem::Atom_RPI.Editor - Gem::Atom_RPI.Builders - Gem::Atom_Feature_Common.Editor - Gem::AtomToolsFramework.Editor - Gem::AtomLyIntegration_CommonFeatures.Editor - Gem::EditorPythonBindings.Editor - Gem::ImageProcessingAtom.Editor -) diff --git a/Gems/Atom/Tools/MaterialEditor/MaterialEditor.xml b/Gems/Atom/Tools/MaterialEditor/MaterialEditor.xml deleted file mode 100644 index 9f91001f66..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/MaterialEditor.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Gems/Atom/Tools/MaterialEditor/gem.json b/Gems/Atom/Tools/MaterialEditor/gem.json index 83d9909a84..85ff434eab 100644 --- a/Gems/Atom/Tools/MaterialEditor/gem.json +++ b/Gems/Atom/Tools/MaterialEditor/gem.json @@ -8,7 +8,16 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "AtomToolsFramework", + "Atom_RPI", + "Atom_RHI", + "Atom_Feature_Common", + "ImageProcessingAtom", + "Atom_Component_DebugCamera", + "CommonFeaturesAtom", + "LyShine" + ] } diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt b/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt index d7e415a279..d384378b09 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt @@ -43,6 +43,7 @@ ly_add_target( NAMESPACE Gem AUTOMOC AUTOUIC + AUTORCC FILES_CMAKE shadermanagementconsolewindow_files.cmake INCLUDE_DIRECTORIES @@ -64,6 +65,8 @@ ly_add_target( FILES_CMAKE shadermanagementconsole_files.cmake ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + PLATFORM_INCLUDE_FILES + ${pal_source_dir}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake INCLUDE_DIRECTORIES PRIVATE . @@ -78,27 +81,16 @@ ly_add_target( Gem::ShaderManagementConsole.Window Gem::ShaderManagementConsole.Document RUNTIME_DEPENDENCIES - Gem::Atom_RHI_DX12.Private - Gem::Atom_RHI_Vulkan.Private - Gem::Atom_RHI.Private - Gem::Atom_RPI.Private - Gem::Atom_RPI.Builders - Gem::Atom_Feature_Common.Editor + Gem::AtomToolsFramework.Editor Gem::EditorPythonBindings.Editor ) +ly_set_gem_variant_to_load(TARGETS ShaderManagementConsole VARIANTS Tools) + # Add build dependency to Editor for the ShaderManagementConsole application since # Editor opens up the ShaderManagementConsole ly_add_dependencies(Editor Gem::ShaderManagementConsole) -ly_add_target_dependencies( - TARGETS - ShaderManagementConsole - DEPENDENCIES_FILES - tool_dependencies.cmake - Source/Platform/${PAL_PLATFORM_NAME}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake -) - # Inject the project path into the ShaderManagementConsole VS debugger command arguments if the build system being invoked # in a project centric view if(NOT PROJECT_NAME STREQUAL "O3DE") @@ -108,9 +100,14 @@ endif() # Adds the ShaderManagementConsole target as a C preprocessor define so that it can be used as a Settings Registry # specialization in order to look up the generated .setreg which contains the dependencies # specified for the target. -set_source_files_properties( - Source/ShaderManagementConsoleApplication.cpp - PROPERTIES - COMPILE_DEFINITIONS - LY_CMAKE_TARGET="ShaderManagementConsole" -) +if(TARGET ShaderManagementConsole) + set_source_files_properties( + Source/ShaderManagementConsoleApplication.cpp + PROPERTIES + COMPILE_DEFINITIONS + LY_CMAKE_TARGET="ShaderManagementConsole" + ) +else() + message(FATAL_ERROR "Cannot set LY_CMAKE_TARGET define to ShaderManagementConsole as the target doesn't exist anymore." + " Perhaps it has been renamed") +endif() diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/PAL_linux.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/PAL_linux.cmake index 762ac16004..b5332e2e15 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/PAL_linux.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/PAL_linux.cmake @@ -6,4 +6,4 @@ # # -set(PAL_TRAIT_ATOM_SHADER_MANAGEMENT_CONSOLE_APPLICATION_SUPPORTED FALSE) +set(PAL_TRAIT_ATOM_SHADER_MANAGEMENT_CONSOLE_APPLICATION_SUPPORTED TRUE) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h new file mode 100644 index 0000000000..2897402f04 --- /dev/null +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#define AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT "" + diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Platform.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Platform.h new file mode 100644 index 0000000000..d381cc79d0 --- /dev/null +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Platform.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/platform_linux_files.cmake index c2c5a11c4c..11babcdf15 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/platform_linux_files.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -7,4 +7,6 @@ # set(FILES + ShaderManagementConsole_Traits_Platform.h + ShaderManagementConsole_Traits_Linux.h ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/tool_dependencies_linux.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/tool_dependencies_linux.cmake index 5bf4d7cb7e..b2885100e9 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/tool_dependencies_linux.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/tool_dependencies_linux.cmake @@ -6,5 +6,5 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp deleted file mode 100644 index 6a05b895e0..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include - -namespace Platform -{ - void ProcessInput(void* message) - { - AZ_Warning("Shader Management Console", false, "ProcessInput() function is not implemented"); - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - AZ_Warning("Shader Management Console", false, "GetWindowHandle() function is not implemented"); - AZ_UNUSED(winId); - return nullptr; - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - AZ_Warning("Shader Management Console", false, "GetClientAreaSize() function is not implemented"); - AZ_UNUSED(window); - return AzFramework::WindowSize{1,1}; - } -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/platform_mac_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/platform_mac_files.cmake index e48e836b73..6fe2859a89 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/platform_mac_files.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/platform_mac_files.cmake @@ -9,5 +9,4 @@ set(FILES ShaderManagementConsole_Traits_Platform.h ShaderManagementConsole_Traits_Mac.h - ShaderManagementConsole_Mac.cpp ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/tool_dependencies_mac.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/tool_dependencies_mac.cmake index 5bf4d7cb7e..b2885100e9 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/tool_dependencies_mac.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/tool_dependencies_mac.cmake @@ -6,5 +6,5 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp deleted file mode 100644 index ff94ef1196..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include - -namespace Platform -{ - void ProcessInput(void* message) - { - MSG* msg = (MSG*)message; - - // Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system, - // but only while in game mode so we don't accumulate raw input events before we start actually - // ticking the input devices, otherwise the queued events will get sent when entering game mode. - if (msg->message == WM_INPUT) - { - UINT rawInputSize; - const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER); - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, NULL, &rawInputSize, rawInputHeaderSize); - - LPBYTE rawInputBytes = new BYTE[rawInputSize]; - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize); - - RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes; - - AzFramework::RawInputNotificationBusWindows::Broadcast( - &AzFramework::RawInputNotificationBusWindows::Events::OnRawInputEvent, *rawInput); - } - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - return reinterpret_cast(winId); - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - RECT r; - if (GetWindowRect(reinterpret_cast(window), &r)) - { - return AzFramework::WindowSize{aznumeric_cast(r.right - r.left), aznumeric_cast(r.bottom - r.top)}; - } - else - { - AZ_Assert(false, "Failed to get dimensions for window"); - return AzFramework::WindowSize{}; - } - } -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/platform_windows_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/platform_windows_files.cmake index 4ec319f402..b978cedeb4 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/platform_windows_files.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -9,6 +9,5 @@ set(FILES ShaderManagementConsole_Traits_Platform.h ShaderManagementConsole_Traits_Windows.h - ShaderManagementConsole_Windows.cpp ShaderManagementConsole.rc ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/tool_dependencies_windows.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/tool_dependencies_windows.cmake index 374438983f..e1e811ff67 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/tool_dependencies_windows.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/tool_dependencies_windows.cmake @@ -6,6 +6,6 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES Gem::QtForPython.Editor ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/tool_dependencies.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/tool_dependencies.cmake deleted file mode 100644 index 8803be3852..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/tool_dependencies.cmake +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -set(GEM_DEPENDENCIES - Gem::Atom_RHI_Null.Private - Gem::Atom_RHI_DX12.Private - Gem::Atom_RHI_Vulkan.Private - Gem::Atom_RHI.Private - Gem::Atom_Component_DebugCamera - Gem::Atom_RPI.Editor - Gem::Atom_RPI.Builders - Gem::Atom_Feature_Common.Editor - Gem::AtomToolsFramework.Editor - Gem::AtomLyIntegration_CommonFeatures.Editor - Gem::EditorPythonBindings.Editor - Gem::ImageProcessingAtom.Editor -) diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h index ea4dd20250..a649fcf0f7 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h @@ -174,7 +174,7 @@ namespace AZ AZStd::unordered_map> m_savedData; // Region color cache - AZStd::unordered_map m_regionColorMap; + AZStd::unordered_map m_regionColorMap; // Tracks the frame boundaries AZStd::vector m_frameEndTicks = { INT64_MIN }; diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl index 61f68197b6..a573e3019a 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl @@ -124,7 +124,7 @@ namespace AZ m_cpuTimingStatisticsWhenPause = currentCpuTimingStatistics; CollectFrameData(); - CullFrameData(currentCpuTimingStatistics); + CullFrameData(currentCpuTimingStatistics); // Only listen to system ticks when the profiler is active if (!SystemTickBus::Handler::BusIsConnected()) @@ -148,7 +148,7 @@ namespace AZ } } ImGui::End(); - + if (m_captureToFile) { AZStd::sys_time_t timeNow = AZStd::GetTimeNowSecond(); @@ -325,7 +325,7 @@ namespace AZ { const bool ascending = sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending; const ImS16 columnToSort = sortSpecs->Specs->ColumnIndex; - + switch (columnToSort) { case (0): // Sort by group name @@ -343,7 +343,7 @@ namespace AZ case (4): // Sort by invocations AZStd::sort(m_tableData.begin(), m_tableData.end(), TableRow::TableRowCompareFunctor(&TableRow::m_invocationsLastFrame, ascending)); break; - case (5): // Sort by total time + case (5): // Sort by total time AZStd::sort(m_tableData.begin(), m_tableData.end(), TableRow::TableRowCompareFunctor(&TableRow::m_lastFrameTotalTicks, ascending)); break; } @@ -401,7 +401,7 @@ namespace AZ } DrawTable(); - } + } } inline void ImGuiCpuProfiler::DrawFilePicker() @@ -460,17 +460,17 @@ namespace AZ const auto [groupRegionNameItr, wasGroupRegionNameInserted] = m_deserializedGroupRegionNamePool.emplace(groupNameItr->c_str(), regionNameItr->c_str()); - const RHI::CachedTimeRegion newRegion(&(*groupRegionNameItr), entry.m_stackDepth, entry.m_startTick, entry.m_endTick); + const RHI::CachedTimeRegion newRegion(*groupRegionNameItr, entry.m_stackDepth, entry.m_startTick, entry.m_endTick); m_savedData[entry.m_threadId].push_back(newRegion); - // Since we don't serialize the frame boundaries, we need to use the RPI's OnSystemTick event as a heuristic. + // Since we don't serialize the frame boundaries, we need to use the RPI's OnSystemTick event as a heuristic. const static Name frameBoundaryName = Name("RPISystem: OnSystemTick"); if (entry.m_regionName == frameBoundaryName) { m_frameEndTicks.push_back(entry.m_endTick); - } + } - // Update running statistics + // Update running statistics if (!m_groupRegionMap[*groupNameItr].contains(*regionNameItr)) { m_groupRegionMap[*groupNameItr][*regionNameItr].m_groupName = *groupNameItr; @@ -487,7 +487,7 @@ namespace AZ // Invariant: each vector in m_savedData must be sorted so that we can efficiently cull region data. for (auto& [threadId, singleThreadData] : m_savedData) { - AZStd::sort(singleThreadData.begin(), singleThreadData.end(), + AZStd::sort(singleThreadData.begin(), singleThreadData.end(), [](const TimeRegion& lhs, const TimeRegion& rhs) { return lhs.m_startTick < rhs.m_startTick; @@ -669,7 +669,7 @@ namespace AZ // Iterate through the entire TimeRegionMap and copy the data since it will get deleted on the next frame for (const auto& [threadId, singleThreadRegionMap] : timeRegionMap) { - const size_t threadIdHashed = AZStd::hash{}(threadId); + const size_t threadIdHashed = AZStd::hash{}(threadId); // The profiler can sometime return threads without any profiling events when dropping threads, FIXME(ATOM-15949) if (singleThreadRegionMap.size() == 0) { @@ -686,7 +686,7 @@ namespace AZ newVisualizerData.push_back(region); // Copies // Also update the statistical view's data - const AZStd::string& groupName = region.m_groupRegionName->m_groupName; + const AZStd::string& groupName = region.m_groupRegionName.m_groupName; if (!m_groupRegionMap[groupName].contains(regionName)) { @@ -765,7 +765,7 @@ namespace AZ inline void ImGuiCpuProfiler::DrawBlock(const TimeRegion& block, u64 targetRow) { // Don't draw anything if the user is searching for regions and this block doesn't pass the filter - if (!m_visualizerHighlightFilter.PassFilter(block.m_groupRegionName->m_regionName)) + if (!m_visualizerHighlightFilter.PassFilter(block.m_groupRegionName.m_regionName)) { return; } @@ -798,7 +798,7 @@ namespace AZ if (regionPixelWidth > maxCharWidth) // We can draw at least one character { const AZStd::string label = - AZStd::string::format("%s/ %s", block.m_groupRegionName->m_groupName, block.m_groupRegionName->m_regionName); + AZStd::string::format("%s/ %s", block.m_groupRegionName.m_groupName, block.m_groupRegionName.m_regionName); const float textWidth = ImGui::CalcTextSize(label.c_str()).x; if (regionPixelWidth < textWidth) // Not enough space in the block to draw the whole name, draw clipped text. @@ -809,7 +809,7 @@ namespace AZ // so we must adjust for the scale manually. const float scaleFactor = ImGui::GetIO().FontGlobalScale; const float fontSize = ImGui::GetFont()->FontSize * scaleFactor; - + ImGui::GetFont()->RenderText(drawList, fontSize, startPoint, IM_COL32_WHITE, clipRect, label.c_str(), 0); } else // We have enough space to draw the entire label, draw and center text. @@ -828,7 +828,7 @@ namespace AZ if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { m_enableVisualizer = false; - const auto newFilter = AZStd::string(block.m_groupRegionName->m_regionName); + const auto newFilter = AZStd::string(block.m_groupRegionName.m_regionName); m_timedRegionFilter = ImGuiTextFilter(newFilter.c_str()); m_timedRegionFilter.Build(); } @@ -836,7 +836,7 @@ namespace AZ drawList->AddRect(startPoint, endPoint, ImGui::GetColorU32({ 1, 1, 1, 1 }), 0.0, 0, 1.5); ImGui::BeginTooltip(); - ImGui::Text("%s::%s", block.m_groupRegionName->m_groupName, block.m_groupRegionName->m_regionName); + ImGui::Text("%s::%s", block.m_groupRegionName.m_groupName, block.m_groupRegionName.m_regionName); ImGui::Text("Execution time: %.3f ms", CpuProfilerImGuiHelper::TicksToMs(block.m_endTick - block.m_startTick)); ImGui::Text("Ticks %lld => %lld", block.m_startTick, block.m_endTick); ImGui::EndTooltip(); @@ -846,10 +846,10 @@ namespace AZ inline ImU32 ImGuiCpuProfiler::GetBlockColor(const TimeRegion& block) { // Use the GroupRegionName pointer a key into the cache, equal regions will have equal pointers - const GroupRegionName* key = block.m_groupRegionName; - if (m_regionColorMap.contains(key)) // Cache hit + const GroupRegionName& key = block.m_groupRegionName; + if (auto iter = m_regionColorMap.find(key); iter != m_regionColorMap.end()) // Cache hit { - return ImGui::GetColorU32(m_regionColorMap[key]); + return ImGui::GetColorU32(iter->second); } // Cache miss, generate a new random color diff --git a/Gems/Atom/gem.json b/Gems/Atom/gem.json index 99ca26025a..1f5e4a37f3 100644 --- a/Gems/Atom/gem.json +++ b/Gems/Atom/gem.json @@ -5,8 +5,24 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Atom Renderer Gem provides Atom Renderer and its associated tools (such as Material Editor), utilites, libraries, and interfaces.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Core"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Core" + ], "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom/", + "dependencies": [ + "Atom_Feature_Common", + "AtomShader", + "Atom_Bootstrap", + "Atom_Component_DebugCamera", + "Atom_RHI", + "Atom_RPI", + "AtomToolsFramework", + "MaterialEditor", + "Atom_AtomBridge" + ] } diff --git a/Gems/AtomContent/.gitignore b/Gems/AtomContent/.gitignore new file mode 100644 index 0000000000..174c2dd972 --- /dev/null +++ b/Gems/AtomContent/.gitignore @@ -0,0 +1,8 @@ +_savebackup/ +.mayaSwatches/ +*.swatches +[Bb]uild/ +[Cc]ache/ +[Uu]ser/ +[Uu]ser_Env.bat +.maya_data/ \ No newline at end of file diff --git a/Gems/AtomContent/ReferenceMaterials/Launch_WingIDE-7-1.bat b/Gems/AtomContent/ReferenceMaterials/Launch_WingIDE-7-1.bat deleted file mode 100644 index c9b380ad64..0000000000 --- a/Gems/AtomContent/ReferenceMaterials/Launch_WingIDE-7-1.bat +++ /dev/null @@ -1,79 +0,0 @@ -@echo off -:: Launches Wing IDE and the DccScriptingInterface Project Files - -REM -REM Copyright (c) Contributors to the Open 3D Engine Project. -REM For complete copyright and license terms please see the LICENSE at the root of this distribution. -REM -REM SPDX-License-Identifier: Apache-2.0 OR MIT -REM -REM - -echo. -echo _____________________________________________________________________ -echo. -echo ~ Setting up LY DCCsi WingIDE Dev Env... -echo _____________________________________________________________________ -echo. - -:: Store current dir -%~d0 -cd %~dp0 -PUSHD %~dp0 - -:: Keep changes local -SETLOCAL enableDelayedExpansion - -SET ABS_PATH=%~dp0 -echo Current Dir, %ABS_PATH% - -:: WingIDE version Major -SET WING_VERSION_MAJOR=7 -echo WING_VERSION_MAJOR = %WING_VERSION_MAJOR% - -:: WingIDE version Major -SET WING_VERSION_MINOR=1 -echo WING_VERSION_MINOR = %WING_VERSION_MINOR% - -:: note the changed path from IDE to Pro -set WINGHOME=%PROGRAMFILES(X86)%\Wing Pro %WING_VERSION_MAJOR%.%WING_VERSION_MINOR% -echo WINGHOME = %WINGHOME% - -CALL %~dp0\Project_Env.bat - -echo. -echo _____________________________________________________________________ -echo. -echo ~ WingIDE Version %WING_VERSION_MAJOR%.%WING_VERSION_MINOR% -echo _____________________________________________________________________ -echo. - -SET WING_PROJ=%DCCSIG_PATH%\Solutions\.wing\DCCsi_%WING_VERSION_MAJOR%x.wpr -echo WING_PROJ = %WING_PROJ% - -echo. -echo _____________________________________________________________________ -echo. -echo ~ Launching %LY_PROJECT% project in WingIDE %WING_VERSION_MAJOR%.%WING_VERSION_MINOR% ... -echo _____________________________________________________________________ -echo. - - -IF EXIST "%WINGHOME%\bin\wing.exe" ( - start "" "%WINGHOME%\bin\wing.exe" "%WING_PROJ%" -) ELSE ( - Where wing.exe 2> NUL - IF ERRORLEVEL 1 ( - echo wing.exe could not be found - pause - ) ELSE ( - start "" wing.exe "%WING_PROJ%" - ) -) - -ENDLOCAL - -:: Return to starting directory -POPD - -:END_OF_FILE diff --git a/Gems/AtomContent/ReferenceMaterials/LyProjectRootStub b/Gems/AtomContent/ReferenceMaterials/LyProjectRootStub deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Gems/AtomContent/ReferenceMaterials/Project_Env.bat b/Gems/AtomContent/ReferenceMaterials/Project_Env.bat deleted file mode 100644 index 6612ca5399..0000000000 --- a/Gems/AtomContent/ReferenceMaterials/Project_Env.bat +++ /dev/null @@ -1,70 +0,0 @@ -@echo off -:: Sets up environment for Lumberyard DCC tools and code access - -REM -REM Copyright (c) Contributors to the Open 3D Engine Project. -REM For complete copyright and license terms please see the LICENSE at the root of this distribution. -REM -REM SPDX-License-Identifier: Apache-2.0 OR MIT -REM -REM - -:: Store current dir -%~d0 -cd %~dp0 -PUSHD %~dp0 - -for %%a in (.) do set LY_PROJECT=%%~na - -echo. -echo _____________________________________________________________________ -echo. -echo ~ Setting up LY DSI PROJECT Environment ... -echo _____________________________________________________________________ -echo. - -echo LY_PROJECT = %LY_PROJECT% - -:: Put you project env vars and overrides here - -:: chanhe the relative path up to dev -set DEV_REL_PATH=../../.. -set ABS_PATH=%~dp0 - -:: Override the default maya version -set MAYA_VERSION=2020 -echo MAYA_VERSION = %MAYA_VERSION% - -set LY_PROJECT_PATH=%ABS_PATH% -echo LY_PROJECT_PATH = %LY_PROJECT_PATH% - -:: Change to root Lumberyard dev dir -CD /d %LY_PROJECT_PATH%\%DEV_REL_PATH% -set LY_DEV=%CD% -echo LY_DEV = %LY_DEV% - -CALL %LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env.bat - -rem :: Constant Vars (Global) -rem SET LYPY_GDEBUG=0 -rem echo LYPY_GDEBUG = %LYPY_GDEBUG% -rem SET LYPY_DEV_MODE=0 -rem echo LYPY_DEV_MODE = %LYPY_DEV_MODE% -rem SET LYPY_DEBUGGER=WING -rem echo LYPY_DEBUGGER = %LYPY_DEBUGGER% - -:: Restore original directory -popd - -:: Change to root dir -CD /D %ABS_PATH% - -:: if the user has set up a custom env call it -IF EXIST "%~dp0User_Env.bat" CALL %~dp0User_Env.bat - -GOTO END_OF_FILE - -:: Return to starting directory -POPD - -:END_OF_FILE diff --git a/Gems/AtomContent/ReferenceMaterials/Launch_Cmd.bat b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Cmd.bat similarity index 72% rename from Gems/AtomContent/ReferenceMaterials/Launch_Cmd.bat rename to Gems/AtomContent/ReferenceMaterials/Tools/Launch_Cmd.bat index 8ec894c21f..d100c9ddc7 100644 --- a/Gems/AtomContent/ReferenceMaterials/Launch_Cmd.bat +++ b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Cmd.bat @@ -1,21 +1,19 @@ -:: Need to set up - @echo off REM -REM Copyright (c) Contributors to the Open 3D Engine Project. -REM For complete copyright and license terms please see the LICENSE at the root of this distribution. -REM +REM Copyright (c) Contributors to the Open 3D Engine Project +REM REM SPDX-License-Identifier: Apache-2.0 OR MIT +REM For complete copyright and license terms please see the LICENSE at the root of this distribution. REM REM -:: Set up and run LY Python CMD prompt -:: Sets up the DccScriptingInterface_Env, +:: Set up and start a O3DE CMD prompt +:: Sets up the current (DCC) Project_Env, :: Puts you in the CMD within the dev environment :: Set up window -TITLE Lumberyard DCC Scripting Interface Cmd +TITLE O3DE Asset Gem Cmd :: Use obvious color to prevent confusion (Grey with Yellow Text) COLOR 8E @@ -31,7 +29,7 @@ CALL %~dp0\Project_Env.bat echo. echo _____________________________________________________________________ echo. -echo ~ LY DCC Scripting Interface CMD ... +echo ~ O3DE Asset Gem CMD ... echo _____________________________________________________________________ echo. @@ -43,4 +41,4 @@ ENDLOCAL :: Return to starting directory POPD -:END_OF_FILE +:END_OF_FILE \ No newline at end of file diff --git a/Gems/AtomContent/ReferenceMaterials/Launch_Maya_2020.bat b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat similarity index 82% rename from Gems/AtomContent/ReferenceMaterials/Launch_Maya_2020.bat rename to Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat index 1dc504e684..b9a6b399f3 100644 --- a/Gems/AtomContent/ReferenceMaterials/Launch_Maya_2020.bat +++ b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat @@ -22,22 +22,22 @@ echo ~ calling PROJ_Env.bat SETLOCAL enableDelayedExpansion :: PY version Major -set DCCSI_PY_VERSION_MAJOR=2 +IF "%DCCSI_PY_VERSION_MAJOR%"=="" (set DCCSI_PY_VERSION_MAJOR=2) echo DCCSI_PY_VERSION_MAJOR = %DCCSI_PY_VERSION_MAJOR% :: PY version Major -set DCCSI_PY_VERSION_MINOR=7 +IF "%DCCSI_PY_VERSION_MINOR%"=="" (set DCCSI_PY_VERSION_MINOR=7) echo DCCSI_PY_VERSION_MINOR = %DCCSI_PY_VERSION_MINOR% :: Maya Version -set MAYA_VERSION=2020 -echo MAYA_VERSION = %MAYA_VERSION% +IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=2020) +echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% :: if a local customEnv.bat exists, run it IF EXIST "%~dp0Project_Env.bat" CALL %~dp0Project_Env.bat echo ________________________________ -echo Launching Maya %MAYA_VERSION% for Lumberyard... +echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard... :::: Set Maya native project acess to this project ::set MAYA_PROJECT=%LY_PROJECT% diff --git a/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat b/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat new file mode 100644 index 0000000000..6e2c8b5914 --- /dev/null +++ b/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat @@ -0,0 +1,110 @@ +@echo off + +REM +REM Copyright (c) Contributors to the Open 3D Engine Project +REM +REM SPDX-License-Identifier: Apache-2.0 OR MIT +REM For complete copyright and license terms please see the LICENSE at the root of this distribution. +REM +REM + +:: Sets up environment for O3DE DCC tools and code access + +:: Set up window +TITLE O3DE Asset Gem +:: Use obvious color to prevent confusion (Grey with Yellow Text) +COLOR 8E + +:: Skip initialization if already completed +IF "%O3DE_PROJ_ENV_INIT%"=="1" GOTO :END_OF_FILE + +:: Store current dir +%~d0 +cd %~dp0 +PUSHD %~dp0 + +:: Put you project env vars and overrides in this file + +:: chanhe the relative path up to dev +set ABS_PATH=%~dp0 + +:: project name as a str tag +IF "%LY_PROJECT_NAME%"=="" ( + for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set LY_PROJECT_NAME=%%~nxJ + ) + +echo. +echo _____________________________________________________________________ +echo. +echo ~ Setting up O3DE %LY_PROJECT_NAME% Environment ... +echo _____________________________________________________________________ +echo. +echo LY_PROJECT_NAME = %LY_PROJECT_NAME% + +:: if the user has set up a custom env call it +:: this should allow the user to locally +:: set env hooks like LY_DEV or LY_PROJECT +IF EXIST "%~dp0User_Env.bat" CALL %~dp0User_Env.bat +echo LY_DEV = %LY_DEV% + +:: Constant Vars (Global) +:: global debug flag (propogates) +:: The intent here is to set and globally enter a debug mode +IF "%DCCSI_GDEBUG%"=="" (set DCCSI_GDEBUG=false) +echo DCCSI_GDEBUG = %DCCSI_GDEBUG% +:: initiates earliest debugger connection +:: we support attaching to WingIDE... PyCharm and VScode in the future +IF "%DCCSI_DEV_MODE%"=="" (set DCCSI_DEV_MODE=false) +echo DCCSI_DEV_MODE = %DCCSI_DEV_MODE% +:: sets debugger, options: WING, PYCHARM +IF "%DCCSI_GDEBUGGER%"=="" (set DCCSI_GDEBUGGER=WING) +echo DCCSI_GDEBUGGER = %DCCSI_GDEBUGGER% +:: Default level logger will handle +:: Override this to control the setting +:: CRITICAL:50 +:: ERROR:40 +:: WARNING:30 +:: INFO:20 +:: DEBUG:10 +:: NOTSET:0 +IF "%DCCSI_LOGLEVEL%"=="" (set DCCSI_LOGLEVEL=20) +echo DCCSI_LOGLEVEL = %DCCSI_LOGLEVEL% + +:: Override the default maya version +IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=2020) +echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% + +:: LY_PROJECT is ideally treated as a full path in the env launchers +:: do to changes in o3de, external engine/project/gem folder structures, etc. +IF "%LY_PROJECT%"=="" ( + for %%i in ("%~dp0..") do set "LY_PROJECT=%%~fi" + ) +echo LY_PROJECT = %LY_PROJECT% + +:: this is here for archaic reasons, WILL DEPRECATE +IF "%LY_PROJECT_PATH%"=="" (set LY_PROJECT_PATH=%LY_PROJECT%) +echo LY_PROJECT_PATH = %LY_PROJECT_PATH% + +:: Change to root Lumberyard dev dir +:: You must set this in a User_Env.bat to match youe engine repo location! +IF "%LY_DEV%"=="" (set LY_DEV=C:\Depot\o3de-engine) +echo LY_DEV = %LY_DEV% + +CALL %LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env_Maya.bat + +:: Restore original directory +popd + +:: Change to root dir +CD /D %ABS_PATH% + +::ENDLOCAL + +:: Set flag so we don't initialize dccsi environment twice +SET O3DE_PROJ_ENV_INIT=1 +GOTO END_OF_FILE + +:: Return to starting directory +POPD + +:END_OF_FILE diff --git a/Gems/AtomContent/ReferenceMaterials/Tools/User_Env.bat.template b/Gems/AtomContent/ReferenceMaterials/Tools/User_Env.bat.template new file mode 100644 index 0000000000..d108f30a5b --- /dev/null +++ b/Gems/AtomContent/ReferenceMaterials/Tools/User_Env.bat.template @@ -0,0 +1,42 @@ +@echo off + +REM +REM Copyright (c) Contributors to the Open 3D Engine Project +REM +REM SPDX-License-Identifier: Apache-2.0 OR MIT +REM For complete copyright and license terms please see the LICENSE at the root of this distribution. +REM +REM + +:: copy this file, rename to User_Env.bat (remove .template) +:: use this file to override any local properties that differ from base + +:: Skip initialization if already completed +IF "%O3DE_USER_ENV_INIT%"=="1" GOTO :END_OF_FILE + +:: Store current dir +%~d0 +cd %~dp0 +PUSHD %~dp0 + +SET O3DE_DEV=C:\Depot\o3de-engine +::SET OCIO_APPS=C:\Depot\o3de-engine\Tools\ColorGrading\ocio\build\src\apps +SET TAG_LY_BUILD_PATH=build +SET DCCSI_GDEBUG=True +SET DCCSI_DEV_MODE=True + +set DCCSI_MAYA_VERSION=2020 + +:: set the your user name here for windows path +SET TAG_USERNAME=NOT_SET +SET DCCSI_PY_REV=rev1 +SET DCCSI_PY_PLATFORM=windows + +:: Set flag so we don't initialize dccsi environment twice +SET O3DE_USER_ENV_INIT=1 +GOTO END_OF_FILE + +:: Return to starting directory +POPD + +:END_OF_FILE \ No newline at end of file diff --git a/Gems/AtomContent/ReferenceMaterials/gem.json b/Gems/AtomContent/ReferenceMaterials/gem.json index d66c2fa1db..f697d50bc4 100644 --- a/Gems/AtomContent/ReferenceMaterials/gem.json +++ b/Gems/AtomContent/ReferenceMaterials/gem.json @@ -1,11 +1,12 @@ { "gem_name": "ReferenceMaterials", - "display_name": "ReferenceMaterials", - "license": "Apache-2.0 Or MIT", - "origin": "Open 3D Engine - o3de.org", + "display_name": "PBR Reference Materials", + "license": "Code, text, data files: Apache-2.0 Or MIT, assets/content/images: CC BY 4.0", + "origin": "https://github.com/aws-lumberyard-dev/o3de.git", "type": "Asset", "summary": "Atom Asset Gem with a library of reference materials for StandardPBR (and others in the future)", "canonical_tags": ["Gem"], - "user_tags": ["Assets"], - "requirements": "" + "user_tags": ["Assets", "PBR", "Materials"], + "icon_path": "preview.png", + "dependencies": [] } diff --git a/Gems/AtomContent/Sponza/Project_Env.bat b/Gems/AtomContent/Sponza/Project_Env.bat deleted file mode 100644 index 74b70a2320..0000000000 --- a/Gems/AtomContent/Sponza/Project_Env.bat +++ /dev/null @@ -1,72 +0,0 @@ -@echo off -REM -REM Copyright (c) Contributors to the Open 3D Engine Project. -REM For complete copyright and license terms please see the LICENSE at the root of this distribution. -REM -REM SPDX-License-Identifier: Apache-2.0 OR MIT -REM -REM - -:: Store current dir -%~d0 -cd %~dp0 -PUSHD %~dp0 - -:: This is a legacy envar which is being migrated to LY_PROJECT_NAME -for %%a in (.) do set LY_PROJECT=%%~na - -echo. -echo _____________________________________________________________________ -echo. -echo ~ Setting up LY DSI PROJECT Environment ... -echo _____________________________________________________________________ -echo. - -echo LY_PROJECT = %LY_PROJECT% - -set LY_PROJECT_NAME=%LY_PROJECT% -echo LY_PROJECT_NAME = %LY_PROJECT_NAME% - -:: Put you project env vars and overrides here - -:: chanhe the relative path up to dev -set DEV_REL_PATH=../../.. -set ABS_PATH=%~dp0 - -:: Override the default maya version -set MAYA_VERSION=2020 -echo MAYA_VERSION = %MAYA_VERSION% - -set LY_PROJECT_PATH=%ABS_PATH% -echo LY_PROJECT_PATH = %LY_PROJECT_PATH% - -:: Change to root Lumberyard dev dir -CD /d %LY_PROJECT_PATH%\%DEV_REL_PATH% -set LY_DEV=%CD% -echo LY_DEV = %LY_DEV% - -CALL %LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env_Maya.bat - -rem :: Constant Vars (Global) -rem SET LYPY_GDEBUG=0 -rem echo LYPY_GDEBUG = %LYPY_GDEBUG% -rem SET LYPY_DEV_MODE=0 -rem echo LYPY_DEV_MODE = %LYPY_DEV_MODE% -rem SET LYPY_DEBUGGER=WING -rem echo LYPY_DEBUGGER = %LYPY_DEBUGGER% - -:: Restore original directory -popd - -:: Change to root dir -CD /D %ABS_PATH% - -:: if the user has set up a custom env call it -IF EXIST "%~dp0User_Env.bat" CALL %~dp0User_Env.bat - -GOTO END_OF_FILE - -:: Return to starting directory -POPD - -:END_OF_FILE diff --git a/Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat b/Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat index 2636b3dea4..99c2c12c51 100644 --- a/Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat +++ b/Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat @@ -1,19 +1,19 @@ @echo off + +REM +REM Copyright (c) Contributors to the Open 3D Engine Project REM -REM Copyright (c) Contributors to the Open 3D Engine Project. -REM For complete copyright and license terms please see the LICENSE at the root of this distribution. -REM REM SPDX-License-Identifier: Apache-2.0 OR MIT +REM For complete copyright and license terms please see the LICENSE at the root of this distribution. REM REM -@echo off -:: Set up and run LY Python CMD prompt -:: Sets up the DccScriptingInterface_Env, +:: Set up and start a O3DE CMD prompt +:: Sets up the current (DCC) Project_Env, :: Puts you in the CMD within the dev environment :: Set up window -TITLE Lumberyard DCC Scripting Interface Cmd +TITLE O3DE DCC Scripting Interface Cmd :: Use obvious color to prevent confusion (Grey with Yellow Text) COLOR 8E @@ -24,7 +24,7 @@ PUSHD %~dp0 :: Keep changes local SETLOCAL enableDelayedExpansion -CALL %~dp0\..\Project_Env.bat +CALL %~dp0\Project_Env.bat echo. echo _____________________________________________________________________ diff --git a/Gems/AtomContent/Sponza/Tools/Maya/Launch_Maya_2020.bat b/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat similarity index 63% rename from Gems/AtomContent/Sponza/Tools/Maya/Launch_Maya_2020.bat rename to Gems/AtomContent/Sponza/Tools/Launch_Maya.bat index 53344d527e..d774adf79b 100644 --- a/Gems/AtomContent/Sponza/Tools/Maya/Launch_Maya_2020.bat +++ b/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat @@ -1,4 +1,5 @@ @echo off + REM REM Copyright (c) Contributors to the Open 3D Engine Project. REM For complete copyright and license terms please see the LICENSE at the root of this distribution. @@ -7,11 +8,6 @@ REM SPDX-License-Identifier: Apache-2.0 OR MIT REM REM -:: Launches maya wityh a bunch of local hooks for Lumberyard -:: ToDo: move all of this to a .json data driven boostrapping system - -@echo off - %~d0 cd %~dp0 PUSHD %~dp0 @@ -23,22 +19,22 @@ echo ~ calling PROJ_Env.bat SETLOCAL enableDelayedExpansion :: PY version Major -set DCCSI_PY_VERSION_MAJOR=2 +IF "%DCCSI_PY_VERSION_MAJOR%"=="" (set DCCSI_PY_VERSION_MAJOR=2) echo DCCSI_PY_VERSION_MAJOR = %DCCSI_PY_VERSION_MAJOR% :: PY version Major -set DCCSI_PY_VERSION_MINOR=7 +IF "%DCCSI_PY_VERSION_MINOR%"=="" (set DCCSI_PY_VERSION_MINOR=7) echo DCCSI_PY_VERSION_MINOR = %DCCSI_PY_VERSION_MINOR% :: Maya Version -set MAYA_VERSION=2020 -echo MAYA_VERSION = %MAYA_VERSION% +IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=2020) +echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% :: if a local customEnv.bat exists, run it -IF EXIST "%~dp0..\..\Project_Env.bat" CALL %~dp0..\..\Project_Env.bat +IF EXIST "%~dp0Project_Env.bat" CALL %~dp0Project_Env.bat echo ________________________________ -echo Launching Maya %MAYA_VERSION% for Lumberyard... +echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard... :::: Set Maya native project acess to this project ::set MAYA_PROJECT=%LY_PROJECT% @@ -49,15 +45,15 @@ Set MAYA_VP2_DEVICE_OVERRIDE = VirtualDeviceDx11 :: Default to the right version of Maya if we can detect it... and launch IF EXIST "%MAYA_LOCATION%\bin\Maya.exe" ( - start "" "%MAYA_LOCATION%\bin\Maya.exe" %* + start "" "%MAYA_LOCATION%\bin\Maya.exe" %* ) ELSE ( - Where maya.exe 2> NUL - IF ERRORLEVEL 1 ( - echo Maya.exe could not be found - pause - ) ELSE ( - start "" Maya.exe %* - ) + Where maya.exe 2> NUL + IF ERRORLEVEL 1 ( + echo Maya.exe could not be found + pause + ) ELSE ( + start "" Maya.exe %* + ) ) :: Return to starting directory @@ -65,4 +61,4 @@ POPD :END_OF_FILE -exit /b 0 \ No newline at end of file +exit /b 0 diff --git a/Gems/AtomContent/Sponza/Tools/Maya/Scripts/stub b/Gems/AtomContent/Sponza/Tools/Maya/Scripts/stub deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Gems/AtomContent/Sponza/Tools/Project_Env.bat b/Gems/AtomContent/Sponza/Tools/Project_Env.bat new file mode 100644 index 0000000000..6e2c8b5914 --- /dev/null +++ b/Gems/AtomContent/Sponza/Tools/Project_Env.bat @@ -0,0 +1,110 @@ +@echo off + +REM +REM Copyright (c) Contributors to the Open 3D Engine Project +REM +REM SPDX-License-Identifier: Apache-2.0 OR MIT +REM For complete copyright and license terms please see the LICENSE at the root of this distribution. +REM +REM + +:: Sets up environment for O3DE DCC tools and code access + +:: Set up window +TITLE O3DE Asset Gem +:: Use obvious color to prevent confusion (Grey with Yellow Text) +COLOR 8E + +:: Skip initialization if already completed +IF "%O3DE_PROJ_ENV_INIT%"=="1" GOTO :END_OF_FILE + +:: Store current dir +%~d0 +cd %~dp0 +PUSHD %~dp0 + +:: Put you project env vars and overrides in this file + +:: chanhe the relative path up to dev +set ABS_PATH=%~dp0 + +:: project name as a str tag +IF "%LY_PROJECT_NAME%"=="" ( + for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set LY_PROJECT_NAME=%%~nxJ + ) + +echo. +echo _____________________________________________________________________ +echo. +echo ~ Setting up O3DE %LY_PROJECT_NAME% Environment ... +echo _____________________________________________________________________ +echo. +echo LY_PROJECT_NAME = %LY_PROJECT_NAME% + +:: if the user has set up a custom env call it +:: this should allow the user to locally +:: set env hooks like LY_DEV or LY_PROJECT +IF EXIST "%~dp0User_Env.bat" CALL %~dp0User_Env.bat +echo LY_DEV = %LY_DEV% + +:: Constant Vars (Global) +:: global debug flag (propogates) +:: The intent here is to set and globally enter a debug mode +IF "%DCCSI_GDEBUG%"=="" (set DCCSI_GDEBUG=false) +echo DCCSI_GDEBUG = %DCCSI_GDEBUG% +:: initiates earliest debugger connection +:: we support attaching to WingIDE... PyCharm and VScode in the future +IF "%DCCSI_DEV_MODE%"=="" (set DCCSI_DEV_MODE=false) +echo DCCSI_DEV_MODE = %DCCSI_DEV_MODE% +:: sets debugger, options: WING, PYCHARM +IF "%DCCSI_GDEBUGGER%"=="" (set DCCSI_GDEBUGGER=WING) +echo DCCSI_GDEBUGGER = %DCCSI_GDEBUGGER% +:: Default level logger will handle +:: Override this to control the setting +:: CRITICAL:50 +:: ERROR:40 +:: WARNING:30 +:: INFO:20 +:: DEBUG:10 +:: NOTSET:0 +IF "%DCCSI_LOGLEVEL%"=="" (set DCCSI_LOGLEVEL=20) +echo DCCSI_LOGLEVEL = %DCCSI_LOGLEVEL% + +:: Override the default maya version +IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=2020) +echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% + +:: LY_PROJECT is ideally treated as a full path in the env launchers +:: do to changes in o3de, external engine/project/gem folder structures, etc. +IF "%LY_PROJECT%"=="" ( + for %%i in ("%~dp0..") do set "LY_PROJECT=%%~fi" + ) +echo LY_PROJECT = %LY_PROJECT% + +:: this is here for archaic reasons, WILL DEPRECATE +IF "%LY_PROJECT_PATH%"=="" (set LY_PROJECT_PATH=%LY_PROJECT%) +echo LY_PROJECT_PATH = %LY_PROJECT_PATH% + +:: Change to root Lumberyard dev dir +:: You must set this in a User_Env.bat to match youe engine repo location! +IF "%LY_DEV%"=="" (set LY_DEV=C:\Depot\o3de-engine) +echo LY_DEV = %LY_DEV% + +CALL %LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env_Maya.bat + +:: Restore original directory +popd + +:: Change to root dir +CD /D %ABS_PATH% + +::ENDLOCAL + +:: Set flag so we don't initialize dccsi environment twice +SET O3DE_PROJ_ENV_INIT=1 +GOTO END_OF_FILE + +:: Return to starting directory +POPD + +:END_OF_FILE diff --git a/Gems/AtomContent/Sponza/Tools/User_Env.bat.template b/Gems/AtomContent/Sponza/Tools/User_Env.bat.template new file mode 100644 index 0000000000..d108f30a5b --- /dev/null +++ b/Gems/AtomContent/Sponza/Tools/User_Env.bat.template @@ -0,0 +1,42 @@ +@echo off + +REM +REM Copyright (c) Contributors to the Open 3D Engine Project +REM +REM SPDX-License-Identifier: Apache-2.0 OR MIT +REM For complete copyright and license terms please see the LICENSE at the root of this distribution. +REM +REM + +:: copy this file, rename to User_Env.bat (remove .template) +:: use this file to override any local properties that differ from base + +:: Skip initialization if already completed +IF "%O3DE_USER_ENV_INIT%"=="1" GOTO :END_OF_FILE + +:: Store current dir +%~d0 +cd %~dp0 +PUSHD %~dp0 + +SET O3DE_DEV=C:\Depot\o3de-engine +::SET OCIO_APPS=C:\Depot\o3de-engine\Tools\ColorGrading\ocio\build\src\apps +SET TAG_LY_BUILD_PATH=build +SET DCCSI_GDEBUG=True +SET DCCSI_DEV_MODE=True + +set DCCSI_MAYA_VERSION=2020 + +:: set the your user name here for windows path +SET TAG_USERNAME=NOT_SET +SET DCCSI_PY_REV=rev1 +SET DCCSI_PY_PLATFORM=windows + +:: Set flag so we don't initialize dccsi environment twice +SET O3DE_USER_ENV_INIT=1 +GOTO END_OF_FILE + +:: Return to starting directory +POPD + +:END_OF_FILE \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/User_env.bat.template b/Gems/AtomContent/Sponza/User_env.bat.template deleted file mode 100644 index 99bc7a951d..0000000000 --- a/Gems/AtomContent/Sponza/User_env.bat.template +++ /dev/null @@ -1 +0,0 @@ -set LY_DEV=C:\Depot\o3de-engine \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/gem.json b/Gems/AtomContent/Sponza/gem.json index 3fc76927e1..64de0e5da0 100644 --- a/Gems/AtomContent/Sponza/gem.json +++ b/Gems/AtomContent/Sponza/gem.json @@ -5,7 +5,12 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "A standard test scene for Global Illumination (forked from crytek sponza scene)", - "canonical_tags": ["Gem"], - "user_tags": ["Assets"], - "requirements": "" + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets" + ], + "requirements": "", + "dependencies": [] } diff --git a/Gems/AtomContent/gem.json b/Gems/AtomContent/gem.json index 6fcb8903e4..1bffe7d989 100644 --- a/Gems/AtomContent/gem.json +++ b/Gems/AtomContent/gem.json @@ -5,8 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Atom Content Gem provides assets for Atom Renderer and a modified version of the Pixar Look Development Studio.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Assets", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Assets", + "Tools" + ], "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-content/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-content/", + "dependencies": [ + "ReferenceMaterials", + "Sponza" + ] } diff --git a/Gems/AtomLyIntegration/AtomBridge/gem.json b/Gems/AtomLyIntegration/AtomBridge/gem.json index 49a56d2201..1d48d8be61 100644 --- a/Gems/AtomLyIntegration/AtomBridge/gem.json +++ b/Gems/AtomLyIntegration/AtomBridge/gem.json @@ -8,7 +8,26 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI", + "Atom_Bootstrap", + "Atom_RHI", + "Atom_RHI_Null", + "Atom_Feature_Common", + "Atom_Component_DebugCamera", + "AtomImGuiTools", + "CommonFeaturesAtom", + "EMotionFX_Atom", + "ImguiAtom", + "AtomFont", + "AtomViewportDisplayInfo", + "Atom", + "AtomShader", + "ImageProcessingAtom", + "AtomToolsFramework", + "AtomViewportDisplayIcons", + "MaterialEditor" + ] } diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h index 8862462093..b1a91b5aa5 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h +++ b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h @@ -82,7 +82,7 @@ namespace AZ FontFamilyPtr LoadFontFamily(const char* fontFamilyName) override; FontFamilyPtr GetFontFamily(const char* fontFamilyName) override; void AddCharsToFontTextures(FontFamilyPtr fontFamily, const char* chars, int glyphSizeX = ICryFont::defaultGlyphSizeX, int glyphSizeY = ICryFont::defaultGlyphSizeY) override; - AZStd::string GetLoadedFontNames() const; + AZStd::string GetLoadedFontNames() const override; void OnLanguageChanged() override; void ReloadAllFonts() override; ////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h index 83f53ffec2..2cc8a67cfa 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h +++ b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h @@ -283,7 +283,7 @@ namespace AZ FontTexture* m_fontTexture = nullptr; size_t m_fontBufferSize = 0; - unsigned char* m_fontBuffer = nullptr; + AZStd::unique_ptr m_fontBuffer; AZ::Data::Instance m_fontStreamingImage; AZ::RHI::Ptr m_fontImage; diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFontSystemComponent.h b/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFontSystemComponent.h index 02290e0fed..59f057ddaf 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFontSystemComponent.h +++ b/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFontSystemComponent.h @@ -38,7 +38,7 @@ namespace AZ //////////////////////////////////////////////////////////////////////// // CrySystemEventBus - void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& initParams); + void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& initParams) override; //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp index 5e96af0b1b..86b3011836 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp +++ b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp @@ -13,8 +13,6 @@ #if !defined(USE_NULLFONT_ALWAYS) -#include - #include #include #include @@ -124,17 +122,10 @@ bool AZ::FFont::Load(const char* fontFilePath, unsigned int width, unsigned int Free(); - auto pPak = gEnv->pCryPak; + auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); - AZStd::string fullFile; - if (pPak->IsAbsPath(fontFilePath)) - { - fullFile = fontFilePath; - } - else - { - fullFile = m_curPath + fontFilePath; - } + AZ::IO::Path fullFile(m_curPath); + fullFile /= fontFilePath; int smoothMethodFlag = (flags & TTFFLAG_SMOOTH_MASK) >> TTFFLAG_SMOOTH_SHIFT; AZ::FontSmoothMethod smoothMethod = AZ::FontSmoothMethod::None; @@ -161,42 +152,41 @@ bool AZ::FFont::Load(const char* fontFilePath, unsigned int width, unsigned int } - AZ::IO::HandleType fileHandle = pPak->FOpen(fullFile.c_str(), "rb"); + AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; + fileIoBase->Open(fullFile.c_str(), AZ::IO::GetOpenModeFromStringMode("rb"), fileHandle); if (fileHandle == AZ::IO::InvalidHandle) { return false; } - size_t fileSize = pPak->FGetSize(fileHandle); + AZ::u64 fileSize{}; + fileIoBase->Size(fileHandle, fileSize); if (!fileSize) { - pPak->FClose(fileHandle); + fileIoBase->Close(fileHandle); return false; } - unsigned char* buffer = new unsigned char[fileSize]; - if (!pPak->FReadRaw(buffer, fileSize, 1, fileHandle)) + auto buffer = AZStd::make_unique(fileSize); + if (!fileIoBase->Read(fileHandle, buffer.get(), fileSize)) { - pPak->FClose(fileHandle); - delete [] buffer; + fileIoBase->Close(fileHandle); return false; } - pPak->FClose(fileHandle); + fileIoBase->Close(fileHandle); if (!m_fontTexture) { m_fontTexture = new FontTexture(); } - - if (!m_fontTexture || !m_fontTexture->CreateFromMemory(buffer, (int)fileSize, width, height, smoothMethod, smoothAmount, widthNumSlots, heightNumSlots, sizeRatio)) + if (!m_fontTexture || !m_fontTexture->CreateFromMemory(buffer.get(), (int)fileSize, width, height, smoothMethod, smoothAmount, widthNumSlots, heightNumSlots, sizeRatio)) { - delete [] buffer; return false; } m_monospacedFont = m_fontTexture->GetMonospaced(); - m_fontBuffer = buffer; + m_fontBuffer = AZStd::move(buffer); m_fontBufferSize = fileSize; m_fontTexDirty = false; m_sizeRatio = sizeRatio; @@ -213,10 +203,9 @@ void AZ::FFont::Free() m_fontImageVersion = 0; delete m_fontTexture; - m_fontTexture = 0; + m_fontTexture = nullptr; - delete[] m_fontBuffer; - m_fontBuffer = 0; + m_fontBuffer.reset(); m_fontBufferSize = 0; } @@ -1737,7 +1726,7 @@ void AZ::FFont::DrawScreenAlignedText3d( { return; } - AZ::Vector3 positionNDC = AzFramework::WorldToScreenNDC( + AZ::Vector3 positionNDC = AzFramework::WorldToScreenNdc( params.m_position, currentView->GetWorldToViewMatrix(), currentView->GetViewToClipMatrix() diff --git a/Gems/AtomLyIntegration/AtomFont/gem.json b/Gems/AtomLyIntegration/AtomFont/gem.json index 7e1a71db86..ed5b488de7 100644 --- a/Gems/AtomLyIntegration/AtomFont/gem.json +++ b/Gems/AtomLyIntegration/AtomFont/gem.json @@ -8,7 +8,11 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI", + "Atom_RPI", + "Atom_AtomBridge" + ] } diff --git a/Gems/AtomLyIntegration/AtomImGuiTools/gem.json b/Gems/AtomLyIntegration/AtomImGuiTools/gem.json index ff20d5a19f..564eeedee2 100644 --- a/Gems/AtomLyIntegration/AtomImGuiTools/gem.json +++ b/Gems/AtomLyIntegration/AtomImGuiTools/gem.json @@ -12,5 +12,9 @@ "Debug", "Rendering" ], - "requirements": "" + "requirements": "", + "dependencies": [ + "ImguiAtom", + "Atom" + ] } diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp index 6ea6a8e85f..c0ca2fe993 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp @@ -82,6 +82,8 @@ namespace AZ::Render void AtomViewportDisplayIconsSystemComponent::Activate() { + m_drawContextRegistered = false; + AzToolsFramework::EditorViewportIconDisplay::Register(this); Bootstrap::NotificationBus::Handler::BusConnect(); @@ -97,9 +99,10 @@ namespace AZ::Render { return; } - if (perViewportDynamicDrawInterface) + if (perViewportDynamicDrawInterface && m_drawContextRegistered) { perViewportDynamicDrawInterface->UnregisterDynamicDrawContext(m_drawContextName); + m_drawContextRegistered = false; } AzToolsFramework::EditorViewportIconDisplay::Unregister(this); @@ -367,6 +370,8 @@ namespace AZ::Render drawContext->EndInit(); }); + m_drawContextRegistered = true; + Data::AssetBus::Handler::BusDisconnect(); } } // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h index 25cc7eb8f6..a0914e7e2d 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h @@ -77,6 +77,8 @@ namespace AZ }; AZStd::unordered_map m_iconData; IconId m_currentId = 0; + + bool m_drawContextRegistered = false; }; } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json index 52dbb219d3..a2a7ba4b77 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json @@ -8,7 +8,12 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI", + "Atom_RPI", + "Atom_Bootstrap", + "Atom_AtomBridge" + ] } diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json index f277d6cf82..be5cc96f95 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json +++ b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json @@ -8,7 +8,10 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI", + "Atom_RPI" + ] } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt index df5ab134fa..e68681315e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt @@ -109,12 +109,14 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) RUNTIME_DEPENDENCIES Gem::Atom_RPI.Editor Gem::Atom_Feature_Common.Editor + Legacy::EditorCommon ) # The AtomLyIntegration_CommonFeatures.Editor module is used for Builders and Tools ly_create_alias(NAME AtomLyIntegration_CommonFeatures.Builders NAMESPACE Gem TARGETS Gem::AtomLyIntegration_CommonFeatures.Editor + Gem::Atom_Feature_Common.Builders Gem::Atom_RPI.Builders Gem::GradientSignal.Builders ) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h index c75d596b52..47fad038b6 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h @@ -7,6 +7,8 @@ */ #pragma once +#include +#include #include #include @@ -23,8 +25,12 @@ namespace AZ static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - //! Open document in material editor - virtual void OpenInMaterialEditor(const AZStd::string& sourcePath) = 0; + //! Open source material in material editor + virtual void OpenMaterialEditor(const AZStd::string& sourcePath) = 0; + + //! Open material instance editor + virtual void OpenMaterialInspector( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) = 0; }; using EditorMaterialSystemComponentRequestBus = AZ::EBus; } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h index 01c87fa2fb..ace16ba6ca 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h @@ -23,12 +23,26 @@ namespace AZ virtual MaterialAssignmentMap GetOriginalMaterialAssignments() const = 0; //! Get material assignment id matching lod and label substring virtual MaterialAssignmentId FindMaterialAssignmentId(const MaterialAssignmentLodIndex lod, const AZStd::string& label) const = 0; + //! Get default material asset + virtual AZ::Data::AssetId GetDefaultMaterialAssetId(const MaterialAssignmentId& materialAssignmentId) const = 0; + //! Get material slot label + virtual AZStd::string GetMaterialSlotLabel(const MaterialAssignmentId& materialAssignmentId) const = 0; //! Set material overrides virtual void SetMaterialOverrides(const MaterialAssignmentMap& materials) = 0; //! Get material overrides virtual const MaterialAssignmentMap& GetMaterialOverrides() const = 0; //! Clear all material overrides virtual void ClearAllMaterialOverrides() = 0; + //! Clear non-lod material overrides + virtual void ClearModelMaterialOverrides() = 0; + //! Clear lod material overrides + virtual void ClearLodMaterialOverrides() = 0; + //! Clear residual materials that don't correspond to the associated model + virtual void ClearIncompatibleMaterialOverrides() = 0; + //! Clear materials that reference missing assets + virtual void ClearInvalidMaterialOverrides() = 0; + //! Repair materials that reference missing assets by assigning the default asset + virtual void RepairInvalidMaterialOverrides() = 0; //! Set default material override virtual void SetDefaultMaterialOverride(const AZ::Data::AssetId& materialAssetId) = 0; //! Get default material override @@ -38,7 +52,7 @@ namespace AZ //! Set material override virtual void SetMaterialOverride(const MaterialAssignmentId& materialAssignmentId, const AZ::Data::AssetId& materialAssetId) = 0; //! Get material override - virtual const AZ::Data::AssetId GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const = 0; + virtual AZ::Data::AssetId GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const = 0; //! Clear material override virtual void ClearMaterialOverride(const MaterialAssignmentId& materialAssignmentId) = 0; //! Set a material property override value wrapped by an AZStd::any @@ -95,8 +109,16 @@ namespace AZ virtual void ClearPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) = 0; //! Clear all property overrides virtual void ClearAllPropertyOverrides() = 0; + //! Set Property overrides for a specific material assignment + virtual void SetPropertyOverrides( + const MaterialAssignmentId& materialAssignmentId, const MaterialPropertyOverrideMap& propertyOverrides) = 0; //! Get Property overrides for a specific material assignment virtual MaterialPropertyOverrideMap GetPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) const = 0; + //! Set Model UV overrides for a specific material assignment + virtual void SetModelUvOverrides( + const MaterialAssignmentId& materialAssignmentId, const AZ::RPI::MaterialModelUvOverrideMap& modelUvOverrides) = 0; + //! Get Model UV overrides for a specific material assignment + virtual AZ::RPI::MaterialModelUvOverrideMap GetModelUvOverrides(const MaterialAssignmentId& materialAssignmentId) const = 0; }; using MaterialComponentRequestBus = EBus; @@ -105,8 +127,15 @@ namespace AZ : public ComponentBus { public: + + //! This message is sent every time a material or property update affects UI. + virtual void OnMaterialsEdited() {} + + //! This message is sent when one or more material property changes have been applied, at most once per frame. virtual void OnMaterialsUpdated([[maybe_unused]] const MaterialAssignmentMap& materials) {} - virtual void OnMaterialsEdited([[maybe_unused]] const MaterialAssignmentMap& materials) {} + + //! This message is sent when the component has created the material instance to be used for rendering. + virtual void OnMaterialInstanceCreated([[maybe_unused]] const MaterialAssignment& materialAssignment) {} }; using MaterialComponentNotificationBus = EBus; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingBus.h new file mode 100644 index 0000000000..32b1c97429 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingBus.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +namespace AZ +{ + namespace Render + { + class HDRColorGradingRequests + : public ComponentBus + { + public: + AZ_RTTI(AZ::Render::HDRColorGradingRequests, "{E414A96B-0AA7-4574-ABC0-968B1F5CEE56}"); + + /// Overrides the default AZ::EBusTraits handler policy to allow one listener only. + static const EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Single; + virtual ~HDRColorGradingRequests() {} + // Auto-gen virtual getters/setters... +#include +#include +#include + }; + + typedef AZ::EBus HDRColorGradingRequestBus; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingComponentConfig.h new file mode 100644 index 0000000000..fe7bc874a4 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingComponentConfig.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + class HDRColorGradingComponentConfig final + : public ComponentConfig + { + public: + AZ_RTTI(AZ::Render::HDRColorGradingComponentConfig, "{8613EA4A-6E1C-49AD-87F3-9FECCB7EA36D}", AZ::ComponentConfig); + + static void Reflect(ReflectContext* context); + + // Generate members... +#include +#include +#include + + // Generate Getters/Setters... +#include +#include +#include + + void CopySettingsTo(HDRColorGradingSettingsInterface* settings); + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/AttachmentComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/AttachmentComponent.h index 3e8feb2182..bd7c52c851 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/AttachmentComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/AttachmentComponent.h @@ -75,7 +75,7 @@ namespace AZ //////////////////////////////////////////////////////////////////////// // AttachmentComponentRequests - void Reattach(bool detachFirst); + void Reattach(bool detachFirst) override; void Attach(AZ::EntityId targetId, const char* targetBoneName, const AZ::Transform& offset) override; void Detach() override; void SetAttachmentOffset(const AZ::Transform& offset) override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimplePointLightDelegate.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimplePointLightDelegate.h index ef6c0b9da8..c239055be8 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimplePointLightDelegate.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimplePointLightDelegate.h @@ -31,8 +31,7 @@ namespace AZ float GetEffectiveSolidAngle() const override { return PhotometricValue::OmnidirectionalSteradians; } private: - virtual void HandleShapeChanged(); - + void HandleShapeChanged() override; }; } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimpleSpotLightDelegate.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimpleSpotLightDelegate.h index 6b2b9bf4bb..7f9c4abc0f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimpleSpotLightDelegate.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SimpleSpotLightDelegate.h @@ -34,7 +34,7 @@ namespace AZ void SetShutterAngles(float innerAngleDegrees, float outerAngleDegrees) override; private: - virtual void HandleShapeChanged(); + void HandleShapeChanged() override; }; } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.cpp index c10e7e2e30..c5d61dd54a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.cpp @@ -32,7 +32,7 @@ namespace AZ if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { editContext->Class( - "Decal (Atom)", "The Decal component allows an entity to project a texture or material onto a mesh") + "Decal", "The Decal component allows an entity to project a texture or material onto a mesh") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "Atom") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Decal.svg") @@ -97,13 +97,25 @@ namespace AZ BaseClass::Deactivate(); } - AZ::Transform EditorDecalComponent::GetTransform() const + AZ::Transform EditorDecalComponent::GetWorldTransform() const { AZ::Transform transform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(transform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); return transform; } + AZ::Matrix3x4 EditorDecalComponent::GetWorldTransformWithNonUniformScale() const + { + const AZ::Transform worldTransform = GetWorldTransform(); + const AZ::Matrix3x3 rotationMat = AZ::Matrix3x3::CreateFromQuaternion(worldTransform.GetRotation()); + + const AZ::Vector3 nonUniformScale = m_controller.m_cachedNonUniformScale * worldTransform.GetUniformScale(); + const AZ::Matrix3x3 nonUniformScaleMat = AZ::Matrix3x3::CreateScale(nonUniformScale); + const AZ::Matrix3x3 rotationAndScale = rotationMat * nonUniformScaleMat; + + return AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(rotationAndScale, worldTransform.GetTranslation()); + } + void EditorDecalComponent::DisplayEntityViewport( [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) @@ -113,11 +125,9 @@ namespace AZ return; } - AZ::Transform transform = GetTransform(); - - debugDisplay.SetColor(AZ::Colors::Red); - debugDisplay.PushMatrix(transform); + const AZ::Matrix3x4 transform = GetWorldTransformWithNonUniformScale(); + debugDisplay.PushPremultipliedMatrix(transform); debugDisplay.DrawWireBox(-AZ::Vector3::CreateOne(), AZ::Vector3::CreateOne()); AZ::Vector3 x1 = AZ::Vector3(-1, 0, 1); @@ -136,7 +146,7 @@ namespace AZ // Two diagonal edges debugDisplay.DrawLine(p0, p2); debugDisplay.DrawLine(p1, p3); - debugDisplay.PopMatrix(); + debugDisplay.PopPremultipliedMatrix(); } AZ::Aabb EditorDecalComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.h index f0748dfef5..1ca88d23fc 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.h @@ -56,7 +56,11 @@ namespace AZ private: - AZ::Transform GetTransform() const; + // Returns the component transform which includes uniform-scale, rotation and translation + AZ::Transform GetWorldTransform() const; + + // Returns the full transform, including both the uniform scale and non-uniform scale along with rotation and translation + AZ::Matrix3x4 GetWorldTransformWithNonUniformScale() const; //! EditorRenderComponentAdapter overrides ... u32 OnConfigurationChanged() override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp index a4e058561e..b7bf191bc9 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp @@ -32,9 +32,6 @@ namespace AZ const char* EditorMaterialComponent::GenerateMaterialsButtonText = "Generate/Manage Source Materials..."; const char* EditorMaterialComponent::GenerateMaterialsToolTipText = "Generate editable source material files from materials provided by the model."; - const char* EditorMaterialComponent::ResetMaterialsButtonText = "Reset Materials"; - const char* EditorMaterialComponent::ResetMaterialsToolTipText = "Clear all settings, materials, and properties then rebuild material slots from the associated model."; - // Update serialized data to the new format and data types bool EditorMaterialComponent::ConvertVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) { @@ -178,128 +175,99 @@ namespace AZ menu->addSeparator(); - action = menu->addAction(ResetMaterialsButtonText, [this]() { ResetMaterialSlots(); }); - action->setToolTip(ResetMaterialsToolTipText); + action = menu->addAction("Clear All Materials", [this]() { + AzToolsFramework::ScopedUndoBatch undoBatch("Clearing all materials."); + SetDirty(); - menu->addSeparator(); + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearAllMaterialOverrides); + + m_materialSlotsByLodEnabled = false; + + UpdateMaterialSlots(); + }); + action->setToolTip("Clear all materials and properties then rebuild material slots from the associated model."); action = menu->addAction("Clear Model Materials", [this]() { AzToolsFramework::ScopedUndoBatch undoBatch("Clearing model materials."); SetDirty(); - for (auto& materialSlotPair : GetMaterialSlots()) - { - EditorMaterialComponentSlot* materialSlot = materialSlotPair.second; - if (materialSlot->m_id.IsSlotIdOnly()) - { - materialSlot->Clear(); - } - } - }); + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearModelMaterialOverrides); + + UpdateMaterialSlots(); + }); + action->setToolTip("Clear model materials and properties then rebuild material slots from the associated model."); + action = menu->addAction("Clear LOD Materials", [this]() { AzToolsFramework::ScopedUndoBatch undoBatch("Clearing LOD materials."); SetDirty(); - for (auto& materialSlotPair : GetMaterialSlots()) - { - EditorMaterialComponentSlot* materialSlot = materialSlotPair.second; - if (materialSlot->m_id.IsLodAndSlotId()) - { - materialSlot->Clear(); - } - } - }); - action->setEnabled(m_materialSlotsByLodEnabled); + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearLodMaterialOverrides); + + m_materialSlotsByLodEnabled = false; + + UpdateMaterialSlots(); + }); + action->setToolTip("Clear LOD materials and properties then rebuild material slots from the associated model."); + + action = menu->addAction("Clear Incompatible Materials", [this]() { + AzToolsFramework::ScopedUndoBatch undoBatch("Clearing incompatible materials."); + SetDirty(); + + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearIncompatibleMaterialOverrides); + + UpdateMaterialSlots(); + }); + action->setToolTip("Clear residual materials that don't correspond to the associated model."); + + action = menu->addAction("Clear Invalid Materials", [this]() { + AzToolsFramework::ScopedUndoBatch undoBatch("Clearing invalid materials."); + SetDirty(); + + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearInvalidMaterialOverrides); + + UpdateMaterialSlots(); + }); + action->setToolTip("Clear materials that reference missing assets."); + + action = menu->addAction("Repair Invalid Materials", [this]() { + AzToolsFramework::ScopedUndoBatch undoBatch("Repairing invalid materials."); + SetDirty(); + + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::RepairInvalidMaterialOverrides); + + UpdateMaterialSlots(); + }); + action->setToolTip("Repair materials that reference missing assets by assigning the default asset."); } void EditorMaterialComponent::SetPrimaryAsset(const AZ::Data::AssetId& assetId) { - m_controller.SetDefaultMaterialOverride(assetId); + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::SetDefaultMaterialOverride, assetId); + + MaterialComponentNotificationBus::Event(GetEntityId(), &MaterialComponentNotifications::OnMaterialsEdited); + + AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( + &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_AttributesAndValues); + } + + void EditorMaterialComponent::OnMaterialInstanceCreated(const MaterialAssignment& materialAssignment) + { + // PSO-impacting property changes are allowed in the editor + // because the saved slice data can be analyzed to pre-compile the necessary PSOs. + if (materialAssignment.m_materialInstance) + { + materialAssignment.m_materialInstance->SetPsoHandlingOverride(AZ::RPI::MaterialPropertyPsoHandling::Allowed); + } } AZ::u32 EditorMaterialComponent::OnConfigurationChanged() { - // Whenever the user makes changes to the editor component data the controller configuration must be rebuilt - m_configurationChangeInProgress = true; - UpdateController(); - m_configurationChangeInProgress = false; - return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; } void EditorMaterialComponent::OnMaterialAssignmentsChanged() { - // [GFX TODO][ATOM-4604] remove flag after mesh component material handling is fixed to not recreate/reload mesh for material changes - if (!m_configurationChangeInProgress) - { - UpdateMaterialSlots(); - } - } - - void EditorMaterialComponent::OnMaterialsEdited(const MaterialAssignmentMap& materials) - { - AzToolsFramework::ScopedUndoBatch undoBatch("Materials edited."); - SetDirty(); - - // The layout of the materials slots is already set. - // We just need to read the values from any edited overrides into the editor component - // and refresh. - for (auto& materialSlotPair : GetMaterialSlots()) - { - EditorMaterialComponentSlot& slot = *materialSlotPair.second; - const MaterialAssignment& materialFromController = GetMaterialAssignmentFromMap(materials, slot.m_id); - slot.m_materialAsset = materialFromController.m_materialAsset; - slot.m_propertyOverrides = materialFromController.m_propertyOverrides; - slot.m_matModUvOverrides = materialFromController.m_matModUvOverrides; - } - - AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, - AzToolsFramework::Refresh_AttributesAndValues); - } - - void EditorMaterialComponent::UpdateConfiguration(const MaterialComponentConfig& config) - { - m_controller.SetMaterialOverrides(config.m_materials); - } - - void EditorMaterialComponent::UpdateController() - { - SetDirty(); - - // Build the controller configuration from the editor configuration - MaterialComponentConfig config = m_controller.GetConfiguration(); - config.m_materials.clear(); - - for (const auto& materialSlotPair : GetMaterialSlots()) - { - const EditorMaterialComponentSlot* materialSlot = materialSlotPair.second; - - // Do not apply materials for lods if they are disabled - if (materialSlot->m_id.m_lodIndex != MaterialAssignmentId::NonLodIndex && !m_materialSlotsByLodEnabled) - { - continue; - } - - // Only material slots with a valid asset IDs or property overrides will be copied - // to minimize the amount of data stored in the controller and game component - if (materialSlot->m_materialAsset.GetId().IsValid()) - { - MaterialAssignment& materialAssignment = config.m_materials[materialSlot->m_id]; - materialAssignment.m_materialAsset = materialSlot->m_materialAsset; - materialAssignment.m_propertyOverrides = materialSlot->m_propertyOverrides; - materialAssignment.m_matModUvOverrides = materialSlot->m_matModUvOverrides; - } - else if (!materialSlot->m_propertyOverrides.empty() || !materialSlot->m_matModUvOverrides.empty()) - { - MaterialAssignment& materialAssignment = config.m_materials[materialSlot->m_id]; - materialAssignment.m_materialAsset = materialSlot->m_defaultMaterialAsset; - materialAssignment.m_propertyOverrides = materialSlot->m_propertyOverrides; - materialAssignment.m_matModUvOverrides = materialSlot->m_matModUvOverrides; - } - } - - UpdateConfiguration(config); + UpdateMaterialSlots(); } void EditorMaterialComponent::UpdateMaterialSlots() @@ -309,70 +277,28 @@ namespace AZ m_materialSlots = {}; m_materialSlotsByLod = {}; - const MaterialComponentConfig& config = m_controller.GetConfiguration(); + // Get current material assignments + MaterialAssignmentMap currentMaterials; + MaterialComponentRequestBus::EventResult( + currentMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetMaterialOverrides); // Get the known material assignment slots from the associated model or other source - MaterialAssignmentMap materialsFromSource; - MaterialReceiverRequestBus::EventResult(materialsFromSource, GetEntityId(), &MaterialReceiverRequestBus::Events::GetMaterialAssignments); + MaterialAssignmentMap originalMaterials; + MaterialComponentRequestBus::EventResult( + originalMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetOriginalMaterialAssignments); - RPI::ModelMaterialSlotMap modelMaterialSlots; - MaterialReceiverRequestBus::EventResult(modelMaterialSlots, GetEntityId(), &MaterialReceiverRequestBus::Events::GetModelMaterialSlots); - // Generate the table of editable materials using the source data to define number of groups, elements, and initial values - for (const auto& materialPair : materialsFromSource) + for (const auto& materialPair : originalMaterials) { // Setup the material slot entry EditorMaterialComponentSlot slot; + slot.m_entityId = GetEntityId(); slot.m_id = materialPair.first; - slot.m_materialChangedCallback = [this]() { - // This callback is triggered whenever an individual material slot changes outside of normal inspector interactions - // So we must manually handle undo, update configuration, and refresh the inspector to display the new values - AzToolsFramework::ScopedUndoBatch undoBatch("Material slot changed."); - SetDirty(); - - OnConfigurationChanged(); - - AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, - AzToolsFramework::Refresh_AttributesAndValues); - }; - slot.m_propertyChangedCallback = [this]() { - OnConfigurationChanged(); - }; - - const char* UnknownSlotName = ""; - - // If this is the default material assignment ID then it represents the default slot which is not contained in any other group - if (slot.m_id == DefaultMaterialAssignmentId) - { - slot.m_label = "Default Material"; - } - else - { - auto slotIter = modelMaterialSlots.find(slot.m_id.m_materialSlotStableId); - if (slotIter != modelMaterialSlots.end()) - { - const Name& displayName = slotIter->second.m_displayName; - slot.m_label = !displayName.IsEmpty() ? displayName.GetStringView() : UnknownSlotName; - - slot.m_defaultMaterialAsset = slotIter->second.m_defaultMaterialAsset; - } - else - { - slot.m_label = UnknownSlotName; - } - } // if material is present in controller configuration, assign its data - const MaterialAssignment& materialFromController = GetMaterialAssignmentFromMap(config.m_materials, slot.m_id); + const MaterialAssignment& materialFromController = GetMaterialAssignmentFromMap(currentMaterials, slot.m_id); slot.m_materialAsset = materialFromController.m_materialAsset; - slot.m_propertyOverrides = materialFromController.m_propertyOverrides; - slot.m_matModUvOverrides = materialFromController.m_matModUvOverrides; - - // Attempt to get the UV names from model meshes. - MaterialReceiverRequestBus::EventResult(slot.m_modelUvNames, GetEntityId(), &MaterialReceiverRequestBus::Events::GetModelUvNames); - if (slot.m_id.IsDefault()) { m_defaultMaterialSlot = slot; @@ -388,13 +314,14 @@ namespace AZ if (slot.m_id.IsLodAndSlotId()) { // Resize the containers to fit all elements - m_materialSlotsByLod.resize(AZ::GetMax(m_materialSlotsByLod.size(), aznumeric_cast(slot.m_id.m_lodIndex + 1))); + m_materialSlotsByLod.resize( + AZ::GetMax(m_materialSlotsByLod.size(), aznumeric_cast(slot.m_id.m_lodIndex + 1))); m_materialSlotsByLod[slot.m_id.m_lodIndex].push_back(slot); continue; } } - // Sort all of the slots by label to ensure stable index values (materialsFromSource is an unordered map) + // Sort all of the slots by label to ensure stable index values (originalMaterials is an unordered map) AZStd::sort(m_materialSlots.begin(), m_materialSlots.end(), [](const auto& a, const auto& b) { return a.GetLabel() < b.GetLabel(); }); @@ -404,54 +331,42 @@ namespace AZ [](const auto& a, const auto& b) { return a.GetLabel() < b.GetLabel(); }); } + MaterialComponentNotificationBus::Event(GetEntityId(), &MaterialComponentNotifications::OnMaterialsEdited); + AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, - AzToolsFramework::Refresh_EntireTree); - } - - AZ::u32 EditorMaterialComponent::ResetMaterialSlots() - { - AzToolsFramework::ScopedUndoBatch undoBatch("Resetting materials."); - SetDirty(); - - UpdateConfiguration(MaterialComponentConfig()); - UpdateMaterialSlots(); - - m_materialSlotsByLodEnabled = false; - - // Forcing refresh in case triggered from context menu action - AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, - AzToolsFramework::Refresh_EntireTree); - - return AZ::Edit::PropertyRefreshLevels::EntireTree; + &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); } AZ::u32 EditorMaterialComponent::OpenMaterialExporter() { AzToolsFramework::ScopedUndoBatch undoBatch("Generating materials."); SetDirty(); - - // First generating a unique set of all material asset IDs that will be used for source data generation - AZStd::unordered_map assetIdMap; - auto materialSlots = GetMaterialSlots(); - for (auto& materialSlotPair : materialSlots) + MaterialAssignmentMap originalMaterials; + MaterialComponentRequestBus::EventResult( + originalMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetOriginalMaterialAssignments); + + // Generate a unique set of all material asset IDs that will be used for source data generation + AZStd::unordered_map assetIdToSlotNameMap; + for (const auto& materialPair : originalMaterials) { - Data::AssetId defaultMaterialAssetId = materialSlotPair.second->m_defaultMaterialAsset.GetId(); - if (defaultMaterialAssetId.IsValid()) + const Data::AssetId originalAssetId = materialPair.second.m_materialAsset.GetId(); + if (originalAssetId.IsValid()) { - assetIdMap[defaultMaterialAssetId] = materialSlotPair.second->GetLabel(); + MaterialComponentRequestBus::EventResult( + assetIdToSlotNameMap[originalAssetId], GetEntityId(), &MaterialComponentRequestBus::Events::GetMaterialSlotLabel, + materialPair.first); } } - // Convert the unique set of asset IDs into export items that can be configured in the dialog + // Convert the unique set of asset IDs into export items that can be configured in the dialog // The order should not matter because the table in the dialog can sort itself for a specific row EditorMaterialComponentExporter::ExportItemsContainer exportItems; - for (auto assetIdInfo : assetIdMap) + exportItems.reserve(assetIdToSlotNameMap.size()); + + for (const auto& [assetId, slotName] : assetIdToSlotNameMap) { - EditorMaterialComponentExporter::ExportItem exportItem{assetIdInfo.first, assetIdInfo.second}; - exportItems.push_back(exportItem); + exportItems.emplace_back(assetId, slotName); } // Display the export dialog so that the user can configure how they want different materials to be exported @@ -467,16 +382,17 @@ namespace AZ const auto& assetIdOutcome = AZ::RPI::AssetUtils::MakeAssetId(exportItem.GetExportPath(), 0); if (assetIdOutcome) { - for (auto& materialSlotPair : materialSlots) + for (const auto& materialPair : originalMaterials) { - EditorMaterialComponentSlot* editorMaterialSlot = materialSlotPair.second; - - if (editorMaterialSlot) + // We need to check whether replaced material corresponds to this slot's default material. + const Data::AssetId originalAssetId = materialPair.second.m_materialAsset.GetId(); + if (originalAssetId == exportItem.GetOriginalAssetId()) { - // We need to check whether replaced material corresponds to this slot's default material. - if (editorMaterialSlot->m_defaultMaterialAsset.GetId() == exportItem.GetOriginalAssetId()) + if (m_materialSlotsByLodEnabled || !materialPair.first.IsLodAndSlotId()) { - editorMaterialSlot->m_materialAsset.Create(assetIdOutcome.GetValue()); + MaterialComponentRequestBus::Event( + GetEntityId(), &MaterialComponentRequestBus::Events::SetMaterialOverride, materialPair.first, + assetIdOutcome.GetValue()); } } } @@ -484,17 +400,23 @@ namespace AZ } } - // Forcing refresh in case triggered from context menu action - AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, - AzToolsFramework::Refresh_AttributesAndValues); + UpdateMaterialSlots(); - return OnConfigurationChanged(); + return AZ::Edit::PropertyRefreshLevels::EntireTree; } AZ::u32 EditorMaterialComponent::OnLodsToggled() { - OnConfigurationChanged(); + AzToolsFramework::ScopedUndoBatch undoBatch("Toggling LOD materials."); + SetDirty(); + + if (!m_materialSlotsByLodEnabled) + { + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearLodMaterialOverrides); + } + + UpdateMaterialSlots(); + return AZ::Edit::PropertyRefreshLevels::EntireTree; } @@ -530,39 +452,5 @@ namespace AZ { return AZStd::string::format("LOD %d", lodIndex); } - - template - void EditorMaterialComponent::BuildMaterialSlotMap(ComponentType& component, ContainerType& materialSlots) - { - materialSlots[DefaultMaterialAssignmentId] = &component.m_defaultMaterialSlot; - - for (auto& slot : component.m_materialSlots) - { - materialSlots[slot.m_id] = &slot; - } - - for (auto& slotsForLod : component.m_materialSlotsByLod) - { - for (auto& slot : slotsForLod) - { - materialSlots[slot.m_id] = &slot; - } - } - } - - AZStd::unordered_map EditorMaterialComponent::GetMaterialSlots() - { - AZStd::unordered_map materialSlots; - BuildMaterialSlotMap(*this, materialSlots); - return AZStd::move(materialSlots); - } - - AZStd::unordered_map EditorMaterialComponent::GetMaterialSlots() const - { - AZStd::unordered_map materialSlots; - BuildMaterialSlotMap(*this, materialSlots); - return AZStd::move(materialSlots); - } } // namespace Render } // namespace AZ - diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h index 88ffef9366..f8895d994f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h @@ -8,11 +8,11 @@ #pragma once +#include #include #include -#include -#include #include +#include namespace AZ { @@ -50,14 +50,7 @@ namespace AZ void OnMaterialAssignmentsChanged() override; //! MaterialComponentNotificationBus::Handler overrides... - void OnMaterialsEdited(const MaterialAssignmentMap& materials) override; - - // Apply a material component configuration to the active controller - void UpdateConfiguration(const MaterialComponentConfig& config); - - // Converts the editor components material slots to the material component - // configuration and updates the controller - void UpdateController(); + void OnMaterialInstanceCreated(const MaterialAssignment& materialAssignment) override; // Regenerates the editor component material slots based on the material and // LOD mapping from the model or other consumer of materials. @@ -65,9 +58,6 @@ namespace AZ // controller configuration then those values will be assigned to the editor component slots. void UpdateMaterialSlots(); - // Clears all values related to the material component and regenerates the editor slots - AZ::u32 ResetMaterialSlots(); - // Opens the source material export dialog and updates editor material slots based on // selected actions AZ::u32 OpenMaterialExporter(); @@ -89,10 +79,6 @@ namespace AZ // Evaluate if materials can be edited bool IsEditingAllowed() const; - template - static void BuildMaterialSlotMap(ComponentType& component, ContainerType& materialSlots); - AZStd::unordered_map GetMaterialSlots(); - AZStd::unordered_map GetMaterialSlots() const; AZStd::string GetLabelForLod(int lodIndex) const; AZStd::string m_message; @@ -101,8 +87,6 @@ namespace AZ EditorMaterialComponentSlotsByLodContainer m_materialSlotsByLod; bool m_materialSlotsByLodEnabled = false; - bool m_configurationChangeInProgress = false; // when true, model changes are ignored - static const char* GenerateMaterialsButtonText; static const char* GenerateMaterialsToolTipText; static const char* ResetMaterialsButtonText; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentExporter.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentExporter.cpp index f55da28fa6..14cc28b91e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentExporter.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentExporter.cpp @@ -55,6 +55,10 @@ namespace AZ bool OpenExportDialog(ExportItemsContainer& exportItems) { + // Sort material entries so they are ordered by name in the table + AZStd::sort(exportItems.begin(), exportItems.end(), + [](const auto& a, const auto& b) { return a.GetMaterialSlotName() < b.GetMaterialSlotName(); }); + QWidget* activeWindow = nullptr; AzToolsFramework::EditorWindowRequestBus::BroadcastResult(activeWindow, &AzToolsFramework::EditorWindowRequests::GetAppMainWindow); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index b9a4adc068..a3152faf87 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -27,19 +27,16 @@ #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 #include -#include #include AZ_POP_DISABLE_WARNING @@ -49,26 +46,59 @@ namespace AZ { namespace EditorMaterialComponentInspector { - MaterialPropertyInspector::MaterialPropertyInspector( - const AZStd::string& slotName, const AZ::Data::AssetId& assetId, PropertyChangedCallback propertyChangedCallback, - QWidget* parent) + MaterialPropertyInspector::MaterialPropertyInspector(QWidget* parent) : AtomToolsFramework::InspectorWidget(parent) - , m_slotName(slotName) - , m_materialAssetId(assetId) - , m_propertyChangedCallback(propertyChangedCallback) { + // Create the menu button + QToolButton* menuButton = new QToolButton(this); + menuButton->setAutoRaise(true); + menuButton->setIcon(QIcon(":/Cards/img/UI20/Cards/menu_ico.svg")); + menuButton->setVisible(true); + QObject::connect(menuButton, &QToolButton::clicked, this, [this]() { OpenMenu(); }); + AddHeading(menuButton); + + m_messageLabel = new QLabel(this); + m_messageLabel->setWordWrap(true); + m_messageLabel->setVisible(true); + m_messageLabel->setAlignment(Qt::AlignCenter); + m_messageLabel->setText(tr("Material not available")); + AddHeading(m_messageLabel); + + AZ::EntitySystemBus::Handler::BusConnect(); } MaterialPropertyInspector::~MaterialPropertyInspector() { AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect(); + AZ::EntitySystemBus::Handler::BusDisconnect(); + AZ::TickBus::Handler::BusDisconnect(); + MaterialComponentNotificationBus::Handler::BusDisconnect(); } - bool MaterialPropertyInspector::LoadMaterial() + bool MaterialPropertyInspector::LoadMaterial( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) { - if (!EditorMaterialComponentUtil::LoadMaterialEditDataFromAssetId(m_materialAssetId, m_editData)) + UnloadMaterial(); + + m_entityId = entityId; + m_materialAssignmentId = materialAssignmentId; + MaterialComponentNotificationBus::Handler::BusDisconnect(); + MaterialComponentNotificationBus::Handler::BusConnect(m_entityId); + + AZ::Data::AssetId materialAssetId = {}; + MaterialComponentRequestBus::EventResult( + materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId); + + if (!materialAssetId.IsValid()) + { + UnloadMaterial(); + return false; + } + + if (!EditorMaterialComponentUtil::LoadMaterialEditDataFromAssetId(materialAssetId, m_editData)) { AZ_Warning("AZ::Render::EditorMaterialComponentInspector", false, "Failed to load material data."); + UnloadMaterial(); return false; } @@ -77,6 +107,7 @@ namespace AZ if (!m_materialInstance) { AZ_Error("AZ::Render::EditorMaterialComponentInspector", false, "Material instance could not be created."); + UnloadMaterial(); return false; } @@ -102,15 +133,36 @@ namespace AZ } } + Populate(); + m_messageLabel->setVisible(false); return true; } + void MaterialPropertyInspector::UnloadMaterial() + { + Reset(); + m_editData = EditorMaterialComponentUtil::MaterialEditData(); + m_materialInstance = {}; + m_dirtyPropertyFlags.set(); + m_editorFunctors = {}; + m_internalEditNotification = {}; + m_messageLabel->setVisible(true); + m_messageLabel->setText(tr("Material not available")); + } + + bool MaterialPropertyInspector::IsLoaded() const + { + return m_entityId.IsValid() && m_materialInstance && m_editData.m_materialAsset.IsReady(); + } + void MaterialPropertyInspector::Reset() { m_activeProperty = {}; m_groups = {}; m_dirtyPropertyFlags.set(); + m_internalEditNotification = {}; + AZ::TickBus::Handler::BusDisconnect(); AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect(); AtomToolsFramework::InspectorWidget::Reset(); } @@ -150,9 +202,18 @@ namespace AZ QFileInfo materialTypeSourceFileInfo(m_editData.m_materialTypeSourcePath.c_str()); QFileInfo materialParentSourceFileInfo(AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_editData.m_materialParentAsset.GetId()).c_str()); + AZStd::string entityName; + AZ::ComponentApplicationBus::BroadcastResult( + entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, m_entityId); + + AZStd::string slotName; + MaterialComponentRequestBus::EventResult( + slotName, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialSlotLabel, m_materialAssignmentId); + QString materialInfo; materialInfo += tr(""); - materialInfo += tr("").arg(m_slotName.c_str()); + materialInfo += tr("").arg(entityName.c_str()); + materialInfo += tr("").arg(slotName.c_str()); if (!materialFileInfo.fileName().isEmpty()) { materialInfo += tr("").arg(materialFileInfo.fileName()); @@ -209,16 +270,8 @@ namespace AZ } // Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties - const AZ::Crc32 saveStateKey(AZStd::string::format( - "MaterialPropertyInspector::PropertyGroup::%s::%s", m_materialAssetId.ToString().c_str(), - groupNameId.c_str())); auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget( - &group, &group, group.TYPEINFO_Uuid(), this, this, saveStateKey, - [](const AzToolsFramework::InstanceDataNode* source, const AzToolsFramework::InstanceDataNode* target) { - AZ_UNUSED(source); - const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(target); - return property && AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue); - }); + &group, nullptr, group.TYPEINFO_Uuid(), this, this, GetSaveStateKeyForGroup(groupNameId)); AddGroup(groupNameId, groupDisplayName, groupDescription, propertyGroupWidget); } @@ -262,35 +315,90 @@ namespace AZ } // Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties - const AZ::Crc32 saveStateKey(AZStd::string::format( - "MaterialPropertyInspector::PropertyGroup::%s::%s", m_materialAssetId.ToString().c_str(), - groupNameId.c_str())); auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget( - &group, &group, group.TYPEINFO_Uuid(), this, this, saveStateKey, - [](const AzToolsFramework::InstanceDataNode* source, const AzToolsFramework::InstanceDataNode* target) { - AZ_UNUSED(source); - const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(target); - return property && AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue); - }); + &group, nullptr, group.TYPEINFO_Uuid(), this, this, GetSaveStateKeyForGroup(groupNameId)); AddGroup(groupNameId, groupDisplayName, groupDescription, propertyGroupWidget); } AddGroupsEnd(); - m_dirtyPropertyFlags.set(); - RunEditorMaterialFunctors(); + LoadOverridesFromEntity(); } - void MaterialPropertyInspector::RunPropertyChangedCallback() + void MaterialPropertyInspector::LoadOverridesFromEntity() { - if (m_propertyChangedCallback) + if (!IsLoaded()) { - m_propertyChangedCallback(m_editData.m_materialPropertyOverrideMap); + return; + } + + m_editData.m_materialPropertyOverrideMap.clear(); + MaterialComponentRequestBus::EventResult( + m_editData.m_materialPropertyOverrideMap, m_entityId, &MaterialComponentRequestBus::Events::GetPropertyOverrides, + m_materialAssignmentId); + + for (auto& group : m_groups) + { + for (auto& property : group.second.m_properties) + { + const AtomToolsFramework::DynamicPropertyConfig& propertyConfig = property.GetConfig(); + const auto overrideItr = m_editData.m_materialPropertyOverrideMap.find(propertyConfig.m_id); + const auto& editValue = overrideItr != m_editData.m_materialPropertyOverrideMap.end() ? overrideItr->second : propertyConfig.m_originalValue; + + // This first converts to an acceptable runtime type in case the value came from script + const auto propertyIndex = m_materialInstance->FindPropertyIndex(property.GetId()); + if (!propertyIndex.IsNull()) + { + const auto runtimeValue = AtomToolsFramework::ConvertToRuntimeType(editValue); + if (runtimeValue.IsValid()) + { + property.SetValue(AtomToolsFramework::ConvertToEditableType(runtimeValue)); + } + } + else + { + property.SetValue(editValue); + } + + UpdateMaterialInstanceProperty(property); + } + } + + m_dirtyPropertyFlags.set(); + RunEditorMaterialFunctors(); + RebuildAll(); + } + + void MaterialPropertyInspector::SaveOverridesToEntity(bool commitChanges) + { + if (!IsLoaded()) + { + return; + } + + MaterialComponentRequestBus::Event( + m_entityId, &MaterialComponentRequestBus::Events::SetPropertyOverrides, m_materialAssignmentId, + m_editData.m_materialPropertyOverrideMap); + + if (commitChanges) + { + AzToolsFramework::ScopedUndoBatch undoBatch("Material slot changed."); + AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast( + &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, m_entityId); + + m_internalEditNotification = true; + MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited); + m_internalEditNotification = false; } } void MaterialPropertyInspector::RunEditorMaterialFunctors() { + if (!IsLoaded()) + { + return; + } + AZStd::unordered_set changedPropertyNames; AZStd::unordered_set changedPropertyGroupNames; @@ -337,11 +445,13 @@ namespace AZ // Apply any changes to material property meta data back to the editor property configurations for (auto& groupPair : m_groups) { - AZ::Name groupName{groupPair.first}; + AZ::Name groupName{ groupPair.first }; if (changedPropertyGroupNames.find(groupName) != changedPropertyGroupNames.end()) { - SetGroupVisible(groupPair.first, propertyGroupDynamicMetadata[groupName].m_visibility == AZ::RPI::MaterialPropertyGroupVisibility::Enabled); + SetGroupVisible( + groupPair.first, + propertyGroupDynamicMetadata[groupName].m_visibility == AZ::RPI::MaterialPropertyGroupVisibility::Enabled); } for (auto& property : groupPair.second.m_properties) @@ -355,12 +465,12 @@ namespace AZ if (oldReadOnly != propertyConfig.m_readOnly) { - RefreshAll(); + RefreshGroup(groupPair.first); } if (oldVisible != propertyConfig.m_visible) { - RebuildAll(); + RebuildGroup(groupPair.first); } } } @@ -368,57 +478,37 @@ namespace AZ void MaterialPropertyInspector::UpdateMaterialInstanceProperty(const AtomToolsFramework::DynamicProperty& property) { - if (m_materialInstance) + if (!IsLoaded()) { - const auto propertyIndex = m_materialInstance->FindPropertyIndex(property.GetId()); - if (!propertyIndex.IsNull()) - { - m_dirtyPropertyFlags.set(propertyIndex.GetIndex()); + return; + } - const auto runtimeValue = AtomToolsFramework::ConvertToRuntimeType(property.GetValue()); - if (runtimeValue.IsValid()) - { - m_materialInstance->SetPropertyValue(propertyIndex, runtimeValue); - } + const auto propertyIndex = m_materialInstance->FindPropertyIndex(property.GetId()); + if (!propertyIndex.IsNull()) + { + m_dirtyPropertyFlags.set(propertyIndex.GetIndex()); + + const auto runtimeValue = AtomToolsFramework::ConvertToRuntimeType(property.GetValue()); + if (runtimeValue.IsValid()) + { + m_materialInstance->SetPropertyValue(propertyIndex, runtimeValue); } } } - void MaterialPropertyInspector::SetOverrides(const MaterialPropertyOverrideMap& propertyOverrideMap) + AZ::Crc32 MaterialPropertyInspector::GetSaveStateKeyForGroup(const AZStd::string& groupNameId) const { - m_editData.m_materialPropertyOverrideMap = propertyOverrideMap; + return AZ::Crc32(AZStd::string::format( + "MaterialPropertyInspector::PropertyGroup::%s::%s", m_editData.m_materialAssetId.ToString().c_str(), + groupNameId.c_str())); + } - for (auto& group : m_groups) - { - for (auto& property : group.second.m_properties) - { - const AtomToolsFramework::DynamicPropertyConfig& propertyConfig = property.GetConfig(); - const auto overrideItr = m_editData.m_materialPropertyOverrideMap.find(propertyConfig.m_id); - const auto& editValue = overrideItr != m_editData.m_materialPropertyOverrideMap.end() ? overrideItr->second : propertyConfig.m_originalValue; - - // This first converts to an acceptable runtime type in case the value came from script - const auto propertyIndex = m_materialInstance->FindPropertyIndex(property.GetId()); - if (!propertyIndex.IsNull()) - { - const auto runtimeValue = AtomToolsFramework::ConvertToRuntimeType(editValue); - if (runtimeValue.IsValid()) - { - property.SetValue(AtomToolsFramework::ConvertToEditableType(runtimeValue)); - } - } - else - { - property.SetValue(editValue); - } - - UpdateMaterialInstanceProperty(property); - } - } - - m_dirtyPropertyFlags.set(); - RunPropertyChangedCallback(); - RunEditorMaterialFunctors(); - RebuildAll(); + bool MaterialPropertyInspector::AreNodePropertyValuesEqual( + const AzToolsFramework::InstanceDataNode* source, const AzToolsFramework::InstanceDataNode* target) + { + AZ_UNUSED(source); + const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(target); + return property && AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue); } bool MaterialPropertyInspector::SaveMaterial() const @@ -446,7 +536,8 @@ namespace AZ bool MaterialPropertyInspector::SaveMaterialToSource() const { - const QString saveFilePath = AtomToolsFramework::GetSaveFileInfo(m_editData.m_materialSourcePath.c_str()).absoluteFilePath(); + const QString saveFilePath = + AtomToolsFramework::GetSaveFileInfo(m_editData.m_materialSourcePath.c_str()).absoluteFilePath(); if (saveFilePath.isEmpty()) { return false; @@ -463,13 +554,13 @@ namespace AZ bool MaterialPropertyInspector::HasMaterialSource() const { - return !m_editData.m_materialSourcePath.empty() && + return IsLoaded() && !m_editData.m_materialSourcePath.empty() && AZ::StringFunc::Path::IsExtension(m_editData.m_materialSourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension); } bool MaterialPropertyInspector::HasMaterialParentSource() const { - return !m_editData.m_materialParentSourcePath.empty() && + return IsLoaded() && !m_editData.m_materialParentSourcePath.empty() && AZ::StringFunc::Path::IsExtension( m_editData.m_materialParentSourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension); } @@ -479,7 +570,7 @@ namespace AZ if (HasMaterialSource()) { EditorMaterialSystemComponentRequestBus::Broadcast( - &EditorMaterialSystemComponentRequestBus::Events::OpenInMaterialEditor, m_editData.m_materialSourcePath); + &EditorMaterialSystemComponentRequestBus::Events::OpenMaterialEditor, m_editData.m_materialSourcePath); } } @@ -488,10 +579,42 @@ namespace AZ if (HasMaterialParentSource()) { EditorMaterialSystemComponentRequestBus::Broadcast( - &EditorMaterialSystemComponentRequestBus::Events::OpenInMaterialEditor, m_editData.m_materialParentSourcePath); + &EditorMaterialSystemComponentRequestBus::Events::OpenMaterialEditor, m_editData.m_materialParentSourcePath); } } + void MaterialPropertyInspector::OpenMenu() + { + QAction* action = nullptr; + + QMenu menu(this); + action = menu.addAction("Clear Overrides", [this] { + MaterialComponentRequestBus::Event( + m_entityId, &MaterialComponentRequestBus::Events::SetPropertyOverrides, m_materialAssignmentId, + MaterialPropertyOverrideMap()); + QueueUpdateUI(); + }); + action->setEnabled(IsLoaded()); + + menu.addSeparator(); + + action = menu.addAction("Save Material", [this] { SaveMaterial(); }); + action->setEnabled(IsLoaded()); + + action = menu.addAction("Save Material To Source", [this] { SaveMaterialToSource(); }); + action->setEnabled(HasMaterialSource()); + + menu.addSeparator(); + + action = menu.addAction("Open Source Material In Editor", [this] { OpenMaterialSourceInEditor(); }); + action->setEnabled(HasMaterialSource()); + + action = menu.addAction("Open Parent Material In Editor", [this] { OpenMaterialParentSourceInEditor(); }); + action->setEnabled(HasMaterialParentSource()); + + menu.exec(QCursor::pos()); + } + const EditorMaterialComponentUtil::MaterialEditData& MaterialPropertyInspector::GetEditData() const { return m_editData; @@ -501,7 +624,8 @@ namespace AZ { // For some reason the reflected property editor notifications are not symmetrical // This function is called continuously anytime a property changes until the edit has completed - // Because of that, we have to track whether or not we are continuing to edit the same property to know when editing has started and ended + // Because of that, we have to track whether or not we are continuing to edit the same property to know when editing has + // started and ended const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode); if (property) { @@ -521,15 +645,16 @@ namespace AZ { m_editData.m_materialPropertyOverrideMap[m_activeProperty->GetId()] = m_activeProperty->GetValue(); UpdateMaterialInstanceProperty(*m_activeProperty); - RunPropertyChangedCallback(); + SaveOverridesToEntity(false); } } } void MaterialPropertyInspector::SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode) { - // As above, there are symmetrical functions on the notification interface for when editing begins and ends and has been completed but they are not being called following that pattern. - // when this function executes the changes to the property are ready to be committed or reverted + // As above, there are symmetrical functions on the notification interface for when editing begins and ends and has been + // completed but they are not being called following that pattern. when this function executes the changes to the property + // are ready to be committed or reverted const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode); if (property) { @@ -537,85 +662,92 @@ namespace AZ { m_editData.m_materialPropertyOverrideMap[m_activeProperty->GetId()] = m_activeProperty->GetValue(); UpdateMaterialInstanceProperty(*m_activeProperty); - RunPropertyChangedCallback(); + SaveOverridesToEntity(true); RunEditorMaterialFunctors(); m_activeProperty = nullptr; } } } - bool OpenInspectorDialog( - const AZStd::string& slotName, const AZ::Data::AssetId& assetId, MaterialPropertyOverrideMap propertyOverrideMap, - PropertyChangedCallback propertyChangedCallback) + void MaterialPropertyInspector::OnEntityInitialized(const AZ::EntityId& entityId) { - QWidget* activeWindow = nullptr; - AzToolsFramework::EditorWindowRequestBus::BroadcastResult(activeWindow, &AzToolsFramework::EditorWindowRequests::GetAppMainWindow); - - // Constructing a dialog with a table to display all configurable material export items - QDialog dialog(activeWindow); - dialog.setWindowTitle("Material Inspector"); - - MaterialPropertyInspector* inspector = new MaterialPropertyInspector(slotName, assetId, propertyChangedCallback, &dialog); - if (!inspector->LoadMaterial()) + if (m_entityId == entityId) { - return false; + UnloadMaterial(); } + } - inspector->Populate(); - inspector->SetOverrides(propertyOverrideMap); + void MaterialPropertyInspector::OnEntityDestroyed(const AZ::EntityId& entityId) + { + if (m_entityId == entityId) + { + UnloadMaterial(); + } + } - // Create the menu button - QToolButton* menuButton = new QToolButton(&dialog); - menuButton->setAutoRaise(true); - menuButton->setIcon(QIcon(":/Cards/img/UI20/Cards/menu_ico.svg")); - menuButton->setVisible(true); - QObject::connect(menuButton, &QToolButton::clicked, &dialog, [&]() { - QAction* action = nullptr; + void MaterialPropertyInspector::OnEntityActivated(const AZ::EntityId& entityId) + { + if (m_entityId == entityId) + { + QueueUpdateUI(); + } + } - QMenu menu(&dialog); - action = menu.addAction("Clear Overrides", [&] { inspector->SetOverrides(MaterialPropertyOverrideMap()); }); - action = menu.addAction("Revert Changes", [&] { inspector->SetOverrides(propertyOverrideMap); }); + void MaterialPropertyInspector::OnEntityDeactivated(const AZ::EntityId& entityId) + { + if (m_entityId == entityId) + { + UnloadMaterial(); + } + } - menu.addSeparator(); - action = menu.addAction("Save Material", [&] { inspector->SaveMaterial(); }); - action = menu.addAction("Save Material To Source", [&] { inspector->SaveMaterialToSource(); }); - action->setEnabled(inspector->HasMaterialSource()); + void MaterialPropertyInspector::OnEntityNameChanged(const AZ::EntityId& entityId, const AZStd::string& name) + { + AZ_UNUSED(name); + if (m_entityId == entityId) + { + QueueUpdateUI(); + } + } - menu.addSeparator(); - action = menu.addAction("Open Source Material In Editor", [&] { inspector->OpenMaterialSourceInEditor(); }); - action->setEnabled(inspector->HasMaterialSource()); - action = menu.addAction("Open Parent Material In Editor", [&] { inspector->OpenMaterialParentSourceInEditor(); }); - action->setEnabled(inspector->HasMaterialParentSource()); - menu.exec(QCursor::pos()); - }); + void MaterialPropertyInspector::OnTick(float deltaTime, ScriptTimePoint time) + { + AZ_UNUSED(time); + AZ_UNUSED(deltaTime); + UpdateUI(); + AZ::TickBus::Handler::BusDisconnect(); + } - QDialogButtonBox* buttonBox = new QDialogButtonBox(&dialog); - buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); - QObject::connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); - QObject::connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); + void MaterialPropertyInspector::OnMaterialsEdited() + { + if (!m_internalEditNotification) + { + QueueUpdateUI(); + } + } - QObject::connect(&dialog, &QDialog::rejected, &dialog, [&] { inspector->SetOverrides(propertyOverrideMap); }); + void MaterialPropertyInspector::UpdateUI() + { + AZ::Data::AssetId assetId; + MaterialComponentRequestBus::EventResult( + assetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId); - QVBoxLayout* dialogLayout = new QVBoxLayout(&dialog); - dialogLayout->addWidget(menuButton); - dialogLayout->addWidget(inspector); - dialogLayout->addWidget(buttonBox); - dialog.setLayout(dialogLayout); - dialog.setModal(true); + if (IsLoaded() && m_editData.m_materialAssetId == assetId) + { + LoadOverridesFromEntity(); + } + else + { + LoadMaterial(m_entityId, m_materialAssignmentId); + } + } - // Forcing the initial dialog size to accomodate typical content. - // Temporarily settng fixed size because dialog.show/exec invokes WindowDecorationWrapper::showEvent. - // This forces the dialog to be centered and sized based on the layout of content. - // Resizing the dialog after show will not be centered and moving the dialog programatically doesn't m0ve the custmk frame. - dialog.setFixedSize(500, 800); - dialog.show(); - - // Removing fixed size to allow drag resizing - dialog.setMinimumSize(0, 0); - dialog.setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); - - // Return true if the user press the export button - return dialog.exec() == QDialog::Accepted; + void MaterialPropertyInspector::QueueUpdateUI() + { + if (!AZ::TickBus::Handler::BusIsConnected()) + { + AZ::TickBus::Handler::BusConnect(); + } } } // namespace EditorMaterialComponentInspector } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h index 12c4fbfdd9..11eb2cec51 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h @@ -9,17 +9,22 @@ #pragma once #if !defined(Q_MOC_RUN) +#include #include #include #include +#include +#include #include #include -#include #include +#include #include #include #endif +class QLabel; + namespace AZ { namespace Render @@ -31,31 +36,33 @@ namespace AZ class MaterialPropertyInspector : public AtomToolsFramework::InspectorWidget , public AzToolsFramework::IPropertyEditorNotify - { + , public AZ::EntitySystemBus::Handler + , public AZ::TickBus::Handler + , public MaterialComponentNotificationBus::Handler + { Q_OBJECT public: AZ_CLASS_ALLOCATOR(MaterialPropertyInspector, AZ::SystemAllocator, 0); - explicit MaterialPropertyInspector( - const AZStd::string& slotName, const AZ::Data::AssetId& assetId, PropertyChangedCallback propertyChangedCallback, - QWidget* parent = nullptr); + MaterialPropertyInspector(QWidget* parent = nullptr); ~MaterialPropertyInspector() override; - bool LoadMaterial(); + bool LoadMaterial(const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId); + void UnloadMaterial(); + bool IsLoaded() const; // AtomToolsFramework::InspectorRequestBus::Handler overrides... void Reset() override; void Populate(); - void SetOverrides(const MaterialPropertyOverrideMap& propertyOverrideMap); - bool SaveMaterial() const; bool SaveMaterialToSource() const; bool HasMaterialSource() const; bool HasMaterialParentSource() const; void OpenMaterialSourceInEditor() const; void OpenMaterialParentSourceInEditor() const; + void OpenMenu(); const EditorMaterialComponentUtil::MaterialEditData& GetEditData() const; private: @@ -69,28 +76,47 @@ namespace AZ void RequestPropertyContextMenu([[maybe_unused]] AzToolsFramework::InstanceDataNode*, const QPoint&) override {} void PropertySelectionChanged([[maybe_unused]] AzToolsFramework::InstanceDataNode*, bool) override {} + // AZ::EntitySystemBus::Handler overrides... + void OnEntityInitialized(const AZ::EntityId& entityId) override; + void OnEntityDestroyed(const AZ::EntityId& entityId) override; + void OnEntityActivated(const AZ::EntityId& entityId) override; + void OnEntityDeactivated(const AZ::EntityId& entityId) override; + void OnEntityNameChanged(const AZ::EntityId& entityId, const AZStd::string& name) override; + + //! AZ::TickBus::Handler overrides... + void OnTick(float deltaTime, ScriptTimePoint time) override; + + //! MaterialComponentNotificationBus::Handler overrides... + void OnMaterialsEdited() override; + + void UpdateUI(); + void QueueUpdateUI(); + void AddDetailsGroup(); void AddUvNamesGroup(); - void RunPropertyChangedCallback(); + + void LoadOverridesFromEntity(); + void SaveOverridesToEntity(bool commitChanges); void RunEditorMaterialFunctors(); void UpdateMaterialInstanceProperty(const AtomToolsFramework::DynamicProperty& property); + AZ::Crc32 GetSaveStateKeyForGroup(const AZStd::string& groupNameId) const; + static bool AreNodePropertyValuesEqual( + const AzToolsFramework::InstanceDataNode* source, const AzToolsFramework::InstanceDataNode* target); + // Tracking the property that is actively being edited in the inspector const AtomToolsFramework::DynamicProperty* m_activeProperty = {}; - AZStd::string m_slotName; - AZ::Data::AssetId m_materialAssetId = {}; + AZ::EntityId m_entityId; + AZ::Render::MaterialAssignmentId m_materialAssignmentId; EditorMaterialComponentUtil::MaterialEditData m_editData; - PropertyChangedCallback m_propertyChangedCallback = {}; AZ::Data::Instance m_materialInstance = {}; AZStd::vector> m_editorFunctors = {}; AZ::RPI::MaterialPropertyFlags m_dirtyPropertyFlags = {}; AZStd::unordered_map m_groups = {}; - }; - - bool OpenInspectorDialog( - const AZStd::string& slotName, const AZ::Data::AssetId& assetId, MaterialPropertyOverrideMap propertyOverrideMap, - PropertyChangedCallback propertyChangedCallback); + bool m_internalEditNotification = {}; + QLabel* m_messageLabel = {}; + }; } // namespace EditorMaterialComponentInspector } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp index 39dde65a99..363221d3fc 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp @@ -80,10 +80,9 @@ namespace AZ if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(6, &EditorMaterialComponentSlot::ConvertVersion) + ->Version(7, &EditorMaterialComponentSlot::ConvertVersion) ->Field("id", &EditorMaterialComponentSlot::m_id) ->Field("materialAsset", &EditorMaterialComponentSlot::m_materialAsset) - ->Field("defaultMaterialAsset", &EditorMaterialComponentSlot::m_defaultMaterialAsset) ; if (AZ::EditContext* editContext = serializeContext->GetEditContext()) @@ -114,20 +113,22 @@ namespace AZ ->Constructor() ->Property("id", BehaviorValueProperty(&EditorMaterialComponentSlot::m_id)) ->Property("materialAsset", BehaviorValueProperty(&EditorMaterialComponentSlot::m_materialAsset)) - ->Property("propertyOverrides", BehaviorValueProperty(&EditorMaterialComponentSlot::m_propertyOverrides)) - ->Property("matModUvOverrides", BehaviorValueProperty(&EditorMaterialComponentSlot::m_matModUvOverrides)) ; } }; AZ::Data::AssetId EditorMaterialComponentSlot::GetDefaultAssetId() const { - return m_defaultMaterialAsset.GetId(); + AZ::Data::AssetId assetId; + MaterialComponentRequestBus::EventResult(assetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, m_id); + return assetId; } AZStd::string EditorMaterialComponentSlot::GetLabel() const { - return m_label; + AZStd::string label; + MaterialComponentRequestBus::EventResult(label, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialSlotLabel, m_id); + return label; } bool EditorMaterialComponentSlot::HasSourceData() const @@ -137,50 +138,45 @@ namespace AZ return !sourcePath.empty() && AZ::StringFunc::Path::IsExtension(sourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension); } - void EditorMaterialComponentSlot::OnMaterialChanged() const + void EditorMaterialComponentSlot::SetAsset(const Data::AssetId& assetId) { - if (m_materialChangedCallback) - { - m_materialChangedCallback(); - } + m_materialAsset = AZ::Data::Asset(assetId, AZ::AzTypeInfo::Uuid()); + MaterialComponentRequestBus::Event( + m_entityId, &MaterialComponentRequestBus::Events::SetMaterialOverride, m_id, m_materialAsset.GetId()); + OnDataChanged(); } - void EditorMaterialComponentSlot::OnPropertyChanged() const + void EditorMaterialComponentSlot::SetAsset(const Data::Asset& asset) { - if (m_propertyChangedCallback) - { - m_propertyChangedCallback(); - } - } - - void EditorMaterialComponentSlot::OpenMaterialEditor() const - { - const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_materialAsset.GetId()); - if (!sourcePath.empty() && AZ::StringFunc::Path::IsExtension(sourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension)) - { - EditorMaterialSystemComponentRequestBus::Broadcast(&EditorMaterialSystemComponentRequestBus::Events::OpenInMaterialEditor, sourcePath); - } + m_materialAsset = asset; + MaterialComponentRequestBus::Event( + m_entityId, &MaterialComponentRequestBus::Events::SetMaterialOverride, m_id, m_materialAsset.GetId()); + OnDataChanged(); } void EditorMaterialComponentSlot::Clear() { m_materialAsset = {}; + MaterialComponentRequestBus::Event( + m_entityId, &MaterialComponentRequestBus::Events::SetMaterialOverride, m_id, m_materialAsset.GetId()); + ClearOverrides(); + } + + void EditorMaterialComponentSlot::ClearToDefaultAsset() + { + m_materialAsset = AZ::Data::Asset(GetDefaultAssetId(), AZ::AzTypeInfo::Uuid()); + MaterialComponentRequestBus::Event( + m_entityId, &MaterialComponentRequestBus::Events::SetMaterialOverride, m_id, m_materialAsset.GetId()); ClearOverrides(); } void EditorMaterialComponentSlot::ClearOverrides() { - m_propertyOverrides = {}; - m_matModUvOverrides = {}; - OnMaterialChanged(); - } - - void EditorMaterialComponentSlot::ResetToDefaultAsset() - { - m_materialAsset = m_defaultMaterialAsset; - m_propertyOverrides = {}; - m_matModUvOverrides = {}; - OnMaterialChanged(); + MaterialComponentRequestBus::Event( + m_entityId, &MaterialComponentRequestBus::Events::SetPropertyOverrides, m_id, MaterialPropertyOverrideMap()); + MaterialComponentRequestBus::Event( + m_entityId, &MaterialComponentRequestBus::Events::SetModelUvOverrides, m_id, AZ::RPI::MaterialModelUvOverrideMap()); + OnDataChanged(); } void EditorMaterialComponentSlot::OpenMaterialExporter() @@ -189,7 +185,7 @@ namespace AZ // But we still need to allow the user to reconfigure it using the dialog EditorMaterialComponentExporter::ExportItemsContainer exportItems; { - EditorMaterialComponentExporter::ExportItem exportItem{m_defaultMaterialAsset.GetId(), m_label}; + EditorMaterialComponentExporter::ExportItem exportItem{ GetDefaultAssetId(), GetLabel() }; exportItems.push_back(exportItem); } @@ -203,11 +199,13 @@ namespace AZ continue; } - // Generate a new asset ID utilizing the export file path so that we can update this material slot to reference the new asset + // Generate a new asset ID utilizing the export file path so that we can update this material slot to reference the new + // asset const auto& assetIdOutcome = AZ::RPI::AssetUtils::MakeAssetId(exportItem.GetExportPath(), 0); if (assetIdOutcome) { - m_materialAsset.Create(assetIdOutcome.GetValue()); + m_materialAsset = AZ::Data::Asset( + assetIdOutcome.GetValue(), AZ::AzTypeInfo::Uuid()); changed = true; } } @@ -219,37 +217,43 @@ namespace AZ } } + void EditorMaterialComponentSlot::OpenMaterialEditor() const + { + const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_materialAsset.GetId()); + if (!sourcePath.empty() && AZ::StringFunc::Path::IsExtension(sourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension)) + { + EditorMaterialSystemComponentRequestBus::Broadcast( + &EditorMaterialSystemComponentRequestBus::Events::OpenMaterialEditor, sourcePath); + } + } + void EditorMaterialComponentSlot::OpenMaterialInspector() { - MaterialPropertyOverrideMap initialPropertyOverrides = m_propertyOverrides; - auto applyPropertyChangedCallback = [this](const MaterialPropertyOverrideMap& propertyOverrides) { - m_propertyOverrides = propertyOverrides; - OnPropertyChanged(); - }; - - if (m_materialAsset.GetId().IsValid()) - { - if (EditorMaterialComponentInspector::OpenInspectorDialog(GetLabel(), m_materialAsset.GetId(), m_propertyOverrides, applyPropertyChangedCallback)) - { - OnMaterialChanged(); - } - } + EditorMaterialSystemComponentRequestBus::Broadcast( + &EditorMaterialSystemComponentRequestBus::Events::OpenMaterialInspector, m_entityId, m_id); } void EditorMaterialComponentSlot::OpenUvNameMapInspector() { - RPI::MaterialModelUvOverrideMap initialUvOverrides = m_matModUvOverrides; - auto applyMatModUvOverrideChangedCallback = [this](const RPI::MaterialModelUvOverrideMap& matModUvOverrides) { - m_matModUvOverrides = matModUvOverrides; - // Treated as a special property. It will be updated together with properties. - OnPropertyChanged(); - }; - if (m_materialAsset.GetId().IsValid()) { - if (EditorMaterialComponentInspector::OpenInspectorDialog(m_materialAsset.GetId(), m_matModUvOverrides, m_modelUvNames, applyMatModUvOverrideChangedCallback)) + AZStd::unordered_set modelUvNames; + MaterialReceiverRequestBus::EventResult(modelUvNames, m_entityId, &MaterialReceiverRequestBus::Events::GetModelUvNames); + + RPI::MaterialModelUvOverrideMap matModUvOverrides; + MaterialComponentRequestBus::EventResult( + matModUvOverrides, m_entityId, &MaterialComponentRequestBus::Events::GetModelUvOverrides, m_id); + + auto applyMatModUvOverrideChangedCallback = [this](const RPI::MaterialModelUvOverrideMap& matModUvOverrides) { - OnMaterialChanged(); + MaterialComponentRequestBus::Event( + m_entityId, &MaterialComponentRequestBus::Events::SetModelUvOverrides, m_id, matModUvOverrides); + }; + + if (EditorMaterialComponentInspector::OpenInspectorDialog( + m_materialAsset.GetId(), matModUvOverrides, modelUvNames, applyMatModUvOverrideChangedCallback)) + { + OnDataChanged(); } } } @@ -261,7 +265,7 @@ namespace AZ QAction* action = nullptr; action = menu.addAction("Generate/Manage Source Material...", [this]() { OpenMaterialExporter(); }); - action->setEnabled(m_defaultMaterialAsset.GetId().IsValid()); + action->setEnabled(GetDefaultAssetId().IsValid()); menu.addSeparator(); @@ -276,10 +280,38 @@ namespace AZ menu.addSeparator(); + MaterialPropertyOverrideMap propertyOverrides; + MaterialComponentRequestBus::EventResult( + propertyOverrides, m_entityId, &MaterialComponentRequestBus::Events::GetPropertyOverrides, m_id); + RPI::MaterialModelUvOverrideMap matModUvOverrides; + MaterialComponentRequestBus::EventResult( + matModUvOverrides, m_entityId, &MaterialComponentRequestBus::Events::GetModelUvOverrides, m_id); + action = menu.addAction("Clear Material Instance Overrides", [this]() { ClearOverrides(); }); - action->setEnabled(!m_propertyOverrides.empty() || !m_matModUvOverrides.empty()); + action->setEnabled(!propertyOverrides.empty() || !matModUvOverrides.empty()); menu.exec(QCursor::pos()); } + + void EditorMaterialComponentSlot::OnMaterialChanged() const + { + MaterialComponentRequestBus::Event( + m_entityId, &MaterialComponentRequestBus::Events::SetMaterialOverride, m_id, m_materialAsset.GetId()); + OnDataChanged(); + } + + void EditorMaterialComponentSlot::OnDataChanged() const + { + // This is triggered whenever a material slot changes outside of normal inspector interactions + // Handle undo, update configuration, and refresh the inspector to display the new values + AzToolsFramework::ScopedUndoBatch undoBatch("Material slot changed."); + AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast( + &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, m_entityId); + + MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited); + + AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( + &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_AttributesAndValues); + } } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h index 58d6fa9ab0..01e357f1bb 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.h @@ -33,29 +33,26 @@ namespace AZ AZ::Data::AssetId GetDefaultAssetId() const; AZStd::string GetLabel() const; bool HasSourceData() const; - void OpenMaterialEditor() const; - void ResetToDefaultAsset(); + + void SetAsset(const Data::AssetId& assetId); + void SetAsset(const Data::Asset& asset); void Clear(); + void ClearToDefaultAsset(); void ClearOverrides(); + void OpenMaterialExporter(); + void OpenMaterialEditor() const; void OpenMaterialInspector(); void OpenUvNameMapInspector(); + AZ::EntityId m_entityId; MaterialAssignmentId m_id; - AZStd::string m_label; Data::Asset m_materialAsset; - Data::Asset m_defaultMaterialAsset; - MaterialPropertyOverrideMap m_propertyOverrides; - AZStd::function m_materialChangedCallback; - AZStd::function m_propertyChangedCallback; - - RPI::MaterialModelUvOverrideMap m_matModUvOverrides; - AZStd::unordered_set m_modelUvNames; // Cached for override options. private: void OpenPopupMenu(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType); void OnMaterialChanged() const; - void OnPropertyChanged() const; + void OnDataChanged() const; }; // Vector of slots for assignable or overridable material data. diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp index e25653988e..83ddbf46c5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp @@ -6,33 +6,31 @@ * */ -#include - -#include +#include +#include #include #include +#include #include #include - #include - #include +#include #include - -#include - -#include - +#include +#include +#include #include // Disables warning messages triggered by the Qt library // 4251: class needs to have dll-interface to be used by clients of class // 4800: forcing value to bool 'true' or 'false' (performance warning) AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") -#include -#include -#include #include +#include +#include +#include +#include AZ_POP_DISABLE_WARNING void InitMaterialEditorResources() @@ -95,6 +93,7 @@ namespace AZ AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect(); AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusConnect(); AzToolsFramework::EditorMenuNotificationBus::Handler::BusConnect(); + AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); SetupThumbnails(); m_materialBrowserInteractions.reset(aznew MaterialBrowserInteractions); @@ -106,6 +105,7 @@ namespace AZ AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect(); AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusDisconnect(); AzToolsFramework::EditorMenuNotificationBus::Handler::BusDisconnect(); + AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); TeardownThumbnails(); m_materialBrowserInteractions.reset(); @@ -117,7 +117,7 @@ namespace AZ } } - void EditorMaterialSystemComponent::OpenInMaterialEditor(const AZStd::string& sourcePath) + void EditorMaterialSystemComponent::OpenMaterialEditor(const AZStd::string& sourcePath) { AZ_TracePrintf("MaterialComponent", "Launching Material Editor"); @@ -140,6 +140,20 @@ namespace AZ AtomToolsFramework::LaunchTool("MaterialEditor", ".exe", arguments); } + void EditorMaterialSystemComponent::OpenMaterialInspector( + const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) + { + auto dockWidget = AzToolsFramework::InstanceViewPane("Material Property Inspector"); + if (dockWidget) + { + auto inspector = static_cast(dockWidget->widget()); + if (inspector) + { + inspector->LoadMaterial(entityId, materialAssignmentId); + } + } + } + void EditorMaterialSystemComponent::OnApplicationAboutToStop() { TeardownThumbnails(); @@ -157,11 +171,12 @@ namespace AZ QObject::connect( m_openMaterialEditorAction, &QAction::triggered, m_openMaterialEditorAction, [this]() { - OpenInMaterialEditor(""); + OpenMaterialEditor(""); } ); - AzToolsFramework::EditorMenuRequestBus::Broadcast(&AzToolsFramework::EditorMenuRequestBus::Handler::AddMenuAction, "ToolMenu", m_openMaterialEditorAction, true); + AzToolsFramework::EditorMenuRequestBus::Broadcast( + &AzToolsFramework::EditorMenuRequestBus::Handler::AddMenuAction, "ToolMenu", m_openMaterialEditorAction, true); } } @@ -174,13 +189,25 @@ namespace AZ } } + void EditorMaterialSystemComponent::NotifyRegisterViews() + { + AzToolsFramework::ViewPaneOptions inspectorOptions; + inspectorOptions.canHaveMultipleInstances = true; + inspectorOptions.preferedDockingArea = Qt::NoDockWidgetArea; + inspectorOptions.paneRect = QRect(50, 50, 400, 700); + inspectorOptions.showInMenu = false; + inspectorOptions.showOnToolsToolbar = false; + AzToolsFramework::RegisterViewPane( + "Material Property Inspector", LyViewPane::CategoryTools, inspectorOptions); + } + void EditorMaterialSystemComponent::SetupThumbnails() { using namespace AzToolsFramework::Thumbnailer; using namespace LyIntegration; - ThumbnailerRequestsBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, - MAKE_TCACHE(Thumbnails::MaterialThumbnailCache), + ThumbnailerRequestsBus::Broadcast( + &ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(Thumbnails::MaterialThumbnailCache), ThumbnailContext::DefaultContext); } @@ -189,12 +216,13 @@ namespace AZ using namespace AzToolsFramework::Thumbnailer; using namespace LyIntegration; - ThumbnailerRequestsBus::Broadcast(&ThumbnailerRequests::UnregisterThumbnailProvider, - Thumbnails::MaterialThumbnailCache::ProviderName, + ThumbnailerRequestsBus::Broadcast( + &ThumbnailerRequests::UnregisterThumbnailProvider, Thumbnails::MaterialThumbnailCache::ProviderName, ThumbnailContext::DefaultContext); } - AzToolsFramework::AssetBrowser::SourceFileDetails EditorMaterialSystemComponent::GetSourceFileDetails(const char* fullSourceFileName) + AzToolsFramework::AssetBrowser::SourceFileDetails EditorMaterialSystemComponent::GetSourceFileDetails( + const char* fullSourceFileName) { static const char* MaterialTypeIconPath = ":/Icons/materialtype.svg"; static const char* MaterialTypeExtension = "materialtype"; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.h index 267f31f92b..7fa43ea309 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.h @@ -8,11 +8,10 @@ #pragma once #include - #include - -#include +#include #include +#include #include #include @@ -28,8 +27,9 @@ namespace AZ : public AZ::Component , private EditorMaterialSystemComponentRequestBus::Handler , private AzFramework::ApplicationLifecycleEvents::Bus::Handler - , public AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler - , public AzToolsFramework::EditorMenuNotificationBus::Handler + , private AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler + , private AzToolsFramework::EditorMenuNotificationBus::Handler + , private AzToolsFramework::EditorEvents::Bus::Handler { public: AZ_COMPONENT(EditorMaterialSystemComponent, "{96652157-DA0B-420F-B49C-0207C585144C}"); @@ -49,7 +49,8 @@ namespace AZ private: //! EditorMaterialSystemComponentRequestBus::Handler overrides... - void OpenInMaterialEditor(const AZStd::string& sourcePath) override; + void OpenMaterialEditor(const AZStd::string& sourcePath) override; + void OpenMaterialInspector(const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) override; // AzFramework::ApplicationLifecycleEvents overrides... void OnApplicationAboutToStop() override; @@ -61,6 +62,9 @@ namespace AZ void OnPopulateToolMenuItems() override; void OnResetToolMenuItems() override; + // AztoolsFramework::EditorEvents::Bus::Handler overrides... + void NotifyRegisterViews() override; + void SetupThumbnails(); void TeardownThumbnails(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialBrowserInteractions.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialBrowserInteractions.cpp index 258727e835..18812d45e9 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialBrowserInteractions.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialBrowserInteractions.cpp @@ -29,16 +29,13 @@ namespace AZ { if (HandlesSource(fullSourceFileName)) { - openers.push_back( - { - "Material_Editor", - "Open in Material Editor...", - QIcon(), + openers.push_back({ "Material_Editor", "Open in Material Editor...", QIcon(), [&](const char* fullSourceFileNameInCallback, [[maybe_unused]] const AZ::Uuid& sourceUUID) - { - EditorMaterialSystemComponentRequestBus::Broadcast(&EditorMaterialSystemComponentRequestBus::Events::OpenInMaterialEditor, fullSourceFileNameInCallback); - } - }); + { + EditorMaterialSystemComponentRequestBus::Broadcast( + &EditorMaterialSystemComponentRequestBus::Events::OpenMaterialEditor, + fullSourceFileNameInCallback); + } }); } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp index 6e1e710282..1d9f1e81dd 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp @@ -35,12 +35,19 @@ namespace AZ ->Attribute(AZ::Script::Attributes::Module, "render") ->Event("GetOriginalMaterialAssignments", &MaterialComponentRequestBus::Events::GetOriginalMaterialAssignments) ->Event("FindMaterialAssignmentId", &MaterialComponentRequestBus::Events::FindMaterialAssignmentId) + ->Event("GetDefaultMaterialAssetId", &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId) + ->Event("GetMaterialSlotLabel", &MaterialComponentRequestBus::Events::GetMaterialSlotLabel) ->Event("SetMaterialOverrides", &MaterialComponentRequestBus::Events::SetMaterialOverrides) ->Event("GetMaterialOverrides", &MaterialComponentRequestBus::Events::GetMaterialOverrides) ->Event("ClearAllMaterialOverrides", &MaterialComponentRequestBus::Events::ClearAllMaterialOverrides) ->Event("SetDefaultMaterialOverride", &MaterialComponentRequestBus::Events::SetDefaultMaterialOverride) ->Event("GetDefaultMaterialOverride", &MaterialComponentRequestBus::Events::GetDefaultMaterialOverride) ->Event("ClearDefaultMaterialOverride", &MaterialComponentRequestBus::Events::ClearDefaultMaterialOverride) + ->Event("ClearModelMaterialOverrides", &MaterialComponentRequestBus::Events::ClearModelMaterialOverrides) + ->Event("ClearLodMaterialOverrides", &MaterialComponentRequestBus::Events::ClearLodMaterialOverrides) + ->Event("ClearIncompatibleMaterialOverrides", &MaterialComponentRequestBus::Events::ClearIncompatibleMaterialOverrides) + ->Event("ClearInvalidMaterialOverrides", &MaterialComponentRequestBus::Events::ClearInvalidMaterialOverrides) + ->Event("RepairInvalidMaterialOverrides", &MaterialComponentRequestBus::Events::RepairInvalidMaterialOverrides) ->Event("SetMaterialOverride", &MaterialComponentRequestBus::Events::SetMaterialOverride) ->Event("GetMaterialOverride", &MaterialComponentRequestBus::Events::GetMaterialOverride) ->Event("ClearMaterialOverride", &MaterialComponentRequestBus::Events::ClearMaterialOverride) @@ -71,6 +78,7 @@ namespace AZ ->Event("ClearPropertyOverride", &MaterialComponentRequestBus::Events::ClearPropertyOverride) ->Event("ClearPropertyOverrides", &MaterialComponentRequestBus::Events::ClearPropertyOverrides) ->Event("ClearAllPropertyOverrides", &MaterialComponentRequestBus::Events::ClearAllPropertyOverrides) + ->Event("SetPropertyOverrides", &MaterialComponentRequestBus::Events::SetPropertyOverrides) ->Event("GetPropertyOverrides", &MaterialComponentRequestBus::Events::GetPropertyOverrides) ; } @@ -168,20 +176,18 @@ namespace AZ const auto& propertyOverrides2 = materialIt->second.m_propertyOverrides; for (auto& propertyPair : propertyOverrides2) { - const auto& materialPropertyIndex = materialInstance->FindPropertyIndex(propertyPair.first); - if (!materialPropertyIndex.IsNull()) + if (propertyPair.second.empty()) { - if (propertyPair.second.is()) - { - const auto& assetId = *AZStd::any_cast(&propertyPair.second); - Data::Asset imageAsset(assetId, azrtti_typeid()); - materialInstance->SetPropertyValue(materialPropertyIndex, AZ::RPI::MaterialPropertyValue(imageAsset)); - } - else - { - materialInstance->SetPropertyValue(materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(propertyPair.second)); - } + continue; } + + const auto& materialPropertyIndex = materialInstance->FindPropertyIndex(propertyPair.first); + if (materialPropertyIndex.IsNull()) + { + continue; + } + + materialInstance->SetPropertyValue(materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(propertyPair.second)); } materialInstance->Compile(); @@ -249,6 +255,7 @@ namespace AZ for (auto& materialPair : m_configuration.m_materials) { materialPair.second.RebuildInstance(); + MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialPair.second); QueuePropertyChanges(materialPair.first); } QueueMaterialUpdateNotification(); @@ -273,10 +280,10 @@ namespace AZ MaterialAssignmentMap MaterialComponentController::GetOriginalMaterialAssignments() const { - MaterialAssignmentMap materialAssignmentMap; + MaterialAssignmentMap originalMaterials; MaterialReceiverRequestBus::EventResult( - materialAssignmentMap, m_entityId, &MaterialReceiverRequestBus::Events::GetMaterialAssignments); - return materialAssignmentMap; + originalMaterials, m_entityId, &MaterialReceiverRequestBus::Events::GetMaterialAssignments); + return originalMaterials; } MaterialAssignmentId MaterialComponentController::FindMaterialAssignmentId( @@ -288,6 +295,40 @@ namespace AZ return materialAssignmentId; } + AZ::Data::AssetId MaterialComponentController::GetDefaultMaterialAssetId(const MaterialAssignmentId& materialAssignmentId) const + { + RPI::ModelMaterialSlotMap modelMaterialSlots; + MaterialReceiverRequestBus::EventResult( + modelMaterialSlots, m_entityId, &MaterialReceiverRequestBus::Events::GetModelMaterialSlots); + + auto slotIter = modelMaterialSlots.find(materialAssignmentId.m_materialSlotStableId); + return slotIter != modelMaterialSlots.end() ? slotIter->second.m_defaultMaterialAsset.GetId() : AZ::Data::AssetId(); + } + + AZStd::string MaterialComponentController::GetMaterialSlotLabel(const MaterialAssignmentId& materialAssignmentId) const + { + if (materialAssignmentId == DefaultMaterialAssignmentId) + { + return "Default Material"; + } + + RPI::ModelMaterialSlotMap modelMaterialSlots; + MaterialReceiverRequestBus::EventResult( + modelMaterialSlots, m_entityId, &MaterialReceiverRequestBus::Events::GetModelMaterialSlots); + + auto slotIter = modelMaterialSlots.find(materialAssignmentId.m_materialSlotStableId); + if (slotIter != modelMaterialSlots.end()) + { + const Name& displayName = slotIter->second.m_displayName; + if (!displayName.IsEmpty()) + { + return displayName.GetStringView(); + } + } + + return ""; + } + void MaterialComponentController::SetMaterialOverrides(const MaterialAssignmentMap& materials) { // this function is called twice once material asset is changed, a temp variable is @@ -309,10 +350,70 @@ namespace AZ { m_configuration.m_materials.clear(); QueueMaterialUpdateNotification(); - MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited, m_configuration.m_materials); } } + void MaterialComponentController::ClearModelMaterialOverrides() + { + AZStd::erase_if(m_configuration.m_materials, [](const auto& materialPair) { + return materialPair.first.IsSlotIdOnly(); + }); + QueueMaterialUpdateNotification(); + } + + void MaterialComponentController::ClearLodMaterialOverrides() + { + AZStd::erase_if(m_configuration.m_materials, [](const auto& materialPair) { + return materialPair.first.IsLodAndSlotId(); + }); + QueueMaterialUpdateNotification(); + } + + void MaterialComponentController::ClearIncompatibleMaterialOverrides() + { + const MaterialAssignmentMap& originalMaterials = GetOriginalMaterialAssignments(); + AZStd::erase_if(m_configuration.m_materials, [&originalMaterials](const auto& materialPair) { + return originalMaterials.find(materialPair.first) == originalMaterials.end(); + }); + QueueMaterialUpdateNotification(); + } + + void MaterialComponentController::ClearInvalidMaterialOverrides() + { + AZStd::erase_if(m_configuration.m_materials, [](const auto& materialPair) { + if (materialPair.second.m_materialAsset.GetId().IsValid()) + { + AZ::Data::AssetInfo assetInfo; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, + materialPair.second.m_materialAsset.GetId()); + return !assetInfo.m_assetId.IsValid(); + } + return false; + }); + QueueMaterialUpdateNotification(); + } + + void MaterialComponentController::RepairInvalidMaterialOverrides() + { + for (auto& materialPair : m_configuration.m_materials) + { + if (materialPair.second.m_materialAsset.GetId().IsValid()) + { + AZ::Data::AssetInfo assetInfo; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, + materialPair.second.m_materialAsset.GetId()); + if (!assetInfo.m_assetId.IsValid()) + { + materialPair.second.m_materialAsset = AZ::Data::Asset( + GetDefaultMaterialAssetId(materialPair.first), AZ::AzTypeInfo::Uuid()); + } + } + } + LoadMaterials(); + } + void MaterialComponentController::SetDefaultMaterialOverride(const AZ::Data::AssetId& materialAssetId) { SetMaterialOverride(DefaultMaterialAssignmentId, materialAssetId); @@ -328,18 +429,19 @@ namespace AZ ClearMaterialOverride(DefaultMaterialAssignmentId); } - void MaterialComponentController::SetMaterialOverride(const MaterialAssignmentId& materialAssignmentId, const AZ::Data::AssetId& materialAssetId) + void MaterialComponentController::SetMaterialOverride( + const MaterialAssignmentId& materialAssignmentId, const AZ::Data::AssetId& materialAssetId) { - m_configuration.m_materials[materialAssignmentId].m_materialAsset.Create(materialAssetId); + m_configuration.m_materials[materialAssignmentId].m_materialAsset = + AZ::Data::Asset(materialAssetId, AZ::AzTypeInfo::Uuid()); LoadMaterials(); } - const AZ::Data::AssetId MaterialComponentController::GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const + AZ::Data::AssetId MaterialComponentController::GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const { auto materialIt = m_configuration.m_materials.find(materialAssignmentId); if (materialIt == m_configuration.m_materials.end()) { - AZ_Error("MaterialComponentController", false, "MaterialAssignmentId not found."); return {}; } @@ -351,7 +453,6 @@ namespace AZ if (m_configuration.m_materials.erase(materialAssignmentId) > 0) { QueueMaterialUpdateNotification(); - MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited, m_configuration.m_materials); } } @@ -364,6 +465,7 @@ namespace AZ { materialAssignment.m_propertyOverrides[AZ::Name(propertyName)] = value; materialAssignment.RebuildInstance(); + MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialAssignment); QueueMaterialUpdateNotification(); } else @@ -372,7 +474,6 @@ namespace AZ } QueuePropertyChanges(materialAssignmentId); - MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited, m_configuration.m_materials); } void MaterialComponentController::SetPropertyOverrideBool( @@ -450,14 +551,12 @@ namespace AZ const auto materialIt = m_configuration.m_materials.find(materialAssignmentId); if (materialIt == m_configuration.m_materials.end()) { - AZ_Error("MaterialComponentController", false, "MaterialAssignmentId not found."); return {}; } const auto propertyIt = materialIt->second.m_propertyOverrides.find(AZ::Name(propertyName)); if (propertyIt == materialIt->second.m_propertyOverrides.end()) { - AZ_Error("MaterialComponentController", false, "Property not found: %s.", propertyName.c_str()); return {}; } @@ -546,14 +645,12 @@ namespace AZ auto materialIt = m_configuration.m_materials.find(materialAssignmentId); if (materialIt == m_configuration.m_materials.end()) { - AZ_Error("MaterialComponentController", false, "MaterialAssignmentId not found."); return; } auto propertyIt = materialIt->second.m_propertyOverrides.find(AZ::Name(propertyName)); if (propertyIt == materialIt->second.m_propertyOverrides.end()) { - AZ_Error("MaterialComponentController", false, "Property not found: %s.", propertyName.c_str()); return; } @@ -561,11 +658,11 @@ namespace AZ if (materialIt->second.m_propertyOverrides.empty()) { materialIt->second.RebuildInstance(); + MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialIt->second); QueueMaterialUpdateNotification(); } QueuePropertyChanges(materialAssignmentId); - MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited, m_configuration.m_materials); } void MaterialComponentController::ClearPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) @@ -573,7 +670,6 @@ namespace AZ auto materialIt = m_configuration.m_materials.find(materialAssignmentId); if (materialIt == m_configuration.m_materials.end()) { - AZ_Error("MaterialComponentController", false, "MaterialAssignmentId not found."); return; } @@ -581,29 +677,39 @@ namespace AZ { materialIt->second.m_propertyOverrides = {}; materialIt->second.RebuildInstance(); + MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialIt->second); QueueMaterialUpdateNotification(); - MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited, m_configuration.m_materials); } } void MaterialComponentController::ClearAllPropertyOverrides() { - bool cleared = false; for (auto& materialPair : m_configuration.m_materials) { if (!materialPair.second.m_propertyOverrides.empty()) { materialPair.second.m_propertyOverrides = {}; materialPair.second.RebuildInstance(); + MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialPair.second); QueueMaterialUpdateNotification(); - cleared = true; } } + } - if (cleared) + void MaterialComponentController::SetPropertyOverrides( + const MaterialAssignmentId& materialAssignmentId, const MaterialPropertyOverrideMap& propertyOverrides) + { + auto& materialAssignment = m_configuration.m_materials[materialAssignmentId]; + const bool wasEmpty = materialAssignment.m_propertyOverrides.empty(); + materialAssignment.m_propertyOverrides = propertyOverrides; + + if (wasEmpty != materialAssignment.m_propertyOverrides.empty()) { - MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited, m_configuration.m_materials); + materialAssignment.RebuildInstance(); + QueueMaterialUpdateNotification(); } + + QueuePropertyChanges(materialAssignmentId); } MaterialPropertyOverrideMap MaterialComponentController::GetPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) const @@ -611,12 +717,38 @@ namespace AZ const auto materialIt = m_configuration.m_materials.find(materialAssignmentId); if (materialIt == m_configuration.m_materials.end()) { - AZ_Warning("MaterialComponentController", false, "MaterialAssignmentId not found."); return {}; } return materialIt->second.m_propertyOverrides; } + void MaterialComponentController::SetModelUvOverrides( + const MaterialAssignmentId& materialAssignmentId, const AZ::RPI::MaterialModelUvOverrideMap& modelUvOverrides) + { + auto& materialAssignment = m_configuration.m_materials[materialAssignmentId]; + const bool wasEmpty = materialAssignment.m_matModUvOverrides.empty(); + materialAssignment.m_matModUvOverrides = modelUvOverrides; + + if (wasEmpty != materialAssignment.m_matModUvOverrides.empty()) + { + materialAssignment.RebuildInstance(); + QueueMaterialUpdateNotification(); + } + + QueuePropertyChanges(materialAssignmentId); + } + + AZ::RPI::MaterialModelUvOverrideMap MaterialComponentController::GetModelUvOverrides( + const MaterialAssignmentId& materialAssignmentId) const + { + const auto materialIt = m_configuration.m_materials.find(materialAssignmentId); + if (materialIt == m_configuration.m_materials.end()) + { + return {}; + } + return materialIt->second.m_matModUvOverrides; + } + void MaterialComponentController::QueuePropertyChanges(const MaterialAssignmentId& materialAssignmentId) { m_queuedPropertyOverrides.emplace(materialAssignmentId); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h index 9e59bbef19..2bf15ca3b8 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h @@ -47,14 +47,21 @@ namespace AZ //! MaterialComponentRequestBus overrides... MaterialAssignmentMap GetOriginalMaterialAssignments() const override; MaterialAssignmentId FindMaterialAssignmentId(const MaterialAssignmentLodIndex lod, const AZStd::string& label) const override; + AZ::Data::AssetId GetDefaultMaterialAssetId(const MaterialAssignmentId& materialAssignmentId) const override; + AZStd::string GetMaterialSlotLabel(const MaterialAssignmentId& materialAssignmentId) const override; void SetMaterialOverrides(const MaterialAssignmentMap& materials) override; const MaterialAssignmentMap& GetMaterialOverrides() const override; void ClearAllMaterialOverrides() override; + void ClearModelMaterialOverrides() override; + void ClearLodMaterialOverrides() override; + void ClearIncompatibleMaterialOverrides() override; + void ClearInvalidMaterialOverrides() override; + void RepairInvalidMaterialOverrides() override; void SetDefaultMaterialOverride(const AZ::Data::AssetId& materialAssetId) override; const AZ::Data::AssetId GetDefaultMaterialOverride() const override; void ClearDefaultMaterialOverride() override; void SetMaterialOverride(const MaterialAssignmentId& materialAssignmentId, const AZ::Data::AssetId& materialAssetId) override; - const AZ::Data::AssetId GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const override; + AZ::Data::AssetId GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const override; void ClearMaterialOverride(const MaterialAssignmentId& materialAssignmentId) override; void SetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::any& value) override; @@ -86,7 +93,12 @@ namespace AZ void ClearPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) override; void ClearPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) override; void ClearAllPropertyOverrides() override; + void SetPropertyOverrides( + const MaterialAssignmentId& materialAssignmentId, const MaterialPropertyOverrideMap& propertyOverrides) override; MaterialPropertyOverrideMap GetPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) const override; + void SetModelUvOverrides( + const MaterialAssignmentId& materialAssignmentId, const AZ::RPI::MaterialModelUvOverrideMap& modelUvOverrides) override; + AZ::RPI::MaterialModelUvOverrideMap GetModelUvOverrides(const MaterialAssignmentId& materialAssignmentId) const override; private: diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h index 2c7cc78979..6b0731fbf7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h @@ -30,9 +30,6 @@ namespace AZ { namespace Render { - - - //! A configuration structure for the MeshComponentController class MeshComponentConfig final : public AZ::ComponentConfig @@ -107,14 +104,14 @@ namespace AZ void SetLodType(RPI::Cullable::LodType lodType) override; RPI::Cullable::LodType GetLodType() const override; - virtual void SetLodOverride(RPI::Cullable::LodOverride lodOverride); - virtual RPI::Cullable::LodOverride GetLodOverride() const; + void SetLodOverride(RPI::Cullable::LodOverride lodOverride) override; + RPI::Cullable::LodOverride GetLodOverride() const override; - virtual void SetMinimumScreenCoverage(float minimumScreenCoverage); - virtual float GetMinimumScreenCoverage() const; + void SetMinimumScreenCoverage(float minimumScreenCoverage) override; + float GetMinimumScreenCoverage() const override; - virtual void SetQualityDecayRate(float qualityDecayRate); - virtual float GetQualityDecayRate() const; + void SetQualityDecayRate(float qualityDecayRate) override; + float GetQualityDecayRate() const override; void SetVisibility(bool visible) override; bool GetVisibility() const override; @@ -130,7 +127,7 @@ namespace AZ void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; // MaterialReceiverRequestBus::Handler overrides ... - virtual MaterialAssignmentId FindMaterialAssignmentId( + MaterialAssignmentId FindMaterialAssignmentId( const MaterialAssignmentLodIndex lod, const AZStd::string& label) const override; RPI::ModelMaterialSlotMap GetModelMaterialSlots() const override; MaterialAssignmentMap GetMaterialAssignments() const override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp index a83e2e9fa1..230130826e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +95,7 @@ namespace AZ DecalComponent::CreateDescriptor(), DirectionalLightComponent::CreateDescriptor(), BloomComponent::CreateDescriptor(), + HDRColorGradingComponent::CreateDescriptor(), DisplayMapperComponent::CreateDescriptor(), DepthOfFieldComponent::CreateDescriptor(), ExposureControlComponent::CreateDescriptor(), @@ -124,6 +127,7 @@ namespace AZ EditorDecalComponent::CreateDescriptor(), EditorDirectionalLightComponent::CreateDescriptor(), EditorBloomComponent::CreateDescriptor(), + EditorHDRColorGradingComponent::CreateDescriptor(), EditorDepthOfFieldComponent::CreateDescriptor(), EditorDisplayMapperComponent::CreateDescriptor(), EditorExposureControlComponent::CreateDescriptor(), diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp new file mode 100644 index 0000000000..49c8275141 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AZ +{ + namespace Render + { + void EditorHDRColorGradingComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class()->Version(1); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "HDR Color Grading", "Tune and apply color grading in HDR.") + ->ClassElement(Edit::ClassElements::EditorData, "") + ->Attribute(Edit::Attributes::Category, "Atom") + ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing. + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing. + ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) + ->Attribute(Edit::Attributes::AutoExpand, true) + ->Attribute(Edit::Attributes::HelpPageURL, "https://") // [TODO ATOM-2672][PostFX] need to create page for PostProcessing. + ; + + editContext->Class( + "HDRColorGradingComponentControl", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &HDRColorGradingComponentController::m_configuration, "Configuration", "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ; + + editContext->Class("HDRColorGradingComponentConfig", "") + ->DataElement(Edit::UIHandlers::CheckBox, &HDRColorGradingComponentConfig::m_enabled, + "Enable HDR color grading", + "Enable HDR color grading.") + ->ClassElement(AZ::Edit::ClassElements::Group, "Color Adjustment") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingExposure, "Exposure", "Exposure Value") + ->Attribute(Edit::Attributes::Min, AZStd::numeric_limits::lowest()) + ->Attribute(Edit::Attributes::Max, AZStd::numeric_limits::max()) + ->Attribute(Edit::Attributes::SoftMin, -20.0f) + ->Attribute(Edit::Attributes::SoftMax, 20.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingContrast, "Contrast", "Contrast Value") + ->Attribute(Edit::Attributes::Min, -100.0f) + ->Attribute(Edit::Attributes::Max, 100.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingPreSaturation, "Pre Saturation", "Pre Saturation Value") + ->Attribute(Edit::Attributes::Min, -100.0f) + ->Attribute(Edit::Attributes::Max, 100.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingFilterIntensity, "Filter Intensity", "Filter Intensity Value") + ->Attribute(Edit::Attributes::Min, AZStd::numeric_limits::lowest()) + ->Attribute(Edit::Attributes::Max, AZStd::numeric_limits::max()) + ->Attribute(Edit::Attributes::SoftMin, -1.0f) + ->Attribute(Edit::Attributes::SoftMax, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingFilterMultiply, "Filter Multiply", "Filter Multiply Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_colorFilterSwatch, "Color Filter Swatch", "Color Filter Swatch Value") + + ->ClassElement(AZ::Edit::ClassElements::Group, "White Balance") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_whiteBalanceKelvin, "Temperature", "Temperature in Kelvin") + ->Attribute(Edit::Attributes::Min, 1000.0f) + ->Attribute(Edit::Attributes::Max, 40000.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_whiteBalanceTint, "Tint", "Tint Value") + ->Attribute(Edit::Attributes::Min, -100.0f) + ->Attribute(Edit::Attributes::Max, 100.0f) + + ->ClassElement(AZ::Edit::ClassElements::Group, "Split Toning") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_splitToneWeight, "Split Tone Weight", "Modulates the split toning effect.") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_splitToneBalance, "Split Tone Balance", "Split Tone Balance Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_splitToneShadowsColor, "Split Tone Shadows Color", "Split Tone Shadows Color") + ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_splitToneHighlightsColor, "Split Tone Highlights Color", "Split Tone Highlights Color") + + ->ClassElement(AZ::Edit::ClassElements::Group, "Channel Mixing") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_channelMixingRed, "Channel Mixing Red", "Channel Mixing Red Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_channelMixingGreen, "Channel Mixing Green", "Channel Mixing Green Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_channelMixingBlue, "Channel Mixing Blue", "Channel Mixing Blue Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + + ->ClassElement(AZ::Edit::ClassElements::Group, "Shadow Midtones Highlights") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhWeight, "SMH Weight", "Modulates the SMH effect.") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhShadowsStart, "SMH Shadows Start", "SMH Shadows Start Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhShadowsEnd, "SMH Shadows End", "SMH Shadows End Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhHighlightsStart, "SMH Highlights Start", "SMH Highlights Start Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhHighlightsEnd, "SMH Highlights End", "SMH Highlights End Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_smhShadowsColor, "SMH Shadows Color", "SMH Shadows Color") + ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_smhMidtonesColor, "SMH Midtones Color", "SMH Midtones Color") + ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_smhHighlightsColor, "SMH Highlights Color", "SMH Highlights Color") + + ->ClassElement(AZ::Edit::ClassElements::Group, "Final Adjustment") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingHueShift, "Hue Shift", "Hue Shift Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingPostSaturation, "Post Saturation", "Post Saturation Value") + ->Attribute(Edit::Attributes::Min, -100.0f) + ->Attribute(Edit::Attributes::Max, 100.0f) + ; + } + } + } + + EditorHDRColorGradingComponent::EditorHDRColorGradingComponent(const HDRColorGradingComponentConfig& config) + : BaseClass(config) + { + } + + u32 EditorHDRColorGradingComponent::OnConfigurationChanged() + { + m_controller.OnConfigChanged(); + return Edit::PropertyRefreshLevels::AttributesAndValues; + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.h new file mode 100644 index 0000000000..8eea568e3a --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + class EditorHDRColorGradingComponent final + : public AzToolsFramework::Components:: + EditorComponentAdapter + { + public: + using BaseClass = AzToolsFramework::Components::EditorComponentAdapter; + AZ_EDITOR_COMPONENT(AZ::Render::EditorHDRColorGradingComponent, "{C1FAB0B1-5847-4533-B08E-7314AC807B8E}", BaseClass); + + static void Reflect(AZ::ReflectContext* context); + + EditorHDRColorGradingComponent() = default; + EditorHDRColorGradingComponent(const HDRColorGradingComponentConfig& config); + + //! EditorRenderComponentAdapter overrides... + AZ::u32 OnConfigurationChanged() override; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponent.cpp new file mode 100644 index 0000000000..d91a91a09f --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponent.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AZ +{ + namespace Render + { + HDRColorGradingComponent::HDRColorGradingComponent(const HDRColorGradingComponentConfig& config) + : BaseClass(config) + { + } + + void HDRColorGradingComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class(); + } + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponent.h new file mode 100644 index 0000000000..6c4bd1418f --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponent.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class HDRColorGradingComponent final + : public AzFramework::Components::ComponentAdapter + { + public: + using BaseClass = AzFramework::Components::ComponentAdapter; + AZ_COMPONENT(AZ::Render::HDRColorGradingComponent, "{51968E0F-4DF0-4851-8405-388CBB15B573}", BaseClass); + + HDRColorGradingComponent() = default; + HDRColorGradingComponent(const HDRColorGradingComponentConfig& config); + + static void Reflect(AZ::ReflectContext* context); + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentConfig.cpp new file mode 100644 index 0000000000..b63fa647a0 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentConfig.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void HDRColorGradingComponentConfig::Reflect(ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class()->Version(0) + + // Auto-gen serialize context code... +#define SERIALIZE_CLASS HDRColorGradingComponentConfig +#include +#include +#include +#undef SERIALIZE_CLASS + ; + } + } + + void HDRColorGradingComponentConfig::CopySettingsTo (HDRColorGradingSettingsInterface* settings) + { + if (!settings) + { + return; + } + +#define COPY_TARGET settings +#include +#include +#include +#undef COPY_TARGET + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentController.cpp new file mode 100644 index 0000000000..c0f70ab145 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentController.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include + +#include + +namespace AZ +{ + namespace Render + { + void HDRColorGradingComponentController::Reflect(ReflectContext* context) + { + HDRColorGradingComponentConfig::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("Configuration", &HDRColorGradingComponentController::m_configuration); + } + + if (auto* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("HDRColorGradingRequestBus") + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + + // Auto-gen behavior context... +#define PARAM_EVENT_BUS HDRColorGradingRequestBus::Events +#include +#include +#include +#undef PARAM_EVENT_BUS + + ; + } + } + + void HDRColorGradingComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("HDRColorGradingService")); + } + + void HDRColorGradingComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("HDRColorGradingService")); + } + + void HDRColorGradingComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("PostFXLayerService")); + } + + HDRColorGradingComponentController::HDRColorGradingComponentController(const HDRColorGradingComponentConfig& config) + : m_configuration(config) + { + } + + void HDRColorGradingComponentController::Activate(EntityId entityId) + { + m_entityId = entityId; + + PostProcessFeatureProcessorInterface* fp = + RPI::Scene::GetFeatureProcessorForEntity(m_entityId); + if (fp) + { + m_postProcessInterface = fp->GetOrCreateSettingsInterface(m_entityId); + if (m_postProcessInterface) + { + m_settingsInterface = m_postProcessInterface->GetOrCreateHDRColorGradingSettingsInterface(); + OnConfigChanged(); + } + } + HDRColorGradingRequestBus::Handler::BusConnect(m_entityId); + } + + void HDRColorGradingComponentController::Deactivate() + { + HDRColorGradingRequestBus::Handler::BusDisconnect(m_entityId); + + if (m_postProcessInterface) + { + m_postProcessInterface->RemoveHDRColorGradingSettingsInterface(); + } + + m_postProcessInterface = nullptr; + m_settingsInterface = nullptr; + m_entityId.SetInvalid(); + } + + void HDRColorGradingComponentController::SetConfiguration(const HDRColorGradingComponentConfig& config) + { + m_configuration = config; + OnConfigChanged(); + } + + const HDRColorGradingComponentConfig& HDRColorGradingComponentController::GetConfiguration() const + { + return m_configuration; + } + + void HDRColorGradingComponentController::OnConfigChanged() + { + if (m_settingsInterface) + { + m_configuration.CopySettingsTo(m_settingsInterface); + m_settingsInterface->OnConfigChanged(); + } + } + + // Auto-gen getter/setter function definitions... + // The setter functions will set the values on the Atom settings class, then get the value back + // from the settings class to set the local configuration. This is in case the settings class + // applies some custom logic that results in the set value being different from the input +#define AZ_GFX_COMMON_PARAM(ValueType, Name, MemberName, DefaultValue) \ + ValueType HDRColorGradingComponentController::Get##Name() const \ + { \ + return m_configuration.MemberName; \ + } \ + void HDRColorGradingComponentController::Set##Name(ValueType val) \ + { \ + if (m_settingsInterface) \ + { \ + m_settingsInterface->Set##Name(val); \ + m_settingsInterface->OnConfigChanged(); \ + m_configuration.MemberName = m_settingsInterface->Get##Name(); \ + } \ + else \ + { \ + m_configuration.MemberName = val; \ + } \ + } + +#include +#include +#include + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentController.h new file mode 100644 index 0000000000..d5743dfbf6 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentController.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class HDRColorGradingComponentController final + : public HDRColorGradingRequestBus::Handler + { + public: + friend class EditorHDRColorGradingComponent; + + AZ_TYPE_INFO(AZ::Render::HDRColorGradingComponentController, "{CA1D635C-64E9-42C7-A8E0-36C6B825B15D}"); + static void Reflect(AZ::ReflectContext* context); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + + HDRColorGradingComponentController() = default; + HDRColorGradingComponentController(const HDRColorGradingComponentConfig& config); + + void Activate(EntityId entityId); + void Deactivate(); + void SetConfiguration(const HDRColorGradingComponentConfig& config); + const HDRColorGradingComponentConfig& GetConfiguration() const; + + // Auto-gen function override declarations (functions definitions in .cpp)... +#include +#include +#include + + private: + AZ_DISABLE_COPY(HDRColorGradingComponentController); + + void OnConfigChanged(); + + PostProcessSettingsInterface* m_postProcessInterface = nullptr; + HDRColorGradingSettingsInterface* m_settingsInterface = nullptr; + HDRColorGradingComponentConfig m_configuration; + EntityId m_entityId; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp index 4f4f21ab0f..aa68333108 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp @@ -212,6 +212,9 @@ namespace AZ AZ::Vector3 position = AZ::Vector3::CreateZero(); AZ::TransformBus::EventResult(position, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation); + AZ::Quaternion rotationQuaternion = AZ::Quaternion::CreateIdentity(); + AZ::TransformBus::EventResult(rotationQuaternion, GetEntityId(), &AZ::TransformBus::Events::GetWorldRotationQuaternion); + AZ::Matrix3x3 rotationMatrix = AZ::Matrix3x3::CreateFromQuaternion(rotationQuaternion); float scale = 1.0f; AZ::TransformBus::EventResult(scale, GetEntityId(), &AZ::TransformBus::Events::GetLocalUniformScale); @@ -224,9 +227,7 @@ namespace AZ AZ::Vector3 innerExtents(configuration.m_innerWidth, configuration.m_innerLength, configuration.m_innerHeight); innerExtents *= scale; - AZ::Vector3 innerMin(position.GetX() - innerExtents.GetX() / 2, position.GetY() - innerExtents.GetY() / 2, position.GetZ() - innerExtents.GetZ() / 2); - AZ::Vector3 innerMax(position.GetX() + innerExtents.GetX() / 2, position.GetY() + innerExtents.GetY() / 2, position.GetZ() + innerExtents.GetZ() / 2); - debugDisplay.DrawWireBox(innerMin, innerMax); + debugDisplay.DrawWireOBB(position, rotationMatrix.GetBasisX(), rotationMatrix.GetBasisY(), rotationMatrix.GetBasisZ(), innerExtents / 2.0f); } AZ::Aabb EditorReflectionProbeComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp index cbb30e6cd8..d8cae4564d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp @@ -231,7 +231,7 @@ namespace SurfaceData void SurfaceDataMeshComponent::UpdateMeshData() { - AZ_PROFILE_FUNCTION(Entity); + AZ_PROFILE_SCOPE(Entity, "SurfaceDataMeshComponent: UpdateMeshData"); bool meshValidBeforeUpdate = false; bool meshValidAfterUpdate = false; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake index 099cb378f5..174eb30b31 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake @@ -63,6 +63,8 @@ set(FILES Source/PostProcess/EditorPostFxLayerComponent.h Source/PostProcess/Bloom/EditorBloomComponent.cpp Source/PostProcess/Bloom/EditorBloomComponent.h + Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp + Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.h Source/PostProcess/DepthOfField/EditorDepthOfFieldComponent.cpp Source/PostProcess/DepthOfField/EditorDepthOfFieldComponent.h Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake index e353795d04..63f5aedf33 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake @@ -83,6 +83,11 @@ set(FILES Source/PostProcess/Bloom/BloomComponentConfig.cpp Source/PostProcess/Bloom/BloomComponentController.cpp Source/PostProcess/Bloom/BloomComponentController.h + Source/PostProcess/ColorGrading/HDRColorGradingComponent.cpp + Source/PostProcess/ColorGrading/HDRColorGradingComponent.h + Source/PostProcess/ColorGrading/HDRColorGradingComponentConfig.cpp + Source/PostProcess/ColorGrading/HDRColorGradingComponentController.cpp + Source/PostProcess/ColorGrading/HDRColorGradingComponentController.h Source/PostProcess/DepthOfField/DepthOfFieldComponent.cpp Source/PostProcess/DepthOfField/DepthOfFieldComponent.h Source/PostProcess/DepthOfField/DepthOfFieldComponentConfig.cpp diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake index 68bbe52415..133856c900 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake @@ -39,6 +39,8 @@ set(FILES Include/AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlBus.h Include/AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlComponentConfig.h Include/AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlComponentConstants.h + Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingBus.h + Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingComponentConfig.h Include/AtomLyIntegration/CommonFeatures/PostProcess/Ssao/SsaoBus.h Include/AtomLyIntegration/CommonFeatures/PostProcess/Ssao/SsaoComponentConfiguration.h Include/AtomLyIntegration/CommonFeatures/PostProcess/LookModification/LookModificationBus.h diff --git a/Gems/AtomLyIntegration/CommonFeatures/gem.json b/Gems/AtomLyIntegration/CommonFeatures/gem.json index 8eceb50932..30738ad14c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/gem.json +++ b/Gems/AtomLyIntegration/CommonFeatures/gem.json @@ -8,7 +8,15 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_Feature_Common", + "LmbrCentral", + "GradientSignal", + "SurfaceData", + "Atom_Bootstrap", + "Atom_RPI", + "AtomToolsFramework" + ] } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h index a74cc46e65..5ddab8bc61 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h @@ -88,7 +88,7 @@ namespace AZ void UpdateBounds() override; void DebugDraw(const DebugOptions& debugOptions) override; void SetMaterials(const EMotionFX::Integration::ActorAsset::MaterialList& materialPerLOD) override { AZ_UNUSED(materialPerLOD); }; - void SetSkinningMethod(EMotionFX::Integration::SkinningMethod emfxSkinningMethod); + void SetSkinningMethod(EMotionFX::Integration::SkinningMethod emfxSkinningMethod) override; SkinningMethod GetAtomSkinningMethod() const; void SetIsVisible(bool isVisible) override; @@ -120,7 +120,7 @@ namespace AZ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // MaterialReceiverRequestBus::Handler overrides... - virtual MaterialAssignmentId FindMaterialAssignmentId( + MaterialAssignmentId FindMaterialAssignmentId( const MaterialAssignmentLodIndex lod, const AZStd::string& label) const override; RPI::ModelMaterialSlotMap GetModelMaterialSlots() const override; MaterialAssignmentMap GetMaterialAssignments() const override; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/gem.json b/Gems/AtomLyIntegration/EMotionFXAtom/gem.json index a7c3e96d2b..f921990360 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/gem.json +++ b/Gems/AtomLyIntegration/EMotionFXAtom/gem.json @@ -8,7 +8,14 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "EMotionFX", + "Atom", + "Atom_Feature_Common", + "Atom_RPI", + "Atom_RHI", + "CommonFeaturesAtom" + ] } diff --git a/Gems/AtomLyIntegration/ImguiAtom/gem.json b/Gems/AtomLyIntegration/ImguiAtom/gem.json index 918414f053..6b032275b9 100644 --- a/Gems/AtomLyIntegration/ImguiAtom/gem.json +++ b/Gems/AtomLyIntegration/ImguiAtom/gem.json @@ -8,7 +8,10 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "ImGui", + "Atom_Feature_Common" + ] } diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Maya.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Maya.bat index f3d9aaec69..cceaa80b75 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Maya.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Maya.bat @@ -27,7 +27,7 @@ IF "%DCCSI_PY_VERSION_MINOR%"=="" (set DCCSI_PY_VERSION_MINOR=7) IF "%DCCSI_PY_VERSION_RELEASE%"=="" (set DCCSI_PY_VERSION_RELEASE=11) :: Default Maya Version -IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=2020) +IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=%MAYA_VERSION%) :: Initialize env CALL %~dp0\Env_Core.bat diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py index 4baf68d85e..2df85cbfaa 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py @@ -7,15 +7,16 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT # # -# -- This line is 75 characters ------------------------------------------- +# note: this module should reamin py2.7 compatible (Maya) so no f'strings +# -------------------------------------------------------------------------- import sys import os import re import site import logging as _logging +# ------------------------------------------------------------------------- + -from pathlib import Path # note: we provide this in py2.7 -# so using it here suggests some boostrapping has occured before using azpy # -------------------------------------------------------------------------- _PACKAGENAME = 'azpy.config_utils' @@ -28,8 +29,31 @@ _LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME})) __all__ = ['get_os', 'return_stub', 'get_stub_check_path', 'get_dccsi_config', 'get_current_project'] +# ------------------------------------------------------------------------- -# note: this module should reamin py2.7 compatible (Maya) so no f'strings + +# ------------------------------------------------------------------------- +# just a quick check to ensure what paths have code access +_G_DEBUG = False # enable for debug prints +if _G_DEBUG: + known_paths = list() + for p in sys.path: + known_paths.append(p) + _LOGGER.debug(known_paths) + +# this import can fail in Maya 2020 (and earlier) stuck on py2.7 +# wrapped in a try, to trap and providing messaging to help user correct +try: + from pathlib import Path # note: we provide this in py2.7 + # so using it here suggests some boostrapping has occured before using azpy +except Exception as e: + _LOGGER.warning('Maya 2020 and below, use py2.7') + _LOGGER.warning('py2.7 does not include pathlib') + _LOGGER.warning('Try installing the O3DE DCCsi py2.7 requirements.txt') + _LOGGER.warning("See instructions: 'C:\\< your o3de engine >\\Gems\\AtomLyIntegration\\TechnicalArt\\DccScriptingInterface\\SDK\Maya\\readme.txt'") + _LOGGER.warning("Other code in this module with fail!!!") + _LOGGER.error(e) + pass # fail gracefully, note: code accesing Path will fail! # ------------------------------------------------------------------------- diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json index f978d777cf..1cee5c8298 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json @@ -10,9 +10,10 @@ ], "user_tags": [ "DCC", - "Digital", - "Content", - "Creation" + "Digital", + "Content", + "Creation" ], - "requirements": "" + "requirements": "", + "dependencies": [] } diff --git a/Gems/AtomLyIntegration/gem.json b/Gems/AtomLyIntegration/gem.json index 5af133c8e8..00b3a25f74 100644 --- a/Gems/AtomLyIntegration/gem.json +++ b/Gems/AtomLyIntegration/gem.json @@ -5,8 +5,24 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Atom O3DE Integration Gem provides components, libraries, and functionality to support and integrate Atom Renderer in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Core", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Core", + "Utility" + ], "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-ly-integration/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-ly-integration/", + "dependencies": [ + "Atom_AtomBridge", + "AtomFont", + "AtomImGuiTools", + "AtomViewportDisplayIcons", + "AtomViewportDisplayInfo", + "CommonFeaturesAtom", + "EMotionFX_Atom", + "ImguiAtom" + ] } diff --git a/Gems/AudioEngineWwise/gem.json b/Gems/AudioEngineWwise/gem.json index 084c924977..0588af1908 100644 --- a/Gems/AudioEngineWwise/gem.json +++ b/Gems/AudioEngineWwise/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Wwise Audio Engine Gem provides support for Audiokinetic Wave Works Interactive Sound Engine (Wwise).", - "canonical_tags": ["Gem"], - "user_tags": ["Audio", "Utility", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Audio", + "Utility", + "Tools" + ], "icon_path": "preview.png", "requirements": "Users will need to download Wwise from the Audiokinetic Web Site.", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/wwise/audio-engine-wwise/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/wwise/audio-engine-wwise/", + "dependencies": [ + "AudioSystem" + ] } diff --git a/Gems/AudioSystem/Code/Source/Editor/ATLControlsPanel.h b/Gems/AudioSystem/Code/Source/Editor/ATLControlsPanel.h index 1bb6cef3de..3942a6b18a 100644 --- a/Gems/AudioSystem/Code/Source/Editor/ATLControlsPanel.h +++ b/Gems/AudioSystem/Code/Source/Editor/ATLControlsPanel.h @@ -73,11 +73,11 @@ namespace AudioControls void HandleExternalDropEvent(QDropEvent* pDropEvent); // ------------- IATLControlModelListener ---------------- - virtual void OnControlAdded(CATLControl* pControl) override; + void OnControlAdded(CATLControl* pControl) override; // ------------------------------------------------------- // ------------------ QWidget ---------------------------- - bool eventFilter(QObject* pObject, QEvent* pEvent); + bool eventFilter(QObject* pObject, QEvent* pEvent) override; // ------------------------------------------------------- private slots: diff --git a/Gems/AudioSystem/Code/Source/Editor/ATLControlsResourceDialog.h b/Gems/AudioSystem/Code/Source/Editor/ATLControlsResourceDialog.h index 10186f9c74..daa8c195c3 100644 --- a/Gems/AudioSystem/Code/Source/Editor/ATLControlsResourceDialog.h +++ b/Gems/AudioSystem/Code/Source/Editor/ATLControlsResourceDialog.h @@ -63,7 +63,7 @@ namespace AudioControls QString GetWindowTitle(EACEControlType type) const; // ------------------ QWidget ---------------------------- - bool eventFilter(QObject* pObject, QEvent* pEvent); + bool eventFilter(QObject* pObject, QEvent* pEvent) override; // ------------------------------------------------------- // Filtering diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h index bfa08828ac..abe5fd4511 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h @@ -63,8 +63,8 @@ namespace AudioControls void Update(); protected: - void keyPressEvent(QKeyEvent* pEvent); - void closeEvent(QCloseEvent* pEvent); + void keyPressEvent(QKeyEvent* pEvent) override; + void closeEvent(QCloseEvent* pEvent) override; private: void UpdateAudioSystemData(); diff --git a/Gems/AudioSystem/Code/Source/Engine/AudioSystem.h b/Gems/AudioSystem/Code/Source/Engine/AudioSystem.h index 1d8f8fb286..615d202e25 100644 --- a/Gems/AudioSystem/Code/Source/Engine/AudioSystem.h +++ b/Gems/AudioSystem/Code/Source/Engine/AudioSystem.h @@ -120,7 +120,7 @@ namespace Audio void FreeAudioProxy(IAudioProxy* const pIAudioProxy) override; TAudioSourceId CreateAudioSource(const SAudioInputConfig& sourceConfig) override; - void DestroyAudioSource(TAudioSourceId sourceId); + void DestroyAudioSource(TAudioSourceId sourceId) override; // When AUDIO_RELEASE is defined, these two functions always return nullptr const char* GetAudioControlName(const EAudioControlType controlType, const TATLIDType atlID) const override; diff --git a/Gems/AudioSystem/gem.json b/Gems/AudioSystem/gem.json index f618103803..64d4d9af5a 100644 --- a/Gems/AudioSystem/gem.json +++ b/Gems/AudioSystem/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Audio System Gem provides the Audio Translation Layer (ATL) and Audio Controls Editor, which add support for audio in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Audio", "Utility", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Audio", + "Utility", + "Tools" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/audio-system/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/audio-system/", + "dependencies": [ + "LmbrCentral" + ] } diff --git a/Gems/BarrierInput/Code/Source/BarrierInputKeyboard.h b/Gems/BarrierInput/Code/Source/BarrierInputKeyboard.h index d7321faaec..946d0df11a 100644 --- a/Gems/BarrierInput/Code/Source/BarrierInputKeyboard.h +++ b/Gems/BarrierInput/Code/Source/BarrierInputKeyboard.h @@ -64,15 +64,15 @@ namespace BarrierInput //////////////////////////////////////////////////////////////////////////////////////////// //! \ref RawInputNotificationsBarrier::OnRawKeyboardKeyDownEvent - virtual void OnRawKeyboardKeyDownEvent(uint32_t scanCode, ModifierMask activeModifiers); + void OnRawKeyboardKeyDownEvent(uint32_t scanCode, ModifierMask activeModifiers) override; //////////////////////////////////////////////////////////////////////////////////////////// //! \ref RawInputNotificationsBarrier::OnRawKeyboardKeyUpEvent - virtual void OnRawKeyboardKeyUpEvent(uint32_t scanCode, ModifierMask activeModifiers); + void OnRawKeyboardKeyUpEvent(uint32_t scanCode, ModifierMask activeModifiers) override; //////////////////////////////////////////////////////////////////////////////////////////// //! \ref RawInputNotificationsBarrier::OnRawKeyboardKeyRepeatEvent - virtual void OnRawKeyboardKeyRepeatEvent(uint32_t scanCode, ModifierMask activeModifiers); + void OnRawKeyboardKeyRepeatEvent(uint32_t scanCode, ModifierMask activeModifiers) override; //////////////////////////////////////////////////////////////////////////////////////////// //! Thread safe method to queue raw key events to be processed in the main thread update diff --git a/Gems/BarrierInput/gem.json b/Gems/BarrierInput/gem.json index 738d644d84..7fdc58e8b3 100644 --- a/Gems/BarrierInput/gem.json +++ b/Gems/BarrierInput/gem.json @@ -5,8 +5,17 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Barrier Input Gem allows the Open 3D Engine to function as a Barrier client so that it can receive input from a remote Barrier server.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Barrier", "Synergy"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Barrier", + "Synergy" + ], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/Blast/gem.json b/Gems/Blast/gem.json index 3b1205d6fe..d6af47f482 100644 --- a/Gems/Blast/gem.json +++ b/Gems/Blast/gem.json @@ -5,9 +5,22 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The NVIDIA Blast Gem provides tools to author fractured mesh assets in Houdini, and functionality to create realistic destruction simulations in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "Animation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "Animation" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-blast/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-blast/", + "dependencies": [ + "Atom_Feature_Common", + "CommonFeaturesAtom", + "PhysX", + "Atom_RPI", + "PythonAssetBuilder" + ] } diff --git a/Gems/Camera/Code/CMakeLists.txt b/Gems/Camera/Code/CMakeLists.txt index 3fbeec0908..c9f0bf768f 100644 --- a/Gems/Camera/Code/CMakeLists.txt +++ b/Gems/Camera/Code/CMakeLists.txt @@ -18,8 +18,8 @@ ly_add_target( PUBLIC Gem::Atom_RPI.Public AZ::AtomCore - PRIVATE - Legacy::CryCommon + AZ::AzCore + AZ::AzFramework ) ly_add_target( @@ -32,7 +32,6 @@ ly_add_target( Source BUILD_DEPENDENCIES PRIVATE - Legacy::CryCommon Gem::Camera.Static ) @@ -56,11 +55,11 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Source BUILD_DEPENDENCIES PRIVATE - Legacy::CryCommon - Legacy::Editor.Headers - Legacy::EditorCommon AZ::AzToolsFramework Gem::Camera.Static + Gem::AtomToolsFramework.Static + RUNTIME_DEPENDENCIES + Legacy::EditorCommon ) # tools and builders use the above module. diff --git a/Gems/Camera/Code/Source/CameraComponent.cpp b/Gems/Camera/Code/Source/CameraComponent.cpp index 0b892b0367..960f13a082 100644 --- a/Gems/Camera/Code/Source/CameraComponent.cpp +++ b/Gems/Camera/Code/Source/CameraComponent.cpp @@ -14,9 +14,7 @@ #include "CameraComponent.h" -#include #include -#include #include namespace Camera diff --git a/Gems/Camera/Code/Source/CameraComponent.h b/Gems/Camera/Code/Source/CameraComponent.h index de6e98154d..b9c94d482f 100644 --- a/Gems/Camera/Code/Source/CameraComponent.h +++ b/Gems/Camera/Code/Source/CameraComponent.h @@ -11,9 +11,6 @@ #include #include -#include -#include -#include #include #include diff --git a/Gems/Camera/Code/Source/CameraComponentController.cpp b/Gems/Camera/Code/Source/CameraComponentController.cpp index a8b5b8992e..b8e5738145 100644 --- a/Gems/Camera/Code/Source/CameraComponentController.cpp +++ b/Gems/Camera/Code/Source/CameraComponentController.cpp @@ -9,7 +9,6 @@ #include "CameraComponentController.h" #include "CameraViewRegistrationBus.h" -#include #include #include #include @@ -53,7 +52,7 @@ namespace Camera ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_fov, "Field of view", "Vertical field of view in degrees") - ->Attribute(AZ::Edit::Attributes::Min, MIN_FOV) + ->Attribute(AZ::Edit::Attributes::Min, MinFoV) ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") ->Attribute(AZ::Edit::Attributes::Step, 1.f) ->Attribute(AZ::Edit::Attributes::Max, AZ::RadToDeg(AZ::Constants::Pi) - 0.0001f) //We assert at fovs >= Pi so set the max for this field to be just under that @@ -61,7 +60,7 @@ namespace Camera ->Attribute(AZ::Edit::Attributes::Visibility, &CameraComponentConfig::GetPerspectiveParameterVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_nearClipDistance, "Near clip distance", "Distance to the near clip plane of the view Frustum") - ->Attribute(AZ::Edit::Attributes::Min, CAMERA_MIN_NEAR) + ->Attribute(AZ::Edit::Attributes::Min, MinimumNearPlaneDistance) ->Attribute(AZ::Edit::Attributes::Suffix, " m") ->Attribute(AZ::Edit::Attributes::Step, 0.1f) ->Attribute(AZ::Edit::Attributes::Max, &CameraComponentConfig::GetFarClipDistance) @@ -150,6 +149,11 @@ namespace Camera } } + void CameraComponentController::SetShouldActivateFunction(AZStd::function shouldActivateFunction) + { + m_shouldActivateFn = shouldActivateFunction; + } + void CameraComponentController::Reflect(AZ::ReflectContext* context) { CameraComponentConfig::Reflect(context); @@ -202,36 +206,6 @@ namespace Camera { m_entityId = entityId; - if ((!m_viewSystem)||(!m_system)) - { - // perform first-time init - if (gEnv) - { - m_system = gEnv->pSystem; - } - if (m_system) - { - // Initialize local view. - m_viewSystem = m_system->GetIViewSystem(); - if (!m_viewSystem) - { - AZ_Error("CameraComponent", m_viewSystem != nullptr, "The CameraComponent shouldn't be used without a local view system"); - } - } - } - - if (m_viewSystem) - { - if (m_view == nullptr) - { - m_view = m_viewSystem->CreateView(); - - AZ::Entity* entity = nullptr; - AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, m_entityId); - m_view->LinkTo(entity); - } - } - auto atomViewportRequests = AZ::Interface::Get(); if (atomViewportRequests) { @@ -270,9 +244,8 @@ namespace Camera CameraBus::Handler::BusConnect(); CameraNotificationBus::Broadcast(&CameraNotificationBus::Events::OnCameraAdded, m_entityId); - // Activate our camera if we're running from the launcher or Editor game mode - // Otherwise, let the Editor keep managing the active camera - if (m_config.m_makeActiveViewOnActivation && (!gEnv || !gEnv->IsEditor() || gEnv->IsEditorGameMode())) + // Only activate if we're configured to do so, and our activation call back indicates that we should + if (m_config.m_makeActiveViewOnActivation && (!m_shouldActivateFn || m_shouldActivateFn())) { MakeActiveView(); } @@ -284,20 +257,6 @@ namespace Camera CameraBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::Handler::BusDisconnect(m_entityId); CameraRequestBus::Handler::BusDisconnect(m_entityId); - if (m_viewSystem) - { - if (m_view != nullptr && m_viewSystem->GetViewId(m_view) != 0) - { - m_view->Unlink(); - } - if (m_viewSystem->GetActiveView() == m_view) - { - m_viewSystem->SetActiveView(m_prevViewId); - } - m_viewSystem->RemoveView(m_view); - m_prevViewId = 0; - m_view = nullptr; - } auto atomViewportRequests = AZ::Interface::Get(); if (atomViewportRequests) @@ -429,13 +388,6 @@ namespace Camera return; } - // Set Legacy Cry view, if it exists - if (m_viewSystem) - { - m_prevViewId = AZ::u32(m_viewSystem->GetActiveViewId()); - m_viewSystem->SetActiveView(m_view); - } - // Set Atom camera, if it exists if (m_atomCamera) { @@ -461,12 +413,6 @@ namespace Camera return; } - if (m_view) - { - CCamera& camera = m_view->GetCamera(); - camera.SetMatrix(AZTransformToLYTransform(world.GetOrthogonalized())); - } - if (m_atomCamera) { m_updatingTransformFromEntity = true; @@ -495,25 +441,15 @@ namespace Camera void CameraComponentController::UpdateCamera() { - if (m_view) - { - auto viewParams = *m_view->GetCurrentParams(); - viewParams.fov = AZ::DegToRad(m_config.m_fov); - viewParams.nearplane = m_config.m_nearClipDistance; - viewParams.farplane = m_config.m_farClipDistance; - m_view->SetCurrentParams(viewParams); - } - if (auto viewportContext = GetViewportContext()) { AZ::Matrix4x4 viewToClipMatrix; - float aspectRatio = m_view ? m_view->GetCamera().GetPixelAspectRatio() : 1.f; if (!m_atomAuxGeom) { SetupAtomAuxGeom(viewportContext); } auto windowSize = viewportContext->GetViewportSize(); - aspectRatio = aznumeric_cast(windowSize.m_width) / aznumeric_cast(windowSize.m_height); + const float aspectRatio = aznumeric_cast(windowSize.m_width) / aznumeric_cast(windowSize.m_height); // This assumes a reversed depth buffer, in line with other LY Atom integration if (m_config.m_orthographic) diff --git a/Gems/Camera/Code/Source/CameraComponentController.h b/Gems/Camera/Code/Source/CameraComponentController.h index 923da65618..5eca6b1711 100644 --- a/Gems/Camera/Code/Source/CameraComponentController.h +++ b/Gems/Camera/Code/Source/CameraComponentController.h @@ -16,15 +16,12 @@ #include #include -#include -#include -#include - namespace Camera { static constexpr float DefaultFoV = 75.0f; static constexpr float MinFoV = std::numeric_limits::epsilon(); static constexpr float MaxFoV = AZ::RadToDeg(AZ::Constants::Pi); + static constexpr float MinimumNearPlaneDistance = 0.001f; static constexpr float DefaultNearPlaneDistance = 0.2f; static constexpr float DefaultFarClipPlaneDistance = 1024.0f; static constexpr float DefaultFrustumDimension = 256.f; @@ -69,6 +66,10 @@ namespace Camera CameraComponentController() = default; explicit CameraComponentController(const CameraComponentConfig& config); + //! Defines a callback for determining whether this camera should push itself to the top of the Atom camera stack. + //! Used by the Editor to disable undesirable camera changes in edit mode. + void SetShouldActivateFunction(AZStd::function shouldActivateFunction); + // Controller interface static void Reflect(AZ::ReflectContext* context); static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); @@ -134,10 +135,6 @@ namespace Camera bool m_updatingTransformFromEntity = false; bool m_isActiveView = false; - // Cry view integration - IView* m_view = nullptr; - AZ::u32 m_prevViewId = 0; - IViewSystem* m_viewSystem = nullptr; - ISystem* m_system = nullptr; + AZStd::function m_shouldActivateFn; }; } // namespace Camera diff --git a/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp b/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp index bf3083bf59..00a147c17a 100644 --- a/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp +++ b/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp @@ -21,17 +21,12 @@ #include #include -#include -#include -#include -#include -#include - -#include -#include #include #include "ViewportCameraSelectorWindow.h" +#include +#include + namespace Camera { void CameraEditorSystemComponent::Reflect(AZ::ReflectContext* context) @@ -72,15 +67,6 @@ namespace Camera void CameraEditorSystemComponent::PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2&, int flags) { - IEditor* editor; - AzToolsFramework::EditorRequests::Bus::BroadcastResult(editor, &AzToolsFramework::EditorRequests::GetEditor); - - CGameEngine* gameEngine = editor->GetGameEngine(); - if (!gameEngine || !gameEngine->IsLevelLoaded()) - { - return; - } - if (!(flags & AzToolsFramework::EditorEvents::eECMF_HIDE_ENTITY_CREATION)) { QAction* action = menu->addAction(QObject::tr("Create camera entity from view")); @@ -90,9 +76,6 @@ namespace Camera void CameraEditorSystemComponent::CreateCameraEntityFromViewport() { - IEditor* editor = nullptr; - AzToolsFramework::EditorRequests::Bus::BroadcastResult(editor, &AzToolsFramework::EditorRequests::GetEditor); - AzFramework::CameraState cameraState{}; AZ::EBusReduceResult> aggregator; Camera::EditorCameraRequestBus::BroadcastResult( diff --git a/Gems/Camera/Code/Source/CameraGem.cpp b/Gems/Camera/Code/Source/CameraGem.cpp index 7efb09a59c..99f270e71b 100644 --- a/Gems/Camera/Code/Source/CameraGem.cpp +++ b/Gems/Camera/Code/Source/CameraGem.cpp @@ -7,7 +7,6 @@ */ #include -#include #include "CameraComponent.h" #include "CameraSystemComponent.h" @@ -22,13 +21,13 @@ namespace Camera { class CameraModule - : public CryHooksModule + : public AZ::Module { public: AZ_RTTI(CameraModule, "{C2E72B0D-BCEF-452A-9BFA-03833015258C}", AZ::Module); CameraModule() - : CryHooksModule() + : AZ::Module() { m_descriptors.insert(m_descriptors.end(), { Camera::CameraComponent::CreateDescriptor(), diff --git a/Gems/Camera/Code/Source/EditorCameraComponent.cpp b/Gems/Camera/Code/Source/EditorCameraComponent.cpp index 4ebec334ec..3ac60cbff8 100644 --- a/Gems/Camera/Code/Source/EditorCameraComponent.cpp +++ b/Gems/Camera/Code/Source/EditorCameraComponent.cpp @@ -12,10 +12,9 @@ #include "EditorCameraComponent.h" #include "ViewportCameraSelectorWindow.h" -#include -#include #include #include +#include #include #include @@ -33,6 +32,14 @@ namespace Camera CameraComponentConfig controllerConfig = m_controller.GetConfiguration(); controllerConfig.m_editorEntityId = GetEntityId().operator AZ::u64(); m_controller.SetConfiguration(controllerConfig); + // Only allow our camera to activate with the component if we're currently in game mode. + m_controller.SetShouldActivateFunction([]() + { + bool isInGameMode = true; + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + isInGameMode, &AzToolsFramework::EditorEntityContextRequestBus::Events::IsEditorRunningGame); + return isInGameMode; + }); // Call base class activate, which in turn calls Activate on our controller. EditorCameraComponentBase::Activate(); diff --git a/Gems/Camera/Code/Source/EditorCameraComponent.h b/Gems/Camera/Code/Source/EditorCameraComponent.h index 02b9d591d0..0cb130e7a0 100644 --- a/Gems/Camera/Code/Source/EditorCameraComponent.h +++ b/Gems/Camera/Code/Source/EditorCameraComponent.h @@ -21,8 +21,6 @@ #include #include "CameraComponent.h" #include "CameraComponentController.h" -#include -#include #include diff --git a/Gems/Camera/Code/Source/ViewportCameraSelectorWindow.cpp b/Gems/Camera/Code/Source/ViewportCameraSelectorWindow.cpp index 7a8d6be020..dc091db9e5 100644 --- a/Gems/Camera/Code/Source/ViewportCameraSelectorWindow.cpp +++ b/Gems/Camera/Code/Source/ViewportCameraSelectorWindow.cpp @@ -7,17 +7,14 @@ */ #include "ViewportCameraSelectorWindow.h" #include "ViewportCameraSelectorWindow_Internals.h" -#include #include #include #include #include #include #include -#include -#include -#include #include +#include namespace Qt { @@ -308,7 +305,7 @@ namespace Camera void RegisterViewportCameraSelectorWindow() { - QtViewOptions viewOptions; + AzToolsFramework::ViewPaneOptions viewOptions; viewOptions.isPreview = true; viewOptions.showInMenu = true; viewOptions.preferedDockingArea = Qt::DockWidgetArea::LeftDockWidgetArea; diff --git a/Gems/Camera/gem.json b/Gems/Camera/gem.json index 2fc2ea8355..9f8ea22412 100644 --- a/Gems/Camera/gem.json +++ b/Gems/Camera/gem.json @@ -5,9 +5,17 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Camera Gem provides a basic camera component that defines a frustum for runtime rendering.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera/", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/CameraFramework/gem.json b/Gems/CameraFramework/gem.json index 2ab25a84b4..c5520fd5b4 100644 --- a/Gems/CameraFramework/gem.json +++ b/Gems/CameraFramework/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Camera Framework Gem provides a base for implementing more complex camera systems.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Framework", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Framework", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera-framework/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera-framework/", + "dependencies": [] } diff --git a/Gems/CertificateManager/gem.json b/Gems/CertificateManager/gem.json index cb49abc6b6..968da2788a 100644 --- a/Gems/CertificateManager/gem.json +++ b/Gems/CertificateManager/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Certificate Manager Gem provides access to authentication files for secure game connections from Amazon S3, files on disk, and other 3rd party sources.", - "canonical_tags": ["Gem"], - "user_tags": ["Network", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Network", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/certificate-manager/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/certificate-manager/", + "dependencies": [] } diff --git a/Gems/CrashReporting/Code/Include/CrashReporting/GameCrashHandler.h b/Gems/CrashReporting/Code/Include/CrashReporting/GameCrashHandler.h index 08d3ccf90b..e5aaaa2e93 100644 --- a/Gems/CrashReporting/Code/Include/CrashReporting/GameCrashHandler.h +++ b/Gems/CrashReporting/Code/Include/CrashReporting/GameCrashHandler.h @@ -22,13 +22,13 @@ namespace CrashHandler static void InitCrashHandler(const std::string& moduleTag, const std::string& devRoot, const std::string& crashUrl = {}, const std::string& crashToken = {}, const std::string& handlerFolder = {}, const CrashHandlerAnnotations& baseAnnotations = CrashHandlerAnnotations(), const CrashHandlerArguments& argumentVec = CrashHandlerArguments()); protected: - virtual const char* GetCrashHandlerExecutableName() const override; - virtual std::string DetermineAppPath() const override; + const char* GetCrashHandlerExecutableName() const override; + std::string DetermineAppPath() const override; - virtual std::string GetCrashSubmissionURL() const override; - virtual std::string GetCrashSubmissionToken() const override; + std::string GetCrashSubmissionURL() const override; + std::string GetCrashSubmissionToken() const override; - virtual std::string GetCrashHandlerPath(const std::string& lyAppRoot) const override; + std::string GetCrashHandlerPath(const std::string& lyAppRoot) const override; }; diff --git a/Gems/CrashReporting/Code/Include/CrashReporting/GameCrashUploader.h b/Gems/CrashReporting/Code/Include/CrashReporting/GameCrashUploader.h index 48d2bd25f7..c48440bed4 100644 --- a/Gems/CrashReporting/Code/Include/CrashReporting/GameCrashUploader.h +++ b/Gems/CrashReporting/Code/Include/CrashReporting/GameCrashUploader.h @@ -17,7 +17,7 @@ namespace O3de { public: GameCrashUploader(int& argcount, char** argv); - virtual bool CheckConfirmation(const crashpad::CrashReportDatabase::Report& report) override; + bool CheckConfirmation(const crashpad::CrashReportDatabase::Report& report) override; static std::string GetRootFolder(); }; diff --git a/Gems/CrashReporting/gem.json b/Gems/CrashReporting/gem.json index bff54dcf36..9b8d3a9e0a 100644 --- a/Gems/CrashReporting/gem.json +++ b/Gems/CrashReporting/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Crash Reporting Gem provides support for external crash reporting for Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/crash-reporting/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/crash-reporting/", + "dependencies": [] } diff --git a/Gems/CustomAssetExample/gem.json b/Gems/CustomAssetExample/gem.json index ac7c2788d7..ab5ed7002d 100644 --- a/Gems/CustomAssetExample/gem.json +++ b/Gems/CustomAssetExample/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Custom Asset Example Gem provides example code for creating a custom asset for Open 3D Engine's asset pipeline.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Tools" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/custom-asset-example/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/custom-asset-example/", + "dependencies": [] } diff --git a/Gems/DebugDraw/Code/Source/DebugDrawSystemComponent.h b/Gems/DebugDraw/Code/Source/DebugDrawSystemComponent.h index 24a6eae3aa..b534bd3bd4 100644 --- a/Gems/DebugDraw/Code/Source/DebugDrawSystemComponent.h +++ b/Gems/DebugDraw/Code/Source/DebugDrawSystemComponent.h @@ -117,7 +117,7 @@ namespace DebugDraw void OnBeginPrepareRender() override; // AZ::Render::Bootstrap::NotificationBus - void OnBootstrapSceneReady(AZ::RPI::Scene* scene); + void OnBootstrapSceneReady(AZ::RPI::Scene* scene) override; // EntityBus void OnEntityDeactivated(const AZ::EntityId& entityId) override; diff --git a/Gems/DebugDraw/gem.json b/Gems/DebugDraw/gem.json index a7939f4077..7e0da07103 100644 --- a/Gems/DebugDraw/gem.json +++ b/Gems/DebugDraw/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Debug Draw Gem provides Editor and runtime debug visualization features for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/debug-draw/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/debug-draw/", + "dependencies": [ + "Atom_RPI", + "Atom_Bootstrap" + ] } diff --git a/Gems/DevTextures/gem.json b/Gems/DevTextures/gem.json index 81d14e78cb..8b40badbf1 100644 --- a/Gems/DevTextures/gem.json +++ b/Gems/DevTextures/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Dev Textures Gem provides a collection of general purpose texture assets useful for prototypes and preproduction.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Debug", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Debug", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/dev-textures/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/dev-textures/", + "dependencies": [] } diff --git a/Gems/EMotionFX/Assets/Editor/Layouts/SimulatedObjects.layout b/Gems/EMotionFX/Assets/Editor/Layouts/SimulatedObjects.layout index a7879584a9..f52df642e8 100644 Binary files a/Gems/EMotionFX/Assets/Editor/Layouts/SimulatedObjects.layout and b/Gems/EMotionFX/Assets/Editor/Layouts/SimulatedObjects.layout differ diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp index dbc25d7a60..bd5a14f772 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp @@ -715,12 +715,15 @@ namespace CommandSystem // restore the workspace dirty flag GetCommandManager()->SetWorkspaceDirtyFlag(m_oldWorkspaceDirtyFlag); - AZStd::string resultString; - GetCommandManager()->ExecuteCommandInsideCommand("Unselect -animGraphIndex SELECT_ALL", resultString); + MCore::CommandGroup commandGroup; + commandGroup.AddCommandString("RecorderClear"); + commandGroup.AddCommandString("Unselect -animGraphIndex SELECT_ALL"); if (animGraph) { - GetCommandManager()->ExecuteCommandInsideCommand(AZStd::string::format("Select -animGraphID %d", animGraph->GetID()), resultString); + commandGroup.AddCommandString(AZStd::string::format("Select -animGraphID %d", animGraph->GetID())); } + AZStd::string resultString; + GetCommandManager()->ExecuteCommandGroupInsideCommand(commandGroup, resultString); return true; } diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Behaviors/ActorGroupBehavior.cpp b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Behaviors/ActorGroupBehavior.cpp index f1f1361585..d89e41f1a6 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Behaviors/ActorGroupBehavior.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Behaviors/ActorGroupBehavior.cpp @@ -176,19 +176,10 @@ namespace EMotionFX return AZ::SceneAPI::Events::ProcessingResult::Ignored; } - const bool hasBoneData = AZ::SceneAPI::Utilities::DoesSceneGraphContainDataLike(scene, true); + // Skip adding the actor group if it doesn't contain any skin and blendshape data. const bool hasSkinData = AZ::SceneAPI::Utilities::DoesSceneGraphContainDataLike(scene, true); - const bool hasBlendShapeData = - AZ::SceneAPI::Utilities::DoesSceneGraphContainDataLike(scene, true); - // Skip adding the actor group if it doesn't contain any bone, skin and blendshape data. - if (!hasBoneData && !hasSkinData && !hasBlendShapeData) - { - return AZ::SceneAPI::Events::ProcessingResult::Ignored; - } - - const bool hasAnimationData = AZ::SceneAPI::Utilities::DoesSceneGraphContainDataLike(scene, true); - // Skip adding the actor group if it contains animation data but doesn't contain any skin or blendshape data. - if (hasAnimationData && !hasSkinData && !hasBlendShapeData) + const bool hasBlendShapeData = AZ::SceneAPI::Utilities::DoesSceneGraphContainDataLike(scene, true); + if (!hasSkinData && !hasBlendShapeData) { return AZ::SceneAPI::Events::ProcessingResult::Ignored; } @@ -197,7 +188,7 @@ namespace EMotionFX AZStd::shared_ptr group = AZStd::make_shared(); // This is a group that's generated automatically so may not be saved to disk but would need to be recreated - // in the same way again. To guarantee the same uuid, generate a stable one instead. + // in the same way again. To guarantee the same uuid, generate a stable one instead. group->OverrideId(AZ::SceneAPI::DataTypes::Utilities::CreateStableUuid(scene, Group::ActorGroup::TYPEINFO_Uuid())); EBUS_EVENT(AZ::SceneAPI::Events::ManifestMetaInfoBus, InitializeObject, scene, *group); diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Groups/ActorGroup.cpp b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Groups/ActorGroup.cpp index 9e080fca3b..90c25c575f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Groups/ActorGroup.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/SceneAPIExt/Groups/ActorGroup.cpp @@ -108,7 +108,7 @@ namespace EMotionFX serializeContext->Class()->Version(3, IActorGroupVersionConverter); - serializeContext->Class()->Version(6, ActorVersionConverter) + serializeContext->Class()->Version(7, ActorVersionConverter) ->Field("name", &ActorGroup::m_name) ->Field("selectedRootBone", &ActorGroup::m_selectedRootBone) ->Field("id", &ActorGroup::m_id) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp index 94a56cc0d5..3e592113b1 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp @@ -1245,7 +1245,7 @@ namespace EMotionFX } else { - SetMotionExtractionNodeIndex(MCORE_INVALIDINDEX32); + SetMotionExtractionNodeIndex(InvalidIndex); } } diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp index 5a4ca95670..b547b92306 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp @@ -372,6 +372,8 @@ namespace EMotionFX // updates the skinning matrices of all nodes void ActorInstance::UpdateSkinningMatrices() { + AZ_PROFILE_SCOPE(Animation, "ActorInstance::UpdateSkinningMatrices"); + AZ::Matrix3x4* skinningMatrices = m_transformData->GetSkinningMatrices(); const Pose* pose = m_transformData->GetCurrentPose(); @@ -596,6 +598,8 @@ namespace EMotionFX // update the bounding volume void ActorInstance::UpdateBounds(size_t geomLODLevel, EBoundsType boundsType, uint32 itemFrequency) { + AZ_PROFILE_SCOPE(Animation, "ActorInstance::UpdateBounds"); + // depending on the bounding volume update type switch (boundsType) { diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphHubNode.h b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphHubNode.h index 6db908cf33..99ad6ced7d 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphHubNode.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphHubNode.h @@ -61,7 +61,7 @@ namespace EMotionFX bool GetSupportsVisualization() const override { return true; } AnimGraphPose* GetMainOutputPose(AnimGraphInstance* animGraphInstance) const override { return GetOutputPose(animGraphInstance, OUTPUTPORT_RESULT)->GetValue(); } bool GetHasOutputPose() const override { return true; } - bool GetCanBeEntryNode() const { return true; } + bool GetCanBeEntryNode() const override { return true; } bool GetCanBeInsideStateMachineOnly() const override { return true; } bool GetHasVisualOutputPorts() const override { return false; } bool GetCanHaveOnlyOneInsideParent() const override { return false; } diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphInstance.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphInstance.cpp index d0c77721d1..7f9e8ba6fc 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphInstance.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphInstance.cpp @@ -218,6 +218,8 @@ namespace EMotionFX // output the results into the internal pose object void AnimGraphInstance::Output(Pose* outputPose) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::Output"); + // reset max used const uint32 threadIndex = m_actorInstance->GetThreadIndex(); AnimGraphPosePool& posePool = GetEMotionFX().GetThreadData(threadIndex)->GetPosePool(); @@ -854,6 +856,8 @@ namespace EMotionFX // synchronize all nodes, based on sync tracks etc void AnimGraphInstance::Update(float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::Update"); + // pass 0: (Optional, networking only) When this instance is shared between network, restore the instance using an animgraph snapshot. if (m_snapshot) { @@ -940,6 +944,8 @@ namespace EMotionFX // reset all node pose ref counts void AnimGraphInstance::ResetPoseRefCountsForAllNodes() { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::ResetPoseRefCountsForAllNodes"); + const size_t numNodes = m_animGraph->GetNumNodes(); for (size_t i = 0; i < numNodes; ++i) { @@ -951,6 +957,8 @@ namespace EMotionFX // reset all node pose ref counts void AnimGraphInstance::ResetRefDataRefCountsForAllNodes() { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::ResetRefDataRefCountsForAllNodes"); + const size_t numNodes = m_animGraph->GetNumNodes(); for (size_t i = 0; i < numNodes; ++i) { @@ -962,6 +970,8 @@ namespace EMotionFX // reset all node flags void AnimGraphInstance::ResetFlagsForAllObjects() { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::ResetFlagsForAllObjects"); + MCore::MemSet(m_objectFlags.data(), 0, sizeof(uint32) * m_objectFlags.size()); for (AnimGraphInstance* childInstance : m_childAnimGraphInstances) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp index 6bcc69360b..c5e50ed9dd 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp @@ -394,6 +394,8 @@ namespace EMotionFX // the main process method of the final node void AnimGraphMotionNode::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphMotionNode::Output"); + // if this motion is disabled, output the bind pose if (m_disabled) { @@ -537,6 +539,8 @@ namespace EMotionFX void AnimGraphMotionNode::UniqueData::Update() { + AZ_PROFILE_SCOPE(Animation, "AnimGraphMotionNode::Update"); + AnimGraphMotionNode* motionNode = azdynamic_cast(m_object); AZ_Assert(motionNode, "Unique data linked to incorrect node type."); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphReferenceNode.h b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphReferenceNode.h index db4dc0ea3e..db1d6e1279 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphReferenceNode.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphReferenceNode.h @@ -82,8 +82,8 @@ namespace EMotionFX AnimGraphReferenceNode(); ~AnimGraphReferenceNode(); - void Reinit(); - void RecursiveReinit(); + void Reinit() override; + void RecursiveReinit() override; bool InitAfterLoading(AnimGraph* animGraph) override; AnimGraphObjectData* CreateUniqueData(AnimGraphInstance* animGraphInstance) override { return aznew UniqueData(this, animGraphInstance); } diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateMachine.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateMachine.cpp index 9b4c2d07ac..0024d97a45 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateMachine.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateMachine.cpp @@ -92,6 +92,8 @@ namespace EMotionFX void AnimGraphStateMachine::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::Update"); + ActorInstance* actorInstance = animGraphInstance->GetActorInstance(); AnimGraphPose* outputPose = nullptr; @@ -476,6 +478,8 @@ namespace EMotionFX void AnimGraphStateMachine::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::Update"); + UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); // Defer switch to entry state. @@ -622,6 +626,8 @@ namespace EMotionFX void AnimGraphStateMachine::PostUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::PostUpdate"); + RequestRefDatas(animGraphInstance); UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); AnimGraphRefCountedData* data = uniqueData->GetRefCountedData(); @@ -1344,6 +1350,8 @@ namespace EMotionFX void AnimGraphStateMachine::TopDownUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::TopDownUpdate"); + UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); if (!IsTransitioning(uniqueData)) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace1DNode.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace1DNode.cpp index 0e823075a4..deeb593c96 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace1DNode.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace1DNode.cpp @@ -166,6 +166,8 @@ namespace EMotionFX void BlendSpace1DNode::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "BlendSpace1DNode::Output"); + if (!AnimGraphInstanceExists(animGraphInstance)) { return; @@ -276,6 +278,8 @@ namespace EMotionFX void BlendSpace1DNode::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendSpace1DNode::Update"); + if (!m_disabled) { EMotionFX::BlendTreeConnection* paramConnection = GetInputPort(INPUTPORT_VALUE).m_connection; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace2DNode.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace2DNode.cpp index 03a7552410..66366087f8 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace2DNode.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace2DNode.cpp @@ -282,6 +282,8 @@ namespace EMotionFX void BlendSpace2DNode::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "BlendSpace2DNode::Output"); + if (!AnimGraphInstanceExists(animGraphInstance)) { return; @@ -402,6 +404,8 @@ namespace EMotionFX void BlendSpace2DNode::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendSpace2DNode::Update"); + if (!AnimGraphInstanceExists(animGraphInstance)) { return; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp index f742696275..a4109a5a60 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp @@ -116,10 +116,11 @@ namespace EMotionFX return nullptr; } - // process the blend tree and calculate its output void BlendTree::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "BlendTree::Output"); + AZ_Assert(m_finalNode, "There should always be a final node. Something seems to be wrong with the blend tree creation."); // get the output pose @@ -164,6 +165,8 @@ namespace EMotionFX // post sync update void BlendTree::PostUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::PostUpdate"); + // if this node is disabled, exit if (m_disabled) { @@ -212,6 +215,8 @@ namespace EMotionFX // update all nodes void BlendTree::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendTree::Update"); + // if this node is disabled, output the bind pose if (m_disabled) { @@ -256,6 +261,8 @@ namespace EMotionFX // top down update void BlendTree::TopDownUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendTree::TopDownUpdate"); + // get the final node AnimGraphNode* finalNode = GetRealFinalNode(); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeBlend2Node.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeBlend2Node.cpp index 06223d4b14..b14f72c7c3 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeBlend2Node.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeBlend2Node.cpp @@ -45,6 +45,8 @@ namespace EMotionFX void BlendTreeBlend2Node::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendTreeBlend2Node::Update"); + if (m_disabled) { AnimGraphNodeData* uniqueData = FindOrCreateUniqueNodeData(animGraphInstance); @@ -88,9 +90,10 @@ namespace EMotionFX } } - void BlendTreeBlend2Node::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "BlendTreeBlend2Node::Output"); + if (m_disabled) { RequestPoses(animGraphInstance); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp index a6ddb776c2..237389312f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp @@ -147,6 +147,8 @@ namespace EMotionFX // update motion queue and instances void MotionSystem::Update(float timePassed, bool updateNodes) { + AZ_PROFILE_SCOPE(Animation, "MotionSystem::Update"); + MCORE_UNUSED(updateNodes); // update the motion queue diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/NodeGroup.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/NodeGroup.cpp index 74e59150e0..8eae2fb127 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/NodeGroup.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/NodeGroup.cpp @@ -6,12 +6,10 @@ * */ -// include the required headers #include "NodeGroup.h" #include "ActorInstance.h" #include - namespace EMotionFX { AZ_CLASS_ALLOCATOR_IMPL(NodeGroup, NodeAllocator, 0) @@ -104,10 +102,7 @@ namespace EMotionFX // remove a given node by its node number void NodeGroup::RemoveNodeByNodeIndex(uint16 nodeIndex) { - if (const auto found = AZStd::find(begin(m_nodes), end(m_nodes), nodeIndex); found) - { - m_nodes.erase(found); - } + m_nodes.erase(AZStd::remove(m_nodes.begin(), m_nodes.end(), nodeIndex), m_nodes.end()); } diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp index 085d164a94..54fb130ad3 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.cpp @@ -1421,17 +1421,16 @@ namespace EMotionFX // extract sorted active items void Recorder::ExtractNodeHistoryItems(const ActorInstanceData& actorInstanceData, float timeValue, bool sort, EValueType valueType, AZStd::vector* outItems, AZStd::vector* outMap) const { - // clear the map array + // Reinit the item array. const size_t maxIndex = CalcMaxNodeHistoryTrackIndex(actorInstanceData); outItems->resize(maxIndex + 1); for (size_t i = 0; i <= maxIndex; ++i) { - ExtractedNodeHistoryItem item; - item.m_trackIndex = i; - item.m_value = 0.0f; + ExtractedNodeHistoryItem& item = (*outItems)[i]; + item.m_nodeHistoryItem = nullptr; + item.m_trackIndex = i; item.m_keyTrackSampleTime = 0.0f; - item.m_nodeHistoryItem = nullptr; - outItems->emplace(AZStd::next(begin(*outItems), i), AZStd::move(item)); + item.m_value = 0.0f; } // find all node history items @@ -1440,7 +1439,7 @@ namespace EMotionFX { if (curItem->m_startTime <= timeValue && curItem->m_endTime > timeValue) { - ExtractedNodeHistoryItem item; + ExtractedNodeHistoryItem& item = (*outItems)[curItem->m_trackIndex]; item.m_trackIndex = curItem->m_trackIndex; item.m_keyTrackSampleTime = timeValue - curItem->m_startTime; item.m_nodeHistoryItem = curItem; @@ -1463,8 +1462,6 @@ namespace EMotionFX MCORE_ASSERT(false); // unsupported mode item.m_value = curItem->m_globalWeights.GetValueAtTime(item.m_keyTrackSampleTime, nullptr, nullptr, m_recordSettings.m_interpolate); } - - outItems->emplace(AZStd::next(begin(*outItems), curItem->m_trackIndex), item); } } @@ -1472,7 +1469,7 @@ namespace EMotionFX outMap->resize(maxIndex + 1); for (size_t i = 0; i <= maxIndex; ++i) { - outMap->emplace(AZStd::next(begin(*outMap), i), i); + (*outMap)[i] = i; } // sort if desired @@ -1482,7 +1479,7 @@ namespace EMotionFX for (size_t i = 0; i <= maxIndex; ++i) { - outMap->emplace(AZStd::next(begin(*outMap), outItems->at(i).m_trackIndex), i); + (*outMap)[outItems->at(i).m_trackIndex] = i; } } } diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.h b/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.h index fe87d36ea1..c486427159 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Recorder.h @@ -168,10 +168,10 @@ namespace EMotionFX struct EMFX_API ExtractedNodeHistoryItem { - NodeHistoryItem* m_nodeHistoryItem; - size_t m_trackIndex; - float m_value; - float m_keyTrackSampleTime; + NodeHistoryItem* m_nodeHistoryItem = nullptr; + size_t m_trackIndex = 0; + float m_value = 0.0f; + float m_keyTrackSampleTime = 0.0f; friend bool operator< (const ExtractedNodeHistoryItem& a, const ExtractedNodeHistoryItem& b) { return (a.m_value > b.m_value); } friend bool operator==(const ExtractedNodeHistoryItem& a, const ExtractedNodeHistoryItem& b) { return (a.m_value == b.m_value); } diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.h index 318838b2b1..a11f11c6a8 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.h @@ -290,7 +290,7 @@ namespace EMStudio /// CommandManagerCallback implementation void OnPreExecuteCommand(MCore::CommandGroup* group, MCore::Command* command, const MCore::CommandLine& commandLine) override; void OnPostExecuteCommand(MCore::CommandGroup* /*group*/, MCore::Command* /*command*/, const MCore::CommandLine& /*commandLine*/, bool /*wasSuccess*/, const AZStd::string& /*outResult*/) override { } - void OnPreUndoCommand(MCore::Command* command, const MCore::CommandLine& commandLine); + void OnPreUndoCommand(MCore::Command* command, const MCore::CommandLine& commandLine) override; void OnPreExecuteCommandGroup(MCore::CommandGroup* /*group*/, bool /*undo*/) override { } void OnPostExecuteCommandGroup(MCore::CommandGroup* /*group*/, bool /*wasSuccess*/) override { } void OnAddCommandToHistory(size_t /*historyIndex*/, MCore::CommandGroup* /*group*/, MCore::Command* /*command*/, const MCore::CommandLine& /*commandLine*/) override { } diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ManipulatorCallbacks.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ManipulatorCallbacks.h index 902354931a..2f9201ffa9 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ManipulatorCallbacks.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ManipulatorCallbacks.h @@ -42,6 +42,7 @@ namespace EMStudio /** * update the actor instance. */ + using MCommon::ManipulatorCallback::Update; void Update(const AZ::Vector3& value) override; /** @@ -85,6 +86,7 @@ namespace EMStudio /** * update the actor instance. */ + using MCommon::ManipulatorCallback::Update; void Update(const AZ::Quaternion& value) override; /** @@ -125,6 +127,7 @@ namespace EMStudio /** * update the actor instance. */ + using MCommon::ManipulatorCallback::Update; void Update(const AZ::Vector3& value) override; /** diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.h index 01678e024d..6e4a68fa6f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.h @@ -95,7 +95,7 @@ namespace EMStudio virtual bool CreateEMStudioActor(EMotionFX::Actor* actor) = 0; // SkeletonOutlinerNotificationBus - void ZoomToJoints(EMotionFX::ActorInstance* actorInstance, const AZStd::vector& joints); + void ZoomToJoints(EMotionFX::ActorInstance* actorInstance, const AZStd::vector& joints) override; // ActorNotificationBus void OnActorReady(EMotionFX::Actor* actor) override; diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.h index d4906355da..01644362de 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.h @@ -81,8 +81,8 @@ namespace EMStudio // overloaded const AZStd::vector GetHandledEventTypes() const override { return { EMotionFX::EVENT_TYPE_ON_DRAW_LINE, EMotionFX::EVENT_TYPE_ON_DRAW_TRIANGLE, EMotionFX::EVENT_TYPE_ON_DRAW_TRIANGLES }; } - MCORE_INLINE void OnDrawTriangle(const AZ::Vector3& posA, const AZ::Vector3& posB, const AZ::Vector3& posC, const AZ::Vector3& normalA, const AZ::Vector3& normalB, const AZ::Vector3& normalC, uint32 color) { m_widget->AddTriangle(posA, posB, posC, normalA, normalB, normalC, color); } - MCORE_INLINE void OnDrawTriangles() { m_widget->RenderTriangles(); } + MCORE_INLINE void OnDrawTriangle(const AZ::Vector3& posA, const AZ::Vector3& posB, const AZ::Vector3& posC, const AZ::Vector3& normalA, const AZ::Vector3& normalB, const AZ::Vector3& normalC, uint32 color) override { m_widget->AddTriangle(posA, posB, posC, normalA, normalB, normalC, color); } + MCORE_INLINE void OnDrawTriangles() override { m_widget->RenderTriangles(); } private: RenderWidget* m_widget; diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/GLWidget.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/GLWidget.h index 685ff4ef88..f821a7710e 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/GLWidget.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/GLWidget.h @@ -53,7 +53,7 @@ namespace EMStudio void initializeGL() override; void paintGL() override; - void resizeGL(int width, int height); + void resizeGL(int width, int height) override; private slots: void CloneSelectedActorInstances() { CommandSystem::CloneSelectedActorInstances(); } @@ -64,16 +64,16 @@ namespace EMStudio void ResetToBindPose() { CommandSystem::ResetToBindPose(); } protected: - void mouseMoveEvent(QMouseEvent* event) { RenderWidget::OnMouseMoveEvent(this, event); } - void mousePressEvent(QMouseEvent* event) { RenderWidget::OnMousePressEvent(this, event); } - void mouseReleaseEvent(QMouseEvent* event) { RenderWidget::OnMouseReleaseEvent(this, event); } - void wheelEvent(QWheelEvent* event) { RenderWidget::OnWheelEvent(this, event); } + void mouseMoveEvent(QMouseEvent* event) override { RenderWidget::OnMouseMoveEvent(this, event); } + void mousePressEvent(QMouseEvent* event) override { RenderWidget::OnMousePressEvent(this, event); } + void mouseReleaseEvent(QMouseEvent* event) override { RenderWidget::OnMouseReleaseEvent(this, event); } + void wheelEvent(QWheelEvent* event) override { RenderWidget::OnWheelEvent(this, event); } - void focusInEvent(QFocusEvent* event); - void focusOutEvent(QFocusEvent* event); + void focusInEvent(QFocusEvent* event) override; + void focusOutEvent(QFocusEvent* event) override; - void Render(); - void Update() { update(); } + void Render() override; + void Update() override { update(); } void RenderBorder(const MCore::RGBAColor& color); RenderGL::GBuffer m_gBuffer; diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/OpenGLRenderPlugin.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/OpenGLRenderPlugin.h index 1203d7e381..ba2bb0f462 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/OpenGLRenderPlugin.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/OpenGLRenderPlugin.h @@ -43,8 +43,8 @@ namespace EMStudio bool GetIsVertical() const override { return false; } // overloaded main init function - bool Init(); - EMStudioPlugin* Clone() { return new OpenGLRenderPlugin(); } + bool Init() override; + EMStudioPlugin* Clone() override { return new OpenGLRenderPlugin(); } // overloaded functions void CreateRenderWidget(RenderViewWidget* renderViewWidget, RenderWidget** outRenderWidget, QWidget** outWidget) override; @@ -57,7 +57,7 @@ namespace EMStudio RenderGL::GraphicsManager* m_graphicsManager; // shared OpenGL engine object // overloaded emstudio actor create function which creates an OpenGL render actor internally - bool CreateEMStudioActor(EMotionFX::Actor* actor); + bool CreateEMStudioActor(EMotionFX::Actor* actor) override; void RenderActorInstance(EMotionFX::ActorInstance* actorInstance, float timePassedInSeconds) override; }; diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/AnimGraphItemDelegate.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/AnimGraphItemDelegate.h index 4066601cf5..c2c7499727 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/AnimGraphItemDelegate.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/AnimGraphItemDelegate.h @@ -28,7 +28,7 @@ namespace EMStudio void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; - void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; + void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override; signals: void linkActivated(const QString& link); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/AttributesWindow.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/AttributesWindow.h index 38bceb4426..b19304b898 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/AttributesWindow.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/AttributesWindow.h @@ -152,7 +152,7 @@ namespace EMStudio void OnDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles); private: AddConditionButton* m_addConditionButton = nullptr; - void contextMenuEvent(QContextMenuEvent* event); + void contextMenuEvent(QContextMenuEvent* event) override; void PasteTransition(bool pasteTransitionProperties, bool pasteConditions); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/BlendTreeVisualNode.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/BlendTreeVisualNode.h index a72985cae6..ef87fc9a00 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/BlendTreeVisualNode.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/BlendTreeVisualNode.h @@ -34,11 +34,11 @@ namespace EMStudio ~BlendTreeVisualNode(); void Sync() override; - uint32 GetType() const { return BlendTreeVisualNode::TYPE_ID; } + uint32 GetType() const override { return BlendTreeVisualNode::TYPE_ID; } void Render(QPainter& painter, QPen* pen, bool renderShadow) override; - int32 CalcRequiredHeight() const; + int32 CalcRequiredHeight() const override; private: QColor GetPortColor(const EMotionFX::AnimGraphNode::Port& port) const; diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/ParameterEditor/RotationParameterEditor.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/ParameterEditor/RotationParameterEditor.cpp index 805ef16b7d..18727f67d2 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/ParameterEditor/RotationParameterEditor.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/ParameterEditor/RotationParameterEditor.cpp @@ -142,6 +142,7 @@ namespace EMStudio , m_manipulatorCallback(manipulatorCallback) {} + using MCommon::ManipulatorCallback::Update; void Update(const AZ::Quaternion& value) override { // call the base class update function diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/ParameterEditor/Vector3GizmoParameterEditor.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/ParameterEditor/Vector3GizmoParameterEditor.cpp index cc97f9d979..7a24ece95a 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/ParameterEditor/Vector3GizmoParameterEditor.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/ParameterEditor/Vector3GizmoParameterEditor.cpp @@ -152,6 +152,7 @@ namespace EMStudio , m_manipulatorCallback(manipulatorCallback) {} + using MCommon::ManipulatorCallback::Update; void Update(const AZ::Vector3& value) override { // call the base class update function diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/ParameterWindow.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/ParameterWindow.cpp index 3f3c7c1c7d..c8ce48fe18 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/ParameterWindow.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/ParameterWindow.cpp @@ -1461,10 +1461,15 @@ namespace EMStudio // show the create window auto createWindow = new ParameterCreateRenameWindow("Create Group", "Please enter the group name:", uniqueGroupName.c_str(), "", invalidNames, this); - connect(createWindow, &QDialog::finished, this, [this, createWindow]() + connect(createWindow, &QDialog::finished, this, [this, createWindow](int resultCode) { createWindow->deleteLater(); + if (resultCode == QDialog::Rejected) + { + return; + } + AZStd::string command = AZStd::string::format("AnimGraphAddGroupParameter -animGraphID %i -name \"%s\"", m_animGraph->GetID(), createWindow->GetName().c_str()); const EMotionFX::GroupParameter* parentGroup = nullptr; const EMotionFX::Parameter* selectedParameter = GetSingleSelectedParameter(); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/TimeView/TrackHeaderWidget.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/TimeView/TrackHeaderWidget.h index b794f248f6..2839e52e11 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/TimeView/TrackHeaderWidget.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/TimeView/TrackHeaderWidget.h @@ -70,8 +70,8 @@ namespace EMStudio void NameEdited(const QString& text); void EnabledCheckBoxChanged(int state); - void keyPressEvent(QKeyEvent* event); - void keyReleaseEvent(QKeyEvent* event); + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; }; /** diff --git a/Gems/EMotionFX/Code/MCore/Source/AttributeBool.h b/Gems/EMotionFX/Code/MCore/Source/AttributeBool.h index bf5d13d8fe..1dac643667 100644 --- a/Gems/EMotionFX/Code/MCore/Source/AttributeBool.h +++ b/Gems/EMotionFX/Code/MCore/Source/AttributeBool.h @@ -44,7 +44,7 @@ namespace MCore // overloaded from the attribute base class Attribute* Clone() const override { return AttributeBool::Create(m_value); } const char* GetTypeString() const override { return "AttributeBool"; } - bool InitFrom(const Attribute* other); + bool InitFrom(const Attribute* other) override; bool InitFromString(const AZStd::string& valueString) override { return AzFramework::StringFunc::LooksLikeBool(valueString.c_str(), &m_value); diff --git a/Gems/EMotionFX/Code/MCore/Source/AttributeInt32.h b/Gems/EMotionFX/Code/MCore/Source/AttributeInt32.h index e195d584a5..ebde060209 100644 --- a/Gems/EMotionFX/Code/MCore/Source/AttributeInt32.h +++ b/Gems/EMotionFX/Code/MCore/Source/AttributeInt32.h @@ -45,7 +45,7 @@ namespace MCore // overloaded from the attribute base class Attribute* Clone() const override { return AttributeInt32::Create(m_value); } const char* GetTypeString() const override { return "AttributeInt32"; } - bool InitFrom(const Attribute* other); + bool InitFrom(const Attribute* other) override; bool InitFromString(const AZStd::string& valueString) override { return AzFramework::StringFunc::LooksLikeInt(valueString.c_str(), &m_value); diff --git a/Gems/EMotionFX/Code/MCore/Source/MCoreCommandManager.h b/Gems/EMotionFX/Code/MCore/Source/MCoreCommandManager.h index d59639dee5..495fdcde07 100644 --- a/Gems/EMotionFX/Code/MCore/Source/MCoreCommandManager.h +++ b/Gems/EMotionFX/Code/MCore/Source/MCoreCommandManager.h @@ -40,7 +40,7 @@ namespace MCore CommandHistoryEntry() : m_commandGroup(nullptr) , m_executedCommand(nullptr) - , m_parameters(nullptr) {} + {} /** * Extended Constructor. diff --git a/Gems/EMotionFX/Code/Source/Editor/ActorJointBrowseEdit.cpp b/Gems/EMotionFX/Code/Source/Editor/ActorJointBrowseEdit.cpp index 41253a513e..b1f8733bfe 100644 --- a/Gems/EMotionFX/Code/Source/Editor/ActorJointBrowseEdit.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/ActorJointBrowseEdit.cpp @@ -66,6 +66,7 @@ namespace EMStudio m_jointSelectionWindow = new NodeSelectionWindow(this, m_singleJointSelection); connect(m_jointSelectionWindow, &NodeSelectionWindow::rejected, this, &ActorJointBrowseEdit::OnSelectionRejected); connect(m_jointSelectionWindow->GetNodeHierarchyWidget()->GetTreeWidget(), &QTreeWidget::itemSelectionChanged, this, &ActorJointBrowseEdit::OnSelectionChanged); + connect(m_jointSelectionWindow->GetNodeHierarchyWidget(), &NodeHierarchyWidget::OnSelectionDone, this, &ActorJointBrowseEdit::OnSelectionDone); NodeSelectionWindow::connect(m_jointSelectionWindow, &QDialog::finished, [=]([[maybe_unused]] int resultCode) { diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointWidget.cpp index 971eb15501..0597b79d90 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointWidget.cpp @@ -69,7 +69,7 @@ namespace EMotionFX void ClothJointWidget::InternalReinit() { - if (m_selectedModelIndices.size() == 1) + if (GetSelectedModelIndices().size() == 1) { Physics::CharacterColliderNodeConfiguration* nodeConfig = GetNodeConfig(); if (nodeConfig) @@ -94,17 +94,17 @@ namespace EMotionFX void ClothJointWidget::OnAddCollider(const AZ::TypeId& colliderType) { - ColliderHelpers::AddCollider(m_selectedModelIndices , PhysicsSetup::Cloth, colliderType); + ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::Cloth, colliderType); } void ClothJointWidget::OnCopyCollider(size_t colliderIndex) { - ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Cloth); + ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Cloth); } void ClothJointWidget::OnPasteCollider(size_t colliderIndex, bool replace) { - ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Cloth, replace); + ColliderHelpers::PasteColliderFromClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Cloth, replace); } void ClothJointWidget::OnRemoveCollider(size_t colliderIndex) @@ -114,7 +114,7 @@ namespace EMotionFX Physics::CharacterColliderNodeConfiguration* ClothJointWidget::GetNodeConfig() const { - AZ_Assert(m_selectedModelIndices.size() == 1, "Get Node config function only return the config when it is single seleted"); + AZ_Assert(GetSelectedModelIndices().size() == 1, "Get Node config function only return the config when it is single seleted"); Actor* actor = GetActor(); Node* joint = GetNode(); if (!actor || !joint) diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointWidget.cpp index 17265668ab..7fc9ee36e2 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointWidget.cpp @@ -65,7 +65,7 @@ namespace EMotionFX void HitDetectionJointWidget::InternalReinit() { - if (m_selectedModelIndices.size() == 1) + if (GetSelectedModelIndices().size() == 1) { Physics::CharacterColliderNodeConfiguration* hitDetectionNodeConfig = GetNodeConfig(); if (hitDetectionNodeConfig) @@ -90,17 +90,17 @@ namespace EMotionFX void HitDetectionJointWidget::OnAddCollider(const AZ::TypeId& colliderType) { - ColliderHelpers::AddCollider(m_selectedModelIndices, PhysicsSetup::HitDetection, colliderType); + ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::HitDetection, colliderType); } void HitDetectionJointWidget::OnCopyCollider(size_t colliderIndex) { - ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::HitDetection); + ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::HitDetection); } void HitDetectionJointWidget::OnPasteCollider(size_t colliderIndex, bool replace) { - ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::HitDetection, replace); + ColliderHelpers::PasteColliderFromClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::HitDetection, replace); } void HitDetectionJointWidget::OnRemoveCollider(size_t colliderIndex) @@ -110,7 +110,7 @@ namespace EMotionFX Physics::CharacterColliderNodeConfiguration* HitDetectionJointWidget::GetNodeConfig() { - AZ_Assert(m_selectedModelIndices.size() == 1, "Get Node config function only return the config when it is single seleted"); + AZ_Assert(GetSelectedModelIndices().size() == 1, "Get Node config function only return the config when it is single seleted"); Actor* actor = GetActor(); Node* node = GetNode(); if (!actor || !node) diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeWidget.cpp index 00cdb90a22..29745beb68 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeWidget.cpp @@ -116,7 +116,8 @@ namespace EMotionFX void RagdollNodeWidget::InternalReinit() { - if (m_selectedModelIndices.size() == 1) + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); + if (selectedModelIndices.size() == 1) { m_ragdollNodeEditor->ClearInstances(false); @@ -142,7 +143,7 @@ namespace EMotionFX m_collidersWidget->Reset(); } - m_jointLimitWidget->Update(m_selectedModelIndices[0]); + m_jointLimitWidget->Update(selectedModelIndices[0]); m_ragdollNodeCard->setExpanded(true); m_ragdollNodeCard->show(); m_jointLimitWidget->show(); @@ -169,31 +170,32 @@ namespace EMotionFX void RagdollNodeWidget::OnAddRemoveRagdollNode() { + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); if (GetRagdollNodeConfig()) { // The node is present in the ragdoll, remove it. - RagdollNodeInspectorPlugin::RemoveFromRagdoll(m_selectedModelIndices); + RagdollNodeInspectorPlugin::RemoveFromRagdoll(selectedModelIndices); } else { // The node is not part of the ragdoll, add it. - RagdollNodeInspectorPlugin::AddToRagdoll(m_selectedModelIndices); + RagdollNodeInspectorPlugin::AddToRagdoll(selectedModelIndices); } } void RagdollNodeWidget::OnAddCollider(const AZ::TypeId& colliderType) { - ColliderHelpers::AddCollider(m_selectedModelIndices, PhysicsSetup::Ragdoll, colliderType); + ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::Ragdoll, colliderType); } void RagdollNodeWidget::OnCopyCollider(size_t colliderIndex) { - ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Ragdoll); + ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Ragdoll); } void RagdollNodeWidget::OnPasteCollider(size_t colliderIndex, bool replace) { - ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Ragdoll, replace); + ColliderHelpers::PasteColliderFromClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Ragdoll, replace); } void RagdollNodeWidget::OnRemoveCollider(size_t colliderIndex) diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectColliderWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectColliderWidget.cpp index 8c6c72934d..b56cafc9be 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectColliderWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectColliderWidget.cpp @@ -131,7 +131,8 @@ namespace EMotionFX void SimulatedObjectColliderWidget::InternalReinit() { - if (m_selectedModelIndices.size() == 1) + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); + if (selectedModelIndices.size() == 1) { Physics::CharacterColliderNodeConfiguration* nodeConfig = GetNodeConfig(); if (nodeConfig) @@ -172,12 +173,13 @@ namespace EMotionFX } AZStd::string labelText; + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); const AZStd::vector& simObjs = actor->GetSimulatedObjectSetup()->GetSimulatedObjects(); for (const SimulatedObject* obj : simObjs) { - for (int i = 0; i < m_selectedModelIndices.size(); ++i) + for (int i = 0; i < selectedModelIndices.size(); ++i) { - Node* node = m_selectedModelIndices[i].data(SkeletonModel::ROLE_POINTER).value(); + Node* node = selectedModelIndices[i].data(SkeletonModel::ROLE_POINTER).value(); if (obj->FindSimulatedJointBySkeletonJointIndex(node->GetNodeIndex())) { if (!labelText.empty()) @@ -208,8 +210,9 @@ namespace EMotionFX return; } + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); // Only show the notification when it is single selection. - if (m_selectedModelIndices.size() != 1) + if (selectedModelIndices.size() != 1) { return; } @@ -250,17 +253,18 @@ namespace EMotionFX void SimulatedObjectColliderWidget::OnAddCollider(const AZ::TypeId& colliderType) { - ColliderHelpers::AddCollider(m_selectedModelIndices, PhysicsSetup::SimulatedObjectCollider, colliderType); + ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::SimulatedObjectCollider, colliderType); } void SimulatedObjectColliderWidget::OnCopyCollider(size_t colliderIndex) { - ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider); + ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider); } void SimulatedObjectColliderWidget::OnPasteCollider(size_t colliderIndex, bool replace) { - ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider, replace); + ColliderHelpers::PasteColliderFromClipboard( + GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider, replace); } void SimulatedObjectColliderWidget::OnRemoveCollider(size_t colliderIndex) @@ -270,7 +274,7 @@ namespace EMotionFX Physics::CharacterColliderNodeConfiguration* SimulatedObjectColliderWidget::GetNodeConfig() const { - AZ_Assert(m_selectedModelIndices.size() == 1, "Get Node config function only return the config when it is single seleted"); + AZ_Assert(GetSelectedModelIndices().size() == 1, "Get Node config function only return the config when it is single seleted"); Actor* actor = GetActor(); Node* joint = GetNode(); if (!actor || !joint) diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp index f8ca291d55..ec8d1147f2 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp @@ -211,15 +211,15 @@ namespace EMotionFX AZ::Outcome SkeletonOutlinerPlugin::GetSelectedRowIndices() { - return AZ::Success(m_selectedRows); + return AZ::Success(m_treeView->selectionModel()->selectedRows()); } void SkeletonOutlinerPlugin::OnSelectionChanged([[maybe_unused]] const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected) { - m_selectedRows = m_treeView->selectionModel()->selectedRows(); - if (m_selectedRows.size() == 1) + QModelIndexList selectedRows = m_treeView->selectionModel()->selectedRows(); + if (selectedRows.size() == 1) { - const QModelIndex& modelIndex = m_selectedRows[0]; + const QModelIndex& modelIndex = selectedRows[0]; Node* selectedNode = modelIndex.data(SkeletonModel::ROLE_POINTER).value(); Actor* selectedActor = modelIndex.data(SkeletonModel::ROLE_ACTOR_POINTER).value(); SkeletonOutlinerNotificationBus::Broadcast(&SkeletonOutlinerNotifications::SingleNodeSelectionChanged, selectedActor, selectedNode); diff --git a/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.cpp index e9eeb7530d..9cfc763fc6 100644 --- a/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.cpp @@ -72,14 +72,7 @@ namespace EMotionFX setLayout(mainLayout); - AZ::Outcome selectedRowIndicesOutcome; - QModelIndexList selectedModelIndices; - SkeletonOutlinerRequestBus::BroadcastResult(selectedRowIndicesOutcome, &SkeletonOutlinerRequests::GetSelectedRowIndices); - if (selectedRowIndicesOutcome.IsSuccess()) - { - selectedModelIndices = selectedRowIndicesOutcome.GetValue(); - } - Reinit(selectedModelIndices); + Reinit(); // Connect to the model. SkeletonModel* skeletonModel = nullptr; @@ -92,9 +85,9 @@ namespace EMotionFX } } - void SkeletonModelJointWidget::Reinit(const QModelIndexList& selectedModelIndices) + void SkeletonModelJointWidget::Reinit() { - m_selectedModelIndices = selectedModelIndices; + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); if (!EMStudio::GetManager()->GetIgnoreVisibility() && !isVisible()) { @@ -103,15 +96,15 @@ namespace EMotionFX if (GetActor()) { - if (!m_selectedModelIndices.isEmpty()) + if (!selectedModelIndices.isEmpty()) { - if (m_selectedModelIndices.size() == 1) + if (selectedModelIndices.size() == 1) { m_jointNameLabel->setText(GetNode()->GetName()); } else { - m_jointNameLabel->setText(QString("%1 joints selected").arg(m_selectedModelIndices.size())); + m_jointNameLabel->setText(QString("%1 joints selected").arg(selectedModelIndices.size())); } m_noSelectionWidget->hide(); @@ -136,7 +129,7 @@ namespace EMotionFX void SkeletonModelJointWidget::showEvent(QShowEvent* event) { QWidget::showEvent(event); - Reinit(m_selectedModelIndices); + Reinit(); } void SkeletonModelJointWidget::OnSelectionChanged([[maybe_unused]] const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected) @@ -146,36 +139,28 @@ namespace EMotionFX if (skeletonModel) { const QModelIndexList selectedRows = skeletonModel->GetSelectionModel().selectedRows(); - Reinit(selectedRows); } + Reinit(); } void SkeletonModelJointWidget::OnDataChanged([[maybe_unused]] const QModelIndex& topLeft, [[maybe_unused]] const QModelIndex& bottomRight, [[maybe_unused]] const QVector& roles) { - Reinit(m_selectedModelIndices); + Reinit(); } void SkeletonModelJointWidget::OnModelReset() { - Reinit(QModelIndexList()); + Reinit(); } Actor* SkeletonModelJointWidget::GetActor() const { Actor* actor = nullptr; - if (!m_selectedModelIndices.empty()) + SkeletonModel* skeletonModel = nullptr; + SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel); + if (skeletonModel) { - actor = m_selectedModelIndices[0].data(SkeletonModel::ROLE_ACTOR_POINTER).value(); - } - - if (!actor) - { - SkeletonModel* skeletonModel = nullptr; - SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel); - if (skeletonModel) - { - actor = skeletonModel->GetActor(); - } + actor = skeletonModel->GetActor(); } return actor; } @@ -183,10 +168,24 @@ namespace EMotionFX Node* SkeletonModelJointWidget::GetNode() const { Node* node = nullptr; - if (!m_selectedModelIndices.empty()) + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); + if (!selectedModelIndices.empty()) { - node = m_selectedModelIndices[0].data(SkeletonModel::ROLE_POINTER).value(); + node = selectedModelIndices[0].data(SkeletonModel::ROLE_POINTER).value(); } return node; } + + QModelIndexList SkeletonModelJointWidget::GetSelectedModelIndices() const + { + QModelIndexList selectedModelIndices; + SkeletonModel* skeletonModel = nullptr; + SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel); + if (skeletonModel) + { + selectedModelIndices = skeletonModel->GetSelectionModel().selectedRows(); + } + + return selectedModelIndices; + } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.h b/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.h index 52178b4b55..399615c42a 100644 --- a/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.h +++ b/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.h @@ -33,13 +33,14 @@ namespace EMotionFX virtual void CreateGUI(); - void Reinit(const QModelIndexList& selectedModelIndices); + void Reinit(); void showEvent(QShowEvent* event) override; protected: Actor* GetActor() const; Node* GetNode() const; + QModelIndexList GetSelectedModelIndices() const; virtual QWidget* CreateContentWidget(QWidget* parent) = 0; virtual QWidget* CreateNoSelectionWidget(QWidget* parent) = 0; virtual void InternalReinit() = 0; @@ -50,7 +51,6 @@ namespace EMotionFX void OnModelReset(); protected: - QModelIndexList m_selectedModelIndices; QLabel* m_jointNameLabel; static int s_jointLabelSpacing; static int s_jointNameSpacing; diff --git a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp index b5a252e293..56c22bb1ff 100644 --- a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp @@ -115,7 +115,7 @@ namespace EMotionFX public: AZ_CLASS_ALLOCATOR(EMotionFXEventHandler, EMotionFXAllocator, 0); - const AZStd::vector GetHandledEventTypes() const + const AZStd::vector GetHandledEventTypes() const override { return { EVENT_TYPE_ON_EVENT, diff --git a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h index 7d0c3f4725..a716f0aa9b 100644 --- a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h @@ -110,7 +110,7 @@ namespace EMotionFX #if defined (EMOTIONFXANIMATION_EDITOR) void UpdateAnimationEditorPlugins(float delta); void NotifyRegisterViews() override; - bool IsSystemActive(EditorAnimationSystemRequests::AnimationSystem systemType); + bool IsSystemActive(EditorAnimationSystemRequests::AnimationSystem systemType) override; ////////////////////////////////////////////////////////////////////////////////////// // AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler diff --git a/Gems/EMotionFX/Code/Tests/AnimGraphTransitionCommandTests.cpp b/Gems/EMotionFX/Code/Tests/AnimGraphTransitionCommandTests.cpp index 49d53f1f91..bb13744b46 100644 --- a/Gems/EMotionFX/Code/Tests/AnimGraphTransitionCommandTests.cpp +++ b/Gems/EMotionFX/Code/Tests/AnimGraphTransitionCommandTests.cpp @@ -62,14 +62,14 @@ namespace EMotionFX m_motionNodeAnimGraph->InitAfterLoading(); } - void SetUp() + void SetUp() override { AnimGraphFixture::SetUp(); m_animGraphInstance->Destroy(); m_animGraphInstance = m_motionNodeAnimGraph->GetAnimGraphInstance(m_actorInstance, m_motionSet); } - void TearDown() + void TearDown() override { AnimGraphFixture::TearDown(); } diff --git a/Gems/EMotionFX/Code/Tests/Mocks/AnimGraphInstance.h b/Gems/EMotionFX/Code/Tests/Mocks/AnimGraphInstance.h index e04f19fa95..b2375180e6 100644 --- a/Gems/EMotionFX/Code/Tests/Mocks/AnimGraphInstance.h +++ b/Gems/EMotionFX/Code/Tests/Mocks/AnimGraphInstance.h @@ -11,6 +11,8 @@ namespace EMotionFX class AnimGraphInstance { public: + virtual ~AnimGraphInstance() = default; + //void Output(Pose* outputPose); //void Start(); //void Stop(); diff --git a/Gems/EMotionFX/Code/Tests/Mocks/AnimGraphNode.h b/Gems/EMotionFX/Code/Tests/Mocks/AnimGraphNode.h index 9c73694d2a..065be187c8 100644 --- a/Gems/EMotionFX/Code/Tests/Mocks/AnimGraphNode.h +++ b/Gems/EMotionFX/Code/Tests/Mocks/AnimGraphNode.h @@ -14,6 +14,8 @@ namespace EMotionFX public: AZ_RTTI(AnimGraphNode, "{7F1C0E1D-4D32-4A6D-963C-20193EA28F95}", AnimGraphObject) + virtual ~AnimGraphNode() = default; + MOCK_CONST_METHOD1(CollectOutgoingConnections, void(AZStd::vector>& outConnections)); MOCK_CONST_METHOD2(CollectOutgoingConnections, void(AZStd::vector>& outConnections, const size_t portIndex)); diff --git a/Gems/EMotionFX/Code/Tests/Mocks/CommandManager.h b/Gems/EMotionFX/Code/Tests/Mocks/CommandManager.h index 0146086f06..50a465f5da 100644 --- a/Gems/EMotionFX/Code/Tests/Mocks/CommandManager.h +++ b/Gems/EMotionFX/Code/Tests/Mocks/CommandManager.h @@ -11,7 +11,7 @@ namespace MCore class CommandManager { public: - //virtual ~CommandManager(); + virtual ~CommandManager() = default; //bool ExecuteCommand(const char* command, AZStd::string& outCommandResult, bool addToHistory = true, Command** outExecutedCommand = nullptr, CommandLine* outExecutedParamters = nullptr, bool callFromCommandGroup = false, bool clearErrors = true, bool handleErrors = true); //bool ExecuteCommand(const AZStd::string& command, AZStd::string& outCommandResult, bool addToHistory = true, Command** outExecutedCommand = nullptr, CommandLine* outExecutedParamters = nullptr, bool callFromCommandGroup = false, bool clearErrors = true, bool handleErrors = true); diff --git a/Gems/EMotionFX/Code/Tests/Mocks/CommandSystemCommandManager.h b/Gems/EMotionFX/Code/Tests/Mocks/CommandSystemCommandManager.h index adef6392bb..b50e9bf8d5 100644 --- a/Gems/EMotionFX/Code/Tests/Mocks/CommandSystemCommandManager.h +++ b/Gems/EMotionFX/Code/Tests/Mocks/CommandSystemCommandManager.h @@ -12,6 +12,8 @@ namespace CommandSystem : public MCore::CommandManager { public: + virtual ~CommandManager() = default; + MOCK_METHOD0(GetCurrentSelection, SelectionList&()); MOCK_METHOD1(SetCurrentSelection, void(SelectionList& selection)); MOCK_CONST_METHOD0(GetLockSelection, bool()); diff --git a/Gems/EMotionFX/Code/Tests/MotionEventTrackTests.cpp b/Gems/EMotionFX/Code/Tests/MotionEventTrackTests.cpp index 30ab36e35a..08d67b8bee 100644 --- a/Gems/EMotionFX/Code/Tests/MotionEventTrackTests.cpp +++ b/Gems/EMotionFX/Code/Tests/MotionEventTrackTests.cpp @@ -122,7 +122,7 @@ namespace EMotionFX { } - virtual const AZStd::vector GetHandledEventTypes() const + const AZStd::vector GetHandledEventTypes() const override { return { EVENT_TYPE_ON_EVENT }; } diff --git a/Gems/EMotionFX/gem.json b/Gems/EMotionFX/gem.json index f00803ec2c..f1734d854d 100644 --- a/Gems/EMotionFX/gem.json +++ b/Gems/EMotionFX/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The EMotion FX Animation Gem provides Open 3D Engine's animation system for rigged actors and includes Animation Editor, a tool for creating animated behaviors, simulated objects, and colliders for rigged actors.", - "canonical_tags": ["Gem"], - "user_tags": ["Animation", "Tools", "Simulation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Animation", + "Tools", + "Simulation" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/emotionfx/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/emotionfx/", + "dependencies": [ + "Atom_RPI", + "LmbrCentral" + ] } diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp b/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp index 38b29bcdf2..f9f9580c1a 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp @@ -195,7 +195,7 @@ namespace EditorPythonBindings if (!m_handler) { - AZ_Error("python", false, "No EBus connection deteced; missing call or failed call to connect()?"); + AZ_Error("python", false, "No EBus connection detected; missing call or failed call to connect()?"); return false; } diff --git a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp index 139337ef00..cecbf15c5a 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp @@ -512,6 +512,8 @@ namespace EditorPythonBindings GetGemSourcePathsVisitor(AZ::SettingsRegistryInterface& settingsRegistry) : m_settingsRegistry(settingsRegistry) {} + + using AZ::SettingsRegistryInterface::Visitor::Visit; void Visit(AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override { diff --git a/Gems/EditorPythonBindings/Code/Tests/PythonAssetTypesTests.cpp b/Gems/EditorPythonBindings/Code/Tests/PythonAssetTypesTests.cpp index 78ac2a0a45..9940a01b7a 100644 --- a/Gems/EditorPythonBindings/Code/Tests/PythonAssetTypesTests.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/PythonAssetTypesTests.cpp @@ -116,7 +116,7 @@ namespace UnitTest return AZ::Data::AssetType("{7FD86523-3903-4037-BCD1-542027BFC553}"); } - virtual const char* GetFileFilter() const + const char* GetFileFilter() const override { return nullptr; } diff --git a/Gems/EditorPythonBindings/gem.json b/Gems/EditorPythonBindings/gem.json index fae091a63f..13c5800dd7 100644 --- a/Gems/EditorPythonBindings/gem.json +++ b/Gems/EditorPythonBindings/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Editor Python Bindings Gem provides Python commands for Open 3D Engine Editor functions.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/editor-python-bindings/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/editor-python-bindings/", + "dependencies": [] } diff --git a/Gems/ExpressionEvaluation/Code/Source/ExpressionEvaluationSystemComponent.cpp b/Gems/ExpressionEvaluation/Code/Source/ExpressionEvaluationSystemComponent.cpp index 1a72f30e7c..5c4b9c61d9 100644 --- a/Gems/ExpressionEvaluation/Code/Source/ExpressionEvaluationSystemComponent.cpp +++ b/Gems/ExpressionEvaluation/Code/Source/ExpressionEvaluationSystemComponent.cpp @@ -37,12 +37,12 @@ namespace ExpressionEvaluation } - ExpressionParserId GetParserId() const + ExpressionParserId GetParserId() const override { return InternalTypes::Interfaces::InternalParser; } - ParseResult ParseElement(const AZStd::string& inputText, size_t offset) const + ParseResult ParseElement(const AZStd::string& inputText, size_t offset) const override { ParseResult result; AZStd::smatch match; @@ -69,7 +69,7 @@ namespace ExpressionEvaluation return result; } - void EvaluateToken(const ElementInformation& parseResult, ExpressionResultStack& evaluationStack) const + void EvaluateToken(const ElementInformation& parseResult, ExpressionResultStack& evaluationStack) const override { AZ_UNUSED(parseResult); AZ_UNUSED(evaluationStack); diff --git a/Gems/ExpressionEvaluation/gem.json b/Gems/ExpressionEvaluation/gem.json index 2f555916a5..6bcc666a4d 100644 --- a/Gems/ExpressionEvaluation/gem.json +++ b/Gems/ExpressionEvaluation/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Expression Evaluation Gem provides a method for parsing and executing string expressions in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/expression-evaluation/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/expression-evaluation/", + "dependencies": [] } diff --git a/Gems/FastNoise/gem.json b/Gems/FastNoise/gem.json index 5ee5bbaf89..ac59fe804e 100644 --- a/Gems/FastNoise/gem.json +++ b/Gems/FastNoise/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The FastNoise Gradient Gem uses the third-party, open source FastNoise library to provide a variety of high-performance noise generation algorithms.", - "canonical_tags": ["Gem"], - "user_tags": ["Utility", "Tools", "Design"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Utility", + "Tools", + "Design" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/fast-noise/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/fast-noise/", + "dependencies": [ + "GradientSignal", + "LmbrCentral", + "SurfaceData" + ] } diff --git a/Gems/GameState/gem.json b/Gems/GameState/gem.json index a1f272c290..7bb3cf4214 100644 --- a/Gems/GameState/gem.json +++ b/Gems/GameState/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Game State Gem provides a generic framework to determine and manage game states and game state transitions in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Framework", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Framework", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state/", + "dependencies": [] } diff --git a/Gems/GameStateSamples/Code/Source/GameStateSamplesModule.cpp b/Gems/GameStateSamples/Code/Source/GameStateSamplesModule.cpp index f175619bf4..a83a5abdd1 100644 --- a/Gems/GameStateSamples/Code/Source/GameStateSamplesModule.cpp +++ b/Gems/GameStateSamples/Code/Source/GameStateSamplesModule.cpp @@ -84,7 +84,7 @@ namespace GameStateSamples } protected: - void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) + void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) override { CryHooksModule::OnCrySystemInitialized(system, systemInitParams); diff --git a/Gems/GameStateSamples/gem.json b/Gems/GameStateSamples/gem.json index f0dd82c5fa..be982b9c5b 100644 --- a/Gems/GameStateSamples/gem.json +++ b/Gems/GameStateSamples/gem.json @@ -5,9 +5,25 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Game State Samples Gem provides a set of sample game states (built on top of the Game State Gem), including primary user selection, main menu, level loading, level running, and level paused.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Sample", "Assets"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Sample", + "Assets" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state-samples/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state-samples/", + "dependencies": [ + "GameState", + "LocalUser", + "LyShine", + "SaveData", + "MessagePopup", + "LmbrCentral", + "UiBasics", + "LyShineExamples" + ] } diff --git a/Gems/Gestures/gem.json b/Gems/Gestures/gem.json index 4412e58c6d..fcc56b4704 100644 --- a/Gems/Gestures/gem.json +++ b/Gems/Gestures/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Gestures Gem provides detection for common gesture-based input actions on iOS and Android devices.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/gestures/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/gestures/", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Editor/EditorGradientComponentBase.h b/Gems/GradientSignal/Code/Include/GradientSignal/Editor/EditorGradientComponentBase.h index 1eac48a109..2083837934 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Editor/EditorGradientComponentBase.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Editor/EditorGradientComponentBase.h @@ -92,7 +92,7 @@ namespace GradientSignal using BaseClassType::m_component; using BaseClassType::m_configuration; - virtual AZ::u32 ConfigurationChanged(); + AZ::u32 ConfigurationChanged() override; // This is used by the preview so we can pass an invalid entity Id if our component is disabled AZ::EntityId GetGradientEntityId() const; diff --git a/Gems/GradientSignal/Code/Source/UI/GradientPreviewDataWidget.h b/Gems/GradientSignal/Code/Source/UI/GradientPreviewDataWidget.h index 3aa5987924..7fc3a53463 100644 --- a/Gems/GradientSignal/Code/Source/UI/GradientPreviewDataWidget.h +++ b/Gems/GradientSignal/Code/Source/UI/GradientPreviewDataWidget.h @@ -66,7 +66,7 @@ namespace GradientSignal AZ::u32 GetHandlerName() const override; bool ReadValueIntoGUI(size_t index, GradientPreviewDataWidget* GUI, void* value, const AZ::Uuid& propertyType) override; - void ConsumeAttribute(GradientPreviewDataWidget* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName); + void ConsumeAttribute(GradientPreviewDataWidget* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override; QWidget* CreateGUI(QWidget* pParent) override; void PreventRefresh(QWidget* widget, bool shouldPrevent) override; diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h b/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h index cf3843ce4a..9c0d8be588 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h @@ -174,8 +174,8 @@ namespace UnitTest } AZ::EntityId GetPreviewEntity() const override { return m_id; } - virtual AZ::Aabb GetPreviewBounds() const { return m_previewBounds; } - virtual bool GetConstrainToShape() const { return m_constrainToShape; } + AZ::Aabb GetPreviewBounds() const override { return m_previewBounds; } + bool GetConstrainToShape() const override { return m_constrainToShape; } protected: AZ::EntityId m_id; diff --git a/Gems/GradientSignal/gem.json b/Gems/GradientSignal/gem.json index 67afb1f819..e87ccfe13a 100644 --- a/Gems/GradientSignal/gem.json +++ b/Gems/GradientSignal/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Gradient Signal Gem provides a number of components for generating, modifying, and mixing gradient signals.", - "canonical_tags": ["Gem"], - "user_tags": ["Utility", "Tools", "Design"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Utility", + "Tools", + "Design" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/gradient-signal/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/gradient-signal/", + "dependencies": [ + "SurfaceData", + "ImageProcessingAtom", + "LmbrCentral" + ] } diff --git a/Gems/GraphCanvas/Code/Include/GraphCanvas/Widgets/RootGraphicsItem.h b/Gems/GraphCanvas/Code/Include/GraphCanvas/Widgets/RootGraphicsItem.h index cd7283c53c..e7c884da92 100644 --- a/Gems/GraphCanvas/Code/Include/GraphCanvas/Widgets/RootGraphicsItem.h +++ b/Gems/GraphCanvas/Code/Include/GraphCanvas/Widgets/RootGraphicsItem.h @@ -163,14 +163,14 @@ namespace GraphCanvas } // StateController - void OnStateChanged([[maybe_unused]] const RootGraphicsItemDisplayState& displayState) + void OnStateChanged([[maybe_unused]] const RootGraphicsItemDisplayState& displayState) override { UpdateActualDisplayState(); } //// // TickBus - void OnTick(float delta, AZ::ScriptTimePoint) + void OnTick(float delta, AZ::ScriptTimePoint) override { m_currentAnimationTime += delta; @@ -191,7 +191,7 @@ namespace GraphCanvas //// // RootGraphicsItemRequestBus - void AnimatePositionTo(const QPointF& scenePoint, const AZStd::chrono::milliseconds& duration) + void AnimatePositionTo(const QPointF& scenePoint, const AZStd::chrono::milliseconds& duration) override { if (!IsAnimating()) { @@ -231,7 +231,7 @@ namespace GraphCanvas GeometryRequestBus::Event(GetEntityId(), &GeometryRequests::SetAnimationTarget, m_targetPoint); } - void CancelAnimation() + void CancelAnimation() override { m_currentAnimationTime = m_animationDuration; CleanUpAnimation(); @@ -315,7 +315,7 @@ namespace GraphCanvas } } - RootGraphicsItemEnabledState GetEnabledState() const + RootGraphicsItemEnabledState GetEnabledState() const override { return m_enabledState; } diff --git a/Gems/GraphCanvas/Code/Source/Components/BookmarkAnchor/BookmarkAnchorVisualComponent.h b/Gems/GraphCanvas/Code/Source/Components/BookmarkAnchor/BookmarkAnchorVisualComponent.h index 2e2d9b9ab0..7e7ac6489f 100644 --- a/Gems/GraphCanvas/Code/Source/Components/BookmarkAnchor/BookmarkAnchorVisualComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/BookmarkAnchor/BookmarkAnchorVisualComponent.h @@ -57,7 +57,7 @@ namespace GraphCanvas //// // StyleNotificationBus - void OnStyleChanged(); + void OnStyleChanged() override; //// // GeometryNotificationBus diff --git a/Gems/GraphCanvas/Code/Source/Components/Connections/ConnectionLayerControllerComponent.h b/Gems/GraphCanvas/Code/Source/Components/Connections/ConnectionLayerControllerComponent.h index f991409b17..2532492d64 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Connections/ConnectionLayerControllerComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Connections/ConnectionLayerControllerComponent.h @@ -39,7 +39,7 @@ namespace GraphCanvas //// // LayerControllerNotificationBus - void OnOffsetsChanged(int selectionOffset, int groupOffset); + void OnOffsetsChanged(int selectionOffset, int groupOffset) override; //// private: diff --git a/Gems/GraphCanvas/Code/Source/Components/GeometryComponent.h b/Gems/GraphCanvas/Code/Source/Components/GeometryComponent.h index 1c7bf39445..61b66bcbf9 100644 --- a/Gems/GraphCanvas/Code/Source/Components/GeometryComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/GeometryComponent.h @@ -69,7 +69,7 @@ namespace GraphCanvas void SetIsPositionAnimating(bool animating) override; - void SetAnimationTarget(const AZ::Vector2& targetPoint); + void SetAnimationTarget(const AZ::Vector2& targetPoint) override; //// // VisualNotificationBus diff --git a/Gems/GraphCanvas/Code/Source/Components/NodePropertyDisplays/StringNodePropertyDisplay.h b/Gems/GraphCanvas/Code/Source/Components/NodePropertyDisplays/StringNodePropertyDisplay.h index ac179832d3..1e50262194 100644 --- a/Gems/GraphCanvas/Code/Source/Components/NodePropertyDisplays/StringNodePropertyDisplay.h +++ b/Gems/GraphCanvas/Code/Source/Components/NodePropertyDisplays/StringNodePropertyDisplay.h @@ -81,7 +81,7 @@ namespace GraphCanvas //// // AZ::SystemTickBus::Handler - void OnSystemTick(); + void OnSystemTick() override; //// private: diff --git a/Gems/GraphCanvas/Code/Source/Components/NodePropertyDisplays/VectorNodePropertyDisplay.h b/Gems/GraphCanvas/Code/Source/Components/NodePropertyDisplays/VectorNodePropertyDisplay.h index e0c50821a8..9c12dae08e 100644 --- a/Gems/GraphCanvas/Code/Source/Components/NodePropertyDisplays/VectorNodePropertyDisplay.h +++ b/Gems/GraphCanvas/Code/Source/Components/NodePropertyDisplays/VectorNodePropertyDisplay.h @@ -95,7 +95,7 @@ namespace GraphCanvas //// // DataSlotNotifications - void OnDragDropStateStateChanged(const DragDropState& dragState); + void OnDragDropStateStateChanged(const DragDropState& dragState) override; //// private: diff --git a/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentNodeFrameComponent.h b/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentNodeFrameComponent.h index 0c85f366ed..9baeccb78f 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentNodeFrameComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentNodeFrameComponent.h @@ -65,7 +65,7 @@ namespace GraphCanvas //// // NodeNotifications - void OnNodeActivated(); + void OnNodeActivated() override; //// private: diff --git a/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentNodeLayoutComponent.h b/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentNodeLayoutComponent.h index 057261743f..8e2692ce0e 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentNodeLayoutComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentNodeLayoutComponent.h @@ -50,9 +50,9 @@ namespace GraphCanvas required.push_back(AZ_CRC("GraphCanvas_StyledGraphicItemService", 0xeae4cdf4)); } - void Init(); - void Activate(); - void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; //// // EntityBus @@ -64,7 +64,7 @@ namespace GraphCanvas //// // NodeNotification - void OnNodeActivated(); + void OnNodeActivated() override; //// protected: diff --git a/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentNodeTextComponent.h b/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentNodeTextComponent.h index 36ddfeafe5..fa047d56a2 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentNodeTextComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentNodeTextComponent.h @@ -82,7 +82,7 @@ namespace GraphCanvas //// // NodeNotification - void OnAddedToScene(const AZ::EntityId&); + void OnAddedToScene(const AZ::EntityId&) override; //// // CommentRequestBus diff --git a/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentTextGraphicsWidget.h b/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentTextGraphicsWidget.h index 1f6a4ce9a7..390fb1ef9e 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentTextGraphicsWidget.h +++ b/Gems/GraphCanvas/Code/Source/Components/Nodes/Comment/CommentTextGraphicsWidget.h @@ -163,7 +163,7 @@ namespace GraphCanvas void SubmitValue(); void UpdateSizePolicies(); - bool sceneEventFilter(QGraphicsItem*, QEvent* event); + bool sceneEventFilter(QGraphicsItem*, QEvent* event) override; const AZ::EntityId& GetEntityId() const { return m_entityId; } void SetupProxyWidget(); diff --git a/Gems/GraphCanvas/Code/Source/Components/Nodes/General/GeneralNodeFrameComponent.h b/Gems/GraphCanvas/Code/Source/Components/Nodes/General/GeneralNodeFrameComponent.h index f38157e82e..123f21f13d 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Nodes/General/GeneralNodeFrameComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Nodes/General/GeneralNodeFrameComponent.h @@ -68,7 +68,7 @@ namespace GraphCanvas //// // NodeNotifications - void OnNodeActivated(); + void OnNodeActivated() override; void OnNodeWrapped(const AZ::EntityId& wrappingNode) override; void OnNodeUnwrapped(const AZ::EntityId& wrappingNode) override; diff --git a/Gems/GraphCanvas/Code/Source/Components/Nodes/General/GeneralSlotLayoutComponent.h b/Gems/GraphCanvas/Code/Source/Components/Nodes/General/GeneralSlotLayoutComponent.h index c9290021c9..397aea5a38 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Nodes/General/GeneralSlotLayoutComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Nodes/General/GeneralSlotLayoutComponent.h @@ -158,7 +158,7 @@ namespace GraphCanvas //// // SceneMemberNotificationBus - void OnSceneSet(const AZ::EntityId& sceneId); + void OnSceneSet(const AZ::EntityId& sceneId) override; //// // SlotLayoutRequestBus @@ -168,7 +168,7 @@ namespace GraphCanvas bool IsSlotGroupVisible(SlotGroup group) const override; void SetSlotGroupVisible(SlotGroup group, bool visible) override; - void ClearSlotGroup(SlotGroup group); + void ClearSlotGroup(SlotGroup group) override; //// // StyleNotificationBus diff --git a/Gems/GraphCanvas/Code/Source/Components/Nodes/Group/CollapsedNodeGroupComponent.h b/Gems/GraphCanvas/Code/Source/Components/Nodes/Group/CollapsedNodeGroupComponent.h index 17ef858b52..fd9706f7ea 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Nodes/Group/CollapsedNodeGroupComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Nodes/Group/CollapsedNodeGroupComponent.h @@ -94,7 +94,7 @@ namespace GraphCanvas //// // GeometryNotifications - void OnBoundsChanged(); + void OnBoundsChanged() override; void OnPositionChanged(const AZ::EntityId& targetEntity, const AZ::Vector2& position) override; //// @@ -117,7 +117,7 @@ namespace GraphCanvas AZ::EntityId GetSourceGroup() const override; - AZStd::vector< Endpoint > GetRedirectedEndpoints() const; + AZStd::vector< Endpoint > GetRedirectedEndpoints() const override; void ForceEndpointRedirection(const AZStd::vector< Endpoint >& endpoints) override; //// diff --git a/Gems/GraphCanvas/Code/Source/Components/Nodes/Group/NodeGroupFrameComponent.h b/Gems/GraphCanvas/Code/Source/Components/Nodes/Group/NodeGroupFrameComponent.h index 4f791d913d..cc54866ccb 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Nodes/Group/NodeGroupFrameComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Nodes/Group/NodeGroupFrameComponent.h @@ -239,7 +239,7 @@ namespace GraphCanvas //// // SystemTickBus - void OnSystemTick(); + void OnSystemTick() override; //// // VisualNotificationBus @@ -446,13 +446,13 @@ namespace GraphCanvas //// // CommentNotificationBus - void OnEditBegin(); - void OnEditEnd(); + void OnEditBegin() override; + void OnEditEnd() override; void OnCommentSizeChanged(const QSizeF& oldSize, const QSizeF& newSize) override; - void OnCommentFontReloadBegin(); - void OnCommentFontReloadEnd(); + void OnCommentFontReloadBegin() override; + void OnCommentFontReloadEnd() override; //// // QGraphicsItem diff --git a/Gems/GraphCanvas/Code/Source/Components/Nodes/Group/NodeGroupLayoutComponent.h b/Gems/GraphCanvas/Code/Source/Components/Nodes/Group/NodeGroupLayoutComponent.h index ec3bbdda28..5685702051 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Nodes/Group/NodeGroupLayoutComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Nodes/Group/NodeGroupLayoutComponent.h @@ -61,13 +61,13 @@ namespace GraphCanvas //// // AZ::Component - void Init(); - void Activate(); - void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; //// // NodeNotification - void OnNodeActivated(); + void OnNodeActivated() override; //// void UpdateLayoutParameters(); diff --git a/Gems/GraphCanvas/Code/Source/Components/Nodes/NodeComponent.h b/Gems/GraphCanvas/Code/Source/Components/Nodes/NodeComponent.h index 996ba2e8c9..4e2052555f 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Nodes/NodeComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Nodes/NodeComponent.h @@ -109,7 +109,7 @@ namespace GraphCanvas void SetTranslationKeyedTooltip(const TranslationKeyedString& tooltip) override; const AZStd::string GetTooltip() const override { return m_configuration.GetTooltip(); } - void SetShowInOutliner(bool showInOutliner) { m_configuration.SetShowInOutliner(showInOutliner); } + void SetShowInOutliner(bool showInOutliner) override { m_configuration.SetShowInOutliner(showInOutliner); } bool ShowInOutliner() const override { return m_configuration.GetShowInOutliner(); } void AddSlot(const AZ::EntityId& slotId) override; diff --git a/Gems/GraphCanvas/Code/Source/Components/Nodes/Wrapper/WrapperNodeLayoutComponent.h b/Gems/GraphCanvas/Code/Source/Components/Nodes/Wrapper/WrapperNodeLayoutComponent.h index 4fa98dbadd..0423f061f6 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Nodes/Wrapper/WrapperNodeLayoutComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Nodes/Wrapper/WrapperNodeLayoutComponent.h @@ -155,9 +155,9 @@ namespace GraphCanvas required.push_back(AZ_CRC("GraphCanvas_StyledGraphicItemService", 0xeae4cdf4)); } - void Init(); - void Activate(); - void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; //// // WrapperNodeRequestBus diff --git a/Gems/GraphCanvas/Code/Source/Components/Slots/Data/DataSlotComponent.h b/Gems/GraphCanvas/Code/Source/Components/Slots/Data/DataSlotComponent.h index d6c6b71771..fb76c2c80e 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Slots/Data/DataSlotComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Slots/Data/DataSlotComponent.h @@ -29,9 +29,9 @@ namespace GraphCanvas ~DataSlotComponent(); // Component - void Init(); - void Activate(); - void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; //// // SlotRequestBus diff --git a/Gems/GraphCanvas/Code/Source/Components/Slots/Default/DefaultSlotLayoutComponent.h b/Gems/GraphCanvas/Code/Source/Components/Slots/Default/DefaultSlotLayoutComponent.h index 7d963af0bf..c1b6b5b0ac 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Slots/Default/DefaultSlotLayoutComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Slots/Default/DefaultSlotLayoutComponent.h @@ -43,7 +43,7 @@ namespace GraphCanvas //// // StyleNotificationBus - void OnStyleChanged(); + void OnStyleChanged() override; //// private: diff --git a/Gems/GraphCanvas/Code/Source/Components/Slots/Execution/ExecutionSlotLayoutComponent.h b/Gems/GraphCanvas/Code/Source/Components/Slots/Execution/ExecutionSlotLayoutComponent.h index 812bef7187..5df2b9f68c 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Slots/Execution/ExecutionSlotLayoutComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Slots/Execution/ExecutionSlotLayoutComponent.h @@ -51,7 +51,7 @@ namespace GraphCanvas //// // StyleNotificationBus - void OnStyleChanged(); + void OnStyleChanged() override; //// private: @@ -88,9 +88,9 @@ namespace GraphCanvas ExecutionSlotLayoutComponent(); ~ExecutionSlotLayoutComponent() override = default; - void Init(); - void Activate(); - void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; private: ExecutionSlotLayout* m_layout; diff --git a/Gems/GraphCanvas/Code/Source/Components/Slots/Extender/ExtenderSlotComponent.h b/Gems/GraphCanvas/Code/Source/Components/Slots/Extender/ExtenderSlotComponent.h index 5ca93d5c26..b37704156d 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Slots/Extender/ExtenderSlotComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Slots/Extender/ExtenderSlotComponent.h @@ -40,9 +40,9 @@ namespace GraphCanvas ~ExtenderSlotComponent(); // Component - void Init(); - void Activate(); - void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; //// // SceneMemberNotifications diff --git a/Gems/GraphCanvas/Code/Source/Components/Slots/Extender/ExtenderSlotLayoutComponent.h b/Gems/GraphCanvas/Code/Source/Components/Slots/Extender/ExtenderSlotLayoutComponent.h index de8e95d7ff..ed477d40cf 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Slots/Extender/ExtenderSlotLayoutComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Slots/Extender/ExtenderSlotLayoutComponent.h @@ -53,7 +53,7 @@ namespace GraphCanvas //// // StyleNotificationBus - void OnStyleChanged(); + void OnStyleChanged() override; //// private: @@ -82,9 +82,9 @@ namespace GraphCanvas ExtenderSlotLayoutComponent(); ~ExtenderSlotLayoutComponent() override = default; - void Init(); - void Activate(); - void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; private: diff --git a/Gems/GraphCanvas/Code/Source/Components/Slots/Property/PropertySlotComponent.h b/Gems/GraphCanvas/Code/Source/Components/Slots/Property/PropertySlotComponent.h index 309e4bb762..65e537458b 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Slots/Property/PropertySlotComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Slots/Property/PropertySlotComponent.h @@ -27,9 +27,9 @@ namespace GraphCanvas ~PropertySlotComponent(); // Component - void Init(); - void Activate(); - void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; //// // Slot RequestBus @@ -38,7 +38,7 @@ namespace GraphCanvas //// // PropertySlotBus - const AZ::Crc32& GetPropertyId() const; + const AZ::Crc32& GetPropertyId() const override; //// private: diff --git a/Gems/GraphCanvas/Code/Source/Components/Slots/Property/PropertySlotLayoutComponent.h b/Gems/GraphCanvas/Code/Source/Components/Slots/Property/PropertySlotLayoutComponent.h index 9b63882ec8..0891f51f06 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Slots/Property/PropertySlotLayoutComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Slots/Property/PropertySlotLayoutComponent.h @@ -49,7 +49,7 @@ namespace GraphCanvas // SlotNotificationBus void OnRegisteredToNode(const AZ::EntityId& nodeId) override; - void OnTooltipChanged(const TranslationKeyedString& tooltip); + void OnTooltipChanged(const TranslationKeyedString& tooltip) override; //// // StyleNotificationBus diff --git a/Gems/GraphCanvas/Code/Source/Components/Slots/SlotComponent.h b/Gems/GraphCanvas/Code/Source/Components/Slots/SlotComponent.h index 319ffd7216..5afa3fbc14 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Slots/SlotComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Slots/SlotComponent.h @@ -72,7 +72,7 @@ namespace GraphCanvas const AZ::EntityId& GetNode() const override; void SetNode(const AZ::EntityId&) override; - Endpoint GetEndpoint() const; + Endpoint GetEndpoint() const override; const AZStd::string GetName() const override { return m_slotConfiguration.m_name.GetDisplayString(); } void SetName(const AZStd::string& name) override; diff --git a/Gems/GraphCanvas/Code/Source/Components/Slots/SlotLayoutComponent.h b/Gems/GraphCanvas/Code/Source/Components/Slots/SlotLayoutComponent.h index 87917cbe29..d73018f40b 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Slots/SlotLayoutComponent.h +++ b/Gems/GraphCanvas/Code/Source/Components/Slots/SlotLayoutComponent.h @@ -52,16 +52,16 @@ namespace GraphCanvas required.push_back(AZ_CRC("GraphCanvas_SlotService", 0x701eaf6b)); } - void Init(); - void Activate(); - void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; //// // VisualRequestBus QGraphicsItem* AsGraphicsItem() override; QGraphicsLayoutItem* AsGraphicsLayoutItem() override; - bool Contains(const AZ::Vector2& position) const; + bool Contains(const AZ::Vector2& position) const override; void SetVisible(bool visible) override; bool IsVisible() const override; //// diff --git a/Gems/GraphCanvas/Code/Source/Components/Slots/SlotLayoutItem.h b/Gems/GraphCanvas/Code/Source/Components/Slots/SlotLayoutItem.h index ee97284be8..98134a7362 100644 --- a/Gems/GraphCanvas/Code/Source/Components/Slots/SlotLayoutItem.h +++ b/Gems/GraphCanvas/Code/Source/Components/Slots/SlotLayoutItem.h @@ -38,7 +38,7 @@ namespace GraphCanvas protected: // QGraphicsItem - void mousePressEvent(QGraphicsSceneMouseEvent* event) + void mousePressEvent(QGraphicsSceneMouseEvent* event) override { bool result = false; VisualNotificationBus::EventResult(result, GetEntityId(), &VisualNotifications::OnMousePress, GetEntityId(), event); @@ -48,7 +48,7 @@ namespace GraphCanvas } } - void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) + void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override { bool result = false; VisualNotificationBus::EventResult(result, GetEntityId(), &VisualNotifications::OnMouseRelease, GetEntityId(), event); diff --git a/Gems/GraphCanvas/Code/Source/Widgets/NodePropertyDisplayWidget.h b/Gems/GraphCanvas/Code/Source/Widgets/NodePropertyDisplayWidget.h index dbd8a983cf..955ec490d6 100644 --- a/Gems/GraphCanvas/Code/Source/Widgets/NodePropertyDisplayWidget.h +++ b/Gems/GraphCanvas/Code/Source/Widgets/NodePropertyDisplayWidget.h @@ -45,7 +45,7 @@ namespace GraphCanvas //// // RootGraphicsItemNotificationBus - void OnDisplayStateChanged(RootGraphicsItemDisplayState oldState, RootGraphicsItemDisplayState newState); + void OnDisplayStateChanged(RootGraphicsItemDisplayState oldState, RootGraphicsItemDisplayState newState) override; //// // NodePropertiesRequestBus @@ -56,7 +56,7 @@ namespace GraphCanvas //// // NodePropertyRequestBus - void SetDisabled(bool disabled); + void SetDisabled(bool disabled) override; void SetNodePropertyDisplay(NodePropertyDisplay* nodePropertyDisplay) override; NodePropertyDisplay* GetNodePropertyDisplay() const override; diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Components/GraphCanvasPropertyBus.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Components/GraphCanvasPropertyBus.h index 6dda23692b..2a26fc3fe0 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Components/GraphCanvasPropertyBus.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Components/GraphCanvasPropertyBus.h @@ -57,12 +57,12 @@ namespace GraphCanvas GraphCanvasPropertyBus::MultiHandler::BusDisconnect(); } - void AddBusId(const AZ::EntityId& busId) override final + void AddBusId(const AZ::EntityId& busId) final { GraphCanvasPropertyBus::MultiHandler::BusConnect(busId); } - void RemoveBusId(const AZ::EntityId& busId) override final + void RemoveBusId(const AZ::EntityId& busId) final { GraphCanvasPropertyBus::MultiHandler::BusDisconnect(busId); } @@ -86,12 +86,12 @@ namespace GraphCanvas void Init() override {}; - void Activate() + void Activate() override { GraphCanvasPropertyBusHandler::OnActivate(GetEntityId()); } - void Deactivate() + void Deactivate() override { GraphCanvasPropertyBusHandler::OnDeactivate(); } diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/GraphicsItems/GlowOutlineGraphicsItem.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/GraphicsItems/GlowOutlineGraphicsItem.h index d54c34f8f0..05302ee889 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/GraphicsItems/GlowOutlineGraphicsItem.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/GraphicsItems/GlowOutlineGraphicsItem.h @@ -77,7 +77,7 @@ namespace GraphCanvas //// // SystemTick - void OnSystemTick(); + void OnSystemTick() override; //// // TickBus @@ -86,7 +86,7 @@ namespace GraphCanvas // GeometryNotificationBus::Handler void OnPositionChanged(const AZ::EntityId& /*targetEntity*/, const AZ::Vector2& /*position*/) override; - void OnBoundsChanged(); + void OnBoundsChanged() override; //// // ViewNotificationBus diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/SelectorImplementations.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/SelectorImplementations.h index 86b2cafdc9..1c65bf1915 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/SelectorImplementations.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/SelectorImplementations.h @@ -37,7 +37,7 @@ namespace GraphCanvas return 0; } - bool Matches([[maybe_unused]] const AZ::EntityId& object) const + bool Matches([[maybe_unused]] const AZ::EntityId& object) const override { return false; } diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Style.cpp b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Style.cpp index 166a333d24..048bdf1736 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Style.cpp +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Style.cpp @@ -129,7 +129,7 @@ namespace : public AZ::SerializeContext::IDataSerializer { /// Store the class data into a binary buffer - virtual size_t Save(const void* classPtr, AZ::IO::GenericStream& stream, bool isDataBigEndian /*= false*/) + size_t Save(const void* classPtr, AZ::IO::GenericStream& stream, bool isDataBigEndian /*= false*/) override { auto variant = reinterpret_cast(classPtr); @@ -142,7 +142,7 @@ namespace } /// Convert binary data to text - virtual size_t DataToText(AZ::IO::GenericStream& in, AZ::IO::GenericStream& out, bool isDataBigEndian /*= false*/) + size_t DataToText(AZ::IO::GenericStream& in, AZ::IO::GenericStream& out, bool isDataBigEndian /*= false*/) override { (void)isDataBigEndian; @@ -152,7 +152,7 @@ namespace } /// Convert text data to binary, to support loading old version formats. We must respect text version if the text->binary format has changed! - virtual size_t TextToData(const char* text, unsigned int textVersion, AZ::IO::GenericStream& stream, bool isDataBigEndian = false) + size_t TextToData(const char* text, unsigned int textVersion, AZ::IO::GenericStream& stream, bool isDataBigEndian = false) override { (void)textVersion; (void)isDataBigEndian; @@ -164,7 +164,7 @@ namespace } /// Load the class data from a stream. - virtual bool Load(void* classPtr, AZ::IO::GenericStream& in, unsigned int, bool isDataBigEndian = false) + bool Load(void* classPtr, AZ::IO::GenericStream& in, unsigned int, bool isDataBigEndian = false) override { QByteArray buffer = ReadAll(in); QDataStream qtStream(&buffer, QIODevice::ReadOnly); diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Types/SceneMemberComponentSaveData.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Types/SceneMemberComponentSaveData.h index 4c07c82aef..b30857d71f 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Types/SceneMemberComponentSaveData.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Types/SceneMemberComponentSaveData.h @@ -43,7 +43,7 @@ namespace GraphCanvas } // SceneMemberNotificationBus::Handler - void OnSceneSet(const AZ::EntityId& graphId) + void OnSceneSet(const AZ::EntityId& graphId) override { const AZ::EntityId* ownerId = SceneMemberNotificationBus::GetCurrentBusId(); diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/StateControllers/PrioritizedStateController.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/StateControllers/PrioritizedStateController.h index 1e31cc6c8a..583e755dfa 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/StateControllers/PrioritizedStateController.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/StateControllers/PrioritizedStateController.h @@ -45,7 +45,7 @@ namespace GraphCanvas m_valueSet.clear(); } - bool HasState() const + bool HasState() const override { return !m_valueSet.empty(); } @@ -85,7 +85,7 @@ namespace GraphCanvas return releasedValue; } - const T& GetCalculatedState() const + const T& GetCalculatedState() const override { auto valueIter = m_valueSet.begin(); return (*valueIter); diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/StateControllers/StackStateController.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/StateControllers/StackStateController.h index d77cd43861..948e286f77 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/StateControllers/StackStateController.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/StateControllers/StackStateController.h @@ -43,7 +43,7 @@ namespace GraphCanvas m_states.clear(); } - bool HasState() const + bool HasState() const override { return !m_states.empty(); } @@ -79,7 +79,7 @@ namespace GraphCanvas return releasedValue; } - const T& GetCalculatedState() const + const T& GetCalculatedState() const override { return m_states.back().second; } diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/Bookmarks/BookmarkDockWidget.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/Bookmarks/BookmarkDockWidget.h index 0326a5e5a0..74207315e1 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/Bookmarks/BookmarkDockWidget.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/Bookmarks/BookmarkDockWidget.h @@ -57,7 +57,7 @@ namespace GraphCanvas //// // GraphCanvas::SceneNotifications - void OnSelectionChanged(); + void OnSelectionChanged() override; //// public Q_SLOTS: diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/Bookmarks/BookmarkTableModel.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/Bookmarks/BookmarkTableModel.h index 2e14011d47..a1c5e15a3b 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/Bookmarks/BookmarkTableModel.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/Bookmarks/BookmarkTableModel.h @@ -79,7 +79,7 @@ namespace GraphCanvas // QAbstractTableModel int rowCount(const QModelIndex& parent = QModelIndex()) const override; - int columnCount(const QModelIndex& index = QModelIndex()) const; + int columnCount(const QModelIndex& index = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; @@ -125,7 +125,7 @@ namespace GraphCanvas BookmarkTableSortProxyModel(BookmarkTableSourceModel* sourceModel); ~BookmarkTableSortProxyModel() override = default; - bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const; + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; void SetFilter(const QString& filter); void ClearFilter(); diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ComboBox/ComboBoxItemModels.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ComboBox/ComboBoxItemModels.h index 4eeb98b23a..1f78a109a9 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ComboBox/ComboBoxItemModels.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/ComboBox/ComboBoxItemModels.h @@ -415,7 +415,7 @@ namespace GraphCanvas return index(nextRow, GetSortColumn()); } - void OnDropDownAboutToShow() + void OnDropDownAboutToShow() override { beginResetModel(); setSourceModel(m_modelInterface->GetDropDownItemModel()); @@ -424,7 +424,7 @@ namespace GraphCanvas invalidate(); } - void OnDropDownHidden() + void OnDropDownHidden() override { beginResetModel(); setSourceModel(nullptr); diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/AlignmentMenuActions/AlignmentContextMenuAction.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/AlignmentMenuActions/AlignmentContextMenuAction.h index 1334dff94a..fb51e76868 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/AlignmentMenuActions/AlignmentContextMenuAction.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/AlignmentMenuActions/AlignmentContextMenuAction.h @@ -22,10 +22,11 @@ namespace GraphCanvas { } + using ContextMenuAction::RefreshAction; void RefreshAction() override { const AZ::EntityId& graphId = GetGraphId(); - const AZ::EntityId& targetId = GetTargetId(); + const AZ::EntityId& targetId = GetTargetId(); bool canAlignSelection = false; SceneRequestBus::EventResult(canAlignSelection, graphId, &SceneRequests::HasMultipleSelection); diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/AlignmentMenuActions/AlignmentContextMenuActions.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/AlignmentMenuActions/AlignmentContextMenuActions.h index bc5d8da1d5..fe261a5ce8 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/AlignmentMenuActions/AlignmentContextMenuActions.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/AlignmentMenuActions/AlignmentContextMenuActions.h @@ -30,7 +30,8 @@ namespace GraphCanvas bool IsInSubMenu() const override; AZStd::string GetSubMenuPath() const override; - + + using AlignmentContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; private: diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/CommentMenuActions/CommentContextMenuAction.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/CommentMenuActions/CommentContextMenuAction.h index 2d2173204c..1ad2f41728 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/CommentMenuActions/CommentContextMenuAction.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/CommentMenuActions/CommentContextMenuAction.h @@ -22,6 +22,7 @@ namespace GraphCanvas { } + using ContextMenuAction::RefreshAction; void RefreshAction(const GraphId& graphId, const AZ::EntityId& targetId) override { AZ_UNUSED(targetId); diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/CommentMenuActions/CommentContextMenuActions.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/CommentMenuActions/CommentContextMenuActions.h index cb4012ec55..8d19e0f9ab 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/CommentMenuActions/CommentContextMenuActions.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/CommentMenuActions/CommentContextMenuActions.h @@ -25,6 +25,8 @@ namespace GraphCanvas using ContextMenuAction::RefreshAction; void RefreshAction() override; + + using CommentContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; } diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/ConstructMenuActions/BookmarkConstructMenuActions.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/ConstructMenuActions/BookmarkConstructMenuActions.h index e0f6320088..d1ac3b083f 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/ConstructMenuActions/BookmarkConstructMenuActions.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/ConstructMenuActions/BookmarkConstructMenuActions.h @@ -20,6 +20,7 @@ namespace GraphCanvas AddBookmarkMenuAction(QObject* parent); virtual ~AddBookmarkMenuAction() = default; + using ConstructContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; } diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/ConstructMenuActions/CommentConstructMenuActions.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/ConstructMenuActions/CommentConstructMenuActions.h index 1a22f40ea0..3c9c767619 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/ConstructMenuActions/CommentConstructMenuActions.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/ConstructMenuActions/CommentConstructMenuActions.h @@ -20,6 +20,7 @@ namespace GraphCanvas AddCommentMenuAction(QObject* parent); virtual ~AddCommentMenuAction() = default; + using ConstructContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; } diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/ConstructMenuActions/ConstructPresetMenuActions.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/ConstructMenuActions/ConstructPresetMenuActions.h index 9b081c9925..b6c393872f 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/ConstructMenuActions/ConstructPresetMenuActions.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/ConstructMenuActions/ConstructPresetMenuActions.h @@ -28,6 +28,7 @@ namespace GraphCanvas bool IsInSubMenu() const override; AZStd::string GetSubMenuPath() const override; + using ConstructContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; private: @@ -52,6 +53,7 @@ namespace GraphCanvas bool IsInSubMenu() const override; AZStd::string GetSubMenuPath() const override; + using ConstructContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; private: @@ -69,6 +71,7 @@ namespace GraphCanvas CreatePresetFromSelection(QObject* parent = nullptr); virtual ~CreatePresetFromSelection(); + using ContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; static ActionGroupId GetCreateConstructContextMenuActionGroupId() diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/DisableMenuActions/DisableMenuActions.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/DisableMenuActions/DisableMenuActions.h index 2252f081bb..b66a33bef8 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/DisableMenuActions/DisableMenuActions.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/DisableMenuActions/DisableMenuActions.h @@ -22,6 +22,7 @@ namespace GraphCanvas void SetEnableState(bool enableState); + using DisableContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; private: diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/EditMenuActions/EditContextMenuAction.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/EditMenuActions/EditContextMenuAction.h index f87374323f..03a16d015b 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/EditMenuActions/EditContextMenuAction.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/EditMenuActions/EditContextMenuAction.h @@ -22,6 +22,7 @@ namespace GraphCanvas { } + using ContextMenuAction::RefreshAction; void RefreshAction(const GraphId& graphId, const AZ::EntityId& targetId) override { AZ_UNUSED(targetId); diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/EditMenuActions/EditContextMenuActions.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/EditMenuActions/EditContextMenuActions.h index 817cd887a6..35195c0c57 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/EditMenuActions/EditContextMenuActions.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/EditMenuActions/EditContextMenuActions.h @@ -22,6 +22,7 @@ namespace GraphCanvas CutGraphSelectionMenuAction(QObject* parent); virtual ~CutGraphSelectionMenuAction() = default; + using EditContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -34,6 +35,7 @@ namespace GraphCanvas CopyGraphSelectionMenuAction(QObject* parent); virtual ~CopyGraphSelectionMenuAction() = default; + using EditContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -49,6 +51,8 @@ namespace GraphCanvas virtual ~PasteGraphSelectionMenuAction() = default; void RefreshAction() override; + + using EditContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -61,6 +65,7 @@ namespace GraphCanvas DeleteGraphSelectionMenuAction(QObject* parent); virtual ~DeleteGraphSelectionMenuAction() = default; + using EditContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -73,6 +78,7 @@ namespace GraphCanvas DuplicateGraphSelectionMenuAction(QObject* parent); virtual ~DuplicateGraphSelectionMenuAction() = default; + using EditContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; } diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/NodeGroupMenuActions/NodeGroupContextMenuAction.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/NodeGroupMenuActions/NodeGroupContextMenuAction.h index 28b36074b7..ade04ab8dc 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/NodeGroupMenuActions/NodeGroupContextMenuAction.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/NodeGroupMenuActions/NodeGroupContextMenuAction.h @@ -22,6 +22,7 @@ namespace GraphCanvas { } + using ContextMenuAction::RefreshAction; void RefreshAction(const GraphId& graphId, const AZ::EntityId& targetId) override { AZ_UNUSED(targetId); diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/NodeGroupMenuActions/NodeGroupContextMenuActions.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/NodeGroupMenuActions/NodeGroupContextMenuActions.h index 2a45e43aee..d0f4101235 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/NodeGroupMenuActions/NodeGroupContextMenuActions.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/NodeGroupMenuActions/NodeGroupContextMenuActions.h @@ -25,6 +25,8 @@ namespace GraphCanvas using ContextMenuAction::RefreshAction; void RefreshAction() override; + + using NodeGroupContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; private: @@ -43,6 +45,8 @@ namespace GraphCanvas using ContextMenuAction::RefreshAction; void RefreshAction() override; + + using NodeGroupContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -58,6 +62,8 @@ namespace GraphCanvas using ContextMenuAction::RefreshAction; void RefreshAction() override; + + using NodeGroupContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -73,6 +79,8 @@ namespace GraphCanvas using ContextMenuAction::RefreshAction; void RefreshAction() override; + + using NodeGroupContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -88,6 +96,8 @@ namespace GraphCanvas using ContextMenuAction::RefreshAction; void RefreshAction() override; + + using NodeGroupContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; } diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/NodeMenuActions/NodeContextMenuActions.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/NodeMenuActions/NodeContextMenuActions.h index 0e812d12e9..0253d31c79 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/NodeMenuActions/NodeContextMenuActions.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/NodeMenuActions/NodeContextMenuActions.h @@ -21,12 +21,14 @@ namespace GraphCanvas ManageUnusedSlotsMenuAction(QObject* parent, bool hideSlots); virtual ~ManageUnusedSlotsMenuAction() = default; - + + using NodeContextMenuAction::RefreshAction; void RefreshAction(const GraphId& grpahId, const AZ::EntityId& targetId) override; + + using NodeContextMenuAction::TriggerAction; SceneReaction TriggerAction(const GraphId& graphId, const AZ::Vector2&) override; private: - bool m_hideSlots = true; AZ::EntityId m_targetId; }; diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/SceneMenuActions/SceneContextMenuActions.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/SceneMenuActions/SceneContextMenuActions.h index 54b2efa046..59bfd847fe 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/SceneMenuActions/SceneContextMenuActions.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/SceneMenuActions/SceneContextMenuActions.h @@ -25,6 +25,7 @@ namespace GraphCanvas bool IsInSubMenu() const override; AZStd::string GetSubMenuPath() const override; + using SceneContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -40,6 +41,7 @@ namespace GraphCanvas bool IsInSubMenu() const override; AZStd::string GetSubMenuPath() const override; + using SceneContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; } diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/SlotMenuActions/SlotContextMenuActions.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/SlotMenuActions/SlotContextMenuActions.h index 29875882a5..f1875bc41d 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/SlotMenuActions/SlotContextMenuActions.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/EditorContextMenu/ContextMenuActions/SlotMenuActions/SlotContextMenuActions.h @@ -22,7 +22,10 @@ namespace GraphCanvas AddSlotMenuAction(QObject* parent); virtual ~AddSlotMenuAction() = default; + using SlotContextMenuAction::RefreshAction; void RefreshAction() override; + + using SlotContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -35,7 +38,10 @@ namespace GraphCanvas RemoveSlotMenuAction(QObject* parent); virtual ~RemoveSlotMenuAction() = default; + using SlotContextMenuAction::RefreshAction; void RefreshAction() override; + + using SlotContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -49,7 +55,10 @@ namespace GraphCanvas ClearConnectionsMenuAction(QObject* parent); virtual ~ClearConnectionsMenuAction() = default; + using SlotContextMenuAction::RefreshAction; void RefreshAction() override; + + using SlotContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -66,7 +75,10 @@ namespace GraphCanvas ResetToDefaultValueMenuAction(QObject* parent); virtual ~ResetToDefaultValueMenuAction() = default; + using SlotContextMenuAction::RefreshAction; void RefreshAction() override; + + using SlotContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -79,7 +91,10 @@ namespace GraphCanvas ToggleReferenceStateAction(QObject* parent); virtual ~ToggleReferenceStateAction() = default; + using SlotContextMenuAction::RefreshAction; void RefreshAction() override; + + using SlotContextMenuAction::TriggerAction; SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; @@ -92,7 +107,10 @@ namespace GraphCanvas PromoteToVariableAction(QObject* parent); virtual ~PromoteToVariableAction() = default; + using SlotContextMenuAction::RefreshAction; void RefreshAction() override; + + using SlotContextMenuAction::TriggerAction; GraphCanvas::ContextMenuAction::SceneReaction TriggerAction(const AZ::Vector2& scenePos) override; }; diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasEditor/GraphCanvasAssetEditorMainWindow.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasEditor/GraphCanvasAssetEditorMainWindow.h index 5aed5d77e7..139c540767 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasEditor/GraphCanvasAssetEditorMainWindow.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasEditor/GraphCanvasAssetEditorMainWindow.h @@ -55,6 +55,8 @@ namespace GraphCanvas struct AssetEditorWindowConfig { + virtual ~AssetEditorWindowConfig() = default; + /// General AssetEditor config parameters EditorId m_editorId; AZStd::string_view m_baseStyleSheet; diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h index 7dd8a01dc3..70b3e16166 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h @@ -129,7 +129,7 @@ namespace GraphCanvas ToastId ShowToastAtCursor(const ToastConfiguration& toastConfiguration) override; ToastId ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint, const ToastConfiguration& toastConfiguration) override; - bool IsShowing() const; + bool IsShowing() const override; //// // TickBus @@ -159,7 +159,7 @@ namespace GraphCanvas void wheelEvent(QWheelEvent* event) override; - void focusOutEvent(QFocusEvent* event); + void focusOutEvent(QFocusEvent* event) override; void resizeEvent(QResizeEvent* event) override; void moveEvent(QMoveEvent* event) override; diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/NodePalette/TreeItems/IconDecoratedNodePaletteTreeItem.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/NodePalette/TreeItems/IconDecoratedNodePaletteTreeItem.h index b214f00416..cfb4fe477a 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/NodePalette/TreeItems/IconDecoratedNodePaletteTreeItem.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/NodePalette/TreeItems/IconDecoratedNodePaletteTreeItem.h @@ -27,8 +27,8 @@ namespace GraphCanvas void AddIconColorPalette(const AZStd::string& colorPalette); - void OnStylesUnloaded(); - void OnStylesLoaded(); + void OnStylesUnloaded() override; + void OnStylesLoaded() override; protected: diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/NodePalette/TreeItems/NodePaletteTreeItem.h b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/NodePalette/TreeItems/NodePaletteTreeItem.h index fdeb20dadf..fcb4d79077 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/NodePalette/TreeItems/NodePaletteTreeItem.h +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/NodePalette/TreeItems/NodePaletteTreeItem.h @@ -95,7 +95,7 @@ namespace GraphCanvas const EditorId& GetEditorId() const; // Child Overrides - virtual bool LessThan(const GraphCanvasTreeItem* graphItem) const; + bool LessThan(const GraphCanvasTreeItem* graphItem) const override; virtual QVariant OnData(const QModelIndex& index, int role) const; virtual Qt::ItemFlags OnFlags() const; diff --git a/Gems/GraphCanvas/gem.json b/Gems/GraphCanvas/gem.json index ab1e7ac77a..760bd157df 100644 --- a/Gems/GraphCanvas/gem.json +++ b/Gems/GraphCanvas/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Graph Canvas Gem provides a C++ framework for creating custom graphical node based editors for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Framework", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Framework", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-canvas/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-canvas/", + "dependencies": [] } diff --git a/Gems/GraphModel/Code/Include/GraphModel/Integration/GraphController.h b/Gems/GraphModel/Code/Include/GraphModel/Integration/GraphController.h index a8051e16e4..3705b91b15 100644 --- a/Gems/GraphModel/Code/Include/GraphModel/Integration/GraphController.h +++ b/Gems/GraphModel/Code/Include/GraphModel/Integration/GraphController.h @@ -78,8 +78,8 @@ namespace GraphModelIntegration GraphModel::NodePtrList GetSelectedNodes() override; void SetSelected(GraphModel::NodePtrList nodes, bool selected) override; void ClearSelection() override; - void EnableNode(GraphModel::NodePtr node); - void DisableNode(GraphModel::NodePtr node); + void EnableNode(GraphModel::NodePtr node) override; + void DisableNode(GraphModel::NodePtr node) override; void CenterOnNodes(GraphModel::NodePtrList nodes) override; AZ::Vector2 GetMajorPitch() const override; @@ -166,7 +166,7 @@ namespace GraphModelIntegration void EnableNodes(const AZStd::unordered_set& nodeIds) override; void DisableNodes(const AZStd::unordered_set& nodeIds) override; - AZStd::string GetDataTypeString(const AZ::Uuid& typeId); + AZStd::string GetDataTypeString(const AZ::Uuid& typeId) override; //! This is where we find all of the graph metadata (like node positions, comments, etc) and store it in the node graph for serialization // CJS TODO: Use this instead of the above undo functions diff --git a/Gems/GraphModel/Code/Include/GraphModel/Model/Module/InputOutputNodes.h b/Gems/GraphModel/Code/Include/GraphModel/Model/Module/InputOutputNodes.h index 51e65705b9..2c3da656fb 100644 --- a/Gems/GraphModel/Code/Include/GraphModel/Model/Module/InputOutputNodes.h +++ b/Gems/GraphModel/Code/Include/GraphModel/Model/Module/InputOutputNodes.h @@ -67,6 +67,7 @@ namespace GraphModel //! \param dataType The type of data represented by this node GraphInputNode(GraphModel::GraphPtr graph, DataTypePtr dataType); + using BaseInputOutputNode::PostLoadSetup; void PostLoadSetup(GraphPtr graph, NodeId id) override; //! Returns the value of the DefaultValue slot, which indicates the default value for this input. This @@ -95,6 +96,7 @@ namespace GraphModel //! \param dataType The type of data represented by this node GraphOutputNode(GraphModel::GraphPtr graph, DataTypePtr dataType); + using BaseInputOutputNode::PostLoadSetup; void PostLoadSetup(GraphPtr graph, NodeId id) override; protected: diff --git a/Gems/GraphModel/Code/Include/GraphModel/Model/Module/ModuleNode.h b/Gems/GraphModel/Code/Include/GraphModel/Model/Module/ModuleNode.h index cce45e5159..f63fd6531f 100644 --- a/Gems/GraphModel/Code/Include/GraphModel/Model/Module/ModuleNode.h +++ b/Gems/GraphModel/Code/Include/GraphModel/Model/Module/ModuleNode.h @@ -36,6 +36,7 @@ namespace GraphModel const char* GetTitle() const override; + using Node::PostLoadSetup; void PostLoadSetup(GraphPtr ownerGraph, NodeId id) override; protected: diff --git a/Gems/GraphModel/Code/Tests/MockGraphCanvas.h b/Gems/GraphModel/Code/Tests/MockGraphCanvas.h index e66a47a79c..774e6aab9f 100644 --- a/Gems/GraphModel/Code/Tests/MockGraphCanvas.h +++ b/Gems/GraphModel/Code/Tests/MockGraphCanvas.h @@ -137,8 +137,8 @@ namespace MockGraphCanvasServices ~MockExtenderSlotComponent() = default; // Component overrides ... - void Activate(); - void Deactivate(); + void Activate() override; + void Deactivate() override; //// // ExtenderSlotComponent overrides ... @@ -177,7 +177,7 @@ namespace MockGraphCanvasServices void SetTooltip(const AZStd::string& tooltip) override; void SetTranslationKeyedTooltip(const GraphCanvas::TranslationKeyedString& tooltip) override; const AZStd::string GetTooltip() const override; - void SetShowInOutliner(bool showInOutliner); + void SetShowInOutliner(bool showInOutliner) override; bool ShowInOutliner() const override; void AddSlot(const AZ::EntityId& slotId) override; void RemoveSlot(const AZ::EntityId& slotId) override; diff --git a/Gems/GraphModel/Code/Tests/TestEnvironment.h b/Gems/GraphModel/Code/Tests/TestEnvironment.h index 908a4990e1..a9b2a548f4 100644 --- a/Gems/GraphModel/Code/Tests/TestEnvironment.h +++ b/Gems/GraphModel/Code/Tests/TestEnvironment.h @@ -87,7 +87,7 @@ namespace GraphModelIntegrationTest const char* GetTitle() const override; protected: - void RegisterSlots(); + void RegisterSlots() override; AZStd::shared_ptr m_graphContext = nullptr; }; @@ -108,7 +108,7 @@ namespace GraphModelIntegrationTest const char* GetTitle() const override; protected: - void RegisterSlots(); + void RegisterSlots() override; AZStd::shared_ptr m_graphContext = nullptr; }; @@ -129,7 +129,7 @@ namespace GraphModelIntegrationTest const char* GetTitle() const override; protected: - void RegisterSlots(); + void RegisterSlots() override; AZStd::shared_ptr m_graphContext = nullptr; }; diff --git a/Gems/GraphModel/gem.json b/Gems/GraphModel/gem.json index 62f1effb7c..256de75f6c 100644 --- a/Gems/GraphModel/gem.json +++ b/Gems/GraphModel/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Graph Model Gem provides a generic node graph data model framework for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Framework", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Framework", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-model/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-model/", + "dependencies": [ + "GraphCanvas" + ] } diff --git a/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp b/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp index 523d84f0f5..d33f37a2d7 100644 --- a/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp +++ b/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp @@ -49,7 +49,6 @@ namespace HttpRequestor { m_thread.join(); } - } void Manager::AddRequest(Parameters && httpRequestParameters) diff --git a/Gems/HttpRequestor/gem.json b/Gems/HttpRequestor/gem.json index e5eb8d6f44..eb1a112b0e 100644 --- a/Gems/HttpRequestor/gem.json +++ b/Gems/HttpRequestor/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The HTTP Requestor Gem provides functionality to make asynchronous HTTP/HTTPS requests and return data through a user-provided call back function.", - "canonical_tags": ["Gem"], - "user_tags": ["Network", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Network", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/http-requestor/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/http-requestor/", + "dependencies": [] } diff --git a/Gems/ImGui/Code/Include/ImGuiBus.h b/Gems/ImGui/Code/Include/ImGuiBus.h index 959577e97f..d481ec7843 100644 --- a/Gems/ImGui/Code/Include/ImGuiBus.h +++ b/Gems/ImGui/Code/Include/ImGuiBus.h @@ -70,6 +70,8 @@ namespace ImGui public: AZ_RTTI(IImGuiManager, "{F5A0F08B-F2DA-43B7-8CD2-C6FC71E1A712}"); + virtual ~IImGuiManager() = default; + static const char* GetUniqueName() { return "IImGuiManager"; } virtual DisplayState GetEditorWindowState() const = 0; diff --git a/Gems/ImGui/Code/Source/ImGuiManager.h b/Gems/ImGui/Code/Source/ImGuiManager.h index 52862205a5..c4fa5169f7 100644 --- a/Gems/ImGui/Code/Source/ImGuiManager.h +++ b/Gems/ImGui/Code/Source/ImGuiManager.h @@ -48,8 +48,8 @@ namespace ImGui void SetClientMenuBarState(DisplayState state) override { m_clientMenuBarState = state; } bool IsControllerSupportModeEnabled(ImGuiControllerModeFlags::FlagType controllerMode) const override; void EnableControllerSupportMode(ImGuiControllerModeFlags::FlagType controllerMode, bool enable) override; - void SetControllerMouseSensitivity(float sensitivity) { m_controllerMouseSensitivity = sensitivity; } - float GetControllerMouseSensitivity() const { return m_controllerMouseSensitivity; } + void SetControllerMouseSensitivity(float sensitivity) override { m_controllerMouseSensitivity = sensitivity; } + float GetControllerMouseSensitivity() const override { return m_controllerMouseSensitivity; } bool GetEnableDiscreteInputMode() const override { return m_enableDiscreteInputMode; } void SetEnableDiscreteInputMode(bool enabled) override { m_enableDiscreteInputMode = enabled; } ImGuiResolutionMode GetResolutionMode() const override { return m_resolutionMode; } diff --git a/Gems/ImGui/gem.json b/Gems/ImGui/gem.json index deaac9925a..c1d89d1728 100644 --- a/Gems/ImGui/gem.json +++ b/Gems/ImGui/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Immediate Mode GUI Gem provides the 3rdParty library IMGUI which can be used to create run time immediate mode overlays for debugging and profiling information in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Rendering", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Rendering", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/imgui/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/imgui/", + "dependencies": [ + "LmbrCentral" + ] } diff --git a/Gems/InAppPurchases/gem.json b/Gems/InAppPurchases/gem.json index ab43075896..1f1debd5fb 100644 --- a/Gems/InAppPurchases/gem.json +++ b/Gems/InAppPurchases/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The In-App Purchases Gem provides functionality for in app purchases for iOS and Android.", - "canonical_tags": ["Gem"], - "user_tags": ["SDK", "Network"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "SDK", + "Network" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/sdk/in-app-purchases/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/sdk/in-app-purchases/", + "dependencies": [] } diff --git a/Gems/LandscapeCanvas/Code/Source/Editor/MainWindow.cpp b/Gems/LandscapeCanvas/Code/Source/Editor/MainWindow.cpp index 836eec682e..4574b938f6 100644 --- a/Gems/LandscapeCanvas/Code/Source/Editor/MainWindow.cpp +++ b/Gems/LandscapeCanvas/Code/Source/Editor/MainWindow.cpp @@ -2512,6 +2512,26 @@ namespace LandscapeCanvasEditor { // See comment above in OnPrefabInstancePropagationBegin m_prefabPropagationInProgress = false; + + // After prefab propagation is complete, the entity tied to one of our open + // graphs might have been deleted (e.g. if a prefab was created from that entity). + // Any open graphs tied to an entity that no longer exists will need to be closed. + // We need to close them in a separate iterator because the CloseEditor API will + // end up modifying m_dockWidgetsByEntity. + AZStd::vector dockWidgetsToDelete; + for (auto [entityId, dockWidgetId] : m_dockWidgetsByEntity) + { + AZ::Entity* entity = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, entityId); + if (!entity) + { + dockWidgetsToDelete.push_back(dockWidgetId); + } + } + for (auto dockWidgetId : dockWidgetsToDelete) + { + CloseEditor(dockWidgetId); + } } void MainWindow::OnCryEditorEndCreate() diff --git a/Gems/LandscapeCanvas/Code/Source/Editor/Menus/SceneContextMenuActions.h b/Gems/LandscapeCanvas/Code/Source/Editor/Menus/SceneContextMenuActions.h index f51dab9bd9..27faad7099 100644 --- a/Gems/LandscapeCanvas/Code/Source/Editor/Menus/SceneContextMenuActions.h +++ b/Gems/LandscapeCanvas/Code/Source/Editor/Menus/SceneContextMenuActions.h @@ -22,7 +22,11 @@ namespace LandscapeCanvasEditor virtual ~FindSelectedNodesAction() = default; GraphCanvas::ActionGroupId GetActionGroupId() const override; + + using GraphCanvas::ContextMenuAction::RefreshAction; void RefreshAction(const GraphCanvas::GraphId& graphId, const AZ::EntityId& targetId) override; + + using GraphCanvas::ContextMenuAction::TriggerAction; GraphCanvas::ContextMenuAction::SceneReaction TriggerAction(const GraphCanvas::GraphId& graphId, const AZ::Vector2& scenePos) override; private: diff --git a/Gems/LandscapeCanvas/gem.json b/Gems/LandscapeCanvas/gem.json index 63a80ec57e..ce0c64b75d 100644 --- a/Gems/LandscapeCanvas/gem.json +++ b/Gems/LandscapeCanvas/gem.json @@ -5,9 +5,23 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Landscape Canvas Gem provides the Landscape Canvas editor, a node-based graph tool for authoring workflows to populate landscape with dynamic vegetation.", - "canonical_tags": ["Gem"], - "user_tags": ["Environment", "Design", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Environment", + "Design", + "Tools" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/landscape-canvas/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/landscape-canvas/", + "dependencies": [ + "GraphModel", + "GradientSignal", + "SurfaceData", + "Vegetation", + "LmbrCentral", + "GraphCanvas" + ] } diff --git a/Gems/LmbrCentral/Code/CMakeLists.txt b/Gems/LmbrCentral/Code/CMakeLists.txt index 00176ae2c2..b047a9f65e 100644 --- a/Gems/LmbrCentral/Code/CMakeLists.txt +++ b/Gems/LmbrCentral/Code/CMakeLists.txt @@ -114,6 +114,16 @@ endif() # Tests ################################################################################ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + ly_add_target( + NAME LmbrCentral.Mocks HEADERONLY + NAMESPACE Gem + FILES_CMAKE + lmbrcentral_mocks_files.cmake + INCLUDE_DIRECTORIES + INTERFACE + Mocks + ) + ly_add_target( NAME LmbrCentral.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} NAMESPACE Gem @@ -131,6 +141,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) Legacy::CryCommon AZ::AzFramework Gem::LmbrCentral.Static + Gem::LmbrCentral.Mocks ) ly_add_googletest( NAME Gem::LmbrCentral.Tests diff --git a/Gems/LmbrCentral/Code/Mocks/LmbrCentral/Shape/MockShapes.h b/Gems/LmbrCentral/Code/Mocks/LmbrCentral/Shape/MockShapes.h new file mode 100644 index 0000000000..20be74dd90 --- /dev/null +++ b/Gems/LmbrCentral/Code/Mocks/LmbrCentral/Shape/MockShapes.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include + +#include +#include +#include + +namespace UnitTest +{ + class MockBoxShapeComponentRequests + : public LmbrCentral::BoxShapeComponentRequestsBus::Handler + { + public: + MockBoxShapeComponentRequests(AZ::EntityId entityId) + { + LmbrCentral::BoxShapeComponentRequestsBus::Handler::BusConnect(entityId); + } + + ~MockBoxShapeComponentRequests() + { + LmbrCentral::BoxShapeComponentRequestsBus::Handler::BusDisconnect(); + } + + MOCK_METHOD0(GetBoxConfiguration, LmbrCentral::BoxShapeConfig()); + MOCK_METHOD0(GetBoxDimensions, AZ::Vector3()); + MOCK_METHOD1(SetBoxDimensions, void(const AZ::Vector3& newDimensions)); + }; + + class MockShapeComponentRequests + : public LmbrCentral::ShapeComponentRequestsBus::Handler + { + public: + MockShapeComponentRequests(AZ::EntityId entityId) + { + LmbrCentral::ShapeComponentRequestsBus::Handler::BusConnect(entityId); + } + + ~MockShapeComponentRequests() + { + LmbrCentral::ShapeComponentRequestsBus::Handler::BusDisconnect(); + } + + MOCK_METHOD0(GetShapeType, AZ::Crc32()); + MOCK_METHOD0(GetEncompassingAabb, AZ::Aabb()); + MOCK_METHOD2(GetTransformAndLocalBounds, void(AZ::Transform& transform, AZ::Aabb& bounds)); + MOCK_METHOD1(IsPointInside, bool(const AZ::Vector3& point)); + MOCK_METHOD1(DistanceSquaredFromPoint, float(const AZ::Vector3& point)); + MOCK_METHOD1(GenerateRandomPointInside, AZ::Vector3(AZ::RandomDistributionType randomDistribution)); + MOCK_METHOD3(IntersectRay, bool(const AZ::Vector3& src, const AZ::Vector3& dir, float& distance)); + }; +} + diff --git a/Gems/LmbrCentral/Code/Source/Ai/NavigationComponent.h b/Gems/LmbrCentral/Code/Source/Ai/NavigationComponent.h index 17a5183dfe..3c6ce745ca 100644 --- a/Gems/LmbrCentral/Code/Source/Ai/NavigationComponent.h +++ b/Gems/LmbrCentral/Code/Source/Ai/NavigationComponent.h @@ -168,9 +168,9 @@ namespace LmbrCentral { public: - bool IsPathIntersectingObstacles(const NavigationMeshID /*meshID*/, const Vec3& /*start*/, const Vec3& /*end*/, float /*radius*/) const { return false; } - bool IsPointInsideObstacles(const Vec3& /*position*/) const { return false; } - bool IsLineSegmentIntersectingObstaclesOrCloseToThem(const Lineseg& /*linesegToTest*/, float /*maxDistanceToConsiderClose*/) const { return false; } + bool IsPathIntersectingObstacles(const NavigationMeshID /*meshID*/, const Vec3& /*start*/, const Vec3& /*end*/, float /*radius*/) const override { return false; } + bool IsPointInsideObstacles(const Vec3& /*position*/) const override { return false; } + bool IsLineSegmentIntersectingObstaclesOrCloseToThem(const Lineseg& /*linesegToTest*/, float /*maxDistanceToConsiderClose*/) const override { return false; } }; NullPathObstacles m_pathObstacles; @@ -344,7 +344,7 @@ namespace LmbrCentral bool GetValidPositionNearby(const Vec3&, Vec3&) const override { return false; } bool GetTeleportPosition(Vec3&) const override { return false; } class IPathFollower* GetPathFollower() const override { return nullptr; } - bool IsPointValidForAgent(const Vec3&, AZ::u32) const { return true; }; + bool IsPointValidForAgent(const Vec3&, AZ::u32) const override { return true; }; //// ~IAIPathAgent }; } // namespace LmbrCentral diff --git a/Gems/LmbrCentral/Code/Source/Audio/AudioSystemComponent.cpp b/Gems/LmbrCentral/Code/Source/Audio/AudioSystemComponent.cpp index cd4cff09ae..0c3531902f 100644 --- a/Gems/LmbrCentral/Code/Source/Audio/AudioSystemComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Audio/AudioSystemComponent.cpp @@ -35,12 +35,12 @@ namespace LmbrCentral OnGameUnpaused ); - void OnGamePaused() + void OnGamePaused() override { Call(FN_OnGamePaused); } - void OnGameUnpaused() + void OnGameUnpaused() override { Call(FN_OnGameUnpaused); } diff --git a/Gems/LmbrCentral/Code/Source/Editor/EditorCommentComponent.cpp b/Gems/LmbrCentral/Code/Source/Editor/EditorCommentComponent.cpp index 7eb0db1832..97f8dc60a2 100644 --- a/Gems/LmbrCentral/Code/Source/Editor/EditorCommentComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Editor/EditorCommentComponent.cpp @@ -30,7 +30,7 @@ namespace LmbrCentral ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "Editor") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Comment.svg") - ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Comment.png") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Comment.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC("Level", 0x9aeacc13), AZ_CRC("Game", 0x232b318c), AZ_CRC("Layer", 0xe4db211a) })) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/comment/") diff --git a/Gems/LmbrCentral/Code/Source/Scripting/EditorRandomTimedSpawnerComponent.h b/Gems/LmbrCentral/Code/Source/Scripting/EditorRandomTimedSpawnerComponent.h index d55463abd8..cc8a1a3a9e 100644 --- a/Gems/LmbrCentral/Code/Source/Scripting/EditorRandomTimedSpawnerComponent.h +++ b/Gems/LmbrCentral/Code/Source/Scripting/EditorRandomTimedSpawnerComponent.h @@ -57,7 +57,7 @@ namespace LmbrCentral void SetSpawnDelayVariation(double spawnDelayVariation) override { m_config.m_spawnDelayVariation = spawnDelayVariation; } double GetSpawnDelayVariation() override { return m_config.m_spawnDelayVariation; } - void BuildGameEntity(AZ::Entity* gameEntity); + void BuildGameEntity(AZ::Entity* gameEntity) override; private: //Reflected members diff --git a/Gems/LmbrCentral/Code/Source/Scripting/SimpleStateComponent.cpp b/Gems/LmbrCentral/Code/Source/Scripting/SimpleStateComponent.cpp index ad60e86834..e278f18e8b 100644 --- a/Gems/LmbrCentral/Code/Source/Scripting/SimpleStateComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Scripting/SimpleStateComponent.cpp @@ -29,7 +29,7 @@ namespace LmbrCentral AZ_EBUS_BEHAVIOR_BINDER(BehaviorSimpleStateComponentNotificationBusHandler, "{F935125C-AE4E-48C1-BB60-24A0559BC4D2}", AZ::SystemAllocator, OnStateChanged); - void OnStateChanged(const char* oldState, const char* newState) + void OnStateChanged(const char* oldState, const char* newState) override { Call(FN_OnStateChanged, oldState, newState); } diff --git a/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp b/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp index 08eb7df669..65847a7fef 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp @@ -213,18 +213,18 @@ namespace UnitTest // AzToolsFramework::AssetSystem::AssetSystemRequestBus::Handler overrides const char* GetAbsoluteDevGameFolderPath() override { return ""; } const char* GetAbsoluteDevRootFolderPath() override { return ""; } - bool GetRelativeProductPathFromFullSourceOrProductPath([[maybe_unused]] const AZStd::string& fullPath, [[maybe_unused]] AZStd::string& relativeProductPath) { return true; } + bool GetRelativeProductPathFromFullSourceOrProductPath([[maybe_unused]] const AZStd::string& fullPath, [[maybe_unused]] AZStd::string& relativeProductPath) override { return true; } bool GenerateRelativeSourcePath( [[maybe_unused]] const AZStd::string& sourcePath, [[maybe_unused]] AZStd::string& relativePath, - [[maybe_unused]] AZStd::string& watchFolder) { return true; } - bool GetFullSourcePathFromRelativeProductPath([[maybe_unused]] const AZStd::string& relPath, [[maybe_unused]] AZStd::string& fullSourcePath) { return true; } - bool GetAssetInfoById([[maybe_unused]] const AZ::Data::AssetId& assetId, [[maybe_unused]] const AZ::Data::AssetType& assetType, [[maybe_unused]] const AZStd::string& platformName, [[maybe_unused]] AZ::Data::AssetInfo& assetInfo, [[maybe_unused]] AZStd::string& rootFilePath) { return true; } - bool GetSourceInfoBySourcePath([[maybe_unused]] const char* sourcePath, [[maybe_unused]] AZ::Data::AssetInfo& assetInfo, [[maybe_unused]] AZStd::string& watchFolder) { return true; } - bool GetSourceInfoBySourceUUID([[maybe_unused]] const AZ::Uuid& sourceUuid, [[maybe_unused]] AZ::Data::AssetInfo& assetInfo, [[maybe_unused]] AZStd::string& watchFolder) { return true; } - bool GetScanFolders([[maybe_unused]] AZStd::vector& scanFolders) { return true; } - bool IsAssetPlatformEnabled([[maybe_unused]] const char* platform) { return true; } - int GetPendingAssetsForPlatform([[maybe_unused]] const char* platform) { return 0; } - bool GetAssetsProducedBySourceUUID([[maybe_unused]] const AZ::Uuid& sourceUuid, [[maybe_unused]] AZStd::vector& productsAssetInfo) { return true; } + [[maybe_unused]] AZStd::string& watchFolder) override { return true; } + bool GetFullSourcePathFromRelativeProductPath([[maybe_unused]] const AZStd::string& relPath, [[maybe_unused]] AZStd::string& fullSourcePath) override { return true; } + bool GetAssetInfoById([[maybe_unused]] const AZ::Data::AssetId& assetId, [[maybe_unused]] const AZ::Data::AssetType& assetType, [[maybe_unused]] const AZStd::string& platformName, [[maybe_unused]] AZ::Data::AssetInfo& assetInfo, [[maybe_unused]] AZStd::string& rootFilePath) override { return true; } + bool GetSourceInfoBySourcePath([[maybe_unused]] const char* sourcePath, [[maybe_unused]] AZ::Data::AssetInfo& assetInfo, [[maybe_unused]] AZStd::string& watchFolder) override { return true; } + bool GetSourceInfoBySourceUUID([[maybe_unused]] const AZ::Uuid& sourceUuid, [[maybe_unused]] AZ::Data::AssetInfo& assetInfo, [[maybe_unused]] AZStd::string& watchFolder) override { return true; } + bool GetScanFolders([[maybe_unused]] AZStd::vector& scanFolders) override { return true; } + bool IsAssetPlatformEnabled([[maybe_unused]] const char* platform) override { return true; } + int GetPendingAssetsForPlatform([[maybe_unused]] const char* platform) override { return 0; } + bool GetAssetsProducedBySourceUUID([[maybe_unused]] const AZ::Uuid& sourceUuid, [[maybe_unused]] AZStd::vector& productsAssetInfo) override { return true; } bool GetAssetSafeFolders(AZStd::vector& assetSafeFolders) override { char resolvedBuffer[AZ_MAX_PATH_LEN] = { 0 }; diff --git a/Gems/LmbrCentral/Code/Tests/LmbrCentralTest.cpp b/Gems/LmbrCentral/Code/Tests/LmbrCentralTest.cpp index 40217ff9bc..f36978f22d 100644 --- a/Gems/LmbrCentral/Code/Tests/LmbrCentralTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/LmbrCentralTest.cpp @@ -8,4 +8,7 @@ #include +// Include any public mocks here to ensure they get compiled as a part of the test project. +#include + AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Gems/LmbrCentral/Code/lmbrcentral_mocks_files.cmake b/Gems/LmbrCentral/Code/lmbrcentral_mocks_files.cmake new file mode 100644 index 0000000000..c3a5cca3f8 --- /dev/null +++ b/Gems/LmbrCentral/Code/lmbrcentral_mocks_files.cmake @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + Mocks/LmbrCentral/Shape/MockShapes.h +) diff --git a/Gems/LmbrCentral/gem.json b/Gems/LmbrCentral/gem.json index b6442bfd06..36de4c4ed3 100644 --- a/Gems/LmbrCentral/gem.json +++ b/Gems/LmbrCentral/gem.json @@ -5,10 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The O3DE Core (LmbrCentral) Gem provides required code and assets for running Open 3D Engine Editor.", - "canonical_tags": ["Gem"], - "user_tags": ["Core", "Framework", "Assets"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Core", + "Framework", + "Assets" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/core/lmbr-central/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/core/lmbr-central/", + "dependencies": [] } - diff --git a/Gems/LocalUser/gem.json b/Gems/LocalUser/gem.json index dcf4d44abb..f86e6e1bf6 100644 --- a/Gems/LocalUser/gem.json +++ b/Gems/LocalUser/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Local User Gem provides functionality for mapping local user ids to local player slots and managing local user profiles.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/local-user/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/local-user/", + "dependencies": [] } diff --git a/Gems/LyShine/Code/Editor/Animation/AnimationContext.h b/Gems/LyShine/Code/Editor/Animation/AnimationContext.h index 9534902f99..cb45fb9653 100644 --- a/Gems/LyShine/Code/Editor/Animation/AnimationContext.h +++ b/Gems/LyShine/Code/Editor/Animation/AnimationContext.h @@ -167,12 +167,12 @@ public: void UpdateTimeRange(); private: - virtual void BeginUndoTransaction() override; - virtual void EndUndoTransaction() override; + void BeginUndoTransaction() override; + void EndUndoTransaction() override; - virtual void OnSequenceRemoved(CUiAnimViewSequence* pSequence) override; + void OnSequenceRemoved(CUiAnimViewSequence* pSequence) override; - virtual void OnEditorNotifyEvent(EEditorNotifyEvent event); + void OnEditorNotifyEvent(EEditorNotifyEvent event) override; void AnimateActiveSequence(); diff --git a/Gems/LyShine/Code/Editor/Animation/Controls/UiSplineCtrlEx.cpp b/Gems/LyShine/Code/Editor/Animation/Controls/UiSplineCtrlEx.cpp index 94a4c77593..60109fc49c 100644 --- a/Gems/LyShine/Code/Editor/Animation/Controls/UiSplineCtrlEx.cpp +++ b/Gems/LyShine/Code/Editor/Animation/Controls/UiSplineCtrlEx.cpp @@ -78,7 +78,7 @@ protected: m_splineEntries.resize(m_splineEntries.size() + 1); SplineEntry& entry = m_splineEntries.back(); ISplineSet* pSplineSet = (pCtrl ? pCtrl->m_pSplineSet : 0); - entry.id = (pSplineSet ? pSplineSet->GetIDFromSpline(pSpline) : 0); + entry.id = (pSplineSet ? pSplineSet->GetIDFromSpline(pSpline) : AZStd::string{}); entry.pSpline = pSpline; const int numKeys = pSpline->GetKeyCount(); diff --git a/Gems/LyShine/Code/Editor/Animation/Controls/UiSplineCtrlEx.h b/Gems/LyShine/Code/Editor/Animation/Controls/UiSplineCtrlEx.h index 1bbcf8eea7..05b86bd380 100644 --- a/Gems/LyShine/Code/Editor/Animation/Controls/UiSplineCtrlEx.h +++ b/Gems/LyShine/Code/Editor/Animation/Controls/UiSplineCtrlEx.h @@ -345,8 +345,8 @@ public: SplineWidget(QWidget* parent); virtual ~SplineWidget(); - void update() { QWidget::update(); } - void update(const QRect& rect) { QWidget::update(rect); } + void update() override { QWidget::update(); } + void update(const QRect& rect) override { QWidget::update(rect); } QPoint mapFromGlobal(const QPoint& point) const override { return QWidget::mapFromGlobal(point); } diff --git a/Gems/LyShine/Code/Editor/Animation/Controls/UiTimelineCtrl.h b/Gems/LyShine/Code/Editor/Animation/Controls/UiTimelineCtrl.h index 701d187ddc..ed0d8ce02d 100644 --- a/Gems/LyShine/Code/Editor/Animation/Controls/UiTimelineCtrl.h +++ b/Gems/LyShine/Code/Editor/Animation/Controls/UiTimelineCtrl.h @@ -54,7 +54,7 @@ public: void setGeometry(const QRect& r) override { QWidget::setGeometry(r); } void SetTimeRange(const Range& r) { m_timeRange = r; } - void SetTimeMarker(float fTime); + void SetTimeMarker(float fTime) override; float GetTimeMarker() const { return m_fTimeMarker; } void SetZoom(float fZoom); @@ -111,7 +111,7 @@ protected: void OnLButtonUp(const QPoint& point, Qt::KeyboardModifiers modifiers); void OnRButtonDown(const QPoint& point, Qt::KeyboardModifiers modifiers); void OnRButtonUp(const QPoint& point, Qt::KeyboardModifiers modifiers); - void keyPressEvent(QKeyEvent* event); + void keyPressEvent(QKeyEvent* event) override; // Drawing functions float ClientToTime(int x); diff --git a/Gems/LyShine/Code/Editor/Animation/UiAVTrackEventKeyUIControls.h b/Gems/LyShine/Code/Editor/Animation/UiAVTrackEventKeyUIControls.h index df3b7fa24f..ed47e6a160 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAVTrackEventKeyUIControls.h +++ b/Gems/LyShine/Code/Editor/Animation/UiAVTrackEventKeyUIControls.h @@ -17,13 +17,13 @@ public: CSmartVariableEnum mv_event; CSmartVariable mv_value; - virtual void OnCreateVars(); + void OnCreateVars() override; bool SupportTrackType(const CUiAnimParamType& paramType, EUiAnimCurveType trackType, EUiAnimValue valueType) const override; bool OnKeySelectionChange(CUiAnimViewKeyBundle& selectedKeys) override; void OnUIChange(IVariable* pVar, CUiAnimViewKeyBundle& keys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimViewCurveEditor.h b/Gems/LyShine/Code/Editor/Animation/UiAnimViewCurveEditor.h index edde246e1c..863e8c1cdd 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAnimViewCurveEditor.h +++ b/Gems/LyShine/Code/Editor/Animation/UiAnimViewCurveEditor.h @@ -49,8 +49,8 @@ public: void SetPlayCallback(const std::function& callback); // IUiAnimationContextListener - virtual void OnSequenceChanged(CUiAnimViewSequence* pNewSequence); - virtual void OnTimeChanged(float newTime); + void OnSequenceChanged(CUiAnimViewSequence* pNewSequence) override; + void OnTimeChanged(float newTime) override; protected: void showEvent(QShowEvent* event) override; @@ -112,8 +112,8 @@ public: float GetFPS() const { return m_widget->GetFPS(); } void SetTickDisplayMode(EUiAVTickMode mode) { m_widget->SetTickDisplayMode(mode); } - virtual void OnSequenceChanged(CUiAnimViewSequence* pNewSequence) { m_widget->OnSequenceChanged(pNewSequence); } - virtual void OnTimeChanged(float newTime) { m_widget->OnTimeChanged(newTime); } + void OnSequenceChanged(CUiAnimViewSequence* pNewSequence) override { m_widget->OnSequenceChanged(pNewSequence); } + void OnTimeChanged(float newTime) override { m_widget->OnTimeChanged(newTime); } virtual void OnKeysChanged(CUiAnimViewSequence* pSequence) override { m_widget->OnKeysChanged(pSequence); } virtual void OnKeySelectionChanged(CUiAnimViewSequence* pSequence) override { m_widget->OnKeySelectionChanged(pSequence); } diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimViewDialog.h b/Gems/LyShine/Code/Editor/Animation/UiAnimViewDialog.h index a7ecbd3499..4671612695 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAnimViewDialog.h +++ b/Gems/LyShine/Code/Editor/Animation/UiAnimViewDialog.h @@ -70,7 +70,7 @@ public: // UiEditorAnimationStateInterface UiEditorAnimationStateInterface::UiEditorAnimationEditState GetCurrentEditState() override; - void RestoreCurrentEditState(const UiEditorAnimationStateInterface::UiEditorAnimationEditState& animEditState); + void RestoreCurrentEditState(const UiEditorAnimationStateInterface::UiEditorAnimationEditState& animEditState) override; // ~UiEditorAnimationStateInterface // UiEditorAnimListenerInterface @@ -167,11 +167,11 @@ private: virtual void OnNodeSelectionChanged(CUiAnimViewSequence* pSequence) override; virtual void OnNodeRenamed(CUiAnimViewNode* pNode, const char* pOldName) override; - virtual void OnSequenceAdded(CUiAnimViewSequence* pSequence); - virtual void OnSequenceRemoved(CUiAnimViewSequence* pSequence); + void OnSequenceAdded(CUiAnimViewSequence* pSequence) override; + void OnSequenceRemoved(CUiAnimViewSequence* pSequence) override; - virtual void BeginUndoTransaction(); - virtual void EndUndoTransaction(); + void BeginUndoTransaction() override; + void EndUndoTransaction() override; void SaveSequenceTimingToXML(); // Instance diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimViewDopeSheetBase.h b/Gems/LyShine/Code/Editor/Animation/UiAnimViewDopeSheetBase.h index ff1984184f..504c39e630 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAnimViewDopeSheetBase.h +++ b/Gems/LyShine/Code/Editor/Animation/UiAnimViewDopeSheetBase.h @@ -87,7 +87,7 @@ public: void SetEditLock(bool bLock) { m_bEditLock = bLock; } // IUiAnimationContextListener - virtual void OnTimeChanged(float newTime); + void OnTimeChanged(float newTime) override; float TickSnap(float time) const; diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimViewNode.h b/Gems/LyShine/Code/Editor/Animation/UiAnimViewNode.h index 80946eb317..c133ec439a 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAnimViewNode.h +++ b/Gems/LyShine/Code/Editor/Animation/UiAnimViewNode.h @@ -115,6 +115,7 @@ class CUiAnimViewKeyBundle public: CUiAnimViewKeyBundle() : m_bAllOfSameType(true) {} + virtual ~CUiAnimViewKeyBundle() = default; virtual bool AreAllKeysOfSameType() const override { return m_bAllOfSameType; } diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimViewSequence.h b/Gems/LyShine/Code/Editor/Animation/UiAnimViewSequence.h index 3aa8d74563..494cebd573 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAnimViewSequence.h +++ b/Gems/LyShine/Code/Editor/Animation/UiAnimViewSequence.h @@ -234,17 +234,17 @@ private: // Called when an animation updates needs to be schedules void ForceAnimation(); - virtual void CopyKeysToClipboard(XmlNodeRef& xmlNode, const bool bOnlySelectedKeys, const bool bOnlyFromSelectedTracks) override; + void CopyKeysToClipboard(XmlNodeRef& xmlNode, const bool bOnlySelectedKeys, const bool bOnlyFromSelectedTracks) override; void UpdateLightAnimationRefs(const char* pOldName, const char* pNewName); std::deque GetMatchingTracks(CUiAnimViewAnimNode* pAnimNode, XmlNodeRef trackNode); void GetMatchedPasteLocationsRec(std::vector& locations, CUiAnimViewNode* pCurrentNode, XmlNodeRef clipboardNode); - virtual void BeginUndoTransaction(); - virtual void EndUndoTransaction(); - virtual void BeginRestoreTransaction(); - virtual void EndRestoreTransaction(); + void BeginUndoTransaction() override; + void EndUndoTransaction() override; + void BeginRestoreTransaction() override; + void EndRestoreTransaction() override; // Current time when animated float m_time; diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimViewSequenceManager.h b/Gems/LyShine/Code/Editor/Animation/UiAnimViewSequenceManager.h index f29f0e58e1..1fe9d96f85 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAnimViewSequenceManager.h +++ b/Gems/LyShine/Code/Editor/Animation/UiAnimViewSequenceManager.h @@ -35,7 +35,7 @@ public: CUiAnimViewSequenceManager(); ~CUiAnimViewSequenceManager(); - virtual void OnEditorNotifyEvent(EEditorNotifyEvent event); + void OnEditorNotifyEvent(EEditorNotifyEvent event) override; unsigned int GetCount() const { return static_cast(m_sequences.size()); } diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimViewSplineCtrl.h b/Gems/LyShine/Code/Editor/Animation/UiAnimViewSplineCtrl.h index 8dd0ec0c79..45b3816f29 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAnimViewSplineCtrl.h +++ b/Gems/LyShine/Code/Editor/Animation/UiAnimViewSplineCtrl.h @@ -26,7 +26,7 @@ public: CUiAnimViewSplineCtrl(QWidget* parent); virtual ~CUiAnimViewSplineCtrl(); - virtual void ClearSelection(); + void ClearSelection() override; void AddSpline(ISplineInterpolator* pSpline, CUiAnimViewTrack* pTrack, const QColor& color); void AddSpline(ISplineInterpolator * pSpline, CUiAnimViewTrack * pTrack, QColor anColorArray[4]); @@ -64,7 +64,7 @@ private: void AdjustTCB(float d_tension, float d_continuity, float d_bias); void MoveSelectedTangentHandleTo(const QPoint& point); - virtual ISplineCtrlUndo* CreateSplineCtrlUndoObject(std::vector& splineContainer); + ISplineCtrlUndo* CreateSplineCtrlUndoObject(std::vector& splineContainer) override; bool m_bKeysFreeze; bool m_bTangentsFreeze; diff --git a/Gems/LyShine/Code/Source/Animation/AnimNode.h b/Gems/LyShine/Code/Source/Animation/AnimNode.h index 6ac198a5c7..0076276204 100644 --- a/Gems/LyShine/Code/Source/Animation/AnimNode.h +++ b/Gems/LyShine/Code/Source/Animation/AnimNode.h @@ -64,10 +64,10 @@ public: // Return Animation Sequence that owns this node. IUiAnimSequence* GetSequence() const override { return m_pSequence; }; - void SetFlags(int flags); - int GetFlags() const; + void SetFlags(int flags) override; + int GetFlags() const override; - IUiAnimationSystem* GetUiAnimationSystem() const { return m_pSequence->GetUiAnimationSystem(); }; + IUiAnimationSystem* GetUiAnimationSystem() const override { return m_pSequence->GetUiAnimationSystem(); }; virtual void OnStart() {} void OnReset() override {} @@ -80,23 +80,23 @@ public: virtual Matrix34 GetReferenceMatrix() const; ////////////////////////////////////////////////////////////////////////// - bool IsParamValid(const CUiAnimParamType& paramType) const; + bool IsParamValid(const CUiAnimParamType& paramType) const override; AZStd::string GetParamName(const CUiAnimParamType& param) const override; - virtual EUiAnimValue GetParamValueType(const CUiAnimParamType& paramType) const; - virtual IUiAnimNode::ESupportedParamFlags GetParamFlags(const CUiAnimParamType& paramType) const; - virtual unsigned int GetParamCount() const { return 0; }; + EUiAnimValue GetParamValueType(const CUiAnimParamType& paramType) const override; + IUiAnimNode::ESupportedParamFlags GetParamFlags(const CUiAnimParamType& paramType) const override; + unsigned int GetParamCount() const override { return 0; }; - bool SetParamValue(float time, CUiAnimParamType param, float val); - bool SetParamValue(float time, CUiAnimParamType param, const Vec3& val); - bool SetParamValue(float time, CUiAnimParamType param, const Vec4& val); - bool GetParamValue(float time, CUiAnimParamType param, float& val); - bool GetParamValue(float time, CUiAnimParamType param, Vec3& val); - bool GetParamValue(float time, CUiAnimParamType param, Vec4& val); + bool SetParamValue(float time, CUiAnimParamType param, float val) override; + bool SetParamValue(float time, CUiAnimParamType param, const Vec3& val) override; + bool SetParamValue(float time, CUiAnimParamType param, const Vec4& val) override; + bool GetParamValue(float time, CUiAnimParamType param, float& val) override; + bool GetParamValue(float time, CUiAnimParamType param, Vec3& val) override; + bool GetParamValue(float time, CUiAnimParamType param, Vec4& val) override; void SetTarget([[maybe_unused]] IUiAnimNode* node) {}; IUiAnimNode* GetTarget() const { return 0; }; - void StillUpdate() {} + void StillUpdate() override {} void Animate(SUiAnimContext& ec) override; virtual void PrecacheStatic([[maybe_unused]] float startTime) {} @@ -109,7 +109,7 @@ public: IUiAnimNodeOwner* GetNodeOwner() override { return m_pOwner; }; // Called by sequence when needs to activate a node. - virtual void Activate(bool bActivate); + void Activate(bool bActivate) override; ////////////////////////////////////////////////////////////////////////// void SetParent(IUiAnimNode* pParent) override; @@ -132,7 +132,7 @@ public: IUiAnimTrack* GetTrackForAzField([[maybe_unused]] const UiAnimParamData& param) const override { return nullptr; } IUiAnimTrack* CreateTrackForAzField([[maybe_unused]] const UiAnimParamData& param) override { return nullptr; } - virtual void SetTrack(const CUiAnimParamType& paramType, IUiAnimTrack* track); + void SetTrack(const CUiAnimParamType& paramType, IUiAnimTrack* track) override; IUiAnimTrack* CreateTrack(const CUiAnimParamType& paramType) override; void SetTimeRange(Range timeRange) override; void AddTrack(IUiAnimTrack* track) override; @@ -147,7 +147,7 @@ public: void SetId(int id) { m_id = id; } const char* GetNameFast() const { return m_name.c_str(); } - virtual void Render(){} + void Render() override{} static void Reflect(AZ::SerializeContext* serializeContext); @@ -168,7 +168,7 @@ protected: // sets track animNode pointer to this node and sorts tracks void RegisterTrack(IUiAnimTrack* track); - virtual bool NeedToRender() const { return false; } + bool NeedToRender() const override { return false; } protected: int m_refCount; @@ -199,7 +199,7 @@ class CUiAnimNodeGroup public: CUiAnimNodeGroup(const int id) : CUiAnimNode(id, eUiAnimNodeType_Group) { SetFlags(GetFlags() | eUiAnimNodeFlags_CanChangeName); } - EUiAnimNodeType GetType() const { return eUiAnimNodeType_Group; } + EUiAnimNodeType GetType() const override { return eUiAnimNodeType_Group; } - virtual CUiAnimParamType GetParamType([[maybe_unused]] unsigned int nIndex) const { return eUiAnimParamType_Invalid; } + CUiAnimParamType GetParamType([[maybe_unused]] unsigned int nIndex) const override { return eUiAnimParamType_Invalid; } }; diff --git a/Gems/LyShine/Code/Source/Animation/AnimSequence.h b/Gems/LyShine/Code/Source/Animation/AnimSequence.h index e4026ce250..5441dc3860 100644 --- a/Gems/LyShine/Code/Source/Animation/AnimSequence.h +++ b/Gems/LyShine/Code/Source/Animation/AnimSequence.h @@ -34,95 +34,95 @@ public: // Animation system. IUiAnimationSystem* GetUiAnimationSystem() const override { return m_pUiAnimationSystem; }; - void SetName(const char* name); - const char* GetName() const; - uint32 GetId() const { return m_id; } + void SetName(const char* name) override; + const char* GetName() const override; + uint32 GetId() const override { return m_id; } float GetTime() const { return m_time; } - virtual void SetOwner(IUiAnimSequenceOwner* pOwner) { m_pOwner = pOwner; } - virtual IUiAnimSequenceOwner* GetOwner() const { return m_pOwner; } + void SetOwner(IUiAnimSequenceOwner* pOwner) override { m_pOwner = pOwner; } + IUiAnimSequenceOwner* GetOwner() const override { return m_pOwner; } - virtual void SetActiveDirector(IUiAnimNode* pDirectorNode); - virtual IUiAnimNode* GetActiveDirector() const; + void SetActiveDirector(IUiAnimNode* pDirectorNode) override; + IUiAnimNode* GetActiveDirector() const override; - virtual void SetFlags(int flags); - virtual int GetFlags() const; - virtual int GetCutSceneFlags(const bool localFlags = false) const; + void SetFlags(int flags) override; + int GetFlags() const override; + int GetCutSceneFlags(const bool localFlags = false) const override; - virtual void SetParentSequence(IUiAnimSequence* pParentSequence); - virtual const IUiAnimSequence* GetParentSequence() const; - virtual bool IsAncestorOf(const IUiAnimSequence* pSequence) const; + void SetParentSequence(IUiAnimSequence* pParentSequence) override; + const IUiAnimSequence* GetParentSequence() const override; + bool IsAncestorOf(const IUiAnimSequence* pSequence) const override; - void SetTimeRange(Range timeRange); - Range GetTimeRange() { return m_timeRange; }; + void SetTimeRange(Range timeRange) override; + Range GetTimeRange() override { return m_timeRange; }; - void AdjustKeysToTimeRange(const Range& timeRange); + void AdjustKeysToTimeRange(const Range& timeRange) override; //! Return number of animation nodes in sequence. - int GetNodeCount() const; + int GetNodeCount() const override; //! Get specified animation node. - IUiAnimNode* GetNode(int index) const; + IUiAnimNode* GetNode(int index) const override; - IUiAnimNode* FindNodeByName(const char* sNodeName, const IUiAnimNode* pParentDirector); + IUiAnimNode* FindNodeByName(const char* sNodeName, const IUiAnimNode* pParentDirector) override; IUiAnimNode* FindNodeById(int nNodeId); - virtual void ReorderNode(IUiAnimNode* node, IUiAnimNode* pPivotNode, bool next); + void ReorderNode(IUiAnimNode* node, IUiAnimNode* pPivotNode, bool next) override; - void Reset(bool bSeekToStart); - void ResetHard(); - void Pause(); - void Resume(); - bool IsPaused() const; + void Reset(bool bSeekToStart) override; + void ResetHard() override; + void Pause() override; + void Resume() override; + bool IsPaused() const override; virtual void OnStart(); virtual void OnStop(); void OnLoop() override; //! Add animation node to sequence. - bool AddNode(IUiAnimNode* node); - IUiAnimNode* CreateNode(EUiAnimNodeType nodeType); - IUiAnimNode* CreateNode(XmlNodeRef node); - void RemoveNode(IUiAnimNode* node); + bool AddNode(IUiAnimNode* node) override; + IUiAnimNode* CreateNode(EUiAnimNodeType nodeType) override; + IUiAnimNode* CreateNode(XmlNodeRef node) override; + void RemoveNode(IUiAnimNode* node) override; //! Add scene node to sequence. - void RemoveAll(); + void RemoveAll() override; - virtual void Activate(); - virtual bool IsActivated() const { return m_bActive; } - virtual void Deactivate(); + void Activate() override; + bool IsActivated() const override { return m_bActive; } + void Deactivate() override; - virtual void PrecacheData(float startTime); + void PrecacheData(float startTime) override; void PrecacheStatic(const float startTime); void PrecacheDynamic(float time); - void StillUpdate(); - void Animate(const SUiAnimContext& ec); - void Render(); + void StillUpdate() override; + void Animate(const SUiAnimContext& ec) override; + void Render() override; - void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks = true, uint32 overrideId = 0, bool bResetLightAnimSet = false); - void InitPostLoad(IUiAnimationSystem* pUiAnimationSystem, bool remapIds, LyShine::EntityIdMap* entityIdMap); + void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks = true, uint32 overrideId = 0, bool bResetLightAnimSet = false) override; + void InitPostLoad(IUiAnimationSystem* pUiAnimationSystem, bool remapIds, LyShine::EntityIdMap* entityIdMap) override; - void CopyNodes(XmlNodeRef& xmlNode, IUiAnimNode** pSelectedNodes, uint32 count); - void PasteNodes(const XmlNodeRef& xmlNode, IUiAnimNode* pParent); + void CopyNodes(XmlNodeRef& xmlNode, IUiAnimNode** pSelectedNodes, uint32 count) override; + void PasteNodes(const XmlNodeRef& xmlNode, IUiAnimNode* pParent) override; //! Add/remove track events in sequence - virtual bool AddTrackEvent(const char* szEvent); - virtual bool RemoveTrackEvent(const char* szEvent); - virtual bool RenameTrackEvent(const char* szEvent, const char* szNewEvent); - virtual bool MoveUpTrackEvent(const char* szEvent); - virtual bool MoveDownTrackEvent(const char* szEvent); - virtual void ClearTrackEvents(); + bool AddTrackEvent(const char* szEvent) override; + bool RemoveTrackEvent(const char* szEvent) override; + bool RenameTrackEvent(const char* szEvent, const char* szNewEvent) override; + bool MoveUpTrackEvent(const char* szEvent) override; + bool MoveDownTrackEvent(const char* szEvent) override; + void ClearTrackEvents() override; //! Get the track events in the sequence - virtual int GetTrackEventsCount() const; - virtual char const* GetTrackEvent(int iIndex) const; - virtual IUiAnimStringTable* GetTrackEventStringTable() { return m_pEventStrings.get(); } + int GetTrackEventsCount() const override; + char const* GetTrackEvent(int iIndex) const override; + IUiAnimStringTable* GetTrackEventStringTable() override { return m_pEventStrings.get(); } //! Call to trigger a track event - virtual void TriggerTrackEvent(const char* event, const char* param = NULL); + void TriggerTrackEvent(const char* event, const char* param = nullptr) override; //! Track event listener - virtual void AddTrackEventListener(IUiTrackEventListener* pListener); - virtual void RemoveTrackEventListener(IUiTrackEventListener* pListener); + void AddTrackEventListener(IUiTrackEventListener* pListener) override; + void RemoveTrackEventListener(IUiTrackEventListener* pListener) override; static void Reflect(AZ::SerializeContext* serializeContext); @@ -130,7 +130,7 @@ private: void ComputeTimeRange(); void CopyNodeChildren(XmlNodeRef& xmlNode, IUiAnimNode* pAnimNode); void NotifyTrackEvent(IUiTrackEventListener::ETrackEventReason reason, - const char* event, const char* param = NULL); + const char* event, const char* param = nullptr); // Create a new animation node. IUiAnimNode* CreateNodeInternal(EUiAnimNodeType nodeType, uint32 nNodeId = -1); diff --git a/Gems/LyShine/Code/Source/Animation/AnimSplineTrack.h b/Gems/LyShine/Code/Source/Animation/AnimSplineTrack.h index 0bc625155a..414b1cb3cb 100644 --- a/Gems/LyShine/Code/Source/Animation/AnimSplineTrack.h +++ b/Gems/LyShine/Code/Source/Animation/AnimSplineTrack.h @@ -45,23 +45,23 @@ public: void release() override; ////////////////////////////////////////////////////////////////////////// - virtual int GetSubTrackCount() const { return 0; }; - virtual IUiAnimTrack* GetSubTrack([[maybe_unused]] int nIndex) const { return 0; }; + int GetSubTrackCount() const override { return 0; }; + IUiAnimTrack* GetSubTrack([[maybe_unused]] int nIndex) const override { return 0; }; AZStd::string GetSubTrackName([[maybe_unused]] int nIndex) const override { return AZStd::string(); }; - virtual void SetSubTrackName([[maybe_unused]] int nIndex, [[maybe_unused]] const char* name) { assert(0); } + void SetSubTrackName([[maybe_unused]] int nIndex, [[maybe_unused]] const char* name) override { assert(0); } - virtual const CUiAnimParamType& GetParameterType() const { return m_nParamType; }; - virtual void SetParameterType(CUiAnimParamType type) { m_nParamType = type; }; + const CUiAnimParamType& GetParameterType() const override { return m_nParamType; }; + void SetParameterType(CUiAnimParamType type) override { m_nParamType = type; }; - virtual const UiAnimParamData& GetParamData() const { return m_componentParamData; } - virtual void SetParamData(const UiAnimParamData& param) { m_componentParamData = param; } + const UiAnimParamData& GetParamData() const override { return m_componentParamData; } + void SetParamData(const UiAnimParamData& param) override { m_componentParamData = param; } - virtual void GetKeyValueRange(float& fMin, float& fMax) const { fMin = m_fMinKeyValue; fMax = m_fMaxKeyValue; }; - virtual void SetKeyValueRange(float fMin, float fMax){ m_fMinKeyValue = fMin; m_fMaxKeyValue = fMax; }; + void GetKeyValueRange(float& fMin, float& fMax) const override { fMin = m_fMinKeyValue; fMax = m_fMaxKeyValue; }; + void SetKeyValueRange(float fMin, float fMax) override{ m_fMinKeyValue = fMin; m_fMaxKeyValue = fMax; }; - ISplineInterpolator* GetSpline() const { return m_spline.get(); }; + ISplineInterpolator* GetSpline() const override { return m_spline.get(); }; - virtual bool IsKeySelected(int key) const + bool IsKeySelected(int key) const override { if (GetSpline() && GetSpline()->IsKeySelectedAtAnyDimension(key)) { @@ -70,7 +70,7 @@ public: return false; } - virtual void SelectKey(int key, bool select) + void SelectKey(int key, bool select) override { if (GetSpline()) { @@ -78,22 +78,22 @@ public: } } - int GetNumKeys() const + int GetNumKeys() const override { return m_spline->num_keys(); } - void SetNumKeys(int numKeys) + void SetNumKeys(int numKeys) override { m_spline->resize(numKeys); } - bool HasKeys() const + bool HasKeys() const override { return GetNumKeys() != 0; } - void RemoveKey(int num) + void RemoveKey(int num) override { if (m_spline && m_spline->num_keys() > num) { @@ -105,7 +105,7 @@ public: } } - void GetKey(int index, IKey* key) const + void GetKey(int index, IKey* key) const override { assert(index >= 0 && index < GetNumKeys()); assert(key != 0); @@ -123,7 +123,7 @@ public: tcbkey->SetValue(k.value); } - void SetKey(int index, IKey* key) + void SetKey(int index, IKey* key) override { assert(index >= 0 && index < GetNumKeys()); assert(key != 0); @@ -140,76 +140,76 @@ public: Invalidate(); } - float GetKeyTime(int index) const + float GetKeyTime(int index) const override { assert(index >= 0 && index < GetNumKeys()); return m_spline->time(index); } - void SetKeyTime(int index, float time) + void SetKeyTime(int index, float time) override { assert(index >= 0 && index < GetNumKeys()); m_spline->SetKeyTime(index, time); Invalidate(); } - int GetKeyFlags(int index) + int GetKeyFlags(int index) override { assert(index >= 0 && index < GetNumKeys()); return m_spline->key(index).flags; } - void SetKeyFlags(int index, int flags) + void SetKeyFlags(int index, int flags) override { assert(index >= 0 && index < GetNumKeys()); m_spline->key(index).flags = flags; } - virtual EUiAnimCurveType GetCurveType() { assert(0); return eUiAnimCurveType_Unknown; } - virtual EUiAnimValue GetValueType() { assert(0); return eUiAnimValue_Unknown; } + EUiAnimCurveType GetCurveType() override { assert(0); return eUiAnimCurveType_Unknown; } + EUiAnimValue GetValueType() override { assert(0); return eUiAnimValue_Unknown; } - virtual void GetValue(float time, float& value) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec3& value) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec4& value) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Quat& value) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Vector2& value) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Vector3& value) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Vector4& value) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Color& value) { assert(0); } + void GetValue(float time, float& value) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec3& value) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec4& value) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Quat& value) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Vector2& value) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Vector3& value) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Vector4& value) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Color& value) override { assert(0); } - virtual void SetValue(float time, const float& value, bool bDefault = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec3& value, [[maybe_unused]] bool bDefault = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec4& value, [[maybe_unused]] bool bDefault = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Quat& value, [[maybe_unused]] bool bDefault = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Vector2& value, [[maybe_unused]] bool bDefault = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Vector3& value, [[maybe_unused]] bool bDefault = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Vector4& value, [[maybe_unused]] bool bDefault = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Color& value, [[maybe_unused]] bool bDefault = false) { assert(0); } + void SetValue(float time, const float& value, bool bDefault = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec3& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec4& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Quat& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Vector2& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Vector3& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Vector4& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Color& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } - virtual void OffsetKeyPosition([[maybe_unused]] const Vec3& value) { assert(0); }; + void OffsetKeyPosition([[maybe_unused]] const Vec3& value) override { assert(0); }; - bool Serialize(IUiAnimationSystem* uiAnimationSystem, XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks); - bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected, float fTimeOffset); + bool Serialize(IUiAnimationSystem* uiAnimationSystem, XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks) override; + bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected, float fTimeOffset) override; - void GetKeyInfo(int key, const char*& description, float& duration) + void GetKeyInfo(int key, const char*& description, float& duration) override { description = 0; duration = 0; } //! Sort keys in track (after time of keys was modified). - void SortKeys() + void SortKeys() override { m_spline->sort_keys(); }; //! Get track flags. - int GetFlags() { return m_flags; } + int GetFlags() override { return m_flags; } //! Check if track is masked by mask - virtual bool IsMasked([[maybe_unused]] const uint32 mask) const { return false; } + bool IsMasked([[maybe_unused]] const uint32 mask) const override { return false; } //! Set track flags. - void SetFlags(int flags) + void SetFlags(int flags) override { m_flags = flags; if (m_flags & eUiAnimTrackFlags_Loop) @@ -231,12 +231,12 @@ public: m_spline->flag_set(Spline::MODIFIED); }; - void SetTimeRange(const Range& timeRange) + void SetTimeRange(const Range& timeRange) override { m_spline->SetRange(timeRange.start, timeRange.end); } - int FindKey(float time) + int FindKey(float time) override { // Find key with given time. int num = m_spline->num_keys(); @@ -252,7 +252,7 @@ public: } //! Create key at given time, and return its index. - int CreateKey(float time) + int CreateKey(float time) override { ValueType value; @@ -272,12 +272,12 @@ public: return m_spline->InsertKey(time, tmp); } - int CloneKey(int srcKey) + int CloneKey(int srcKey) override { return CopyKey(this, srcKey); } - int CopyKey(IUiAnimTrack* pFromTrack, int nFromKey) + int CopyKey(IUiAnimTrack* pFromTrack, int nFromKey) override { ITcbKey key; pFromTrack->GetKey(nFromKey, &key); @@ -326,16 +326,16 @@ public: m_defaultValue = value; } - virtual ColorB GetCustomColor() const + ColorB GetCustomColor() const { return m_customColor; } - virtual void SetCustomColor(ColorB color) + void SetCustomColor(ColorB color) { m_customColor = color; m_bCustomColorSet = true; } - virtual bool HasCustomColor() const + bool HasCustomColor() const { return m_bCustomColorSet; } - virtual void ClearCustomColor() + void ClearCustomColor() { m_bCustomColorSet = false; } static void Reflect(AZ::SerializeContext* serializeContext) {} diff --git a/Gems/LyShine/Code/Source/Animation/AnimTrack.h b/Gems/LyShine/Code/Source/Animation/AnimTrack.h index 9645572e3e..1ab5c26ab0 100644 --- a/Gems/LyShine/Code/Source/Animation/AnimTrack.h +++ b/Gems/LyShine/Code/Source/Animation/AnimTrack.h @@ -27,19 +27,19 @@ public: TUiAnimTrack(); - virtual EUiAnimCurveType GetCurveType() { return eUiAnimCurveType_Unknown; }; - virtual EUiAnimValue GetValueType() { return eUiAnimValue_Unknown; } + EUiAnimCurveType GetCurveType() override { return eUiAnimCurveType_Unknown; }; + EUiAnimValue GetValueType() override { return eUiAnimValue_Unknown; } - virtual int GetSubTrackCount() const { return 0; }; - virtual IUiAnimTrack* GetSubTrack([[maybe_unused]] int nIndex) const { return 0; }; + int GetSubTrackCount() const override { return 0; }; + IUiAnimTrack* GetSubTrack([[maybe_unused]] int nIndex) const override { return 0; }; AZStd::string GetSubTrackName([[maybe_unused]] int nIndex) const override { return AZStd::string(); }; - virtual void SetSubTrackName([[maybe_unused]] int nIndex, [[maybe_unused]] const char* name) { assert(0); } + void SetSubTrackName([[maybe_unused]] int nIndex, [[maybe_unused]] const char* name) override { assert(0); } - virtual const CUiAnimParamType& GetParameterType() const { return m_nParamType; }; - virtual void SetParameterType(CUiAnimParamType type) { m_nParamType = type; }; + const CUiAnimParamType& GetParameterType() const override { return m_nParamType; }; + void SetParameterType(CUiAnimParamType type) override { m_nParamType = type; }; - virtual const UiAnimParamData& GetParamData() const { return m_componentParamData; } - virtual void SetParamData(const UiAnimParamData& param) { m_componentParamData = param; } + const UiAnimParamData& GetParamData() const override { return m_componentParamData; } + void SetParamData(const UiAnimParamData& param) override { m_componentParamData = param; } ////////////////////////////////////////////////////////////////////////// // for intrusive_ptr support @@ -47,7 +47,7 @@ public: void release() override; ////////////////////////////////////////////////////////////////////////// - virtual bool IsKeySelected(int key) const + bool IsKeySelected(int key) const override { AZ_Assert(key >= 0 && key < (int)m_keys.size(), "Key index is out of range"); if (m_keys[key].flags & AKEY_SELECTED) @@ -57,7 +57,7 @@ public: return false; } - virtual void SelectKey(int key, bool select) + void SelectKey(int key, bool select) override { AZ_Assert(key >= 0 && key < (int)m_keys.size(), "Key index is out of range"); if (select) @@ -71,100 +71,100 @@ public: } //! Return number of keys in track. - virtual int GetNumKeys() const { return static_cast(m_keys.size()); }; + int GetNumKeys() const override { return static_cast(m_keys.size()); }; //! Return true if keys exists in this track - virtual bool HasKeys() const { return !m_keys.empty(); } + bool HasKeys() const override { return !m_keys.empty(); } //! Set number of keys in track. //! If needed adds empty keys at end or remove keys from end. - virtual void SetNumKeys(int numKeys) { m_keys.resize(numKeys); }; + void SetNumKeys(int numKeys) override { m_keys.resize(numKeys); }; //! Remove specified key. - virtual void RemoveKey(int num); + void RemoveKey(int num) override; - int CreateKey(float time); - int CloneKey(int fromKey); - int CopyKey(IUiAnimTrack* pFromTrack, int nFromKey); + int CreateKey(float time) override; + int CloneKey(int fromKey) override; + int CopyKey(IUiAnimTrack* pFromTrack, int nFromKey) override; //! Get key at specified location. //! @param key Must be valid pointer to compatible key structure, to be filled with specified key location. - virtual void GetKey(int index, IKey* key) const; + void GetKey(int index, IKey* key) const override; //! Get time of specified key. //! @return key time. - virtual float GetKeyTime(int index) const; + float GetKeyTime(int index) const override; //! Find key at given time. //! @return Index of found key, or -1 if key with this time not found. - virtual int FindKey(float time); + int FindKey(float time) override; //! Get flags of specified key. //! @return key time. - virtual int GetKeyFlags(int index); + int GetKeyFlags(int index) override; //! Set key at specified location. //! @param key Must be valid pointer to compatible key structure. - virtual void SetKey(int index, IKey* key); + void SetKey(int index, IKey* key) override; //! Set time of specified key. - virtual void SetKeyTime(int index, float time); + void SetKeyTime(int index, float time) override; //! Set flags of specified key. - virtual void SetKeyFlags(int index, int flags); + void SetKeyFlags(int index, int flags) override; //! Sort keys in track (after time of keys was modified). - virtual void SortKeys(); + void SortKeys() override; //! Get track flags. - virtual int GetFlags() { return m_flags; } + int GetFlags() override { return m_flags; } //! Check if track is masked - virtual bool IsMasked([[maybe_unused]] const uint32 mask) const { return false; } + bool IsMasked([[maybe_unused]] const uint32 mask) const override { return false; } //! Set track flags. - virtual void SetFlags(int flags) { m_flags = flags; } + void SetFlags(int flags) override { m_flags = flags; } ////////////////////////////////////////////////////////////////////////// // Get track value at specified time. // Interpolates keys if needed. ////////////////////////////////////////////////////////////////////////// - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] float& value) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec3& value) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec4& value) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Quat& value) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Vector2& value) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Vector3& value) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Vector4& value) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Color& value) { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] float& value) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec3& value) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec4& value) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Quat& value) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Vector2& value) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Vector3& value) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Vector4& value) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] AZ::Color& value) override { assert(0); }; ////////////////////////////////////////////////////////////////////////// // Set track value at specified time. // Adds new keys if required. ////////////////////////////////////////////////////////////////////////// - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const float& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec3& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec4& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Quat& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Vector2& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Vector3& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Vector4& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Color& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const float& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec3& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec4& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Quat& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Vector2& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Vector3& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Vector4& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const AZ::Color& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; - virtual void OffsetKeyPosition([[maybe_unused]] const Vec3& value) { assert(0); }; + void OffsetKeyPosition([[maybe_unused]] const Vec3& value) override { assert(0); }; /** Assign active time range for this track. */ - virtual void SetTimeRange(const Range& timeRange) { m_timeRange = timeRange; }; + void SetTimeRange(const Range& timeRange) override { m_timeRange = timeRange; }; /** Serialize this animation track to XML. Do not override this method, prefer to override SerializeKey. */ - virtual bool Serialize(IUiAnimationSystem* uiAnimationSystem, XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks = true); + bool Serialize(IUiAnimationSystem* uiAnimationSystem, XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks = true) override; - virtual bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected = false, float fTimeOffset = 0); + bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected = false, float fTimeOffset = 0) override; /** Serialize single key of this track. @@ -181,21 +181,21 @@ public: int GetActiveKey(float time, KeyType* key); #ifdef UI_ANIMATION_SYSTEM_SUPPORT_EDITING - virtual ColorB GetCustomColor() const + ColorB GetCustomColor() const override { return m_customColor; } - virtual void SetCustomColor(ColorB color) + void SetCustomColor(ColorB color) override { m_customColor = color; m_bCustomColorSet = true; } - virtual bool HasCustomColor() const + bool HasCustomColor() const override { return m_bCustomColorSet; } - virtual void ClearCustomColor() + void ClearCustomColor() override { m_bCustomColorSet = false; } #endif - virtual void GetKeyValueRange(float& fMin, float& fMax) const { fMin = m_fMinKeyValue; fMax = m_fMaxKeyValue; }; - virtual void SetKeyValueRange(float fMin, float fMax){ m_fMinKeyValue = fMin; m_fMaxKeyValue = fMax; }; + void GetKeyValueRange(float& fMin, float& fMax) const override { fMin = m_fMinKeyValue; fMax = m_fMaxKeyValue; }; + void SetKeyValueRange(float fMin, float fMax) override{ m_fMinKeyValue = fMin; m_fMaxKeyValue = fMax; }; static void Reflect(AZ::SerializeContext* serializeContext) {} diff --git a/Gems/LyShine/Code/Source/Animation/AzEntityNode.h b/Gems/LyShine/Code/Source/Animation/AzEntityNode.h index 9771ac7be3..f3a0fd641f 100644 --- a/Gems/LyShine/Code/Source/Animation/AzEntityNode.h +++ b/Gems/LyShine/Code/Source/Animation/AzEntityNode.h @@ -37,24 +37,24 @@ public: void EnableEntityPhysics(bool bEnable); - virtual EUiAnimNodeType GetType() const { return eUiAnimNodeType_AzEntity; } + EUiAnimNodeType GetType() const override { return eUiAnimNodeType_AzEntity; } - virtual void AddTrack(IUiAnimTrack* track); + void AddTrack(IUiAnimTrack* track) override; ////////////////////////////////////////////////////////////////////////// // Overrides from CUiAnimNode ////////////////////////////////////////////////////////////////////////// // UiAnimNodeInterface - virtual AZ::EntityId GetAzEntityId() override { return m_entityId; }; - virtual void SetAzEntity(AZ::Entity* entity) override { m_entityId = entity->GetId(); } + AZ::EntityId GetAzEntityId() override { return m_entityId; }; + void SetAzEntity(AZ::Entity* entity) override { m_entityId = entity->GetId(); } // ~UiAnimNodeInterface - virtual void StillUpdate(); - virtual void Animate(SUiAnimContext& ec); + void StillUpdate() override; + void Animate(SUiAnimContext& ec) override; - virtual void CreateDefaultTracks(); + void CreateDefaultTracks() override; bool SetParamValueAz(float time, const UiAnimParamData& param, float value) override; bool SetParamValueAz(float time, const UiAnimParamData& param, bool value) override; @@ -67,30 +67,30 @@ public: bool GetParamValueAz(float time, const UiAnimParamData& param, float& value) override; - virtual void PrecacheStatic(float startTime) override; - virtual void PrecacheDynamic(float time) override; + void PrecacheStatic(float startTime) override; + void PrecacheDynamic(float time) override; Vec3 GetPos() { return m_pos; }; Quat GetRotate() { return m_rotate; }; Vec3 GetScale() { return m_scale; }; - virtual void Activate(bool bActivate); + void Activate(bool bActivate) override; IUiAnimTrack* GetTrackForAzField(const UiAnimParamData& param) const override; IUiAnimTrack* CreateTrackForAzField(const UiAnimParamData& param) override; ////////////////////////////////////////////////////////////////////////// - void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks); - virtual void InitPostLoad(IUiAnimSequence* pSequence, bool remapIds, LyShine::EntityIdMap* entityIdMap); - void OnReset(); - void OnResetHard(); - void OnStart(); - void OnPause(); - void OnStop(); + void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks) override; + void InitPostLoad(IUiAnimSequence* pSequence, bool remapIds, LyShine::EntityIdMap* entityIdMap) override; + void OnReset() override; + void OnResetHard() override; + void OnStart() override; + void OnPause() override; + void OnStop() override; ////////////////////////////////////////////////////////////////////////// - virtual unsigned int GetParamCount() const; - virtual CUiAnimParamType GetParamType(unsigned int nIndex) const; + unsigned int GetParamCount() const override; + CUiAnimParamType GetParamType(unsigned int nIndex) const override; AZStd::string GetParamName(const CUiAnimParamType& param) const override; AZStd::string GetParamNameForTrack(const CUiAnimParamType& param, const IUiAnimTrack* track) const override; @@ -100,7 +100,7 @@ public: static void Reflect(AZ::SerializeContext* serializeContext); protected: - virtual bool GetParamInfoFromType(const CUiAnimParamType& paramId, SParamInfo& info) const; + bool GetParamInfoFromType(const CUiAnimParamType& paramId, SParamInfo& info) const override; //! Given the class data definition and a track for a field within it, //! compute the offset for the field and set it in the track @@ -115,7 +115,7 @@ protected: void ReleaseSounds(); // functions involved in the process to parse and store lua animated properties - virtual void UpdateDynamicParams(); + void UpdateDynamicParams() override; virtual void UpdateDynamicParams_Editor(); virtual void UpdateDynamicParams_PureGame(); diff --git a/Gems/LyShine/Code/Source/Animation/CompoundSplineTrack.h b/Gems/LyShine/Code/Source/Animation/CompoundSplineTrack.h index 127b6593fb..83a1f82588 100644 --- a/Gems/LyShine/Code/Source/Animation/CompoundSplineTrack.h +++ b/Gems/LyShine/Code/Source/Animation/CompoundSplineTrack.h @@ -26,8 +26,8 @@ public: UiCompoundSplineTrack(int nDims, EUiAnimValue inValueType, CUiAnimParamType subTrackParamTypes[MAX_SUBTRACKS]); UiCompoundSplineTrack(); - void add_ref() { ++m_refCount; } - void release() + void add_ref() override { ++m_refCount; } + void release() override { if (--m_refCount <= 0) { @@ -35,107 +35,107 @@ public: } } - virtual int GetSubTrackCount() const { return m_nDimensions; }; - virtual IUiAnimTrack* GetSubTrack(int nIndex) const; + int GetSubTrackCount() const override { return m_nDimensions; }; + IUiAnimTrack* GetSubTrack(int nIndex) const override; AZStd::string GetSubTrackName(int nIndex) const override; - virtual void SetSubTrackName(int nIndex, const char* name); + void SetSubTrackName(int nIndex, const char* name) override; - virtual EUiAnimCurveType GetCurveType() { return eUiAnimCurveType_BezierFloat; }; - virtual EUiAnimValue GetValueType() { return m_valueType; }; + EUiAnimCurveType GetCurveType() override { return eUiAnimCurveType_BezierFloat; }; + EUiAnimValue GetValueType() override { return m_valueType; }; - virtual const CUiAnimParamType& GetParameterType() const { return m_nParamType; }; - virtual void SetParameterType(CUiAnimParamType type) { m_nParamType = type; } + const CUiAnimParamType& GetParameterType() const override { return m_nParamType; }; + void SetParameterType(CUiAnimParamType type) override { m_nParamType = type; } - virtual const UiAnimParamData& GetParamData() const { return m_componentParamData; } - virtual void SetParamData(const UiAnimParamData& param) { m_componentParamData = param; } + const UiAnimParamData& GetParamData() const override { return m_componentParamData; } + void SetParamData(const UiAnimParamData& param) override { m_componentParamData = param; } - virtual int GetNumKeys() const; - virtual void SetNumKeys([[maybe_unused]] int numKeys) { assert(0); }; - virtual bool HasKeys() const; - virtual void RemoveKey(int num); + int GetNumKeys() const override; + void SetNumKeys([[maybe_unused]] int numKeys) override { assert(0); }; + bool HasKeys() const override; + void RemoveKey(int num) override; - virtual void GetKeyInfo(int key, const char*& description, float& duration); - virtual int CreateKey([[maybe_unused]] float time) { assert(0); return 0; }; - virtual int CloneKey([[maybe_unused]] int fromKey) { assert(0); return 0; }; - virtual int CopyKey([[maybe_unused]] IUiAnimTrack* pFromTrack, [[maybe_unused]] int nFromKey) { assert(0); return 0; }; - virtual void GetKey([[maybe_unused]] int index, [[maybe_unused]] IKey* key) const { assert(0); }; - virtual float GetKeyTime(int index) const; - virtual int FindKey([[maybe_unused]] float time) { assert(0); return 0; }; - virtual int GetKeyFlags([[maybe_unused]] int index) { assert(0); return 0; }; - virtual void SetKey([[maybe_unused]] int index, [[maybe_unused]] IKey* key) { assert(0); }; - virtual void SetKeyTime(int index, float time); - virtual void SetKeyFlags([[maybe_unused]] int index, [[maybe_unused]] int flags) { assert(0); }; - virtual void SortKeys() { assert(0); }; + void GetKeyInfo(int key, const char*& description, float& duration) override; + int CreateKey([[maybe_unused]] float time) override { assert(0); return 0; }; + int CloneKey([[maybe_unused]] int fromKey) override { assert(0); return 0; }; + int CopyKey([[maybe_unused]] IUiAnimTrack* pFromTrack, [[maybe_unused]] int nFromKey) override { assert(0); return 0; }; + void GetKey([[maybe_unused]] int index, [[maybe_unused]] IKey* key) const override { assert(0); }; + float GetKeyTime(int index) const override; + int FindKey([[maybe_unused]] float time) override { assert(0); return 0; }; + int GetKeyFlags([[maybe_unused]] int index) override { assert(0); return 0; }; + void SetKey([[maybe_unused]] int index, [[maybe_unused]] IKey* key) override { assert(0); }; + void SetKeyTime(int index, float time) override; + void SetKeyFlags([[maybe_unused]] int index, [[maybe_unused]] int flags) override { assert(0); }; + void SortKeys() override { assert(0); }; - virtual bool IsKeySelected(int key) const; - virtual void SelectKey(int key, bool select); + bool IsKeySelected(int key) const override; + void SelectKey(int key, bool select) override; - virtual int GetFlags() { return m_flags; }; - virtual bool IsMasked([[maybe_unused]] const uint32 mask) const { return false; } - virtual void SetFlags(int flags) { m_flags = flags; }; + int GetFlags() override { return m_flags; }; + bool IsMasked([[maybe_unused]] const uint32 mask) const override { return false; } + void SetFlags(int flags) override { m_flags = flags; }; ////////////////////////////////////////////////////////////////////////// // Get track value at specified time. // Interpolates keys if needed. ////////////////////////////////////////////////////////////////////////// - virtual void GetValue(float time, float& value); - virtual void GetValue(float time, Vec3& value); - virtual void GetValue(float time, Vec4& value); - virtual void GetValue(float time, Quat& value); - virtual void GetValue(float time, AZ::Vector2& value); - virtual void GetValue(float time, AZ::Vector3& value); - virtual void GetValue(float time, AZ::Vector4& value); - virtual void GetValue(float time, AZ::Color& value); - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) { assert(0); }; + void GetValue(float time, float& value) override; + void GetValue(float time, Vec3& value) override; + void GetValue(float time, Vec4& value) override; + void GetValue(float time, Quat& value) override; + void GetValue(float time, AZ::Vector2& value) override; + void GetValue(float time, AZ::Vector3& value) override; + void GetValue(float time, AZ::Vector4& value) override; + void GetValue(float time, AZ::Color& value) override; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) override { assert(0); }; ////////////////////////////////////////////////////////////////////////// // Set track value at specified time. // Adds new keys if required. ////////////////////////////////////////////////////////////////////////// - virtual void SetValue(float time, const float& value, bool bDefault = false); - virtual void SetValue(float time, const Vec3& value, bool bDefault = false); - void SetValue(float time, const Vec4& value, bool bDefault = false); - virtual void SetValue(float time, const Quat& value, bool bDefault = false); - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; - virtual void SetValue(float time, const AZ::Vector2& value, bool bDefault = false); - virtual void SetValue(float time, const AZ::Vector3& value, bool bDefault = false); - virtual void SetValue(float time, const AZ::Vector4& value, bool bDefault = false); - virtual void SetValue(float time, const AZ::Color& value, bool bDefault = false); + void SetValue(float time, const float& value, bool bDefault = false) override; + void SetValue(float time, const Vec3& value, bool bDefault = false) override; + void SetValue(float time, const Vec4& value, bool bDefault = false) override; + void SetValue(float time, const Quat& value, bool bDefault = false) override; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; + void SetValue(float time, const AZ::Vector2& value, bool bDefault = false) override; + void SetValue(float time, const AZ::Vector3& value, bool bDefault = false) override; + void SetValue(float time, const AZ::Vector4& value, bool bDefault = false) override; + void SetValue(float time, const AZ::Color& value, bool bDefault = false) override; - virtual void OffsetKeyPosition(const Vec3& value); + void OffsetKeyPosition(const Vec3& value) override; - virtual void SetTimeRange(const Range& timeRange); + void SetTimeRange(const Range& timeRange) override; - virtual bool Serialize(IUiAnimationSystem* uiAnimationSystem, XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks = true); + bool Serialize(IUiAnimationSystem* uiAnimationSystem, XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks = true) override; - virtual bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected = false, float fTimeOffset = 0); + bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected = false, float fTimeOffset = 0) override; - virtual int NextKeyByTime(int key) const; + int NextKeyByTime(int key) const override; void SetSubTrackName(const int i, const AZStd::string& name) { assert (i < MAX_SUBTRACKS); m_subTrackNames[i] = name; } #ifdef UI_ANIMATION_SYSTEM_SUPPORT_EDITING - virtual ColorB GetCustomColor() const + ColorB GetCustomColor() const override { return m_customColor; } - virtual void SetCustomColor(ColorB color) + void SetCustomColor(ColorB color) override { m_customColor = color; m_bCustomColorSet = true; } - virtual bool HasCustomColor() const + bool HasCustomColor() const override { return m_bCustomColorSet; } - virtual void ClearCustomColor() + void ClearCustomColor() override { m_bCustomColorSet = false; } #endif - virtual void GetKeyValueRange(float& fMin, float& fMax) const + void GetKeyValueRange(float& fMin, float& fMax) const override { if (GetSubTrackCount() > 0) { m_subTracks[0]->GetKeyValueRange(fMin, fMax); } }; - virtual void SetKeyValueRange(float fMin, float fMax) + void SetKeyValueRange(float fMin, float fMax) override { for (int i = 0; i < m_nDimensions; ++i) { diff --git a/Gems/LyShine/Code/Source/Animation/TrackEventTrack.h b/Gems/LyShine/Code/Source/Animation/TrackEventTrack.h index 5896b19a2a..c7499c15d0 100644 --- a/Gems/LyShine/Code/Source/Animation/TrackEventTrack.h +++ b/Gems/LyShine/Code/Source/Animation/TrackEventTrack.h @@ -66,9 +66,9 @@ public: ////////////////////////////////////////////////////////////////////////// // Overrides of IAnimTrack. ////////////////////////////////////////////////////////////////////////// - void GetKeyInfo(int key, const char*& description, float& duration); - void SerializeKey(IEventKey& key, XmlNodeRef& keyNode, bool bLoading); - void SetKey(int index, IKey* key); + void GetKeyInfo(int key, const char*& description, float& duration) override; + void SerializeKey(IEventKey& key, XmlNodeRef& keyNode, bool bLoading) override; + void SetKey(int index, IKey* key) override; void InitPostLoad(IUiAnimSequence* sequence) override; static void Reflect(AZ::SerializeContext* serializeContext); diff --git a/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.h b/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.h index c1dbdcfcda..c270d9ca60 100644 --- a/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.h +++ b/Gems/LyShine/Code/Source/Animation/UiAnimationSystem.h @@ -44,94 +44,94 @@ public: ~UiAnimationSystem(); - void Release() { delete this; }; + void Release() override { delete this; }; - bool Load(const char* pszFile, const char* pszMission); + bool Load(const char* pszFile, const char* pszMission) override; - ISystem* GetSystem() { return m_pSystem; } + ISystem* GetSystem() override { return m_pSystem; } - IUiAnimTrack* CreateTrack(EUiAnimCurveType type); + IUiAnimTrack* CreateTrack(EUiAnimCurveType type) override; - IUiAnimSequence* CreateSequence(const char* sequence, bool bLoad = false, uint32 id = 0); + IUiAnimSequence* CreateSequence(const char* sequence, bool bLoad = false, uint32 id = 0) override; IUiAnimSequence* LoadSequence(const char* pszFilePath); - IUiAnimSequence* LoadSequence(XmlNodeRef& xmlNode, bool bLoadEmpty = true); + IUiAnimSequence* LoadSequence(XmlNodeRef& xmlNode, bool bLoadEmpty = true) override; - void AddSequence(IUiAnimSequence* pSequence); - void RemoveSequence(IUiAnimSequence* pSequence); - IUiAnimSequence* FindSequence(const char* sequence) const; - IUiAnimSequence* FindSequenceById(uint32 id) const; - IUiAnimSequence* GetSequence(int i) const; - int GetNumSequences() const; - IUiAnimSequence* GetPlayingSequence(int i) const; - int GetNumPlayingSequences() const; - bool IsCutScenePlaying() const; + void AddSequence(IUiAnimSequence* pSequence) override; + void RemoveSequence(IUiAnimSequence* pSequence) override; + IUiAnimSequence* FindSequence(const char* sequence) const override; + IUiAnimSequence* FindSequenceById(uint32 id) const override; + IUiAnimSequence* GetSequence(int i) const override; + int GetNumSequences() const override; + IUiAnimSequence* GetPlayingSequence(int i) const override; + int GetNumPlayingSequences() const override; + bool IsCutScenePlaying() const override; - uint32 GrabNextSequenceId() + uint32 GrabNextSequenceId() override { return m_nextSequenceId++; } - int OnSequenceRenamed(const char* before, const char* after); - int OnCameraRenamed(const char* before, const char* after); + int OnSequenceRenamed(const char* before, const char* after) override; + int OnCameraRenamed(const char* before, const char* after) override; - bool AddUiAnimationListener(IUiAnimSequence* pSequence, IUiAnimationListener* pListener); - bool RemoveUiAnimationListener(IUiAnimSequence* pSequence, IUiAnimationListener* pListener); + bool AddUiAnimationListener(IUiAnimSequence* pSequence, IUiAnimationListener* pListener) override; + bool RemoveUiAnimationListener(IUiAnimSequence* pSequence, IUiAnimationListener* pListener) override; - void RemoveAllSequences(); + void RemoveAllSequences() override; ////////////////////////////////////////////////////////////////////////// // Sequence playback. ////////////////////////////////////////////////////////////////////////// void PlaySequence(const char* sequence, IUiAnimSequence* parentSeq = NULL, bool bResetFX = true, - bool bTrackedSequence = false, float startTime = -FLT_MAX, float endTime = -FLT_MAX); + bool bTrackedSequence = false, float startTime = -FLT_MAX, float endTime = -FLT_MAX) override; void PlaySequence(IUiAnimSequence* seq, IUiAnimSequence* parentSeq = NULL, bool bResetFX = true, - bool bTrackedSequence = false, float startTime = -FLT_MAX, float endTime = -FLT_MAX); - void PlayOnLoadSequences(); + bool bTrackedSequence = false, float startTime = -FLT_MAX, float endTime = -FLT_MAX) override; + void PlayOnLoadSequences() override; - bool StopSequence(const char* sequence); - bool StopSequence(IUiAnimSequence* seq); - bool AbortSequence(IUiAnimSequence* seq, bool bLeaveTime = false); + bool StopSequence(const char* sequence) override; + bool StopSequence(IUiAnimSequence* seq) override; + bool AbortSequence(IUiAnimSequence* seq, bool bLeaveTime = false) override; - void StopAllSequences(); - void StopAllCutScenes(); + void StopAllSequences() override; + void StopAllCutScenes() override; void Pause(bool bPause); - void Reset(bool bPlayOnReset, bool bSeekToStart); - void StillUpdate(); - void PreUpdate(const float dt); - void PostUpdate(const float dt); - void Render(); + void Reset(bool bPlayOnReset, bool bSeekToStart) override; + void StillUpdate() override; + void PreUpdate(const float dt) override; + void PostUpdate(const float dt) override; + void Render() override; - bool IsPlaying(IUiAnimSequence* seq) const; + bool IsPlaying(IUiAnimSequence* seq) const override; - void Pause(); - void Resume(); + void Pause() override; + void Resume() override; - void SetRecording(bool recording) { m_bRecording = recording; }; - bool IsRecording() const { return m_bRecording; }; + void SetRecording(bool recording) override { m_bRecording = recording; }; + bool IsRecording() const override { return m_bRecording; }; - void SetCallback(IUiAnimationCallback* pCallback) { m_pCallback = pCallback; } - IUiAnimationCallback* GetCallback() { return m_pCallback; } + void SetCallback(IUiAnimationCallback* pCallback) override { m_pCallback = pCallback; } + IUiAnimationCallback* GetCallback() override { return m_pCallback; } void Callback(IUiAnimationCallback::ECallbackReason Reason, IUiAnimNode* pNode); - void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bRemoveOldNodes = false, bool bLoadEmpty = true); - void InitPostLoad(bool remapIds, LyShine::EntityIdMap* entityIdMap); + void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bRemoveOldNodes = false, bool bLoadEmpty = true) override; + void InitPostLoad(bool remapIds, LyShine::EntityIdMap* entityIdMap) override; - void SetSequenceStopBehavior(ESequenceStopBehavior behavior); - IUiAnimationSystem::ESequenceStopBehavior GetSequenceStopBehavior(); + void SetSequenceStopBehavior(ESequenceStopBehavior behavior) override; + IUiAnimationSystem::ESequenceStopBehavior GetSequenceStopBehavior() override; - float GetPlayingTime(IUiAnimSequence* pSeq); - bool SetPlayingTime(IUiAnimSequence* pSeq, float fTime); + float GetPlayingTime(IUiAnimSequence* pSeq) override; + bool SetPlayingTime(IUiAnimSequence* pSeq, float fTime) override; - float GetPlayingSpeed(IUiAnimSequence* pSeq); - bool SetPlayingSpeed(IUiAnimSequence* pSeq, float fTime); + float GetPlayingSpeed(IUiAnimSequence* pSeq) override; + bool SetPlayingSpeed(IUiAnimSequence* pSeq, float fTime) override; - bool GetStartEndTime(IUiAnimSequence* pSeq, float& fStartTime, float& fEndTime); - bool SetStartEndTime(IUiAnimSequence* pSeq, const float fStartTime, const float fEndTime); + bool GetStartEndTime(IUiAnimSequence* pSeq, float& fStartTime, float& fEndTime) override; + bool SetStartEndTime(IUiAnimSequence* pSeq, const float fStartTime, const float fEndTime) override; - void GoToFrame(const char* seqName, float targetFrame); + void GoToFrame(const char* seqName, float targetFrame) override; void SerializeNodeType(EUiAnimNodeType& animNodeType, XmlNodeRef& xmlNode, bool bLoading, const uint version, int flags); - virtual void SerializeParamType(CUiAnimParamType& animParamType, XmlNodeRef& xmlNode, bool bLoading, const uint version); - virtual void SerializeParamData(UiAnimParamData& animParamData, XmlNodeRef& xmlNode, bool bLoading); + void SerializeParamType(CUiAnimParamType& animParamType, XmlNodeRef& xmlNode, bool bLoading, const uint version) override; + void SerializeParamData(UiAnimParamData& animParamData, XmlNodeRef& xmlNode, bool bLoading) override; static const char* GetParamTypeName(const CUiAnimParamType& animParamType); @@ -156,8 +156,8 @@ private: void UpdateInternal(const float dt, const bool bPreUpdate); #ifdef UI_ANIMATION_SYSTEM_SUPPORT_EDITING - virtual EUiAnimNodeType GetNodeTypeFromString(const char* pString) const; - virtual CUiAnimParamType GetParamTypeFromString(const char* pString) const; + EUiAnimNodeType GetNodeTypeFromString(const char* pString) const override; + CUiAnimParamType GetParamTypeFromString(const char* pString) const override; #endif ISystem* m_pSystem; diff --git a/Gems/LyShine/Code/Source/LyShineSystemComponent.h b/Gems/LyShine/Code/Source/LyShineSystemComponent.h index 3d5e81f7c5..5b4086007b 100644 --- a/Gems/LyShine/Code/Source/LyShineSystemComponent.h +++ b/Gems/LyShine/Code/Source/LyShineSystemComponent.h @@ -65,7 +65,7 @@ namespace LyShine // UiSystemBus interface implementation void RegisterComponentTypeForMenuOrdering(const AZ::Uuid& typeUuid) override; const AZStd::vector* GetComponentTypesForMenuOrdering() override; - const AZStd::list* GetLyShineComponentDescriptors(); + const AZStd::list* GetLyShineComponentDescriptors() override; //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// @@ -89,7 +89,7 @@ namespace LyShine // CrySystemEventBus /////////////////////////////////////////////////////// void OnCrySystemInitialized(ISystem& system, const SSystemInitParams&) override; - virtual void OnCrySystemShutdown(ISystem&) override; + void OnCrySystemShutdown(ISystem&) override; //////////////////////////////////////////////////////////////////////////// void BroadcastCursorImagePathname(); diff --git a/Gems/LyShine/Code/Source/Sprite.h b/Gems/LyShine/Code/Source/Sprite.h index 8a645b78b1..0b2c790cf6 100644 --- a/Gems/LyShine/Code/Source/Sprite.h +++ b/Gems/LyShine/Code/Source/Sprite.h @@ -44,7 +44,7 @@ public: // member functions AZ::Vector2 GetSize() override; AZ::Vector2 GetCellSize(int cellIndex) override; const SpriteSheetCellContainer& GetSpriteSheetCells() const override; - virtual void SetSpriteSheetCells(const SpriteSheetCellContainer& cells); + void SetSpriteSheetCells(const SpriteSheetCellContainer& cells) override; void ClearSpriteSheetCells() override; void AddSpriteSheetCell(const SpriteSheetCell& spriteSheetCell) override; AZ::Vector2 GetCellUvSize(int cellIndex) const override; @@ -96,7 +96,7 @@ protected: // member functions bool CellIndexWithinRange(int cellIndex) const; private: // types - typedef AZStd::unordered_map, stl::equality_string_caseless > CSpriteHashMap; + using CSpriteHashMap = AZStd::unordered_map, stl::equality_string_caseless >; private: // member functions bool LoadFromXmlFile(); diff --git a/Gems/LyShine/Code/Source/UiLayoutFitterComponent.h b/Gems/LyShine/Code/Source/UiLayoutFitterComponent.h index d8ca9462bc..c5ceef4a74 100644 --- a/Gems/LyShine/Code/Source/UiLayoutFitterComponent.h +++ b/Gems/LyShine/Code/Source/UiLayoutFitterComponent.h @@ -72,7 +72,7 @@ protected: // member functions // ~AZ::Component // UiLayoutControllerInterface - unsigned int GetPriority() const; + unsigned int GetPriority() const override; // ~UiLayoutControllerInterface AZ_DISABLE_COPY_MOVE(UiLayoutFitterComponent); diff --git a/Gems/LyShine/Code/Source/UiScrollBarComponent.h b/Gems/LyShine/Code/Source/UiScrollBarComponent.h index 78bfa708c7..ba7abdf5e5 100644 --- a/Gems/LyShine/Code/Source/UiScrollBarComponent.h +++ b/Gems/LyShine/Code/Source/UiScrollBarComponent.h @@ -53,7 +53,7 @@ public: // member functions // UiScrollerInterface Orientation GetOrientation() override; - void SetOrientation(Orientation orientation); + void SetOrientation(Orientation orientation) override; AZ::EntityId GetScrollableEntity() override; void SetScrollableEntity(AZ::EntityId entityId) override; float GetValue() override; diff --git a/Gems/LyShine/Code/Source/UiTooltipDisplayComponent.h b/Gems/LyShine/Code/Source/UiTooltipDisplayComponent.h index 7aa0c0f618..d1fd53ec28 100644 --- a/Gems/LyShine/Code/Source/UiTooltipDisplayComponent.h +++ b/Gems/LyShine/Code/Source/UiTooltipDisplayComponent.h @@ -70,7 +70,7 @@ public: // member functions // ~UiInitializationInterface //! IUiAnimationListener - void OnUiAnimationEvent(EUiAnimationEvent uiAnimationEvent, IUiAnimSequence* pAnimSequence); + void OnUiAnimationEvent(EUiAnimationEvent uiAnimationEvent, IUiAnimSequence* pAnimSequence) override; // ~IUiAnimationListener State GetState(); diff --git a/Gems/LyShine/gem.json b/Gems/LyShine/gem.json index e0fdb860e5..e2da8afa4d 100644 --- a/Gems/LyShine/gem.json +++ b/Gems/LyShine/gem.json @@ -5,9 +5,24 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The LyShine Gem provides the runtime UI system and creation tools for Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["UI", "Tools", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "UI", + "Tools", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine/", + "dependencies": [ + "LmbrCentral", + "Atom_RPI", + "Atom", + "Atom_Bootstrap", + "AtomFont", + "TextureAtlas", + "AtomToolsFramework" + ] } diff --git a/Gems/LyShineExamples/gem.json b/Gems/LyShineExamples/gem.json index a411bfb363..122273f6d9 100644 --- a/Gems/LyShineExamples/gem.json +++ b/Gems/LyShineExamples/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The LyShine Examples Gem provides example code and assets for LyShine, the runtime UI system and editor for Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["UI", "Sample", "Assets"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "UI", + "Sample", + "Assets" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine-examples/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine-examples/", + "dependencies": [ + "LmbrCentral", + "LyShine" + ] } diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimAZEntityNode.h b/Gems/Maestro/Code/Source/Cinematics/AnimAZEntityNode.h index f5af0a7ab8..68399aa53e 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimAZEntityNode.h +++ b/Gems/Maestro/Code/Source/Cinematics/AnimAZEntityNode.h @@ -63,7 +63,7 @@ public: Vec3 GetScale() override; ////////////////////////////////////////////////////////////////////////// - void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks); + void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks) override; // this is an unfortunate hold-over from legacy entities - used when a SceneNode overrides the camera animation so // we must disable the transform and camera components from updating animation on this entity because the SceneNode diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimComponentNode.h b/Gems/Maestro/Code/Source/Cinematics/AnimComponentNode.h index 43f49e8b80..6f4fe4cdd6 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimComponentNode.h +++ b/Gems/Maestro/Code/Source/Cinematics/AnimComponentNode.h @@ -86,7 +86,7 @@ public: // EditorSequenceAgentComponentNotificationBus::Handler Interface void OnSequenceAgentConnected() override; - void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks); + void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks) override; const AZ::Uuid& GetComponentTypeId() const { return m_componentTypeId; } diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimNode.h b/Gems/Maestro/Code/Source/Cinematics/AnimNode.h index 7c56d8f641..d0f22599e8 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimNode.h +++ b/Gems/Maestro/Code/Source/Cinematics/AnimNode.h @@ -46,7 +46,7 @@ public: ////////////////////////////////////////////////////////////////////////// void SetName(const char* name) override { m_name = name; }; - const char* GetName() { return m_name.c_str(); }; + const char* GetName() override { return m_name.c_str(); }; void SetSequence(IAnimSequence* sequence) override { m_pSequence = sequence; } // Return Animation Sequence that owns this node. @@ -60,7 +60,7 @@ public: int GetFlags() const override; bool AreFlagsSetOnNodeOrAnyParent(EAnimNodeFlags flagsToCheck) const override; - IMovieSystem* GetMovieSystem() const { return gEnv->pMovieSystem; }; + IMovieSystem* GetMovieSystem() const override { return gEnv->pMovieSystem; }; virtual void OnStart() {} void OnReset() override {} @@ -85,23 +85,23 @@ public: virtual Matrix34 GetReferenceMatrix() const; ////////////////////////////////////////////////////////////////////////// - bool IsParamValid(const CAnimParamType& paramType) const; + bool IsParamValid(const CAnimParamType& paramType) const override; AZStd::string GetParamName(const CAnimParamType& param) const override; - virtual AnimValueType GetParamValueType(const CAnimParamType& paramType) const; - virtual IAnimNode::ESupportedParamFlags GetParamFlags(const CAnimParamType& paramType) const; - virtual unsigned int GetParamCount() const { return 0; }; + AnimValueType GetParamValueType(const CAnimParamType& paramType) const override; + IAnimNode::ESupportedParamFlags GetParamFlags(const CAnimParamType& paramType) const override; + unsigned int GetParamCount() const override { return 0; }; - bool SetParamValue(float time, CAnimParamType param, float val); - bool SetParamValue(float time, CAnimParamType param, const Vec3& val); - bool SetParamValue(float time, CAnimParamType param, const Vec4& val); - bool GetParamValue(float time, CAnimParamType param, float& val); - bool GetParamValue(float time, CAnimParamType param, Vec3& val); - bool GetParamValue(float time, CAnimParamType param, Vec4& val); + bool SetParamValue(float time, CAnimParamType param, float val) override; + bool SetParamValue(float time, CAnimParamType param, const Vec3& val) override; + bool SetParamValue(float time, CAnimParamType param, const Vec4& val) override; + bool GetParamValue(float time, CAnimParamType param, float& val) override; + bool GetParamValue(float time, CAnimParamType param, Vec3& val) override; + bool GetParamValue(float time, CAnimParamType param, Vec4& val) override; void SetTarget([[maybe_unused]] IAnimNode* node) {}; IAnimNode* GetTarget() const { return 0; }; - void StillUpdate() {} + void StillUpdate() override {} void Animate(SAnimContext& ec) override; virtual void PrecacheStatic([[maybe_unused]] float startTime) {} @@ -114,7 +114,7 @@ public: IAnimNodeOwner* GetNodeOwner() override { return m_pOwner; }; // Called by sequence when needs to activate a node. - virtual void Activate(bool bActivate); + void Activate(bool bActivate) override; ////////////////////////////////////////////////////////////////////////// void SetParent(IAnimNode* parent) override; @@ -149,7 +149,7 @@ public: void SetId(int id) { m_id = id; } const char* GetNameFast() const { return m_name.c_str(); } - virtual void Render(){} + void Render() override{} void UpdateDynamicParams() final; @@ -177,7 +177,7 @@ protected: CMovieSystem* GetCMovieSystem() const { return (CMovieSystem*)gEnv->pMovieSystem; } - virtual bool NeedToRender() const { return false; } + bool NeedToRender() const override { return false; } // nodes which support sounds should override this to reset their start/stop sound states virtual void ResetSounds() {} diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.h b/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.h index 6ce44607aa..25c4c89b94 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.h +++ b/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.h @@ -37,32 +37,32 @@ public: //----------------------------------------------------------------------------- //! - virtual void SerializeAnims(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks); + void SerializeAnims(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks) override; //----------------------------------------------------------------------------- //! - virtual unsigned int GetParamCount() const; - virtual CAnimParamType GetParamType(unsigned int nIndex) const; + unsigned int GetParamCount() const override; + CAnimParamType GetParamType(unsigned int nIndex) const override; //----------------------------------------------------------------------------- //! //----------------------------------------------------------------------------- //! - virtual void CreateDefaultTracks(); + void CreateDefaultTracks() override; - virtual void OnReset(); + void OnReset() override; //----------------------------------------------------------------------------- //! - virtual void Animate(SAnimContext& ac); + void Animate(SAnimContext& ac) override; void InitPostLoad(IAnimSequence* sequence) override; static void Reflect(AZ::ReflectContext* context); protected: - virtual bool GetParamInfoFromType(const CAnimParamType& paramId, SParamInfo& info) const; + bool GetParamInfoFromType(const CAnimParamType& paramId, SParamInfo& info) const override; typedef std::map< AnimNodeType, _smart_ptr > FxNodeDescriptionMap; static StaticInstance s_fxNodeDescriptions; diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimScreenFaderNode.h b/Gems/Maestro/Code/Source/Cinematics/AnimScreenFaderNode.h index 1e16cc5fb0..8f4b0de4fb 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimScreenFaderNode.h +++ b/Gems/Maestro/Code/Source/Cinematics/AnimScreenFaderNode.h @@ -32,32 +32,32 @@ public: //----------------------------------------------------------------------------- //! Overrides from CAnimNode - virtual void Animate(SAnimContext& ac); + void Animate(SAnimContext& ac) override; - virtual void CreateDefaultTracks(); + void CreateDefaultTracks() override; - virtual void OnReset(); + void OnReset() override; - virtual void Activate(bool bActivate); + void Activate(bool bActivate) override; - virtual void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks); + void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks) override; //----------------------------------------------------------------------------- //! Overrides from IAnimNode - virtual unsigned int GetParamCount() const; - virtual CAnimParamType GetParamType(unsigned int nIndex) const; + unsigned int GetParamCount() const override; + CAnimParamType GetParamType(unsigned int nIndex) const override; void SetFlags(int flags) override; - virtual void Render(); + void Render() override; bool IsAnyTextureVisible() const; static void Reflect(AZ::ReflectContext* context); protected: - virtual bool GetParamInfoFromType(const CAnimParamType& paramId, SParamInfo& info) const; + bool GetParamInfoFromType(const CAnimParamType& paramId, SParamInfo& info) const override; - virtual bool NeedToRender() const { return true; } + bool NeedToRender() const override { return true; } private: CAnimScreenFaderNode(const CAnimScreenFaderNode&); diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimSequence.h b/Gems/Maestro/Code/Source/Cinematics/AnimSequence.h index 0a1d4a931c..f824cf519d 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimSequence.h +++ b/Gems/Maestro/Code/Source/Cinematics/AnimSequence.h @@ -40,48 +40,48 @@ public: // Movie system. IMovieSystem* GetMovieSystem() const { return m_pMovieSystem; }; - void SetName(const char* name); - const char* GetName() const; - uint32 GetId() const { return m_id; } + void SetName(const char* name) override; + const char* GetName() const override; + uint32 GetId() const override { return m_id; } void ResetId() override; float GetTime() const { return m_time; } void SetLegacySequenceObject(IAnimLegacySequenceObject* legacySequenceObject) override { m_legacySequenceObject = legacySequenceObject; } - virtual IAnimLegacySequenceObject* GetLegacySequenceObject() const override { return m_legacySequenceObject; } + IAnimLegacySequenceObject* GetLegacySequenceObject() const override { return m_legacySequenceObject; } void SetSequenceEntityId(const AZ::EntityId& sequenceEntityId) override; const AZ::EntityId& GetSequenceEntityId() const override { return m_sequenceEntityId; } - virtual void SetActiveDirector(IAnimNode* pDirectorNode); - virtual IAnimNode* GetActiveDirector() const; + void SetActiveDirector(IAnimNode* pDirectorNode) override; + IAnimNode* GetActiveDirector() const override; - virtual void SetFlags(int flags); - virtual int GetFlags() const; - virtual int GetCutSceneFlags(const bool localFlags = false) const; + void SetFlags(int flags) override; + int GetFlags() const override; + int GetCutSceneFlags(const bool localFlags = false) const override; - virtual void SetParentSequence(IAnimSequence* pParentSequence); - virtual const IAnimSequence* GetParentSequence() const; - virtual bool IsAncestorOf(const IAnimSequence* pSequence) const; + void SetParentSequence(IAnimSequence* pParentSequence) override; + const IAnimSequence* GetParentSequence() const override; + bool IsAncestorOf(const IAnimSequence* pSequence) const override; - void SetTimeRange(Range timeRange); - Range GetTimeRange() { return m_timeRange; }; + void SetTimeRange(Range timeRange) override; + Range GetTimeRange() override { return m_timeRange; }; - void AdjustKeysToTimeRange(const Range& timeRange); + void AdjustKeysToTimeRange(const Range& timeRange) override; //! Return number of animation nodes in sequence. - int GetNodeCount() const; + int GetNodeCount() const override; //! Get specified animation node. - IAnimNode* GetNode(int index) const; + IAnimNode* GetNode(int index) const override; - IAnimNode* FindNodeByName(const char* sNodeName, const IAnimNode* pParentDirector); + IAnimNode* FindNodeByName(const char* sNodeName, const IAnimNode* pParentDirector) override; IAnimNode* FindNodeById(int nNodeId); - virtual void ReorderNode(IAnimNode* node, IAnimNode* pPivotNode, bool next); + void ReorderNode(IAnimNode* node, IAnimNode* pPivotNode, bool next) override; - void Reset(bool bSeekToStart); - void ResetHard(); - void Pause(); - void Resume(); - bool IsPaused() const; + void Reset(bool bSeekToStart) override; + void ResetHard() override; + void Pause() override; + void Resume() override; + bool IsPaused() const override; virtual void OnStart(); virtual void OnStop(); @@ -90,49 +90,49 @@ public: void TimeChanged(float newTime) override; //! Add animation node to sequence. - bool AddNode(IAnimNode* node); - IAnimNode* CreateNode(AnimNodeType nodeType); - IAnimNode* CreateNode(XmlNodeRef node); + bool AddNode(IAnimNode* node) override; + IAnimNode* CreateNode(AnimNodeType nodeType) override; + IAnimNode* CreateNode(XmlNodeRef node) override; void RemoveNode(IAnimNode* node, bool removeChildRelationships=true) override; //! Add scene node to sequence. - void RemoveAll(); + void RemoveAll() override; - virtual void Activate(); - virtual bool IsActivated() const { return m_bActive; } - virtual void Deactivate(); + void Activate() override; + bool IsActivated() const override { return m_bActive; } + void Deactivate() override; - virtual void PrecacheData(float startTime); + void PrecacheData(float startTime) override; void PrecacheStatic(const float startTime); void PrecacheDynamic(float time); - void StillUpdate(); - void Animate(const SAnimContext& ec); - void Render(); + void StillUpdate() override; + void Animate(const SAnimContext& ec) override; + void Render() override; void InitPostLoad() override; - void CopyNodes(XmlNodeRef& xmlNode, IAnimNode** pSelectedNodes, uint32 count); - void PasteNodes(const XmlNodeRef& xmlNode, IAnimNode* pParent); + void CopyNodes(XmlNodeRef& xmlNode, IAnimNode** pSelectedNodes, uint32 count) override; + void PasteNodes(const XmlNodeRef& xmlNode, IAnimNode* pParent) override; //! Add/remove track events in sequence - virtual bool AddTrackEvent(const char* szEvent); - virtual bool RemoveTrackEvent(const char* szEvent); - virtual bool RenameTrackEvent(const char* szEvent, const char* szNewEvent); - virtual bool MoveUpTrackEvent(const char* szEvent); - virtual bool MoveDownTrackEvent(const char* szEvent); - virtual void ClearTrackEvents(); + bool AddTrackEvent(const char* szEvent) override; + bool RemoveTrackEvent(const char* szEvent) override; + bool RenameTrackEvent(const char* szEvent, const char* szNewEvent) override; + bool MoveUpTrackEvent(const char* szEvent) override; + bool MoveDownTrackEvent(const char* szEvent) override; + void ClearTrackEvents() override; //! Get the track events in the sequence - virtual int GetTrackEventsCount() const; - virtual char const* GetTrackEvent(int iIndex) const; - virtual IAnimStringTable* GetTrackEventStringTable() { return m_pEventStrings.get(); } + int GetTrackEventsCount() const override; + char const* GetTrackEvent(int iIndex) const override; + IAnimStringTable* GetTrackEventStringTable() override { return m_pEventStrings.get(); } //! Call to trigger a track event - virtual void TriggerTrackEvent(const char* event, const char* param = NULL); + void TriggerTrackEvent(const char* event, const char* param = NULL) override; //! Track event listener - virtual void AddTrackEventListener(ITrackEventListener* pListener); - virtual void RemoveTrackEventListener(ITrackEventListener* pListener); + void AddTrackEventListener(ITrackEventListener* pListener) override; + void RemoveTrackEventListener(ITrackEventListener* pListener) override; SequenceType GetSequenceType() const override { return m_sequenceType; } diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimSplineTrack.h b/Gems/Maestro/Code/Source/Cinematics/AnimSplineTrack.h index 46dcb63daa..6388c9709e 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimSplineTrack.h +++ b/Gems/Maestro/Code/Source/Cinematics/AnimSplineTrack.h @@ -52,24 +52,24 @@ public: ////////////////////////////////////////////////////////////////////////// - virtual int GetSubTrackCount() const { return 0; }; - virtual IAnimTrack* GetSubTrack([[maybe_unused]] int nIndex) const { return 0; }; - AZStd::string GetSubTrackName([[maybe_unused]] int nIndex) const { return AZStd::string(); }; - virtual void SetSubTrackName([[maybe_unused]] int nIndex, [[maybe_unused]] const char* name) { assert(0); } + int GetSubTrackCount() const override { return 0; }; + IAnimTrack* GetSubTrack([[maybe_unused]] int nIndex) const override { return 0; }; + AZStd::string GetSubTrackName([[maybe_unused]] int nIndex) const override { return AZStd::string(); }; + void SetSubTrackName([[maybe_unused]] int nIndex, [[maybe_unused]] const char* name) override { assert(0); } void SetNode(IAnimNode* node) override { m_node = node; } // Return Animation Node that owns this Track. IAnimNode* GetNode() override { return m_node; } - virtual const CAnimParamType& GetParameterType() const { return m_nParamType; }; - virtual void SetParameterType(CAnimParamType type) { m_nParamType = type; }; + const CAnimParamType& GetParameterType() const override { return m_nParamType; }; + void SetParameterType(CAnimParamType type) override { m_nParamType = type; }; - virtual void GetKeyValueRange(float& fMin, float& fMax) const { fMin = m_fMinKeyValue; fMax = m_fMaxKeyValue; }; - virtual void SetKeyValueRange(float fMin, float fMax){ m_fMinKeyValue = fMin; m_fMaxKeyValue = fMax; }; + void GetKeyValueRange(float& fMin, float& fMax) const override { fMin = m_fMinKeyValue; fMax = m_fMaxKeyValue; }; + void SetKeyValueRange(float fMin, float fMax) override{ m_fMinKeyValue = fMin; m_fMaxKeyValue = fMax; }; - ISplineInterpolator* GetSpline() const { return m_spline.get(); }; + ISplineInterpolator* GetSpline() const override { return m_spline.get(); }; - virtual bool IsKeySelected(int key) const + bool IsKeySelected(int key) const override { if (GetSpline() && GetSpline()->IsKeySelectedAtAnyDimension(key)) { @@ -78,7 +78,7 @@ public: return false; } - virtual void SelectKey(int key, bool select) + void SelectKey(int key, bool select) override { if (GetSpline()) { @@ -86,22 +86,22 @@ public: } } - int GetNumKeys() const + int GetNumKeys() const override { return m_spline->num_keys(); } - void SetNumKeys(int numKeys) + void SetNumKeys(int numKeys) override { m_spline->resize(numKeys); } - bool HasKeys() const + bool HasKeys() const override { return GetNumKeys() != 0; } - void RemoveKey(int num) + void RemoveKey(int num) override { if (m_spline && m_spline->num_keys() > num) { @@ -113,7 +113,7 @@ public: } } - void GetKey(int index, IKey* key) const + void GetKey(int index, IKey* key) const override { assert(index >= 0 && index < GetNumKeys()); assert(key != 0); @@ -131,7 +131,7 @@ public: tcbkey->SetValue(k.value); } - void SetKey(int index, IKey* key) + void SetKey(int index, IKey* key) override { assert(index >= 0 && index < GetNumKeys()); assert(key != 0); @@ -148,71 +148,71 @@ public: Invalidate(); } - float GetKeyTime(int index) const + float GetKeyTime(int index) const override { assert(index >= 0 && index < GetNumKeys()); return m_spline->time(index); } - void SetKeyTime(int index, float time) + void SetKeyTime(int index, float time) override { assert(index >= 0 && index < GetNumKeys()); m_spline->SetKeyTime(index, time); Invalidate(); } - int GetKeyFlags(int index) + int GetKeyFlags(int index) override { assert(index >= 0 && index < GetNumKeys()); return m_spline->key(index).flags; } - void SetKeyFlags(int index, int flags) + void SetKeyFlags(int index, int flags) override { assert(index >= 0 && index < GetNumKeys()); m_spline->key(index).flags = flags; } - virtual EAnimCurveType GetCurveType() { assert(0); return eAnimCurveType_Unknown; } - virtual AnimValueType GetValueType() { assert(0); return static_cast(0xFFFFFFFF); } + EAnimCurveType GetCurveType() override { assert(0); return eAnimCurveType_Unknown; } + AnimValueType GetValueType() override { assert(0); return static_cast(0xFFFFFFFF); } - virtual void GetValue(float time, float& value, bool applyMultiplier = false) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec3& value, [[maybe_unused]] bool applyMultiplier = false) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec4& value, [[maybe_unused]] bool applyMultiplier = false) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Quat& value) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) { assert(0); } - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Maestro::AssetBlends& value) { assert(0); } + void GetValue(float time, float& value, bool applyMultiplier = false) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec3& value, [[maybe_unused]] bool applyMultiplier = false) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec4& value, [[maybe_unused]] bool applyMultiplier = false) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Quat& value) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) override { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Maestro::AssetBlends& value) override { assert(0); } - virtual void SetValue(float time, const float& value, bool bDefault = false, bool applyMultiplier = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec3& value, [[maybe_unused]] bool bDefault = false, [[maybe_unused]] bool applyMultiplier = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec4& value, [[maybe_unused]] bool bDefault = false, [[maybe_unused]] bool applyMultiplier = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Quat& value, [[maybe_unused]] bool bDefault = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) { assert(0); } - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Maestro::AssetBlends& value, [[maybe_unused]] bool bDefault = false) { assert(0); } + void SetValue(float time, const float& value, bool bDefault = false, bool applyMultiplier = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec3& value, [[maybe_unused]] bool bDefault = false, [[maybe_unused]] bool applyMultiplier = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec4& value, [[maybe_unused]] bool bDefault = false, [[maybe_unused]] bool applyMultiplier = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Quat& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Maestro::AssetBlends& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } - virtual void OffsetKeyPosition([[maybe_unused]] const Vec3& value) { assert(0); }; - virtual void UpdateKeyDataAfterParentChanged([[maybe_unused]] const AZ::Transform& oldParentWorldTM, [[maybe_unused]] const AZ::Transform& newParentWorldTM) { assert(0); }; + void OffsetKeyPosition([[maybe_unused]] const Vec3& value) override { assert(0); }; + void UpdateKeyDataAfterParentChanged([[maybe_unused]] const AZ::Transform& oldParentWorldTM, [[maybe_unused]] const AZ::Transform& newParentWorldTM) override { assert(0); }; - bool Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks); - bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected, float fTimeOffset); + bool Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks) override; + bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected, float fTimeOffset) override; - void GetKeyInfo(int key, const char*& description, float& duration) + void GetKeyInfo(int key, const char*& description, float& duration) override { description = 0; duration = 0; } //! Sort keys in track (after time of keys was modified). - void SortKeys() + void SortKeys() override { m_spline->sort_keys(); }; //! Get track flags. - int GetFlags() { return m_flags; }; + int GetFlags() override { return m_flags; }; //! Check if track is masked by mask - virtual bool IsMasked([[maybe_unused]] const uint32 mask) const { return false; } + bool IsMasked([[maybe_unused]] const uint32 mask) const override { return false; } //! Set track flags. - void SetFlags(int flags) + void SetFlags(int flags) override { m_flags = flags; if (m_flags & eAnimTrackFlags_Loop) @@ -234,12 +234,12 @@ public: m_spline->flag_set(Spline::MODIFIED); }; - void SetTimeRange(const Range& timeRange) + void SetTimeRange(const Range& timeRange) override { m_spline->SetRange(timeRange.start, timeRange.end); } - int FindKey(float time) + int FindKey(float time) override { // Find key with given time. int num = m_spline->num_keys(); @@ -255,7 +255,7 @@ public: } //! Create key at given time, and return its index. - int CreateKey(float time) + int CreateKey(float time) override { ValueType value; @@ -275,12 +275,12 @@ public: return m_spline->InsertKey(time, tmp); } - int CloneKey(int srcKey) + int CloneKey(int srcKey) override { return CopyKey(this, srcKey); } - int CopyKey(IAnimTrack* pFromTrack, int nFromKey) + int CopyKey(IAnimTrack* pFromTrack, int nFromKey) override { ITcbKey key; pFromTrack->GetKey(nFromKey, &key); @@ -329,16 +329,16 @@ public: m_defaultValue = value; } - virtual ColorB GetCustomColor() const + ColorB GetCustomColor() const { return m_customColor; } - virtual void SetCustomColor(ColorB color) + void SetCustomColor(ColorB color) { m_customColor = color; m_bCustomColorSet = true; } - virtual bool HasCustomColor() const + bool HasCustomColor() const { return m_bCustomColorSet; } - virtual void ClearCustomColor() + void ClearCustomColor() { m_bCustomColorSet = false; } void SetMultiplier(float trackMultiplier) override @@ -346,12 +346,12 @@ public: m_trackMultiplier = trackMultiplier; } - void SetExpanded([[maybe_unused]] bool expanded) + void SetExpanded([[maybe_unused]] bool expanded) override { AZ_Assert(false, "Not expected to be used."); } - bool GetExpanded() const + bool GetExpanded() const override { return false; } diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimTrack.h b/Gems/Maestro/Code/Source/Cinematics/AnimTrack.h index e16f64c7c1..e39ae3e2ee 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimTrack.h +++ b/Gems/Maestro/Code/Source/Cinematics/AnimTrack.h @@ -28,20 +28,20 @@ public: TAnimTrack(); - virtual EAnimCurveType GetCurveType() { return eAnimCurveType_Unknown; }; - virtual AnimValueType GetValueType() { return kAnimValueUnknown; } + EAnimCurveType GetCurveType() override { return eAnimCurveType_Unknown; }; + AnimValueType GetValueType() override { return kAnimValueUnknown; } void SetNode(IAnimNode* node) override { m_node = node; } // Return Animation Node that owns this Track. IAnimNode* GetNode() override { return m_node; } - virtual int GetSubTrackCount() const { return 0; }; - virtual IAnimTrack* GetSubTrack([[maybe_unused]] int nIndex) const { return 0; }; + int GetSubTrackCount() const override { return 0; }; + IAnimTrack* GetSubTrack([[maybe_unused]] int nIndex) const override { return 0; }; AZStd::string GetSubTrackName([[maybe_unused]] int nIndex) const override { return AZStd::string(); }; - virtual void SetSubTrackName([[maybe_unused]] int nIndex, [[maybe_unused]] const char* name) { assert(0); } + void SetSubTrackName([[maybe_unused]] int nIndex, [[maybe_unused]] const char* name) override { assert(0); } - virtual const CAnimParamType& GetParameterType() const { return m_nParamType; }; - virtual void SetParameterType(CAnimParamType type) { m_nParamType = type; }; + const CAnimParamType& GetParameterType() const override { return m_nParamType; }; + void SetParameterType(CAnimParamType type) override { m_nParamType = type; }; ////////////////////////////////////////////////////////////////////////// // for intrusive_ptr support @@ -49,7 +49,7 @@ public: void release() override; ////////////////////////////////////////////////////////////////////////// - virtual bool IsKeySelected(int key) const + bool IsKeySelected(int key) const override { AZ_Assert(key >= 0 && key < (int)m_keys.size(), "Key index is out of range"); if (m_keys[key].flags & AKEY_SELECTED) @@ -59,7 +59,7 @@ public: return false; } - virtual void SelectKey(int key, bool select) + void SelectKey(int key, bool select) override { AZ_Assert(key >= 0 && key < (int)m_keys.size(), "Key index is out of range"); if (select) @@ -96,59 +96,59 @@ public: } //! Return number of keys in track. - virtual int GetNumKeys() const { return static_cast(m_keys.size()); }; + int GetNumKeys() const override { return static_cast(m_keys.size()); }; //! Return true if keys exists in this track - virtual bool HasKeys() const { return !m_keys.empty(); } + bool HasKeys() const override { return !m_keys.empty(); } //! Set number of keys in track. //! If needed adds empty keys at end or remove keys from end. - virtual void SetNumKeys(int numKeys) { m_keys.resize(numKeys); }; + void SetNumKeys(int numKeys) override { m_keys.resize(numKeys); }; //! Remove specified key. - virtual void RemoveKey(int num); + void RemoveKey(int num) override; - int CreateKey(float time); - int CloneKey(int fromKey); - int CopyKey(IAnimTrack* pFromTrack, int nFromKey); + int CreateKey(float time) override; + int CloneKey(int fromKey) override; + int CopyKey(IAnimTrack* pFromTrack, int nFromKey) override; //! Get key at specified location. //! @param key Must be valid pointer to compatible key structure, to be filled with specified key location. - virtual void GetKey(int index, IKey* key) const; + void GetKey(int index, IKey* key) const override; //! Get time of specified key. //! @return key time. - virtual float GetKeyTime(int index) const; + float GetKeyTime(int index) const override; //! Find key at given time. //! @return Index of found key, or -1 if key with this time not found. - virtual int FindKey(float time); + int FindKey(float time) override; //! Get flags of specified key. //! @return key time. - virtual int GetKeyFlags(int index); + int GetKeyFlags(int index) override; //! Set key at specified location. //! @param key Must be valid pointer to compatible key structure. - virtual void SetKey(int index, IKey* key); + void SetKey(int index, IKey* key) override; //! Set time of specified key. - virtual void SetKeyTime(int index, float time); + void SetKeyTime(int index, float time) override; //! Set flags of specified key. - virtual void SetKeyFlags(int index, int flags); + void SetKeyFlags(int index, int flags) override; //! Sort keys in track (after time of keys was modified). - virtual void SortKeys(); + void SortKeys() override; //! Get track flags. - virtual int GetFlags() { return m_flags; }; + int GetFlags() override { return m_flags; }; //! Check if track is masked - virtual bool IsMasked([[maybe_unused]] const uint32 mask) const { return false; } + bool IsMasked([[maybe_unused]] const uint32 mask) const override { return false; } //! Set track flags. - virtual void SetFlags(int flags) + void SetFlags(int flags) override { m_flags = flags; } @@ -157,37 +157,37 @@ public: // Get track value at specified time. // Interpolates keys if needed. ////////////////////////////////////////////////////////////////////////// - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] float& value, [[maybe_unused]] bool applyMultiplier = false) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec3& value, [[maybe_unused]] bool applyMultiplier = false) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec4& value, [[maybe_unused]] bool applyMultiplier = false) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Quat& value) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Maestro::AssetBlends& value) { assert(0); } + void GetValue([[maybe_unused]] float time, [[maybe_unused]] float& value, [[maybe_unused]] bool applyMultiplier = false) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec3& value, [[maybe_unused]] bool applyMultiplier = false) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec4& value, [[maybe_unused]] bool applyMultiplier = false) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Quat& value) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Maestro::AssetBlends& value) override { assert(0); } ////////////////////////////////////////////////////////////////////////// // Set track value at specified time. // Adds new keys if required. ////////////////////////////////////////////////////////////////////////// - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const float& value, [[maybe_unused]] bool bDefault = false, [[maybe_unused]] bool applyMultiplier = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec3& value, [[maybe_unused]] bool bDefault = false, [[maybe_unused]] bool applyMultiplier = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec4& value, [[maybe_unused]] bool bDefault = false, [[maybe_unused]] bool applyMultiplier = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Quat& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Maestro::AssetBlends& value, [[maybe_unused]] bool bDefault = false) { assert(0); } + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const float& value, [[maybe_unused]] bool bDefault = false, [[maybe_unused]] bool applyMultiplier = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec3& value, [[maybe_unused]] bool bDefault = false, [[maybe_unused]] bool applyMultiplier = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec4& value, [[maybe_unused]] bool bDefault = false, [[maybe_unused]] bool applyMultiplier = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Quat& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Maestro::AssetBlends& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } - virtual void OffsetKeyPosition([[maybe_unused]] const Vec3& value) { assert(0); }; - virtual void UpdateKeyDataAfterParentChanged([[maybe_unused]] const AZ::Transform& oldParentWorldTM, [[maybe_unused]] const AZ::Transform& newParentWorldTM) { assert(0); }; + void OffsetKeyPosition([[maybe_unused]] const Vec3& value) override { assert(0); }; + void UpdateKeyDataAfterParentChanged([[maybe_unused]] const AZ::Transform& oldParentWorldTM, [[maybe_unused]] const AZ::Transform& newParentWorldTM) override { assert(0); }; /** Assign active time range for this track. */ - virtual void SetTimeRange(const Range& timeRange) { m_timeRange = timeRange; }; + void SetTimeRange(const Range& timeRange) override { m_timeRange = timeRange; }; /** Serialize this animation track to XML. Do not override this method, prefer to override SerializeKey. */ - virtual bool Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks = true); + bool Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks = true) override; - virtual bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected = false, float fTimeOffset = 0); + bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected = false, float fTimeOffset = 0) override; /** Serialize single key of this track. @@ -204,33 +204,33 @@ public: int GetActiveKey(float time, KeyType* key); #ifdef MOVIESYSTEM_SUPPORT_EDITING - virtual ColorB GetCustomColor() const + ColorB GetCustomColor() const override { return m_customColor; } - virtual void SetCustomColor(ColorB color) + void SetCustomColor(ColorB color) override { m_customColor = color; m_bCustomColorSet = true; } - virtual bool HasCustomColor() const + bool HasCustomColor() const override { return m_bCustomColorSet; } - virtual void ClearCustomColor() + void ClearCustomColor() override { m_bCustomColorSet = false; } #endif - virtual void GetKeyValueRange(float& fMin, float& fMax) const { fMin = m_fMinKeyValue; fMax = m_fMaxKeyValue; }; - virtual void SetKeyValueRange(float fMin, float fMax){ m_fMinKeyValue = fMin; m_fMaxKeyValue = fMax; }; + void GetKeyValueRange(float& fMin, float& fMax) const override { fMin = m_fMinKeyValue; fMax = m_fMaxKeyValue; }; + void SetKeyValueRange(float fMin, float fMax) override{ m_fMinKeyValue = fMin; m_fMaxKeyValue = fMax; }; void SetMultiplier(float trackMultiplier) override { m_trackMultiplier = trackMultiplier; } - void SetExpanded([[maybe_unused]] bool expanded) + void SetExpanded([[maybe_unused]] bool expanded) override { AZ_Assert(false, "Not expected to be used."); } - bool GetExpanded() const + bool GetExpanded() const override { return false; } diff --git a/Gems/Maestro/Code/Source/Cinematics/BoolTrack.h b/Gems/Maestro/Code/Source/Cinematics/BoolTrack.h index edee77437c..a24f695b56 100644 --- a/Gems/Maestro/Code/Source/Cinematics/BoolTrack.h +++ b/Gems/Maestro/Code/Source/Cinematics/BoolTrack.h @@ -28,14 +28,14 @@ public: CBoolTrack(); - virtual AnimValueType GetValueType(); + AnimValueType GetValueType() override; - virtual void GetValue(float time, bool& value); - virtual void SetValue(float time, const bool& value, bool bDefault = false); + void GetValue(float time, bool& value) override; + void SetValue(float time, const bool& value, bool bDefault = false) override; - void SerializeKey([[maybe_unused]] IBoolKey& key, [[maybe_unused]] XmlNodeRef& keyNode, [[maybe_unused]] bool bLoading) {}; - void GetKeyInfo(int key, const char*& description, float& duration); + void SerializeKey([[maybe_unused]] IBoolKey& key, [[maybe_unused]] XmlNodeRef& keyNode, [[maybe_unused]] bool bLoading) override {}; + void GetKeyInfo(int key, const char*& description, float& duration) override; void SetDefaultValue(const bool bDefaultValue); diff --git a/Gems/Maestro/Code/Source/Cinematics/CVarNode.h b/Gems/Maestro/Code/Source/Cinematics/CVarNode.h index 5068140f11..af5ce43b2f 100644 --- a/Gems/Maestro/Code/Source/Cinematics/CVarNode.h +++ b/Gems/Maestro/Code/Source/Cinematics/CVarNode.h @@ -26,21 +26,21 @@ public: ////////////////////////////////////////////////////////////////////////// // Overrides from CAnimNode ////////////////////////////////////////////////////////////////////////// - void SetName(const char* name); - void Animate(SAnimContext& ec); - void CreateDefaultTracks(); - void OnReset(); - void OnResume(); + void SetName(const char* name) override; + void Animate(SAnimContext& ec) override; + void CreateDefaultTracks() override; + void OnReset() override; + void OnResume() override; - virtual unsigned int GetParamCount() const; - virtual CAnimParamType GetParamType(unsigned int nIndex) const; + unsigned int GetParamCount() const override; + CAnimParamType GetParamType(unsigned int nIndex) const override; int GetDefaultKeyTangentFlags() const override; static void Reflect(AZ::ReflectContext* context); protected: - virtual bool GetParamInfoFromType(const CAnimParamType& paramId, SParamInfo& info) const; + bool GetParamInfoFromType(const CAnimParamType& paramId, SParamInfo& info) const override; private: float m_value; diff --git a/Gems/Maestro/Code/Source/Cinematics/CompoundSplineTrack.h b/Gems/Maestro/Code/Source/Cinematics/CompoundSplineTrack.h index 443bad584b..4d7065a06d 100644 --- a/Gems/Maestro/Code/Source/Cinematics/CompoundSplineTrack.h +++ b/Gems/Maestro/Code/Source/Cinematics/CompoundSplineTrack.h @@ -36,41 +36,41 @@ public: // Return Animation Node that owns this Track. IAnimNode* GetNode() override { return m_node; } - virtual int GetSubTrackCount() const { return m_nDimensions; }; - virtual IAnimTrack* GetSubTrack(int nIndex) const; - AZStd::string GetSubTrackName(int nIndex) const; - virtual void SetSubTrackName(int nIndex, const char* name); + int GetSubTrackCount() const override { return m_nDimensions; }; + IAnimTrack* GetSubTrack(int nIndex) const override; + AZStd::string GetSubTrackName(int nIndex) const override; + void SetSubTrackName(int nIndex, const char* name) override; - virtual EAnimCurveType GetCurveType() { return eAnimCurveType_BezierFloat; }; - virtual AnimValueType GetValueType() { return m_valueType; }; + EAnimCurveType GetCurveType() override { return eAnimCurveType_BezierFloat; }; + AnimValueType GetValueType() override { return m_valueType; }; - virtual const CAnimParamType& GetParameterType() const { return m_nParamType; }; - virtual void SetParameterType(CAnimParamType type) { m_nParamType = type; } + const CAnimParamType& GetParameterType() const override { return m_nParamType; }; + void SetParameterType(CAnimParamType type) override { m_nParamType = type; } - virtual int GetNumKeys() const; - virtual void SetNumKeys([[maybe_unused]] int numKeys) { assert(0); }; - virtual bool HasKeys() const; - virtual void RemoveKey(int num); + int GetNumKeys() const override; + void SetNumKeys([[maybe_unused]] int numKeys) override { assert(0); }; + bool HasKeys() const override; + void RemoveKey(int num) override; - virtual void GetKeyInfo(int key, const char*& description, float& duration); - virtual int CreateKey([[maybe_unused]] float time) { assert(0); return 0; }; - virtual int CloneKey([[maybe_unused]] int fromKey) { assert(0); return 0; }; - virtual int CopyKey([[maybe_unused]] IAnimTrack* pFromTrack, [[maybe_unused]] int nFromKey) { assert(0); return 0; }; - virtual void GetKey([[maybe_unused]] int index, [[maybe_unused]] IKey* key) const { assert(0); }; - virtual float GetKeyTime(int index) const; - virtual int FindKey([[maybe_unused]] float time) { assert(0); return 0; }; - virtual int GetKeyFlags([[maybe_unused]] int index) { assert(0); return 0; }; - virtual void SetKey([[maybe_unused]] int index, [[maybe_unused]] IKey* key) { assert(0); }; - virtual void SetKeyTime(int index, float time); - virtual void SetKeyFlags([[maybe_unused]] int index, [[maybe_unused]] int flags) { assert(0); }; - virtual void SortKeys() { assert(0); }; + void GetKeyInfo(int key, const char*& description, float& duration) override; + int CreateKey([[maybe_unused]] float time) override { assert(0); return 0; }; + int CloneKey([[maybe_unused]] int fromKey) override { assert(0); return 0; }; + int CopyKey([[maybe_unused]] IAnimTrack* pFromTrack, [[maybe_unused]] int nFromKey) override { assert(0); return 0; }; + void GetKey([[maybe_unused]] int index, [[maybe_unused]] IKey* key) const override { assert(0); }; + float GetKeyTime(int index) const override; + int FindKey([[maybe_unused]] float time) override { assert(0); return 0; }; + int GetKeyFlags([[maybe_unused]] int index) override { assert(0); return 0; }; + void SetKey([[maybe_unused]] int index, [[maybe_unused]] IKey* key) override { assert(0); }; + void SetKeyTime(int index, float time) override; + void SetKeyFlags([[maybe_unused]] int index, [[maybe_unused]] int flags) override { assert(0); }; + void SortKeys() override { assert(0); }; - virtual bool IsKeySelected(int key) const; - virtual void SelectKey(int key, bool select); + bool IsKeySelected(int key) const override; + void SelectKey(int key, bool select) override; - virtual int GetFlags() { return m_flags; }; - virtual bool IsMasked([[maybe_unused]] const uint32 mask) const { return false; } - virtual void SetFlags(int flags) + int GetFlags() override { return m_flags; }; + bool IsMasked([[maybe_unused]] const uint32 mask) const override { return false; } + void SetFlags(int flags) override { m_flags = flags; } @@ -79,59 +79,59 @@ public: // Get track value at specified time. // Interpolates keys if needed. ////////////////////////////////////////////////////////////////////////// - virtual void GetValue(float time, float& value, bool applyMultiplier = false); - virtual void GetValue(float time, Vec3& value, bool applyMultiplier = false); - virtual void GetValue(float time, Vec4& value, bool applyMultiplier = false); - virtual void GetValue(float time, Quat& value); - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) { assert(0); }; - virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Maestro::AssetBlends& value) { assert(0); } + void GetValue(float time, float& value, bool applyMultiplier = false) override; + void GetValue(float time, Vec3& value, bool applyMultiplier = false) override; + void GetValue(float time, Vec4& value, bool applyMultiplier = false) override; + void GetValue(float time, Quat& value) override; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) override { assert(0); }; + void GetValue([[maybe_unused]] float time, [[maybe_unused]] Maestro::AssetBlends& value) override { assert(0); } ////////////////////////////////////////////////////////////////////////// // Set track value at specified time. // Adds new keys if required. ////////////////////////////////////////////////////////////////////////// - virtual void SetValue(float time, const float& value, bool bDefault = false, bool applyMultiplier = false); - virtual void SetValue(float time, const Vec3& value, bool bDefault = false, bool applyMultiplier = false); - void SetValue(float time, const Vec4& value, bool bDefault = false, bool applyMultiplier = false); - virtual void SetValue(float time, const Quat& value, bool bDefault = false); - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) { assert(0); }; - virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Maestro::AssetBlends& value, [[maybe_unused]] bool bDefault = false) { assert(0); } + void SetValue(float time, const float& value, bool bDefault = false, bool applyMultiplier = false) override; + void SetValue(float time, const Vec3& value, bool bDefault = false, bool applyMultiplier = false) override; + void SetValue(float time, const Vec4& value, bool bDefault = false, bool applyMultiplier = false) override; + void SetValue(float time, const Quat& value, bool bDefault = false) override; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) override { assert(0); }; + void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Maestro::AssetBlends& value, [[maybe_unused]] bool bDefault = false) override { assert(0); } - virtual void OffsetKeyPosition(const Vec3& value); - virtual void UpdateKeyDataAfterParentChanged(const AZ::Transform& oldParentWorldTM, const AZ::Transform& newParentWorldTM); + void OffsetKeyPosition(const Vec3& value) override; + void UpdateKeyDataAfterParentChanged(const AZ::Transform& oldParentWorldTM, const AZ::Transform& newParentWorldTM) override; - virtual void SetTimeRange(const Range& timeRange); + void SetTimeRange(const Range& timeRange) override; - virtual bool Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks = true); + bool Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks = true) override; - virtual bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected = false, float fTimeOffset = 0); + bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected = false, float fTimeOffset = 0) override; - virtual int NextKeyByTime(int key) const; + int NextKeyByTime(int key) const override; void SetSubTrackName(const int i, const AZStd::string& name) { assert (i < MAX_SUBTRACKS); m_subTrackNames[i] = name; } #ifdef MOVIESYSTEM_SUPPORT_EDITING - virtual ColorB GetCustomColor() const + ColorB GetCustomColor() const override { return m_customColor; } - virtual void SetCustomColor(ColorB color) + void SetCustomColor(ColorB color) override { m_customColor = color; m_bCustomColorSet = true; } - virtual bool HasCustomColor() const + bool HasCustomColor() const override { return m_bCustomColorSet; } - virtual void ClearCustomColor() + void ClearCustomColor() override { m_bCustomColorSet = false; } #endif - virtual void GetKeyValueRange(float& fMin, float& fMax) const + void GetKeyValueRange(float& fMin, float& fMax) const override { if (GetSubTrackCount() > 0) { m_subTracks[0]->GetKeyValueRange(fMin, fMax); } }; - virtual void SetKeyValueRange(float fMin, float fMax) + void SetKeyValueRange(float fMin, float fMax) override { for (int i = 0; i < m_nDimensions; ++i) { diff --git a/Gems/Maestro/Code/Source/Cinematics/EventTrack.h b/Gems/Maestro/Code/Source/Cinematics/EventTrack.h index afe7eb6602..6430119e5e 100644 --- a/Gems/Maestro/Code/Source/Cinematics/EventTrack.h +++ b/Gems/Maestro/Code/Source/Cinematics/EventTrack.h @@ -33,9 +33,9 @@ public: ////////////////////////////////////////////////////////////////////////// // Overrides of IAnimTrack. ////////////////////////////////////////////////////////////////////////// - void GetKeyInfo(int key, const char*& description, float& duration); - void SerializeKey(IEventKey& key, XmlNodeRef& keyNode, bool bLoading); - void SetKey(int index, IKey* key); + void GetKeyInfo(int key, const char*& description, float& duration) override; + void SerializeKey(IEventKey& key, XmlNodeRef& keyNode, bool bLoading) override; + void SetKey(int index, IKey* key) override; void InitPostLoad(IAnimSequence* sequence) override; static void Reflect(AZ::ReflectContext* context); diff --git a/Gems/Maestro/Code/Source/Cinematics/MaterialNode.h b/Gems/Maestro/Code/Source/Cinematics/MaterialNode.h index fcc308e5c8..5878ee0269 100644 --- a/Gems/Maestro/Code/Source/Cinematics/MaterialNode.h +++ b/Gems/Maestro/Code/Source/Cinematics/MaterialNode.h @@ -26,19 +26,19 @@ public: CAnimMaterialNode(const int id); static void Initialize(); - virtual void SetName(const char* name); + void SetName(const char* name) override; ////////////////////////////////////////////////////////////////////////// // Overrides from CAnimNode ////////////////////////////////////////////////////////////////////////// - void Animate(SAnimContext& ec); + void Animate(SAnimContext& ec) override; void AddTrack(IAnimTrack* track) override; ////////////////////////////////////////////////////////////////////////// // Supported tracks description. ////////////////////////////////////////////////////////////////////////// - virtual unsigned int GetParamCount() const; - virtual CAnimParamType GetParamType(unsigned int nIndex) const; + unsigned int GetParamCount() const override; + CAnimParamType GetParamType(unsigned int nIndex) const override; AZStd::string GetParamName(const CAnimParamType& paramType) const override; virtual void GetKeyValueRange(float& fMin, float& fMax) const { fMin = m_fMinKeyValue; fMax = m_fMaxKeyValue; }; @@ -49,7 +49,7 @@ public: static void Reflect(AZ::ReflectContext* context); protected: - virtual bool GetParamInfoFromType(const CAnimParamType& paramId, SParamInfo& info) const; + bool GetParamInfoFromType(const CAnimParamType& paramId, SParamInfo& info) const override; void UpdateDynamicParamsInternal() override; private: diff --git a/Gems/Maestro/Code/Source/Cinematics/Movie.h b/Gems/Maestro/Code/Source/Cinematics/Movie.h index 8ab888178d..15295da353 100644 --- a/Gems/Maestro/Code/Source/Cinematics/Movie.h +++ b/Gems/Maestro/Code/Source/Cinematics/Movie.h @@ -42,7 +42,7 @@ class CLightAnimWrapper { public: // ILightAnimWrapper interface - virtual bool Resolve(); + bool Resolve() override; public: static CLightAnimWrapper* Create(const char* name); @@ -80,116 +80,116 @@ public: CMovieSystem(ISystem* system); CMovieSystem(); - void Release() { delete this; }; + void Release() override { delete this; }; - void SetUser(IMovieUser* pUser) { m_pUser = pUser; } - IMovieUser* GetUser() { return m_pUser; } + void SetUser(IMovieUser* pUser) override { m_pUser = pUser; } + IMovieUser* GetUser() override { return m_pUser; } - ISystem* GetSystem() { return m_pSystem; } + ISystem* GetSystem() override { return m_pSystem; } - IAnimSequence* CreateSequence(const char* sequence, bool bLoad = false, uint32 id = 0, SequenceType = kSequenceTypeDefault, AZ::EntityId entityId = AZ::EntityId()); + IAnimSequence* CreateSequence(const char* sequence, bool bLoad = false, uint32 id = 0, SequenceType = kSequenceTypeDefault, AZ::EntityId entityId = AZ::EntityId()) override; - void AddSequence(IAnimSequence* pSequence); - void RemoveSequence(IAnimSequence* pSequence); + void AddSequence(IAnimSequence* pSequence) override; + void RemoveSequence(IAnimSequence* pSequence) override; IAnimSequence* FindLegacySequenceByName(const char* sequence) const override; IAnimSequence* FindSequence(const AZ::EntityId& componentEntitySequenceId) const override; IAnimSequence* FindSequenceById(uint32 id) const override; - IAnimSequence* GetSequence(int i) const; - int GetNumSequences() const; - IAnimSequence* GetPlayingSequence(int i) const; - int GetNumPlayingSequences() const; - bool IsCutScenePlaying() const; + IAnimSequence* GetSequence(int i) const override; + int GetNumSequences() const override; + IAnimSequence* GetPlayingSequence(int i) const override; + int GetNumPlayingSequences() const override; + bool IsCutScenePlaying() const override; uint32 GrabNextSequenceId() override { return m_nextSequenceId++; } void OnSetSequenceId(uint32 sequenceId) override; - int OnSequenceRenamed(const char* before, const char* after); - int OnCameraRenamed(const char* before, const char* after); + int OnSequenceRenamed(const char* before, const char* after) override; + int OnCameraRenamed(const char* before, const char* after) override; - bool AddMovieListener(IAnimSequence* pSequence, IMovieListener* pListener); - bool RemoveMovieListener(IAnimSequence* pSequence, IMovieListener* pListener); + bool AddMovieListener(IAnimSequence* pSequence, IMovieListener* pListener) override; + bool RemoveMovieListener(IAnimSequence* pSequence, IMovieListener* pListener) override; - void RemoveAllSequences(); + void RemoveAllSequences() override; ////////////////////////////////////////////////////////////////////////// // Sequence playback. ////////////////////////////////////////////////////////////////////////// void PlaySequence(const char* sequence, IAnimSequence* parentSeq = NULL, bool bResetFX = true, - bool bTrackedSequence = false, float startTime = -FLT_MAX, float endTime = -FLT_MAX); + bool bTrackedSequence = false, float startTime = -FLT_MAX, float endTime = -FLT_MAX) override; void PlaySequence(IAnimSequence* seq, IAnimSequence* parentSeq = NULL, bool bResetFX = true, - bool bTrackedSequence = false, float startTime = -FLT_MAX, float endTime = -FLT_MAX); - void PlayOnLoadSequences(); + bool bTrackedSequence = false, float startTime = -FLT_MAX, float endTime = -FLT_MAX) override; + void PlayOnLoadSequences() override; - bool StopSequence(const char* sequence); - bool StopSequence(IAnimSequence* seq); - bool AbortSequence(IAnimSequence* seq, bool bLeaveTime = false); + bool StopSequence(const char* sequence) override; + bool StopSequence(IAnimSequence* seq) override; + bool AbortSequence(IAnimSequence* seq, bool bLeaveTime = false) override; - void StopAllSequences(); - void StopAllCutScenes(); + void StopAllSequences() override; + void StopAllCutScenes() override; void Pause(bool bPause); - void Reset(bool bPlayOnReset, bool bSeekToStart); - void StillUpdate(); - void PreUpdate(const float dt); - void PostUpdate(const float dt); - void Render(); + void Reset(bool bPlayOnReset, bool bSeekToStart) override; + void StillUpdate() override; + void PreUpdate(const float dt) override; + void PostUpdate(const float dt) override; + void Render() override; - void EnableFixedStepForCapture(float step); - void DisableFixedStepForCapture(); - void StartCapture(const ICaptureKey& key, int frame); - void EndCapture(); - void ControlCapture(); - bool IsCapturing() const; + void EnableFixedStepForCapture(float step) override; + void DisableFixedStepForCapture() override; + void StartCapture(const ICaptureKey& key, int frame) override; + void EndCapture() override; + void ControlCapture() override; + bool IsCapturing() const override; - bool IsPlaying(IAnimSequence* seq) const; + bool IsPlaying(IAnimSequence* seq) const override; - void Pause(); - void Resume(); + void Pause() override; + void Resume() override; - virtual void PauseCutScenes(); - virtual void ResumeCutScenes(); + void PauseCutScenes() override; + void ResumeCutScenes() override; - void SetRecording(bool recording) { m_bRecording = recording; }; - bool IsRecording() const { return m_bRecording; }; + void SetRecording(bool recording) override { m_bRecording = recording; }; + bool IsRecording() const override { return m_bRecording; }; - void EnableCameraShake(bool bEnabled){ m_bEnableCameraShake = bEnabled; }; + void EnableCameraShake(bool bEnabled) override{ m_bEnableCameraShake = bEnabled; }; - void SetCallback(IMovieCallback* pCallback) { m_pCallback = pCallback; } - IMovieCallback* GetCallback() { return m_pCallback; } + void SetCallback(IMovieCallback* pCallback) override { m_pCallback = pCallback; } + IMovieCallback* GetCallback() override { return m_pCallback; } void Callback(IMovieCallback::ECallbackReason Reason, IAnimNode* pNode); - const SCameraParams& GetCameraParams() const { return m_ActiveCameraParams; } - void SetCameraParams(const SCameraParams& Params); + const SCameraParams& GetCameraParams() const override { return m_ActiveCameraParams; } + void SetCameraParams(const SCameraParams& Params) override; - void SendGlobalEvent(const char* pszEvent); - void SetSequenceStopBehavior(ESequenceStopBehavior behavior); - IMovieSystem::ESequenceStopBehavior GetSequenceStopBehavior(); + void SendGlobalEvent(const char* pszEvent) override; + void SetSequenceStopBehavior(ESequenceStopBehavior behavior) override; + IMovieSystem::ESequenceStopBehavior GetSequenceStopBehavior() override; - float GetPlayingTime(IAnimSequence* pSeq); - bool SetPlayingTime(IAnimSequence* pSeq, float fTime); + float GetPlayingTime(IAnimSequence* pSeq) override; + bool SetPlayingTime(IAnimSequence* pSeq, float fTime) override; - float GetPlayingSpeed(IAnimSequence* pSeq); - bool SetPlayingSpeed(IAnimSequence* pSeq, float fTime); + float GetPlayingSpeed(IAnimSequence* pSeq) override; + bool SetPlayingSpeed(IAnimSequence* pSeq, float fTime) override; - bool GetStartEndTime(IAnimSequence* pSeq, float& fStartTime, float& fEndTime); - bool SetStartEndTime(IAnimSequence* pSeq, const float fStartTime, const float fEndTime); + bool GetStartEndTime(IAnimSequence* pSeq, float& fStartTime, float& fEndTime) override; + bool SetStartEndTime(IAnimSequence* pSeq, const float fStartTime, const float fEndTime) override; - void GoToFrame(const char* seqName, float targetFrame); + void GoToFrame(const char* seqName, float targetFrame) override; - const char* GetOverrideCamName() const + const char* GetOverrideCamName() const override { return m_mov_overrideCam->GetString(); } - virtual bool IsPhysicsEventsEnabled() const { return m_bPhysicsEventsEnabled; } - virtual void EnablePhysicsEvents(bool enable) { m_bPhysicsEventsEnabled = enable; } + bool IsPhysicsEventsEnabled() const override { return m_bPhysicsEventsEnabled; } + void EnablePhysicsEvents(bool enable) override { m_bPhysicsEventsEnabled = enable; } - virtual void EnableBatchRenderMode(bool bOn) { m_bBatchRenderMode = bOn; } - virtual bool IsInBatchRenderMode() const { return m_bBatchRenderMode; } + void EnableBatchRenderMode(bool bOn) override { m_bBatchRenderMode = bOn; } + bool IsInBatchRenderMode() const override { return m_bBatchRenderMode; } void SerializeNodeType(AnimNodeType& animNodeType, XmlNodeRef& xmlNode, bool bLoading, const uint version, int flags) override; - virtual void LoadParamTypeFromXml(CAnimParamType& animParamType, const XmlNodeRef& xmlNode, const uint version) override; - virtual void SaveParamTypeToXml(const CAnimParamType& animParamType, XmlNodeRef& xmlNode) override; - virtual void SerializeParamType(CAnimParamType& animParamType, XmlNodeRef& xmlNode, bool bLoading, const uint version); + void LoadParamTypeFromXml(CAnimParamType& animParamType, const XmlNodeRef& xmlNode, const uint version) override; + void SaveParamTypeToXml(const CAnimParamType& animParamType, XmlNodeRef& xmlNode) override; + void SerializeParamType(CAnimParamType& animParamType, XmlNodeRef& xmlNode, bool bLoading, const uint version) override; static const char* GetParamTypeName(const CAnimParamType& animParamType); @@ -226,8 +226,8 @@ private: void UpdateInternal(const float dt, const bool bPreUpdate); #ifdef MOVIESYSTEM_SUPPORT_EDITING - virtual AnimNodeType GetNodeTypeFromString(const char* pString) const; - virtual CAnimParamType GetParamTypeFromString(const char* pString) const; + AnimNodeType GetNodeTypeFromString(const char* pString) const override; + CAnimParamType GetParamTypeFromString(const char* pString) const override; #endif ISystem* m_pSystem; diff --git a/Gems/Maestro/Code/Source/Cinematics/SceneNode.cpp b/Gems/Maestro/Code/Source/Cinematics/SceneNode.cpp index 10014a8700..b93215d397 100644 --- a/Gems/Maestro/Code/Source/Cinematics/SceneNode.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/SceneNode.cpp @@ -89,13 +89,13 @@ namespace { AZ::Quaternion quat = LYQuaternionToAZQuaternion(localRotation); AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, quat); } - float GetFoV() const + float GetFoV() const override { float retFoV = DEFAULT_FOV; Camera::CameraRequestBus::EventResult(retFoV, m_cameraEntityId, &Camera::CameraComponentRequests::GetFovDegrees); return retFoV; } - float GetNearZ() const + float GetNearZ() const override { float retNearZ = DEFAULT_NEAR; Camera::CameraRequestBus::EventResult(retNearZ, m_cameraEntityId, &Camera::CameraComponentRequests::GetNearClipDistance); diff --git a/Gems/Maestro/Code/Source/Cinematics/SceneNode.h b/Gems/Maestro/Code/Source/Cinematics/SceneNode.h index d4d0410728..c577839fce 100644 --- a/Gems/Maestro/Code/Source/Cinematics/SceneNode.h +++ b/Gems/Maestro/Code/Source/Cinematics/SceneNode.h @@ -65,12 +65,12 @@ public: ////////////////////////////////////////////////////////////////////////// // Overrides from CAnimNode ////////////////////////////////////////////////////////////////////////// - void Animate(SAnimContext& ec); - void CreateDefaultTracks(); + void Animate(SAnimContext& ec) override; + void CreateDefaultTracks() override; - virtual void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks); + void Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks) override; - virtual void Activate(bool bActivate); + void Activate(bool bActivate) override; // overridden from IAnimNode/CAnimNode void OnStart() override; @@ -80,11 +80,11 @@ public: void OnLoop() override; ////////////////////////////////////////////////////////////////////////// - virtual unsigned int GetParamCount() const; - virtual CAnimParamType GetParamType(unsigned int nIndex) const; + unsigned int GetParamCount() const override; + CAnimParamType GetParamType(unsigned int nIndex) const override; - virtual void PrecacheStatic(float startTime) override; - virtual void PrecacheDynamic(float time) override; + void PrecacheStatic(float startTime) override; + void PrecacheDynamic(float time) override; static void Reflect(AZ::ReflectContext* context); @@ -92,7 +92,7 @@ public: static IAnimSequence* GetSequenceFromSequenceKey(const ISequenceKey& sequenceKey); protected: - virtual bool GetParamInfoFromType(const CAnimParamType& paramId, SParamInfo& info) const; + bool GetParamInfoFromType(const CAnimParamType& paramId, SParamInfo& info) const override; void ResetSounds() override; void ReleaseSounds(); // Stops audio @@ -111,7 +111,7 @@ private: void InterpolateCameras(SCameraParams& retInterpolatedCameraParams, ISceneCamera* firstCamera, ISelectKey& firstKey, ISelectKey& secondKey, float time); - virtual void InitializeTrackDefaultValue(IAnimTrack* pTrack, const CAnimParamType& paramType) override; + void InitializeTrackDefaultValue(IAnimTrack* pTrack, const CAnimParamType& paramType) override; // Cached parameters of node at given time. float m_time = 0.0f; diff --git a/Gems/Maestro/Code/Source/Cinematics/ScreenFaderTrack.h b/Gems/Maestro/Code/Source/Cinematics/ScreenFaderTrack.h index 410dd41e2c..b5cb81dbc6 100644 --- a/Gems/Maestro/Code/Source/Cinematics/ScreenFaderTrack.h +++ b/Gems/Maestro/Code/Source/Cinematics/ScreenFaderTrack.h @@ -30,8 +30,8 @@ public: //----------------------------------------------------------------------------- //! IAnimTrack Method Overriding. //----------------------------------------------------------------------------- - virtual void GetKeyInfo(int key, const char*& description, float& duration); - virtual void SerializeKey(IScreenFaderKey& key, XmlNodeRef& keyNode, bool bLoading); + void GetKeyInfo(int key, const char*& description, float& duration) override; + void SerializeKey(IScreenFaderKey& key, XmlNodeRef& keyNode, bool bLoading) override; void SetFlags(int flags) override; void PreloadTextures(); diff --git a/Gems/Maestro/Code/Source/Cinematics/SoundTrack.h b/Gems/Maestro/Code/Source/Cinematics/SoundTrack.h index c55b2de520..435eccfb0b 100644 --- a/Gems/Maestro/Code/Source/Cinematics/SoundTrack.h +++ b/Gems/Maestro/Code/Source/Cinematics/SoundTrack.h @@ -29,11 +29,11 @@ public: AZ_CLASS_ALLOCATOR(CSoundTrack, AZ::SystemAllocator, 0); AZ_RTTI(CSoundTrack, "{B87D8805-F583-4154-B554-45518BC487F4}", IAnimTrack); - void GetKeyInfo(int key, const char*& description, float& duration); - void SerializeKey(ISoundKey& key, XmlNodeRef& keyNode, bool bLoading); + void GetKeyInfo(int key, const char*& description, float& duration) override; + void SerializeKey(ISoundKey& key, XmlNodeRef& keyNode, bool bLoading) override; //! Check if track is masked - virtual bool IsMasked(const uint32 mask) const { return (mask & eTrackMask_MaskSound) != 0; } + bool IsMasked(const uint32 mask) const override { return (mask & eTrackMask_MaskSound) != 0; } bool UsesMute() const override { return true; } diff --git a/Gems/Maestro/Code/Source/Cinematics/TrackEventTrack.h b/Gems/Maestro/Code/Source/Cinematics/TrackEventTrack.h index 13a6549220..b0a7de63a7 100644 --- a/Gems/Maestro/Code/Source/Cinematics/TrackEventTrack.h +++ b/Gems/Maestro/Code/Source/Cinematics/TrackEventTrack.h @@ -70,9 +70,9 @@ public: ////////////////////////////////////////////////////////////////////////// // Overrides of IAnimTrack. ////////////////////////////////////////////////////////////////////////// - void GetKeyInfo(int key, const char*& description, float& duration); - void SerializeKey(IEventKey& key, XmlNodeRef& keyNode, bool bLoading); - void SetKey(int index, IKey* key); + void GetKeyInfo(int key, const char*& description, float& duration) override; + void SerializeKey(IEventKey& key, XmlNodeRef& keyNode, bool bLoading) override; + void SetKey(int index, IKey* key) override; void InitPostLoad(IAnimSequence* sequence) override; static void Reflect(AZ::ReflectContext* context); diff --git a/Gems/Maestro/Code/Source/Components/EditorSequenceComponent.h b/Gems/Maestro/Code/Source/Components/EditorSequenceComponent.h index 83caaeea19..76b21faff4 100644 --- a/Gems/Maestro/Code/Source/Components/EditorSequenceComponent.h +++ b/Gems/Maestro/Code/Source/Components/EditorSequenceComponent.h @@ -78,7 +78,7 @@ namespace Maestro ////////////////////////////////////////////////////////////////////////// // TickBus - used to refresh property displays when values are animated - virtual void OnTick(float deltaTime, AZ::ScriptTimePoint time); + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; ////////////////////////////////////////////////////////////////////////// // TODO - this should be on a Bus, right? diff --git a/Gems/Maestro/Code/Source/Components/SequenceAgent.h b/Gems/Maestro/Code/Source/Components/SequenceAgent.h index 892e4ee7d1..333679f372 100644 --- a/Gems/Maestro/Code/Source/Components/SequenceAgent.h +++ b/Gems/Maestro/Code/Source/Components/SequenceAgent.h @@ -20,6 +20,8 @@ namespace Maestro friend class AZ::SerializeContext; protected: + virtual ~SequenceAgent() = default; + // This pure virtual is required for the Editor and RunTime to find the componentTypeId - in the Editor // it accounts for the GenericComponentWrapper component virtual const AZ::Uuid& GetComponentTypeUuid(const AZ::Component& component) const = 0; diff --git a/Gems/Maestro/gem.json b/Gems/Maestro/gem.json index 6ee989aa55..5149df7c14 100644 --- a/Gems/Maestro/gem.json +++ b/Gems/Maestro/gem.json @@ -5,10 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Maestro Cinematics Gem provides Track View, Open 3D Engine's animated sequence and cinematics editor.", - "canonical_tags": ["Gem"], - "user_tags": ["Animation", "Tools", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Animation", + "Tools", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/maestro/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/maestro/", + "dependencies": [ + "LmbrCentral" + ] } - diff --git a/Gems/MessagePopup/gem.json b/Gems/MessagePopup/gem.json index 2560b8869a..05d36bc2df 100644 --- a/Gems/MessagePopup/gem.json +++ b/Gems/MessagePopup/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Message Popup Gem provides an example implementation of popup messages using LyShine in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Sample"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Sample" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/message-popup/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/message-popup/", + "dependencies": [] } diff --git a/Gems/Metastream/gem.json b/Gems/Metastream/gem.json index 429d174cf5..862b17dd1d 100644 --- a/Gems/Metastream/gem.json +++ b/Gems/Metastream/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Metastream Gem provides functionality for an HTTP server that allows broadcasters to customize game streams with overlays of statistics and event data from a game session.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Network", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Network", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/metastream/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/metastream/", + "dependencies": [] } diff --git a/Gems/Microphone/gem.json b/Gems/Microphone/gem.json index cc47cbcd7e..68492ea786 100644 --- a/Gems/Microphone/gem.json +++ b/Gems/Microphone/gem.json @@ -5,9 +5,17 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Microphone Gem provides support for audio input through microphones.", - "canonical_tags": ["Gem"], - "user_tags": ["Audio", "Input"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Audio", + "Input" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/microphone/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/microphone/", + "dependencies": [ + "AudioSystem" + ] } diff --git a/Gems/Multiplayer/Code/CMakeLists.txt b/Gems/Multiplayer/Code/CMakeLists.txt index e8b38c8799..49b404fb1c 100644 --- a/Gems/Multiplayer/Code/CMakeLists.txt +++ b/Gems/Multiplayer/Code/CMakeLists.txt @@ -25,6 +25,9 @@ ly_add_target( AZ::AzCore AZ::AzFramework AZ::AzNetworking + PRIVATE + Gem::EMotionFXStaticLib + Gem::PhysX.Static AUTOGEN_RULES *.AutoPackets.xml,AutoPackets_Header.jinja,$path/$fileprefix.AutoPackets.h *.AutoPackets.xml,AutoPackets_Inline.jinja,$path/$fileprefix.AutoPackets.inl diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Header.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja similarity index 100% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Header.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Source.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja similarity index 95% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Source.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja index 7bb4bdcc2c..c551c8bc20 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Source.jinja +++ b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja @@ -14,7 +14,7 @@ {% set Namespace = dataFiles[0].attrib['Namespace'] %} {% for Component in dataFiles %} {% if Component.attrib['Namespace'] != Namespace %} -#error "mismatched component namespaces detected in declared multiplayer components, expected {{ Namespace }} but found {{ Component.attrib['Namespace'] }}" +#error "mismatched component namespaces detected in declared multiplayer components, expected {{ Namespace }} but {{ Component.attrib['Name'] }} is using {{ Component.attrib['Namespace'] }} namespace." {% endif %} {% endfor %} namespace {{ Namespace }} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Common.jinja similarity index 97% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Common.jinja index 5bf0a4823f..02ab556cea 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja +++ b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Common.jinja @@ -170,12 +170,20 @@ AZ::Event<{{ Property.attrib['Type'] }}> {% set PropertyName = UpperFirst(Property.attrib['Name']) %} {{ ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} {% if IsOverride %} +{% if paramDefines|count > 0 %} void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnection, {{ ', '.join(paramDefines) }}) override {} +{% else %} +void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnection) override {} +{% endif %} {% else %} //! {{ PropertyName }} Handler //! {{ Property.attrib['Description'] }} //! HandleOn {{ HandleOn }} +{% if paramDefines|count > 0 %} virtual void Handle{{ PropertyName }}([[maybe_unused]] AzNetworking::IConnection* invokingConnection, [[maybe_unused]] {{ ', [[maybe_unused]] '.join(paramDefines) }}) {} +{% else %} +virtual void Handle{{ PropertyName }}([[maybe_unused]] AzNetworking::IConnection* invokingConnection) {} +{% endif %} {% endif %} {% endmacro %} {# diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja similarity index 98% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja index 0f62da11f3..41f0fa1b02 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja @@ -7,21 +7,21 @@ {% macro DeclareNetworkPropertyGetter(Property) %} {% set PropertyName = UpperFirst(Property.attrib['Name']) %} {% if Property.attrib['Container'] == 'Array' %} -{% if Property.attrib['IsRewindable']|booleanTrue %} +{% if Property.attrib['IsRewindable']|booleanTrue %} const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& Get{{ PropertyName }}Array() const; -{% else %} +{% else %} const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& Get{{ PropertyName }}Array() const; -{% endif %} +{% endif %} const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}(int32_t index) const; {% if Property.attrib['GenerateEventBindings']|booleanTrue %} void {{ PropertyName }}AddEvent(AZ::Event::Handler& handler); {% endif %} {% elif Property.attrib['Container'] == 'Vector' %} -{% if Property.attrib['IsRewindable']|booleanTrue %} +{% if Property.attrib['IsRewindable']|booleanTrue %} const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& Get{{ PropertyName }}Vector() const; -{% else %} +{% else %} const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& Get{{ PropertyName }}Vector() const; -{% endif %} +{% endif %} const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}(int32_t index) const; const {{ Property.attrib['Type'] }}& {{ PropertyName }}GetBack() const; uint32_t {{ PropertyName }}GetSize() const; @@ -31,6 +31,9 @@ void {{ PropertyName }}SizeChangedAddEvent(AZ::Event::Handler& handler {% endif %} {% else %} const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}() const; +{% if Property.attrib['IsRewindable']|booleanTrue %} +const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}Previous() const; +{% endif %} {% if Property.attrib['GenerateEventBindings']|booleanTrue %} void {{ PropertyName }}AddEvent(AZ::Event<{{ Property.attrib['Type'] }}>::Handler& handler); {% endif %} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja similarity index 99% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja index e57e8dad2f..cf62f6f901 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja @@ -3,11 +3,11 @@ {% macro LowerFirst(text) %}{{ text[0] | lower}}{{ text[1:] }}{% endmacro %} {% macro DefineNetworkPropertyGet(ClassName, Property, Prefix = '') %} {% if Property.attrib['Container'] == 'Array' %} -{% if Property.attrib['IsRewindable']|booleanTrue %} +{% if Property.attrib['IsRewindable']|booleanTrue %} const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const -{% else %} +{% else %} const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const -{% endif %} +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -25,11 +25,11 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}AddEvent(AZ::Even {% endif %} {% elif Property.attrib['Container'] == 'Vector' %} -{% if Property.attrib['IsRewindable']|booleanTrue %} +{% if Property.attrib['IsRewindable']|booleanTrue %} const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const -{% else %} +{% else %} const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const -{% endif %} +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -68,7 +68,12 @@ const {{ Property.attrib['Type'] }}& {{ ClassName }}::Get{{ UpperFirst(Property. { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } - +{% if Property.attrib['IsRewindable']|booleanTrue %} +const {{ Property.attrib['Type'] }}& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Previous() const +{ + return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}.GetPrevious(); +} +{% endif %} {% if Property.attrib['GenerateEventBindings']|booleanTrue %} void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}AddEvent(AZ::Event<{{ Property.attrib['Type'] }}>::Handler& handler) { @@ -904,6 +909,7 @@ enum class NetworkProperties controller->Set{{ UpperFirst(Property.attrib['Name']) }}({{ LowerFirst(Property.attrib['Name']) }}); {% endif %} }) +{% if Property.attrib['GenerateEventBindings']|booleanTrue %} {% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' -%} ->Method("GetOn{{ UpperFirst(Property.attrib['Name']) }}ChangedEvent", [](AZ::EntityId id) -> AZ::Event* {% else %} @@ -931,6 +937,7 @@ enum class NetworkProperties {% else %} ->Attribute(AZ::Script::Attributes::AzEventDescription, AZ::BehaviorAzEventDescription{ "On {{ UpperFirst(Property.attrib['Name']) }} Changed Event", {"New {{ Property.attrib['Type'] }}"} }) {% endif %} +{% endif %} {% endif %} {% endcall %} @@ -1513,7 +1520,7 @@ namespace {{ Component.attrib['Namespace'] }} {{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} {{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Client')|indent(4) }} - behaviorContext->Class<{{ ComponentName }}>("{{ ComponentName }}") + behaviorContext->Class<{{ ComponentBaseName }}>("{{ ComponentBaseName }}") ->Attribute(AZ::Script::Attributes::Module, "{{ LowerFirst(Component.attrib['Namespace']) }}") ->Attribute(AZ::Script::Attributes::Category, "{{ UpperFirst(Component.attrib['Namespace']) }}") diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h index 65bac09726..743315e8bc 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h @@ -35,7 +35,7 @@ namespace Multiplayer using EntityMigrationStartEvent = AZ::Event; using EntityMigrationEndEvent = AZ::Event<>; using EntityServerMigrationEvent = AZ::Event; - using EntityPreRenderEvent = AZ::Event; + using EntityPreRenderEvent = AZ::Event; using EntityCorrectionEvent = AZ::Event<>; //! @class NetBindComponent @@ -118,7 +118,7 @@ namespace Multiplayer void NotifyMigrationStart(ClientInputId migratedInputId); void NotifyMigrationEnd(); void NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId); - void NotifyPreRender(float deltaTime, float blendFactor); + void NotifyPreRender(float deltaTime); void NotifyCorrection(); void AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler); @@ -199,6 +199,8 @@ namespace Multiplayer friend class NetworkEntityManager; friend class EntityReplicationManager; + + friend class HierarchyTests; }; bool NetworkRoleHasController(NetEntityRole networkRole); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkCharacterComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkCharacterComponent.h new file mode 100644 index 0000000000..9af22b1fdf --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkCharacterComponent.h @@ -0,0 +1,108 @@ +/* + * 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 Physics +{ + class Character; +} + +namespace Multiplayer +{ + //! NetworkCharacterRequests + //! ComponentBus handled by NetworkCharacterComponentController. + //! Bus was created for exposing controller methods to script; C++ users should access the controller directly. + class NetworkCharacterRequests : public AZ::ComponentBus + { + public: + //! TryMoveWithVelocity + //! Will move this character entity kinematically through physical world while also ensuring the network stays in-sync. + //! Velocity will be applied over delta-time to determine the movement amount. + //! Returns this entity's world-space position after the move. + virtual AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime) = 0; + }; + + typedef AZ::EBus NetworkCharacterRequestBus; + + + //! NetworkCharacterComponent + //! Provides multiplayer support for game-play player characters. + class NetworkCharacterComponent + : public NetworkCharacterComponentBase + , private PhysX::CharacterGameplayRequestBus::Handler + { + friend class NetworkCharacterComponentController; + + public: + AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkCharacterComponent, s_networkCharacterComponentConcreteUuid, Multiplayer::NetworkCharacterComponentBase) + + static void Reflect(AZ::ReflectContext* context); + + NetworkCharacterComponent(); + + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("NetworkRigidBodyService")); + } + + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + NetworkCharacterComponentBase::GetRequiredServices(required); + required.push_back(AZ_CRC_CE("PhysXCharacterControllerService")); + } + + // AZ::Component + void OnInit() override {} + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + private: + void OnTranslationChangedEvent(const AZ::Vector3& translation); + void OnSyncRewind(); + + // CharacterGameplayRequestBus + bool IsOnGround() const override; + float GetGravityMultiplier() const override { return {}; } + void SetGravityMultiplier([[maybe_unused]] float gravityMultiplier) override {} + AZ::Vector3 GetFallingVelocity() const override { return {}; } + void SetFallingVelocity([[maybe_unused]] const AZ::Vector3& fallingVelocity) override {} + + Physics::Character* m_physicsCharacter = nullptr; + Multiplayer::EntitySyncRewindEvent::Handler m_syncRewindHandler = Multiplayer::EntitySyncRewindEvent::Handler([this]() { OnSyncRewind(); }); + AZ::Event::Handler m_translationEventHandler; + }; + + //! NetworkCharacterComponentController + //! This is the network controller for NetworkCharacterComponent. + //! Class provides the ability to move characters in physical space while keeping the network in-sync. + class NetworkCharacterComponentController + : public NetworkCharacterComponentControllerBase + , private NetworkCharacterRequestBus::Handler + { + public: + AZ_RTTI(NetworkCharacterComponentController, "{C91851A2-8B95-4484-9F97-BFF9D1F528A0}") + static void Reflect(AZ::ReflectContext* context); + NetworkCharacterComponentController(NetworkCharacterComponent& parent); + + // NetworkCharacterComponentControllerBase + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + // NetworkCharacterRequestBus::Handler + //! TryMoveWithVelocity + //! Will move this character entity kinematically through physical world while also ensuring the network stays in-sync. + //! Velocity will be applied over delta-time to determine the movement amount. + //! Returns this entity's world-space position after the move. + AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime) override; + }; +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h new file mode 100644 index 0000000000..45119a89e3 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +namespace Multiplayer +{ + using NetworkHierarchyChangedEvent = AZ::Event; + using NetworkHierarchyLeaveEvent = AZ::Event<>; + + class NetworkHierarchyRequests + : public AZ::ComponentBus + { + public: + //! @returns true if the entity a hierarchical component attached should be considered for inclusion in a hierarchy + //! this should return false when an entity is deactivating + virtual bool IsHierarchyEnabled() const = 0; + + //! @returns hierarchical entities, the first element is the top level root + virtual AZStd::vector GetHierarchicalEntities() const = 0; + + //! @returns the top level root of a hierarchy, or nullptr if this entity is not in a hierarchy + virtual AZ::Entity* GetHierarchicalRoot() const = 0; + + //! @return true if this entity is a child entity within a hierarchy + virtual bool IsHierarchicalChild() const = 0; + + //! @return true if this entity is the top level root of a hierarchy + virtual bool IsHierarchicalRoot() const = 0; + + //! Binds the provided NetworkHierarchyChangedEvent handler to a Network Hierarchy component. + //! @param handler the handler to invoke when the entity's network hierarchy has been modified. + virtual void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) = 0; + + //! Binds the provided NetworkHierarchyLeaveEvent handler to a Network Hierarchy component. + //! @param handler the handler to invoke when the entity left its network hierarchy. + virtual void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) = 0; + }; + + typedef AZ::EBus NetworkHierarchyRequestBus; +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h new file mode 100644 index 0000000000..544fb3d6cb --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace Multiplayer +{ + class NetworkHierarchyRootComponent; + + //! @class NetworkHierarchyChildComponent + //! @brief Component that declares network dependency on the parent of this entity + /* + * The parent of this entity should have @NetworkHierarchyChildComponent (or @NetworkHierarchyRootComponent). + * A network hierarchy is a collection of entities with one @NetworkHierarchyRootComponent at the top parent + * and one or more @NetworkHierarchyChildComponent on its child entities. + */ + class NetworkHierarchyChildComponent final + : public NetworkHierarchyChildComponentBase + , public NetworkHierarchyRequestBus::Handler + { + friend class NetworkHierarchyRootComponent; + + public: + AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyChildComponent, s_networkHierarchyChildComponentConcreteUuid, Multiplayer::NetworkHierarchyChildComponentBase); + + static void Reflect(AZ::ReflectContext* context); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + + NetworkHierarchyChildComponent(); + + //! NetworkHierarchyChildComponentBase overrides. + //! @{ + void OnInit() override; + void OnActivate(EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(EntityIsMigrating entityIsMigrating) override; + //! @} + + //! NetworkHierarchyRequestBus overrides. + //! @{ + bool IsHierarchyEnabled() const override; + bool IsHierarchicalChild() const override; + bool IsHierarchicalRoot() const override { return false; } + AZ::Entity* GetHierarchicalRoot() const override; + AZStd::vector GetHierarchicalEntities() const override; + void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) override; + void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; + //! @} + + protected: + //! Used by @NetworkHierarchyRootComponent + void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); + + private: + AZ::ChildChangedEvent::Handler m_childChangedHandler; + AZ::ParentChangedEvent::Handler m_parentChangedHandler; + + void OnChildChanged(AZ::ChildChangeType type, AZ::EntityId child); + void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId parent); + + //! Points to the top level root. + AZ::Entity* m_rootEntity = nullptr; + + AZ::Event::Handler m_hierarchyRootNetIdChanged; + void OnHierarchyRootNetIdChanged(NetEntityId rootNetId); + + NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent; + NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent; + + //! Set to false when deactivating or otherwise not to be included in hierarchy considerations. + bool m_isHierarchyEnabled = true; + + void NotifyChildrenHierarchyDisbanded(); + }; +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h new file mode 100644 index 0000000000..4c9f94c004 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace Multiplayer +{ + //! @class NetworkHierarchyRootComponent + //! @brief Component that declares the top level entity of a network hierarchy. + /* + * Call @GetHierarchicalEntities to get the list of hierarchical entities. + * A network hierarchy is meant to be a small group of entities. You can control the maximum supported size of + * a network hierarchy by modifying CVar @bg_hierarchyEntityMaxLimit. + * + * A root component marks either a top most root of a hierarchy, or an inner root of an attach hierarchy. + */ + class NetworkHierarchyRootComponent final + : public NetworkHierarchyRootComponentBase + , public NetworkHierarchyRequestBus::Handler + { + friend class NetworkHierarchyChildComponent; + public: + AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyRootComponent, s_networkHierarchyRootComponentConcreteUuid, Multiplayer::NetworkHierarchyRootComponentBase); + + static void Reflect(AZ::ReflectContext* context); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + + NetworkHierarchyRootComponent(); + + //! NetworkHierarchyRootComponentBase overrides. + //! @{ + void OnInit() override; + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + //! @} + + //! NetworkHierarchyRequestBus overrides. + //! @{ + bool IsHierarchyEnabled() const override; + bool IsHierarchicalRoot() const override; + bool IsHierarchicalChild() const override; + AZStd::vector GetHierarchicalEntities() const override; + AZ::Entity* GetHierarchicalRoot() const override; + void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) override; + void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; + //! @} + + protected: + void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); + + private: + AZ::ChildChangedEvent::Handler m_childChangedHandler; + AZ::ParentChangedEvent::Handler m_parentChangedHandler; + + void OnChildChanged(AZ::ChildChangeType type, AZ::EntityId child); + void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId parent); + + NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent; + NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent; + + //! Points to the top level root, if this root is an inner root in this hierarchy. + AZ::Entity* m_rootEntity = nullptr; + + AZStd::vector m_hierarchicalEntities; + + //! Rebuilds hierarchy starting from this root component's entity. + void RebuildHierarchy(); + + //! @param underEntity Walk the child entities that belong to @underEntity and consider adding them to the hierarchy + //! @param currentEntityCount The total number of entities in the hierarchy prior to calling this method, + //! used to avoid adding too many entities to the hierarchy while walking recursively the relevant entities. + //! @currentEntityCount will be modified to reflect the total entity count upon completion of this method. + //! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise + bool RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount); + + //! @param entity Add the child entity and any of its relevant children to the hierarchy + //! @param currentEntityCount The total number of entities in the hierarchy prior to calling this method, + //! used to avoid adding too many entities to the hierarchy while walking recursively the relevant entities. + //! @currentEntityCount will be modified to reflect the total entity count upon completion of this method. + //! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise + bool RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount); + + void SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity); + + //! Set to false when deactivating or otherwise not to be included in hierarchy considerations. + bool m_isHierarchyEnabled = true; + }; +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHitVolumesComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHitVolumesComponent.h new file mode 100644 index 0000000000..215359f209 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHitVolumesComponent.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace Physics +{ + class CharacterRequests; + class CharacterHitDetectionConfiguration; +} + +namespace Multiplayer +{ + class NetworkHitVolumesComponent + : public NetworkHitVolumesComponentBase + , private EMotionFX::Integration::ActorComponentNotificationBus::Handler + { + public: + struct AnimatedHitVolume final + { + AnimatedHitVolume + ( + AzNetworking::ConnectionId connectionId, + Physics::CharacterRequests* character, + const char* hitVolumeName, + const Physics::ColliderConfiguration* colliderConfig, + const Physics::ShapeConfiguration* shapeConfig, + const uint32_t jointIndex + ); + + ~AnimatedHitVolume() = default; + + void UpdateTransform(const AZ::Transform& transform); + void SyncToCurrentTransform(); + + Multiplayer::RewindableObject m_transform; + AZStd::shared_ptr m_physicsShape; + + // Cached so we don't have to do subsequent lookups by name + const Physics::ColliderConfiguration* m_colliderConfig = nullptr; + const Physics::ShapeConfiguration* m_shapeConfig = nullptr; + AZ::Transform m_colliderOffSetTransform; + const AZ::u32 m_jointIndex = 0; + }; + + AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHitVolumesComponent, s_networkHitVolumesComponentConcreteUuid, Multiplayer::NetworkHitVolumesComponentBase); + + static void Reflect(AZ::ReflectContext* context); + + NetworkHitVolumesComponent(); + + void OnInit() override; + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + private: + void OnPreRender(float deltaTime); + void OnTransformUpdate(const AZ::Transform& transform); + void OnSyncRewind(); + + void CreateHitVolumes(); + void DestroyHitVolumes(); + + //! ActorComponentNotificationBus::Handler + //! @{ + void OnActorInstanceCreated(EMotionFX::ActorInstance* actorInstance) override; + void OnActorInstanceDestroyed(EMotionFX::ActorInstance* actorInstance) override; + //! @} + + Physics::CharacterRequests* m_physicsCharacter = nullptr; + EMotionFX::Integration::ActorComponentRequests* m_actorComponent = nullptr; + const Physics::CharacterColliderConfiguration* m_hitDetectionConfig = nullptr; + + AZStd::vector m_animatedHitVolumes; + + Multiplayer::EntitySyncRewindEvent::Handler m_syncRewindHandler; + Multiplayer::EntityPreRenderEvent::Handler m_preRenderHandler; + AZ::TransformChangedEvent::Handler m_transformChangedHandler; + }; +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkRigidBodyComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkRigidBodyComponent.h new file mode 100644 index 0000000000..cec73b1b71 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkRigidBodyComponent.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include + +namespace Physics +{ + class RigidBodyRequests; +} + +namespace Multiplayer +{ + //! Bus for requests to the network rigid body component. + class NetworkRigidBodyRequests : public AZ::ComponentBus + { + }; + using NetworkRigidBodyRequestBus = AZ::EBus; + + class NetworkRigidBodyComponent final + : public NetworkRigidBodyComponentBase + , private NetworkRigidBodyRequestBus::Handler + { + friend class NetworkRigidBodyComponentController; + + public: + AZ_MULTIPLAYER_COMPONENT( + Multiplayer::NetworkRigidBodyComponent, s_networkRigidBodyComponentConcreteUuid, Multiplayer::NetworkRigidBodyComponentBase); + + static void Reflect(AZ::ReflectContext* context); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + NetworkRigidBodyComponent(); + + void OnInit() override; + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + private: + void OnTransformUpdate(const AZ::Transform& worldTm); + void OnSyncRewind(); + + Multiplayer::EntitySyncRewindEvent::Handler m_syncRewindHandler; + AZ::TransformChangedEvent::Handler m_transformChangedHandler; + Physics::RigidBodyRequests* m_physicsRigidBodyComponent = nullptr; + Multiplayer::RewindableObject m_transform; + }; + + class NetworkRigidBodyComponentController + : public NetworkRigidBodyComponentControllerBase + { + public: + NetworkRigidBodyComponentController(NetworkRigidBodyComponent& parent); + + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + void HandleSendApplyImpulse(AzNetworking::IConnection* invokingConnection, const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) override; + }; +} // namespace Multiplayer diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h index 914aeaadd3..35bf6e9f50 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h @@ -29,26 +29,13 @@ namespace Multiplayer void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; private: - void OnPreRender(float deltaTime, float blendFactor); + void OnPreRender(float deltaTime); void OnCorrection(); - - void OnRotationChangedEvent(const AZ::Quaternion& rotation); - void OnTranslationChangedEvent(const AZ::Vector3& translation); - void OnScaleChangedEvent(float scale); - void OnResetCountChangedEvent(); - - void UpdateTargetHostFrameId(); - - AZ::Transform m_previousTransform = AZ::Transform::CreateIdentity(); - AZ::Transform m_targetTransform = AZ::Transform::CreateIdentity(); - - AZ::Event::Handler m_rotationEventHandler; - AZ::Event::Handler m_translationEventHandler; - AZ::Event::Handler m_scaleEventHandler; - AZ::Event::Handler m_resetCountEventHandler; - + void OnParentChanged(NetEntityId parentId); + EntityPreRenderEvent::Handler m_entityPreRenderEventHandler; EntityCorrectionEvent::Handler m_entityCorrectionEventHandler; + AZ::Event::Handler m_parentChangedEventHandler; Multiplayer::HostFrameId m_targetHostFrameId = HostFrameId(0); }; @@ -64,7 +51,9 @@ namespace Multiplayer private: void OnTransformChangedEvent(const AZ::Transform& worldTm); + void OnParentIdChangedEvent(AZ::EntityId oldParent, AZ::EntityId newParent); AZ::TransformChangedEvent::Handler m_transformChangedHandler; + AZ::ParentChangedEvent::Handler m_parentIdChangedHandler; }; } diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index 4f714068db..e32b6188eb 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -193,15 +193,13 @@ namespace Multiplayer m_previousHostFrameId = time->GetHostFrameId(); m_previousHostTimeMs = time->GetHostTimeMs(); m_previousRewindConnectionId = time->GetRewindingConnectionId(); - time->AlterTime(frameId, timeMs, connectionId); m_previousBlendFactor = time->GetHostBlendFactor(); - time->AlterBlendFactor(blendFactor); + time->AlterTime(frameId, timeMs, blendFactor, connectionId); } inline ~ScopedAlterTime() { INetworkTime* time = GetNetworkTime(); - time->AlterTime(m_previousHostFrameId, m_previousHostTimeMs, m_previousRewindConnectionId); - time->AlterBlendFactor(m_previousBlendFactor); + time->AlterTime(m_previousHostFrameId, m_previousHostTimeMs, m_previousBlendFactor, m_previousRewindConnectionId); } private: HostFrameId m_previousHostFrameId = InvalidHostFrameId; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h index 8a5fec869c..5bcf038eff 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace Multiplayer { @@ -75,6 +76,15 @@ namespace Multiplayer const AZ::Transform& transform ) = 0; + //! Requests a network spawnable to instantiate at a given transform + //! This is an async function. The instantiated entities are not available immediately but will be constructed by the spawnable system + //! The spawnable ticket has to be kept for the whole lifetime of the entities + //! @param netSpawnable the network spawnable to spawn + //! @param transform the transform where the spawnable should be spawned + //! @return the ticket for managing the spawned entities + [[nodiscard]] virtual AZStd::unique_ptr RequestNetSpawnableInstantiation( + const AZ::Data::Asset& netSpawnable, const AZ::Transform& transform) = 0; + //! Configures new networked entity //! @param netEntity the entity to setup //! @param prefabEntryId the name of the spawnable the entity originated from diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h index ae47aa8373..c12cbb660b 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h @@ -52,12 +52,6 @@ namespace Multiplayer //! @return the ConnectionId of the connection requesting the rewind operation virtual AzNetworking::ConnectionId GetRewindingConnectionId() const = 0; - //! Get the controlling connection that may be currently altering global game time. - //! Note this abstraction is required at a relatively high level to allow for 'don't rewind the shooter' semantics - //! @param rewindConnectionId if this parameter matches the current rewindConnectionId, it will return the unaltered hostFrameId - //! @return the HostFrameId taking into account the provided rewinding connectionId - virtual HostFrameId GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const = 0; - //! Forcibly sets the current network time to the provided frameId and game time in milliseconds. //! @param frameId the new HostFrameId to use //! @param timeMs the new HostTimeMs to use @@ -66,12 +60,9 @@ namespace Multiplayer //! Alters the current HostFrameId and binds that alteration to the provided ConnectionId. //! @param frameId the new HostFrameId to use //! @param timeMs the new HostTimeMs to use + //! @param blendFactor the factor used to blend between values at the current and previous HostFrameId //! @param rewindConnectionId the rewinding ConnectionId - virtual void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) = 0; - - //! Alters the current Host blend factor. Used to drive interpolation in rewound states. - //! @param blendFactor the blend factor to use - virtual void AlterBlendFactor(float blendFactor) = 0; + virtual void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, float blendFactor, AzNetworking::ConnectionId rewindConnectionId) = 0; //! Syncs all entities contained within a volume to the current rewind state. //! @param rewindVolume the volume to rewind entities within (needed for physics entities) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h index a8af564c15..152f6f47a7 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h @@ -60,7 +60,7 @@ namespace Multiplayer //! @return value in const base type form const BASE_TYPE& Get() const; - //! Const base type retriever for one host frame behind Get(). Only intended for use in SyncRewind contexts. + //! Const base type retriever for one host frame behind Get() when contextually appropriate, otherwise identical to Get(). //! @return value in const base type form const BASE_TYPE& GetPrevious() const; @@ -86,9 +86,13 @@ namespace Multiplayer private: //! Returns what the appropriate current time is for this rewindable property. - //! @return the appropriate current time is for this rewindable property + //! @return the appropriate current time for this rewindable property HostFrameId GetCurrentTimeForProperty() const; + //! Returns what the appropriate previous time is for this rewindable property. + //! @return the appropriate previous time for this rewindable property + HostFrameId GetPreviousTimeForProperty() const; + //! Updates the latest value for this object instance, if frameTime represents a current or future time. //! Any attempts to set old values on the object will fail //! @param value the new value to set in the object history diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.inl b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.inl index b0c9bc0c46..9183a1e9da 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.inl +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.inl @@ -69,7 +69,7 @@ namespace Multiplayer template inline const BASE_TYPE& RewindableObject::GetPrevious() const { - return GetValueForTime(GetCurrentTimeForProperty() - HostFrameId(1)); + return GetValueForTime(GetPreviousTimeForProperty()); } template @@ -118,7 +118,22 @@ namespace Multiplayer inline HostFrameId RewindableObject::GetCurrentTimeForProperty() const { INetworkTime* networkTime = Multiplayer::GetNetworkTime(); - return networkTime->GetHostFrameIdForRewindingConnection(m_owningConnectionId); + if (networkTime->IsTimeRewound() && (m_owningConnectionId == networkTime->GetRewindingConnectionId())) + { + return networkTime->GetUnalteredHostFrameId(); + } + return networkTime->GetHostFrameId(); + } + + template + inline HostFrameId RewindableObject::GetPreviousTimeForProperty() const + { + INetworkTime* networkTime = Multiplayer::GetNetworkTime(); + if (networkTime->IsTimeRewound() && (m_owningConnectionId == networkTime->GetRewindingConnectionId())) + { + return networkTime->GetUnalteredHostFrameId(); + } + return networkTime->GetHostFrameId() - HostFrameId(1); } template diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml index 203750d761..c553bc5351 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml @@ -32,11 +32,11 @@ - + - + diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkCharacterComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkCharacterComponent.AutoComponent.xml new file mode 100644 index 0000000000..83e15800e0 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkCharacterComponent.AutoComponent.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml new file mode 100644 index 0000000000..46523d3724 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml new file mode 100644 index 0000000000..0f33e1f642 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHitVolumesComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHitVolumesComponent.AutoComponent.xml new file mode 100644 index 0000000000..d3c31a5bc0 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHitVolumesComponent.AutoComponent.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkRigidBodyComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkRigidBodyComponent.AutoComponent.xml new file mode 100644 index 0000000000..b6cdfca9c2 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkRigidBodyComponent.AutoComponent.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml index cec005cc26..8ab2e61e5e 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml @@ -12,9 +12,9 @@ - + - + diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp index 418bea79dd..c5f9b265f9 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp @@ -185,12 +185,9 @@ namespace Multiplayer // Discard move input events, client may be speed hacking if (m_clientBankedTime < sv_MaxBankTimeWindowSec) { - // Client blends from previous frame to target so here we subtract blend factor to get to that state - const float blendFactor = AZStd::min(AZStd::max(0.f, input.GetHostBlendFactor()), 1.0f); - const AZ::TimeMs blendMs = AZ::TimeMs(static_cast(static_cast(cl_InputRateMs)) * (1.0f - blendFactor)); m_clientBankedTime = AZStd::min(m_clientBankedTime + clientInputRateSec, (double)sv_MaxBankTimeWindowSec); // clamp to boundary { - ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs() - blendMs, input.GetHostBlendFactor(), invokingConnection->GetConnectionId()); + ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), input.GetHostBlendFactor(), invokingConnection->GetConnectionId()); GetNetBindComponent()->ProcessInput(input, static_cast(clientInputRateSec)); } @@ -436,10 +433,13 @@ namespace Multiplayer NetworkInputArray inputArray(GetEntityHandle()); NetworkInput& input = inputArray[0]; + const float blendFactor = AZStd::min(AZStd::max(0.f, multiplayer->GetCurrentBlendFactor()), 1.0f); + const AZ::TimeMs blendMs = AZ::TimeMs(static_cast(static_cast(cl_InputRateMs)) * (1.0f - blendFactor)); input.SetClientInputId(m_clientInputId); input.SetHostFrameId(networkTime->GetHostFrameId()); - input.SetHostTimeMs(multiplayer->GetCurrentHostTimeMs()); + // Account for the client blending from previous frame to current + input.SetHostTimeMs(multiplayer->GetCurrentHostTimeMs() - blendMs); input.SetHostBlendFactor(multiplayer->GetCurrentBlendFactor()); // Allow components to form the input for this frame diff --git a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp index 43577525c0..634cac74b2 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp @@ -405,9 +405,9 @@ namespace Multiplayer m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId); } - void NetBindComponent::NotifyPreRender(float deltaTime, float blendFactor) + void NetBindComponent::NotifyPreRender(float deltaTime) { - m_entityPreRenderEvent.Signal(deltaTime, blendFactor); + m_entityPreRenderEvent.Signal(deltaTime); } void NetBindComponent::NotifyCorrection() diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp new file mode 100644 index 0000000000..33eb26653a --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + + bool CollisionLayerBasedControllerFilter(const physx::PxController& controllerA, const physx::PxController& controllerB) + { + PHYSX_SCENE_READ_LOCK(controllerA.getActor()->getScene()); + physx::PxRigidDynamic* actorA = controllerA.getActor(); + physx::PxRigidDynamic* actorB = controllerB.getActor(); + + if (actorA && actorA->getNbShapes() > 0 && actorB && actorB->getNbShapes() > 0) + { + physx::PxShape* shapeA = nullptr; + actorA->getShapes(&shapeA, 1, 0); + physx::PxFilterData filterDataA = shapeA->getSimulationFilterData(); + physx::PxShape* shapeB = nullptr; + actorB->getShapes(&shapeB, 1, 0); + physx::PxFilterData filterDataB = shapeB->getSimulationFilterData(); + return PhysX::Utils::Collision::ShouldCollide(filterDataA, filterDataB); + } + + return true; + } + + physx::PxQueryHitType::Enum CollisionLayerBasedObjectPreFilter( + const physx::PxFilterData& filterData, + const physx::PxShape* shape, + const physx::PxRigidActor* actor, + [[maybe_unused]] physx::PxHitFlags& queryFlags) + { + // non-kinematic dynamic bodies should not impede the movement of the character + if (actor->getConcreteType() == physx::PxConcreteType::eRIGID_DYNAMIC) + { + const physx::PxRigidDynamic* rigidDynamic = static_cast(actor); + + bool isKinematic = (rigidDynamic->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC); + if (isKinematic) + { + const PhysX::ActorData* actorData = PhysX::Utils::GetUserData(rigidDynamic); + if (actorData) + { + const AZ::EntityId entityId = actorData->GetEntityId(); + + if (Multiplayer::NetworkRigidBodyRequestBus::FindFirstHandler(entityId) != nullptr) + { + // Network rigid bodies are kinematic on the client but dynamic on the server, + // hence filtering treats these actors as dynamic to support client prediction and avoid desyncs + isKinematic = false; + } + } + } + + if (!isKinematic) + { + return physx::PxQueryHitType::eNONE; + } + } + + // all other cases should be determined by collision filters + if (PhysX::Utils::Collision::ShouldCollide(filterData, shape->getSimulationFilterData())) + { + return physx::PxQueryHitType::eBLOCK; + } + + return physx::PxQueryHitType::eNONE; + } + + void NetworkCharacterComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + } + NetworkCharacterComponentBase::Reflect(context); + NetworkCharacterComponentController::Reflect(context); + } + + NetworkCharacterComponent::NetworkCharacterComponent() + : m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); }) + { + } + + void NetworkCharacterComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + Physics::CharacterRequests* characterRequests = Physics::CharacterRequestBus::FindFirstHandler(GetEntityId()); + m_physicsCharacter = (characterRequests != nullptr) ? characterRequests->GetCharacter() : nullptr; + GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler); + + if (m_physicsCharacter) + { + auto controller = static_cast(m_physicsCharacter); + controller->SetFilterFlags(physx::PxQueryFlag::eSTATIC | physx::PxQueryFlag::eDYNAMIC | physx::PxQueryFlag::ePREFILTER); + if (auto callbackManager = controller->GetCallbackManager()) + { + callbackManager->SetControllerFilter(CollisionLayerBasedControllerFilter); + callbackManager->SetObjectPreFilter(CollisionLayerBasedObjectPreFilter); + } + } + + if (!HasController()) + { + GetNetworkTransformComponent()->TranslationAddEvent(m_translationEventHandler); + } + } + + void NetworkCharacterComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + ; + } + + void NetworkCharacterComponent::OnTranslationChangedEvent([[maybe_unused]] const AZ::Vector3& translation) + { + OnSyncRewind(); + } + + void NetworkCharacterComponent::OnSyncRewind() + { + if (m_physicsCharacter == nullptr) + { + return; + } + + const AZ::Vector3 currPosition = m_physicsCharacter->GetBasePosition(); + if (!currPosition.IsClose(GetNetworkTransformComponent()->GetTranslation())) + { + uint32_t frameId = static_cast(Multiplayer::GetNetworkTime()->GetHostFrameId()); + m_physicsCharacter->SetFrameId(frameId); + //m_physicsCharacter->SetBasePosition(GetNetworkTransformComponent()->GetTranslation()); + } + } + + bool NetworkCharacterComponent::IsOnGround() const + { + auto pxController = static_cast(m_physicsCharacter->GetNativePointer()); + if (!pxController) + { + return true; + } + + physx::PxControllerState state; + pxController->getState(state); + return state.touchedActor != nullptr || (state.collisionFlags & physx::PxControllerCollisionFlag::eCOLLISION_DOWN) != 0; + } + + void NetworkCharacterComponentController::Reflect(AZ::ReflectContext* context) + { + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("NetworkCharacterRequestBus") + ->Event("TryMoveWithVelocity", &NetworkCharacterRequestBus::Events::TryMoveWithVelocity, {{ { "Velocity" }, { "DeltaTime" } }}); + + behaviorContext->Class("NetworkCharacterComponentController") + ->RequestBus("NetworkCharacterRequestBus"); + } + } + + NetworkCharacterComponentController::NetworkCharacterComponentController(NetworkCharacterComponent& parent) + : NetworkCharacterComponentControllerBase(parent) + { + ; + } + + void NetworkCharacterComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + NetworkCharacterRequestBus::Handler::BusConnect(GetEntity()->GetId()); + } + + void NetworkCharacterComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + NetworkCharacterRequestBus::Handler::BusDisconnect(GetEntity()->GetId()); + } + + AZ::Vector3 NetworkCharacterComponentController::TryMoveWithVelocity(const AZ::Vector3& velocity, [[maybe_unused]] float deltaTime) + { + // Ensure any entities that we might interact with are properly synchronized to their rewind state + if (IsAuthority()) + { + const AZ::Aabb entityStartBounds = AZ::Interface::Get()->GetEntityLocalBoundsUnion(GetEntity()->GetId()); + const AZ::Aabb entityFinalBounds = entityStartBounds.GetTranslated(velocity); + AZ::Aabb entitySweptBounds = entityStartBounds; + entitySweptBounds.AddAabb(entityFinalBounds); + Multiplayer::GetNetworkTime()->SyncEntitiesToRewindState(entitySweptBounds); + } + + if ((GetParent().m_physicsCharacter == nullptr) || (velocity.GetLengthSq() <= 0.0f)) + { + return GetEntity()->GetTransform()->GetWorldTranslation(); + } + GetParent().m_physicsCharacter->AddVelocity(velocity); + GetParent().m_physicsCharacter->ApplyRequestedVelocity(deltaTime); + GetEntity()->GetTransform()->SetWorldTranslation(GetParent().m_physicsCharacter->GetBasePosition()); + AZLOG + ( + NET_Movement, + "Moved to position %f x %f x %f", + GetParent().m_physicsCharacter->GetBasePosition().GetX(), + GetParent().m_physicsCharacter->GetBasePosition().GetY(), + GetParent().m_physicsCharacter->GetBasePosition().GetZ() + ); + return GetEntity()->GetTransform()->GetWorldTranslation(); + } +} diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp new file mode 100644 index 0000000000..3124eccaa8 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + void NetworkHierarchyChildComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Network Hierarchy Child", "Declares a network dependency on the root of this hierarchy.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Multiplayer") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) + ; + } + } + NetworkHierarchyChildComponentBase::Reflect(context); + } + + void NetworkHierarchyChildComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("NetworkTransformComponent")); + } + + void NetworkHierarchyChildComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent")); + } + + void NetworkHierarchyChildComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent")); + incompatible.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent")); + } + + NetworkHierarchyChildComponent::NetworkHierarchyChildComponent() + : m_childChangedHandler([this](AZ::ChildChangeType type, AZ::EntityId child) { OnChildChanged(type, child); }) + , m_parentChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId parent) { OnParentChanged(oldParent, parent); }) + , m_hierarchyRootNetIdChanged([this](NetEntityId rootNetId) {OnHierarchyRootNetIdChanged(rootNetId); }) + { + + } + + void NetworkHierarchyChildComponent::OnInit() + { + } + + void NetworkHierarchyChildComponent::OnActivate([[maybe_unused]] EntityIsMigrating entityIsMigrating) + { + m_isHierarchyEnabled = true; + + HierarchyRootAddEvent(m_hierarchyRootNetIdChanged); + NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId()); + + if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent()) + { + transformComponent->BindChildChangedEventHandler(m_childChangedHandler); + transformComponent->BindParentChangedEventHandler(m_parentChangedHandler); + } + } + + void NetworkHierarchyChildComponent::OnDeactivate([[maybe_unused]] EntityIsMigrating entityIsMigrating) + { + m_isHierarchyEnabled = false; + + if (m_rootEntity) + { + if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) + { + root->RebuildHierarchy(); + } + } + + NotifyChildrenHierarchyDisbanded(); + + NetworkHierarchyRequestBus::Handler::BusDisconnect(); + } + + bool NetworkHierarchyChildComponent::IsHierarchyEnabled() const + { + return m_isHierarchyEnabled; + } + + bool NetworkHierarchyChildComponent::IsHierarchicalChild() const + { + return GetHierarchyRoot() != InvalidNetEntityId; + } + + AZ::Entity* NetworkHierarchyChildComponent::GetHierarchicalRoot() const + { + return m_rootEntity; + } + + AZStd::vector NetworkHierarchyChildComponent::GetHierarchicalEntities() const + { + if (m_rootEntity) + { + return m_rootEntity->FindComponent()->GetHierarchicalEntities(); + } + + return {}; + } + + void NetworkHierarchyChildComponent::BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) + { + handler.Connect(m_networkHierarchyChangedEvent); + } + + void NetworkHierarchyChildComponent::BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) + { + handler.Connect(m_networkHierarchyLeaveEvent); + } + + void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot) + { + m_rootEntity = hierarchyRoot; + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + { + NetworkHierarchyChildComponentController* controller = static_cast(GetController()); + if (m_rootEntity) + { + const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId()); + controller->SetHierarchyRoot(netRootId); + + m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); + } + else + { + controller->SetHierarchyRoot(InvalidNetEntityId); + + m_networkHierarchyLeaveEvent.Signal(); + } + } + + if (m_rootEntity == nullptr) + { + NotifyChildrenHierarchyDisbanded(); + } + } + + void NetworkHierarchyChildComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child) + { + if (m_rootEntity) + { + if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) + { + root->RebuildHierarchy(); + } + } + } + + void NetworkHierarchyChildComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, [[maybe_unused]] AZ::EntityId parent) + { + if (m_rootEntity) + { + if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) + { + root->RebuildHierarchy(); + } + } + } + + void NetworkHierarchyChildComponent::OnHierarchyRootNetIdChanged(NetEntityId rootNetId) + { + ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(rootNetId); + if (rootHandle.Exists()) + { + AZ::Entity* newRoot = rootHandle.GetEntity(); + if (m_rootEntity != newRoot) + { + m_rootEntity = newRoot; + m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); + } + } + else + { + m_isHierarchyEnabled = false; + m_rootEntity = nullptr; + m_networkHierarchyLeaveEvent.Signal(); + } + } + + void NetworkHierarchyChildComponent::NotifyChildrenHierarchyDisbanded() + { + AZStd::vector allChildren; + AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren); + for (const AZ::EntityId& childEntityId : allChildren) + { + if (const AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(childEntityId)) + { + if (auto* hierarchyChildComponent = childEntity->FindComponent()) + { + hierarchyChildComponent->SetTopLevelHierarchyRootEntity(nullptr); + } + else if (auto* hierarchyRootComponent = childEntity->FindComponent()) + { + hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr); + } + } + } + } +} diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp new file mode 100644 index 0000000000..4a5e9ce8d2 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -0,0 +1,329 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +AZ_CVAR(uint32_t, bg_hierarchyEntityMaxLimit, 16, nullptr, AZ::ConsoleFunctorFlags::Null, + "Maximum allowed size of network entity hierarchies, including top level entity."); + +namespace Multiplayer +{ + void NetworkHierarchyRootComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Network Hierarchy Root", "Marks the entity as the root of an entity hierarchy.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Multiplayer") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) + ; + } + } + NetworkHierarchyRootComponentBase::Reflect(context); + } + + void NetworkHierarchyRootComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("NetworkTransformComponent")); + } + + void NetworkHierarchyRootComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent")); + } + + void NetworkHierarchyRootComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent")); + incompatible.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent")); + } + + NetworkHierarchyRootComponent::NetworkHierarchyRootComponent() + : m_childChangedHandler([this](AZ::ChildChangeType type, AZ::EntityId child) { OnChildChanged(type, child); }) + , m_parentChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId parent) { OnParentChanged(oldParent, parent); }) + { + } + + void NetworkHierarchyRootComponent::OnInit() + { + } + + void NetworkHierarchyRootComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + m_isHierarchyEnabled = true; + m_hierarchicalEntities.push_back(GetEntity()); + + NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId()); + + if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent()) + { + transformComponent->BindChildChangedEventHandler(m_childChangedHandler); + transformComponent->BindParentChangedEventHandler(m_parentChangedHandler); + } + } + + void NetworkHierarchyRootComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + m_isHierarchyEnabled = false; + + if (m_rootEntity) + { + // Tell parent to re-build the hierarchy + if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) + { + root->RebuildHierarchy(); + } + } + else + { + // Notify children that the hierarchy is disbanding + AZStd::vector allChildren; + AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren); + + for (const AZ::EntityId& childEntityId : allChildren) + { + if (const AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(childEntityId)) + { + SetRootForEntity(nullptr, childEntity); + } + } + } + + m_childChangedHandler.Disconnect(); + m_parentChangedHandler.Disconnect(); + + NetworkHierarchyRequestBus::Handler::BusDisconnect(); + + m_hierarchicalEntities.clear(); + m_rootEntity = nullptr; + } + + bool NetworkHierarchyRootComponent::IsHierarchyEnabled() const + { + return m_isHierarchyEnabled; + } + + bool NetworkHierarchyRootComponent::IsHierarchicalRoot() const + { + return GetHierarchyRoot() == InvalidNetEntityId; + } + + bool NetworkHierarchyRootComponent::IsHierarchicalChild() const + { + return !IsHierarchicalRoot(); + } + + AZStd::vector NetworkHierarchyRootComponent::GetHierarchicalEntities() const + { + return m_hierarchicalEntities; + } + + AZ::Entity* NetworkHierarchyRootComponent::GetHierarchicalRoot() const + { + if (m_rootEntity) + { + return m_rootEntity; + } + + return GetEntity(); + } + + void NetworkHierarchyRootComponent::BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) + { + handler.Connect(m_networkHierarchyChangedEvent); + } + + void NetworkHierarchyRootComponent::BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) + { + handler.Connect(m_networkHierarchyLeaveEvent); + } + + void NetworkHierarchyRootComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child) + { + if (IsHierarchicalRoot()) + { + // Parent-child notifications are not reliable enough to avoid duplicate notifications, + // so we will rebuild from scratch to avoid duplicate entries in @m_hierarchicalEntities. + RebuildHierarchy(); + } + else if (NetworkHierarchyRootComponent* root = GetHierarchicalRoot()->FindComponent()) + { + root->RebuildHierarchy(); + } + } + + void NetworkHierarchyRootComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent) + { + // If the parent is part of a hierarchy, it will detect this entity as a new child and rebuild hierarchy. + // Thus, we only need to take care of a case when the parent is not part of a hierarchy, + // in which case, this entity will be a new root of a new hierarchy. + + if (AZ::Entity* parentEntity = AZ::Interface::Get()->FindEntity(newParent)) + { + if (parentEntity->FindComponent() == nullptr && + parentEntity->FindComponent() == nullptr) + { + RebuildHierarchy(); + } + else + { + m_hierarchicalEntities.clear(); + } + } + else + { + // Detached from parent + RebuildHierarchy(); + } + } + + void NetworkHierarchyRootComponent::RebuildHierarchy() + { + AZStd::vector previousEntities; + m_hierarchicalEntities.swap(previousEntities); + + m_hierarchicalEntities.push_back(GetEntity()); // Add the root. + + uint32_t currentEntityCount = aznumeric_cast(m_hierarchicalEntities.size()); + RecursiveAttachHierarchicalEntities(GetEntityId(), currentEntityCount); + + bool hierarchyChanged = false; + + // Send out join and leave events. + for (AZ::Entity* currentEntity : m_hierarchicalEntities) + { + const auto prevEntityIterator = AZStd::find(previousEntities.begin(), previousEntities.end(), currentEntity); + if (prevEntityIterator != previousEntities.end()) + { + // This entity was here before the build of the hierarchy. + previousEntities.erase(prevEntityIterator); + } + else + { + // This is a newly added entity to the network hierarchy. + hierarchyChanged = true; + SetRootForEntity(GetEntity(), currentEntity); + } + } + + // These entities were removed since last rebuild. + for (const AZ::Entity* previousEntity : previousEntities) + { + SetRootForEntity(nullptr, previousEntity); + } + + if (!previousEntities.empty()) + { + hierarchyChanged = true; + } + + if (hierarchyChanged) + { + m_networkHierarchyChangedEvent.Signal(GetEntityId()); + } + } + + void NetworkHierarchyRootComponent::SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity) + { + if (auto* hierarchyChildComponent = childEntity->FindComponent()) + { + hierarchyChildComponent->SetTopLevelHierarchyRootEntity(root); + } + else if (auto* hierarchyRootComponent = childEntity->FindComponent()) + { + hierarchyRootComponent->SetTopLevelHierarchyRootEntity(root); + } + } + + bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount) + { + AZStd::vector allChildren; + AZ::TransformBus::EventResult(allChildren, underEntity, &AZ::TransformBus::Events::GetChildren); + + for (const AZ::EntityId& newChildId : allChildren) + { + if (!RecursiveAttachHierarchicalChild(newChildId, currentEntityCount)) + { + return false; + } + } + + return true; + } + + bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount) + { + if (currentEntityCount >= bg_hierarchyEntityMaxLimit) + { + AZLOG_WARN("Entity %s is trying to build a network hierarchy that is too large. bg_hierarchyEntityMaxLimit is currently set to (%u)", + GetEntity()->GetName().c_str(), static_cast(bg_hierarchyEntityMaxLimit)); + return false; + } + + if (AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(entity)) + { + auto* hierarchyChildComponent = childEntity->FindComponent(); + auto* hierarchyRootComponent = childEntity->FindComponent(); + + if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchyEnabled()) || + (hierarchyRootComponent && hierarchyRootComponent->IsHierarchyEnabled())) + { + m_hierarchicalEntities.push_back(childEntity); + ++currentEntityCount; + + if (!RecursiveAttachHierarchicalEntities(entity, currentEntityCount)) + { + return false; + } + } + } + + return true; + } + + void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot) + { + m_rootEntity = hierarchyRoot; + + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + { + NetworkHierarchyChildComponentController* controller = static_cast(GetController()); + if (hierarchyRoot) + { + const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(hierarchyRoot->GetId()); + controller->SetHierarchyRoot(netRootId); + } + else + { + controller->SetHierarchyRoot(InvalidNetEntityId); + } + } + + if (m_rootEntity == nullptr) + { + // We lost the parent hierarchical entity, so as a root we need to re-build our own hierarchy. + RebuildHierarchy(); + } + } +} diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHitVolumesComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHitVolumesComponent.cpp new file mode 100644 index 0000000000..c14bb643e1 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHitVolumesComponent.cpp @@ -0,0 +1,221 @@ +/* + * 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 Multiplayer +{ + AZ_CVAR(bool, bg_DrawArticulatedHitVolumes, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables debug draw of articulated hit volumes"); + AZ_CVAR(float, bg_DrawDebugHitVolumeLifetime, 0.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "The lifetime for hit volume draw-debug shapes"); + + AZ_CVAR(float, bg_RewindPositionTolerance, 0.0001f, nullptr, AZ::ConsoleFunctorFlags::Null, "Don't sync the physx entity if the square of delta position is less than this value"); + AZ_CVAR(float, bg_RewindOrientationTolerance, 0.001f, nullptr, AZ::ConsoleFunctorFlags::Null, "Don't sync the physx entity if the square of delta orientation is less than this value"); + + NetworkHitVolumesComponent::AnimatedHitVolume::AnimatedHitVolume + ( + AzNetworking::ConnectionId connectionId, + Physics::CharacterRequests* character, + const char* hitVolumeName, + const Physics::ColliderConfiguration* colliderConfig, + const Physics::ShapeConfiguration* shapeConfig, + const uint32_t jointIndex + ) + : m_colliderConfig(colliderConfig) + , m_shapeConfig(shapeConfig) + , m_jointIndex(jointIndex) + { + m_transform.SetOwningConnectionId(connectionId); + + m_colliderOffSetTransform = AZ::Transform::CreateFromQuaternionAndTranslation(m_colliderConfig->m_rotation, m_colliderConfig->m_position); + + if (m_colliderConfig->m_isExclusive) + { + Physics::SystemRequestBus::BroadcastResult(m_physicsShape, &Physics::SystemRequests::CreateShape, *m_colliderConfig, *m_shapeConfig); + } + else + { + Physics::ColliderConfiguration colliderConfiguration = *m_colliderConfig; + colliderConfiguration.m_isExclusive = true; + colliderConfiguration.m_isSimulated = false; + colliderConfiguration.m_isInSceneQueries = true; + Physics::SystemRequestBus::BroadcastResult(m_physicsShape, &Physics::SystemRequests::CreateShape, colliderConfiguration, *m_shapeConfig); + } + + if (m_physicsShape) + { + m_physicsShape->SetName(hitVolumeName); + character->GetCharacter()->AttachShape(m_physicsShape); + } + } + + void NetworkHitVolumesComponent::AnimatedHitVolume::UpdateTransform(const AZ::Transform& transform) + { + m_transform = transform; + m_physicsShape->SetLocalPose(transform.GetTranslation(), transform.GetRotation()); + } + + void NetworkHitVolumesComponent::AnimatedHitVolume::SyncToCurrentTransform() + { + AZ::Transform rewoundTransform; + const AZ::Transform& targetTransform = m_transform.Get(); + const float blendFactor = Multiplayer::GetNetworkTime()->GetHostBlendFactor(); + if (blendFactor < 1.f) + { + // If a blend factor was supplied, interpolate the transform appropriately + const AZ::Transform& previousTransform = m_transform.GetPrevious(); + rewoundTransform.SetRotation(previousTransform.GetRotation().Slerp(targetTransform.GetRotation(), blendFactor)); + rewoundTransform.SetTranslation(previousTransform.GetTranslation().Lerp(targetTransform.GetTranslation(), blendFactor)); + rewoundTransform.SetUniformScale(AZ::Lerp(previousTransform.GetUniformScale(), targetTransform.GetUniformScale(), blendFactor)); + } + else + { + rewoundTransform = m_transform.Get(); + } + + const AZ::Transform physicsTransform = AZ::Transform::CreateFromQuaternionAndTranslation(m_physicsShape->GetLocalPose().second, m_physicsShape->GetLocalPose().first); + + // Don't call SetLocalPose unless the transforms are actually different + const AZ::Vector3 positionDelta = physicsTransform.GetTranslation() - rewoundTransform.GetTranslation(); + const AZ::Quaternion orientationDelta = physicsTransform.GetRotation() - rewoundTransform.GetRotation(); + + if ((positionDelta.GetLengthSq() >= bg_RewindPositionTolerance) || (orientationDelta.GetLengthSq() >= bg_RewindOrientationTolerance)) + { + m_physicsShape->SetLocalPose(rewoundTransform.GetTranslation(), rewoundTransform.GetRotation()); + } + } + + void NetworkHitVolumesComponent::NetworkHitVolumesComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + } + NetworkHitVolumesComponentBase::Reflect(context); + } + + NetworkHitVolumesComponent::NetworkHitVolumesComponent() + : m_syncRewindHandler([this]() { OnSyncRewind(); }) + , m_preRenderHandler([this](float deltaTime) { OnPreRender(deltaTime); }) + , m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform& worldTm) { OnTransformUpdate(worldTm); }) + { + ; + } + + void NetworkHitVolumesComponent::OnInit() + { + ; + } + + void NetworkHitVolumesComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + EMotionFX::Integration::ActorComponentNotificationBus::Handler::BusConnect(GetEntityId()); + GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler); + m_physicsCharacter = Physics::CharacterRequestBus::FindFirstHandler(GetEntityId()); + GetTransformComponent()->BindTransformChangedEventHandler(m_transformChangedHandler); + OnTransformUpdate(GetTransformComponent()->GetWorldTM()); + } + + void NetworkHitVolumesComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + DestroyHitVolumes(); + EMotionFX::Integration::ActorComponentNotificationBus::Handler::BusDisconnect(); + } + + void NetworkHitVolumesComponent::OnPreRender([[maybe_unused]] float deltaTime) + { + if (m_animatedHitVolumes.size() <= 0) + { + CreateHitVolumes(); + } + + AZ::Vector3 position, scale; + AZ::Quaternion rotation; + for (AnimatedHitVolume& hitVolume : m_animatedHitVolumes) + { + m_actorComponent->GetJointTransformComponents(hitVolume.m_jointIndex, EMotionFX::Integration::Space::ModelSpace, position, rotation, scale); + hitVolume.UpdateTransform(AZ::Transform::CreateFromQuaternionAndTranslation(rotation, position) * hitVolume.m_colliderOffSetTransform); + } + } + + void NetworkHitVolumesComponent::OnTransformUpdate([[maybe_unused]] const AZ::Transform& transform) + { + OnSyncRewind(); + } + + void NetworkHitVolumesComponent::OnSyncRewind() + { + if (m_physicsCharacter && m_physicsCharacter->GetCharacter()) + { + uint32_t frameId = static_cast(Multiplayer::GetNetworkTime()->GetHostFrameId()); + m_physicsCharacter->GetCharacter()->SetFrameId(frameId); + } + + for (AnimatedHitVolume& hitVolume : m_animatedHitVolumes) + { + hitVolume.SyncToCurrentTransform(); + } + } + + void NetworkHitVolumesComponent::CreateHitVolumes() + { + if (m_physicsCharacter == nullptr || m_actorComponent == nullptr) + { + return; + } + + const Physics::AnimationConfiguration* physicsConfig = m_actorComponent->GetPhysicsConfig(); + if (physicsConfig == nullptr) + { + return; + } + + m_hitDetectionConfig = &physicsConfig->m_hitDetectionConfig; + const AzNetworking::ConnectionId owningConnectionId = GetNetBindComponent()->GetOwningConnectionId(); + + m_animatedHitVolumes.reserve(m_hitDetectionConfig->m_nodes.size()); + for (const Physics::CharacterColliderNodeConfiguration& nodeConfig : m_hitDetectionConfig->m_nodes) + { + const AZStd::size_t jointIndex = m_actorComponent->GetJointIndexByName(nodeConfig.m_name.c_str()); + if (jointIndex == EMotionFX::Integration::ActorComponentRequests::s_invalidJointIndex) + { + continue; + } + + for (const AzPhysics::ShapeColliderPair& coliderPair : nodeConfig.m_shapes) + { + const Physics::ColliderConfiguration* colliderConfig = coliderPair.first.get(); + Physics::ShapeConfiguration* shapeConfig = coliderPair.second.get(); + m_animatedHitVolumes.emplace_back(owningConnectionId, m_physicsCharacter, nodeConfig.m_name.c_str(), colliderConfig, shapeConfig, aznumeric_cast(jointIndex)); + } + } + } + + void NetworkHitVolumesComponent::DestroyHitVolumes() + { + m_animatedHitVolumes.clear(); + } + + void NetworkHitVolumesComponent::OnActorInstanceCreated([[maybe_unused]] EMotionFX::ActorInstance* actorInstance) + { + m_actorComponent = EMotionFX::Integration::ActorComponentRequestBus::FindFirstHandler(GetEntity()->GetId()); + } + + void NetworkHitVolumesComponent::OnActorInstanceDestroyed([[maybe_unused]] EMotionFX::ActorInstance* actorInstance) + { + m_actorComponent = nullptr; + } +} diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkRigidBodyComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkRigidBodyComponent.cpp new file mode 100644 index 0000000000..725ebc024c --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Components/NetworkRigidBodyComponent.cpp @@ -0,0 +1,148 @@ +/* + * 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 Multiplayer +{ + AZ_CVAR_EXTERNED(float, bg_RewindPositionTolerance); + AZ_CVAR_EXTERNED(float, bg_RewindOrientationTolerance); + + void NetworkRigidBodyComponent::NetworkRigidBodyComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class()->Version(1); + } + NetworkRigidBodyComponentBase::Reflect(context); + } + + void NetworkRigidBodyComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("NetworkRigidBodyService")); + } + + void NetworkRigidBodyComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("PhysXRigidBodyService")); + } + + void NetworkRigidBodyComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + dependent.push_back(AZ_CRC_CE("TransformService")); + dependent.push_back(AZ_CRC_CE("PhysXRigidBodyService")); + } + + NetworkRigidBodyComponent::NetworkRigidBodyComponent() + : m_syncRewindHandler([this](){ OnSyncRewind(); }) + , m_transformChangedHandler([this]([[maybe_unused]] const AZ::Transform& localTm, const AZ::Transform& worldTm){ OnTransformUpdate(worldTm); }) + { + } + + void NetworkRigidBodyComponent::OnInit() + { + } + + void NetworkRigidBodyComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + NetworkRigidBodyRequestBus::Handler::BusConnect(GetEntityId()); + + GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler); + GetEntity()->FindComponent()->BindTransformChangedEventHandler(m_transformChangedHandler); + + m_physicsRigidBodyComponent = + Physics::RigidBodyRequestBus::FindFirstHandler(GetEntity()->GetId()); + AZ_Assert(m_physicsRigidBodyComponent, "PhysX Rigid Body Component is required on entity %s", GetEntity()->GetName().c_str()); + + if (!HasController()) + { + m_physicsRigidBodyComponent->SetKinematic(true); + } + } + + void NetworkRigidBodyComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + NetworkRigidBodyRequestBus::Handler::BusDisconnect(); + } + + void NetworkRigidBodyComponent::OnTransformUpdate(const AZ::Transform& worldTm) + { + m_transform = worldTm; + + if (!HasController()) + { + m_physicsRigidBodyComponent->SetKinematicTarget(worldTm); + } + } + + void NetworkRigidBodyComponent::OnSyncRewind() + { + uint32_t frameId = static_cast(Multiplayer::GetNetworkTime()->GetHostFrameId()); + + AzPhysics::RigidBody* rigidBody = m_physicsRigidBodyComponent->GetRigidBody(); + rigidBody->SetFrameId(frameId); + + AZ::Transform rewoundTransform; + const AZ::Transform& targetTransform = m_transform.Get(); + const float blendFactor = Multiplayer::GetNetworkTime()->GetHostBlendFactor(); + if (blendFactor < 1.f) + { + // If a blend factor was supplied, interpolate the transform appropriately + const AZ::Transform& previousTransform = m_transform.GetPrevious(); + rewoundTransform.SetRotation(previousTransform.GetRotation().Slerp(targetTransform.GetRotation(), blendFactor)); + rewoundTransform.SetTranslation(previousTransform.GetTranslation().Lerp(targetTransform.GetTranslation(), blendFactor)); + rewoundTransform.SetUniformScale(AZ::Lerp(previousTransform.GetUniformScale(), targetTransform.GetUniformScale(), blendFactor)); + } + else + { + rewoundTransform = m_transform.Get(); + } + const AZ::Transform& physicsTransform = rigidBody->GetTransform(); + + // Don't call SetLocalPose unless the transforms are actually different + const AZ::Vector3 positionDelta = physicsTransform.GetTranslation() - rewoundTransform.GetTranslation(); + const AZ::Quaternion orientationDelta = physicsTransform.GetRotation() - rewoundTransform.GetRotation(); + + if ((positionDelta.GetLengthSq() >= bg_RewindPositionTolerance) || + (orientationDelta.GetLengthSq() >= bg_RewindOrientationTolerance)) + { + rigidBody->SetTransform(rewoundTransform); + } + } + + NetworkRigidBodyComponentController::NetworkRigidBodyComponentController(NetworkRigidBodyComponent& parent) + : NetworkRigidBodyComponentControllerBase(parent) + { + ; + } + + void NetworkRigidBodyComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + ; + } + + void NetworkRigidBodyComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + ; + } + + void NetworkRigidBodyComponentController::HandleSendApplyImpulse + ( + [[maybe_unused]] AzNetworking::IConnection* invokingConnection, + const AZ::Vector3& impulse, + const AZ::Vector3& worldPoint + ) + { + AzPhysics::RigidBody* rigidBody = GetParent().m_physicsRigidBodyComponent->GetRigidBody(); + rigidBody->ApplyLinearImpulseAtWorldPoint(impulse, worldPoint); + } +} // namespace Multiplayer diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index fa08794c4d..d284f100ff 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -26,12 +26,9 @@ namespace Multiplayer } NetworkTransformComponent::NetworkTransformComponent() - : m_rotationEventHandler([this](const AZ::Quaternion& rotation) { OnRotationChangedEvent(rotation); }) - , m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); }) - , m_scaleEventHandler([this](float scale) { OnScaleChangedEvent(scale); }) - , m_resetCountEventHandler([this](const uint8_t&) { OnResetCountChangedEvent(); }) - , m_entityPreRenderEventHandler([this](float deltaTime, float blendFactor) { OnPreRender(deltaTime, blendFactor); }) + : m_entityPreRenderEventHandler([this](float deltaTime) { OnPreRender(deltaTime); }) , m_entityCorrectionEventHandler([this]() { OnCorrection(); }) + , m_parentChangedEventHandler([this](NetEntityId parentId) { OnParentChanged(parentId); }) { ; } @@ -43,15 +40,9 @@ namespace Multiplayer void NetworkTransformComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) { - RotationAddEvent(m_rotationEventHandler); - TranslationAddEvent(m_translationEventHandler); - ScaleAddEvent(m_scaleEventHandler); - ResetCountAddEvent(m_resetCountEventHandler); GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler); GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler); - - // When coming into relevance, reset all blending factors so we don't interpolate to our start position - OnResetCountChangedEvent(); + ParentEntityIdAddEvent(m_parentChangedEventHandler); } void NetworkTransformComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) @@ -59,59 +50,31 @@ namespace Multiplayer ; } - void NetworkTransformComponent::OnRotationChangedEvent(const AZ::Quaternion& rotation) - { - m_previousTransform.SetRotation(m_targetTransform.GetRotation()); - m_targetTransform.SetRotation(rotation); - UpdateTargetHostFrameId(); - } - - void NetworkTransformComponent::OnTranslationChangedEvent(const AZ::Vector3& translation) - { - m_previousTransform.SetTranslation(m_targetTransform.GetTranslation()); - m_targetTransform.SetTranslation(translation); - UpdateTargetHostFrameId(); - } - - void NetworkTransformComponent::OnScaleChangedEvent(float scale) - { - m_previousTransform.SetUniformScale(m_targetTransform.GetUniformScale()); - m_targetTransform.SetUniformScale(scale); - UpdateTargetHostFrameId(); - } - - void NetworkTransformComponent::OnResetCountChangedEvent() - { - m_targetTransform.SetRotation(GetRotation()); - m_targetTransform.SetTranslation(GetTranslation()); - m_targetTransform.SetUniformScale(GetScale()); - m_previousTransform = m_targetTransform; - } - - void NetworkTransformComponent::UpdateTargetHostFrameId() - { - HostFrameId currentHostFrameId = Multiplayer::GetNetworkTime()->GetHostFrameId(); - if (currentHostFrameId > m_targetHostFrameId) - { - m_targetHostFrameId = currentHostFrameId; - } - } - - void NetworkTransformComponent::OnPreRender([[maybe_unused]] float deltaTime, float blendFactor) + void NetworkTransformComponent::OnPreRender([[maybe_unused]] float deltaTime) { if (!HasController()) { AZ::Transform blendTransform; - if (Multiplayer::GetNetworkTime() && Multiplayer::GetNetworkTime()->GetHostFrameId() > m_targetHostFrameId) + blendTransform.SetRotation(GetRotation()); + blendTransform.SetTranslation(GetTranslation()); + blendTransform.SetUniformScale(GetScale()); + + const float blendFactor = GetMultiplayer()->GetCurrentBlendFactor(); + if (!AZ::IsClose(blendFactor, 1.0f)) { - m_previousTransform = m_targetTransform; - blendTransform = m_targetTransform; - } - else - { - blendTransform.SetRotation(m_previousTransform.GetRotation().Slerp(m_targetTransform.GetRotation(), blendFactor)); - blendTransform.SetTranslation(m_previousTransform.GetTranslation().Lerp(m_targetTransform.GetTranslation(), blendFactor)); - blendTransform.SetUniformScale(AZ::Lerp(m_previousTransform.GetUniformScale(), m_targetTransform.GetUniformScale(), blendFactor)); + AZ::Transform blendTransformPrevious; + blendTransformPrevious.SetRotation(GetRotationPrevious()); + blendTransformPrevious.SetTranslation(GetTranslationPrevious()); + blendTransformPrevious.SetUniformScale(GetScalePrevious()); + + if (!blendTransform.IsClose(blendTransformPrevious)) + { + blendTransform.SetRotation(blendTransformPrevious.GetRotation().Slerp(blendTransform.GetRotation(), blendFactor)); + blendTransform.SetTranslation( + blendTransformPrevious.GetTranslation().Lerp(blendTransform.GetTranslation(), blendFactor)); + blendTransform.SetUniformScale( + AZ::Lerp(blendTransformPrevious.GetUniformScale(), blendTransform.GetUniformScale(), blendFactor)); + } } if (!GetTransformComponent()->GetWorldTM().IsClose(blendTransform)) @@ -124,19 +87,38 @@ namespace Multiplayer void NetworkTransformComponent::OnCorrection() { // Snap to latest - OnResetCountChangedEvent(); + AZ::Transform targetTransform; + targetTransform.SetRotation(GetRotation()); + targetTransform.SetTranslation(GetTranslation()); + targetTransform.SetUniformScale(GetScale()); // Hard set the entities transform - if (!GetTransformComponent()->GetWorldTM().IsClose(m_targetTransform)) + if (!GetTransformComponent()->GetWorldTM().IsClose(targetTransform)) { - GetTransformComponent()->SetWorldTM(m_targetTransform); + GetTransformComponent()->SetWorldTM(targetTransform); } } + void NetworkTransformComponent::OnParentChanged(NetEntityId parentId) + { + const ConstNetworkEntityHandle parentEntityHandle = GetNetworkEntityManager()->GetEntity(parentId); + if (parentEntityHandle.Exists()) + { + if (const AZ::Entity* parentEntity = parentEntityHandle.GetEntity()) + { + GetEntity()->GetTransform()->SetParent(parentEntity->GetId()); + } + } + else + { + GetEntity()->GetTransform()->SetParent(AZ::EntityId()); + } + } NetworkTransformComponentController::NetworkTransformComponentController(NetworkTransformComponent& parent) : NetworkTransformComponentControllerBase(parent) , m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform& worldTm) { OnTransformChangedEvent(worldTm); }) + , m_parentIdChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId newParent) { OnParentIdChangedEvent(oldParent, newParent); }) { ; } @@ -145,6 +127,9 @@ namespace Multiplayer { GetParent().GetTransformComponent()->BindTransformChangedEventHandler(m_transformChangedHandler); OnTransformChangedEvent(GetParent().GetTransformComponent()->GetWorldTM()); + + GetParent().GetTransformComponent()->BindParentChangedEventHandler(m_parentIdChangedHandler); + OnParentIdChangedEvent(AZ::EntityId(), GetParent().GetTransformComponent()->GetParentId()); } void NetworkTransformComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) @@ -158,4 +143,14 @@ namespace Multiplayer SetTranslation(worldTm.GetTranslation()); SetScale(worldTm.GetUniformScale()); } + + void NetworkTransformComponentController::OnParentIdChangedEvent([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent) + { + AZ::Entity* parentEntity = AZ::Interface::Get()->FindEntity(newParent); + if (parentEntity) + { + const ConstNetworkEntityHandle parentHandle(parentEntity, GetNetworkEntityTracker()); + SetParentEntityId(parentHandle.GetNetEntityId()); + } + } } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp b/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp index 1ad7be1a0a..030964d81c 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp @@ -6,13 +6,14 @@ * */ +#include +#include +#include +#include #include #include #include -#include #include -#include -#include namespace Multiplayer { @@ -23,7 +24,6 @@ namespace Multiplayer AzNetworking::NetworkingSystemComponent::CreateDescriptor(), MultiplayerSystemComponent::CreateDescriptor(), NetBindComponent::CreateDescriptor(), - NetBindMarkerComponent::CreateDescriptor(), NetworkSpawnableHolderComponent::CreateDescriptor(), }); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 24f865194f..e66eb8d3a3 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -922,7 +922,7 @@ namespace Multiplayer for (NetBindComponent* netBindComponent : gatheredEntities) { - netBindComponent->NotifyPreRender(deltaTime, m_renderBlendFactor); + netBindComponent->NotifyPreRender(deltaTime); } } else @@ -934,7 +934,7 @@ namespace Multiplayer NetBindComponent* netBindComponent = entity->FindComponent(); if (netBindComponent != nullptr) { - netBindComponent->NotifyPreRender(deltaTime, m_renderBlendFactor); + netBindComponent->NotifyPreRender(deltaTime); } } } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index 482d3a1ee8..b45c05cda7 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -75,6 +75,8 @@ namespace Multiplayer void EntityReplicationManager::ActivatePendingEntities() { + AZStd::vector notReadyEntities; + const AZ::TimeMs endTimeMs = AZ::GetElapsedTimeMs() + m_entityActivationTimeSliceMs; while (!m_entitiesPendingActivation.empty()) { @@ -83,7 +85,14 @@ namespace Multiplayer EntityReplicator* entityReplicator = GetEntityReplicator(entityId); if (entityReplicator && !entityReplicator->IsMarkedForRemoval()) { - entityReplicator->ActivateNetworkEntity(); + if (entityReplicator->IsReadyToActivate()) + { + entityReplicator->ActivateNetworkEntity(); + } + else + { + notReadyEntities.push_back(entityId); + } } if (m_entityActivationTimeSliceMs > AZ::TimeMs{ 0 } && AZ::GetElapsedTimeMs() > endTimeMs) { @@ -91,6 +100,11 @@ namespace Multiplayer break; } } + + for (NetEntityId netEntityId : notReadyEntities) + { + m_entitiesPendingActivation.push_back(netEntityId); + } } void EntityReplicationManager::SendUpdates(AZ::TimeMs hostTimeMs) @@ -249,15 +263,15 @@ namespace Multiplayer void EntityReplicationManager::SendEntityUpdates(AZ::TimeMs hostTimeMs) { EntityReplicatorList toSendList = GenerateEntityUpdateList(); - + AZLOG(NET_ReplicationInfo, "Sending %zd updates from %d to %d", toSendList.size(), (uint8_t)GetNetworkEntityManager()->GetHostId(), (uint8_t)GetRemoteHostId()); - + // prep a replication record for send, at this point, everything needs to be sent for (EntityReplicator* replicator : toSendList) { replicator->GetPropertyPublisher()->PrepareSerialization(); } - + // While our to send list is not empty, build up another packet to send do { @@ -524,7 +538,7 @@ namespace Multiplayer bool EntityReplicationManager::HandlePropertyChangeMessage ( - AzNetworking::IConnection* invokingConnection, + AzNetworking::IConnection* invokingConnection, EntityReplicator* entityReplicator, AzNetworking::PacketId packetId, NetEntityId netEntityId, @@ -1137,7 +1151,7 @@ namespace Multiplayer AzNetworking::TrackChangedSerializer outputSerializer(message.m_propertyUpdateData.GetBuffer(), static_cast(message.m_propertyUpdateData.GetSize())); if (!HandlePropertyChangeMessage ( - invokingConnection, + invokingConnection, replicator, AzNetworking::InvalidPacketId, message.m_entityId, diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h index 731a1a7556..ef05e22e3e 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h @@ -153,6 +153,7 @@ namespace Multiplayer { public: OrphanedEntityRpcs(EntityReplicationManager& replicationManager); + virtual ~OrphanedEntityRpcs() = default; void Update(); bool DispatchOrphanedRpcs(EntityReplicator& entityReplicator); void AddOrphanedRpc(NetEntityId entityId, NetworkEntityRpcMessage& entityRpcMessage); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp index 14df1bb028..b684473ea3 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -6,23 +6,25 @@ * */ -#include -#include -#include -#include -#include -#include -#include #include #include +#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include -#include #include #include @@ -48,7 +50,7 @@ namespace Multiplayer , m_onForwardRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); }) , m_onSendAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); }) , m_onForwardAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); }) - , m_onEntityStopHandler([this](const ConstNetworkEntityHandle &) { OnEntityRemovedEvent(); }) + , m_onEntityStopHandler([this](const ConstNetworkEntityHandle&) { OnEntityRemovedEvent(); }) , m_proxyRemovalEvent([this] { OnProxyRemovalTimedEvent(); }, AZ::Name("ProxyRemovalTimedEvent")) { if (auto localEnt = m_entityHandle.GetEntity()) @@ -119,12 +121,12 @@ namespace Multiplayer { m_replicationManager.AddReplicatorToPendingSend(*this); m_propertyPublisher = AZStd::make_unique - ( - GetRemoteNetworkRole(), - !RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False, - m_netBindComponent, - *m_connection - ); + ( + GetRemoteNetworkRole(), + !RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False, + m_netBindComponent, + *m_connection + ); m_netBindComponent->AddEntityDirtiedEventHandler(m_onEntityDirtiedHandler); } else @@ -279,7 +281,7 @@ namespace Multiplayer AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent"); bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) - && (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole()); + && (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole()); bool isClient = GetRemoteNetworkRole() == NetEntityRole::Client; bool isAutonomous = GetBoundLocalNetworkRole() == NetEntityRole::Autonomous; if (isAuthority || isClient || isAutonomous) @@ -306,9 +308,9 @@ namespace Multiplayer bool EntityReplicator::RemoteManagerOwnsEntityLifetime() const { bool isServer = (GetBoundLocalNetworkRole() == NetEntityRole::Server) - && (GetRemoteNetworkRole() == NetEntityRole::Authority); + && (GetRemoteNetworkRole() == NetEntityRole::Authority); bool isClient = (GetBoundLocalNetworkRole() == NetEntityRole::Client) - || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous); + || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous); return isServer || isClient; } @@ -405,6 +407,62 @@ namespace Multiplayer return m_replicationManager.GetResendTimeoutTimeMs(); } + bool EntityReplicator::IsReadyToActivate() const + { + const AZ::Entity* entity = m_entityHandle.GetEntity(); + AZ_Assert(entity, "Entity replicator entity unexpectedly missing"); + + const NetworkHierarchyChildComponent* hierarchyChildComponent = entity->FindComponent(); + const NetworkHierarchyRootComponent* hierarchyRootComponent = nullptr; + + if (hierarchyChildComponent == nullptr) + { + // Child and root hierarchy components are mutually exclusive + hierarchyRootComponent = entity->FindComponent(); + } + + if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchicalChild()) + || (hierarchyRootComponent && hierarchyRootComponent->IsHierarchicalChild())) + { + // If hierarchy is enabled for the entity, check if the parent is available + if (const NetworkTransformComponent* networkTransform = entity->FindComponent()) + { + const NetEntityId parentId = networkTransform->GetParentEntityId(); + /* + * For root entities attached to a level, a network parent won't be set. + * In this case, this entity is the root entity of the hierarchy and it will be activated first. + */ + if (parentId != InvalidNetEntityId) + { + ConstNetworkEntityHandle parentHandle = GetNetworkEntityManager()->GetEntity(parentId); + + const AZ::Entity* parentEntity = parentHandle.GetEntity(); + if (parentEntity && parentEntity->GetState() == AZ::Entity::State::Active) + { + AZLOG + ( + NET_HierarchyActivationInfo, + "Hierchical entity %s asking for activation - granted", + entity->GetName().c_str() + ); + return true; + } + + AZLOG + ( + NET_HierarchyActivationInfo, + "Hierchical entity %s asking for activation - waiting on the parent %u", + entity->GetName().c_str(), + aznumeric_cast(parentId) + ); + return false; + } + } + } + + return true; + } + NetworkEntityUpdateMessage EntityReplicator::GenerateUpdatePacket() { if (IsMarkedForRemoval() && OwnsReplicatorLifetime()) // TODO: clean this up diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.h b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.h index ec4bd8c4f5..e4dc62bc26 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.h @@ -36,7 +36,7 @@ namespace Multiplayer { public: EntityReplicator(EntityReplicationManager& replicationManager, AzNetworking::IConnection* connection, NetEntityRole remoteNetworkRole, const ConstNetworkEntityHandle& entityHandle); - virtual ~EntityReplicator(); + ~EntityReplicator() override; NetEntityRole GetBoundLocalNetworkRole() const; NetEntityRole GetRemoteNetworkRole() const; @@ -62,6 +62,8 @@ namespace Multiplayer bool IsDeletionAcknowledged() const; bool WasMigrated() const; void SetWasMigrated(bool wasMigrated); + // If an entity is part of a network hierarchy then it is only ready to activate when its direct parent entity is active. + bool IsReadyToActivate() const; NetworkEntityUpdateMessage GenerateUpdatePacket(); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index 69d2726c65..bcd4c9aad8 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -281,8 +281,6 @@ namespace Multiplayer void NetworkEntityManager::RemoveEntities() { - //RewindableObjectState::ClearRewoundEntities(); - AZStd::vector removeList; removeList.swap(m_removeList); for (NetEntityId entityId : removeList) @@ -467,8 +465,60 @@ namespace Multiplayer return netEntityId; } - void NetworkEntityManager::OnRootSpawnableAssigned( - [[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) + AZStd::unique_ptr NetworkEntityManager::RequestNetSpawnableInstantiation( + const AZ::Data::Asset& netSpawnable, const AZ::Transform& transform) + { + // Prepare the parameters for the spawning process + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_priority = AzFramework::SpawnablePriority_High; + + const AZ::Name netSpawnableName = + AZ::Interface::Get()->GetSpawnableNameFromAssetId(netSpawnable.GetId()); + + if (netSpawnableName.IsEmpty()) + { + AZ_Error("NetworkEntityManager", false, + "RequestNetSpawnableInstantiation: Requested spawnable %s doesn't exist in the NetworkSpawnableLibrary. Please make sure it is a network spawnable", + netSpawnable.GetHint().c_str()); + return nullptr; + } + + // Pre-insertion callback allows us to do network-specific setup for the entities before they are added to the scene + optionalArgs.m_preInsertionCallback = [netSpawnableName, rootTransform = transform] + (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities) + { + bool shouldUpdateTransform = (rootTransform.IsClose(AZ::Transform::Identity()) == false); + + for (uint32_t netEntityIndex = 0, entitiesSize = aznumeric_cast(entities.size()); + netEntityIndex < entitiesSize; ++netEntityIndex) + { + AZ::Entity* netEntity = *(entities.begin() + netEntityIndex); + + if (shouldUpdateTransform) + { + AzFramework::TransformComponent* netEntityTransform = + netEntity->FindComponent(); + + AZ::Transform worldTm = netEntityTransform->GetWorldTM(); + worldTm = rootTransform * worldTm; + netEntityTransform->SetWorldTM(worldTm); + } + + PrefabEntityId prefabEntityId; + prefabEntityId.m_prefabName = netSpawnableName; + prefabEntityId.m_entityOffset = netEntityIndex; + AZ::Interface::Get()->SetupNetEntity(netEntity, prefabEntityId, NetEntityRole::Authority); + } + }; + + // Spawn with the newly created ticket. This allows the calling code to manage the lifetime of the constructed entities + auto ticket = AZStd::make_unique(netSpawnable); + AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(*ticket, AZStd::move(optionalArgs)); + return ticket; + } + + void NetworkEntityManager::OnRootSpawnableAssigned(AZ::Data::Asset rootSpawnable, + [[maybe_unused]] uint32_t generation) { auto* multiplayer = GetMultiplayer(); const auto agentType = multiplayer->GetAgentType(); @@ -481,7 +531,6 @@ namespace Multiplayer void NetworkEntityManager::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation) { - // TODO: Do we need to clear all entities here? auto* multiplayer = GetMultiplayer(); const auto agentType = multiplayer->GetAgentType(); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h index 9ccc576447..50e6beedad 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h @@ -60,6 +60,9 @@ namespace Multiplayer const AZ::Transform& transform ) override; + AZStd::unique_ptr RequestNetSpawnableInstantiation( + const AZ::Data::Asset& netSpawnable, const AZ::Transform& transform) override; + void SetupNetEntity(AZ::Entity* netEntity, PrefabEntityId prefabEntityId, NetEntityRole netEntityRole) override; uint32_t GetEntityCount() const override; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityTracker.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityTracker.h index 09238acaee..0e632dc7b4 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityTracker.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityTracker.h @@ -37,6 +37,7 @@ namespace Multiplayer NetworkEntityHandle Get(NetEntityId netEntityId); ConstNetworkEntityHandle Get(NetEntityId netEntityId) const; + //! Returns Net Entity ID for a given AZ Entity ID. NetEntityId Get(const AZ::EntityId& entityId) const; //! Returns true if the netEntityId exists. diff --git a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp index db8d6bd2f7..b37f485ff4 100644 --- a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp @@ -65,11 +65,6 @@ namespace Multiplayer return m_rewindingConnectionId; } - HostFrameId NetworkTime::GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const - { - return (IsTimeRewound() && (rewindConnectionId == m_rewindingConnectionId)) ? m_unalteredFrameId : m_hostFrameId; - } - void NetworkTime::ForceSetTime(HostFrameId frameId, AZ::TimeMs timeMs) { AZ_Assert(!IsTimeRewound(), "Forcibly setting network time is unsupported under a rewound time scope"); @@ -79,20 +74,23 @@ namespace Multiplayer m_rewindingConnectionId = AzNetworking::InvalidConnectionId; } - void NetworkTime::AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) + void NetworkTime::AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, float blendFactor, AzNetworking::ConnectionId rewindConnectionId) { m_hostFrameId = frameId; m_hostTimeMs = timeMs; - m_rewindingConnectionId = rewindConnectionId; - } - - void NetworkTime::AlterBlendFactor(float blendFactor) - { m_hostBlendFactor = blendFactor; + m_rewindingConnectionId = rewindConnectionId; } void NetworkTime::SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) { + if (!IsTimeRewound()) + { + // If we're not inside a rewind scope then reset any rewound state and exit + ClearRewoundEntities(); + return; + } + // Since the vis system doesn't support rewound queries, first query with an expanded volume to catch any fast moving entities const AZ::Aabb expandedVolume = rewindVolume.GetExpanded(AZ::Vector3(sv_RewindVolumeExtrudeDistance)); @@ -114,8 +112,15 @@ namespace Multiplayer if (networkTransform != nullptr) { - // We're not presently factoring in interpolated position here - const AZ::Vector3 rewindCenter = networkTransform->GetTranslation(); // Get the rewound position + // Get the rewound position for target host frame ID plus the one preceding it for potential lerp + AZ::Vector3 rewindCenter = networkTransform->GetTranslation(); + const AZ::Vector3 rewindCenterPrevious = networkTransform->GetTranslationPrevious(); + const float blendFactor = GetNetworkTime()->GetHostBlendFactor(); + if (!AZ::IsClose(blendFactor, 1.0f) && !rewindCenter.IsClose(rewindCenterPrevious)) + { + // If we have a blend factor, lerp the translation for accuracy + rewindCenter = rewindCenterPrevious.Lerp(rewindCenter, blendFactor); + } const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb diff --git a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h index 0278ddd2b0..2bcf019623 100644 --- a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h +++ b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h @@ -32,10 +32,8 @@ namespace Multiplayer AZ::TimeMs GetHostTimeMs() const override; float GetHostBlendFactor() const override; AzNetworking::ConnectionId GetRewindingConnectionId() const override; - HostFrameId GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const override; void ForceSetTime(HostFrameId frameId, AZ::TimeMs timeMs) override; - void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) override; - void AlterBlendFactor(float blendFactor) override; + void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, float blendFactor, AzNetworking::ConnectionId rewindConnectionId) override; void SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) override; void ClearRewoundEntities() override; //! @} diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp deleted file mode 100644 index c8abac25cb..0000000000 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Multiplayer -{ - void NetBindMarkerComponent::Reflect(AZ::ReflectContext* context) - { - AZ::SerializeContext* serializeContext = azrtti_cast(context); - if (serializeContext) - { - serializeContext->Class() - ->Version(1) - ->Field("NetEntityIndex", &NetBindMarkerComponent::m_netEntityIndex) - ->Field("NetSpawnableAsset", &NetBindMarkerComponent::m_networkSpawnableAsset); - } - } - - AzFramework::Spawnable* GetSpawnableFromAsset(AZ::Data::Asset& asset) - { - AzFramework::Spawnable* spawnable = asset.GetAs(); - if (!spawnable) - { - asset = - AZ::Data::AssetManager::Instance().GetAsset(asset.GetId(), AZ::Data::AssetLoadBehavior::PreLoad); - AZ::Data::AssetManager::Instance().BlockUntilLoadComplete(asset); - - spawnable = asset.GetAs(); - } - - return spawnable; - } - - - void NetBindMarkerComponent::Activate() - { - const auto agentType = AZ::Interface::Get()->GetAgentType(); - const bool spawnImmediately = - (agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer); - - if (spawnImmediately && m_networkSpawnableAsset.GetId().IsValid()) - { - AZ::Transform worldTm = GetEntity()->FindComponent()->GetWorldTM(); - auto preInsertionCallback = - [worldTm = AZStd::move(worldTm), netEntityIndex = m_netEntityIndex, spawnableAssetId = m_networkSpawnableAsset.GetId()] - (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities) - { - if (entities.size() == 1) - { - AZ::Entity* netEntity = *entities.begin(); - - auto* transformComponent = netEntity->FindComponent(); - transformComponent->SetWorldTM(worldTm); - - AZ::Name spawnableName = AZ::Interface::Get()->GetSpawnableNameFromAssetId(spawnableAssetId); - PrefabEntityId prefabEntityId; - prefabEntityId.m_prefabName = spawnableName; - prefabEntityId.m_entityOffset = static_cast(netEntityIndex); - AZ::Interface::Get()->SetupNetEntity(netEntity, prefabEntityId, NetEntityRole::Authority); - } - else - { - AZ_Error("NetBindMarkerComponent", false, "Requested to spawn 1 entity, but received %d", entities.size()); - } - }; - - m_netSpawnTicket = AzFramework::EntitySpawnTicket(m_networkSpawnableAsset); - AzFramework::SpawnEntitiesOptionalArgs optionalArgs; - optionalArgs.m_preInsertionCallback = AZStd::move(preInsertionCallback); - AzFramework::SpawnableEntitiesInterface::Get()->SpawnEntities( - m_netSpawnTicket, { m_netEntityIndex }, AZStd::move(optionalArgs)); - } - } - - void NetBindMarkerComponent::Deactivate() - { - if(m_netSpawnTicket.IsValid()) - { - AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket); - } - } - - size_t NetBindMarkerComponent::GetNetEntityIndex() const - { - return m_netEntityIndex; - } - - void NetBindMarkerComponent::SetNetEntityIndex(size_t netEntityIndex) - { - m_netEntityIndex = netEntityIndex; - } - - void NetBindMarkerComponent::SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset) - { - m_networkSpawnableAsset = networkSpawnableAsset; - } - - AZ::Data::Asset NetBindMarkerComponent::GetNetworkSpawnableAsset() const - { - return m_networkSpawnableAsset; - } - -} diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.h b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.h deleted file mode 100644 index dce3252200..0000000000 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include -#include -#include - -namespace Multiplayer -{ - //! @class NetBindMarkerComponent - //! @brief Component for tracking net entities in the original non-networked spawnable. - class NetBindMarkerComponent final : public AZ::Component - { - public: - AZ_COMPONENT(NetBindMarkerComponent, "{40612C1B-427D-45C6-A2F0-04E16DF5B718}"); - - static void Reflect(AZ::ReflectContext* context); - - NetBindMarkerComponent() = default; - ~NetBindMarkerComponent() override = default; - - //! AZ::Component overrides. - //! @{ - void Activate() override; - void Deactivate() override; - //! @} - - size_t GetNetEntityIndex() const; - void SetNetEntityIndex(size_t val); - - void SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset); - AZ::Data::Asset GetNetworkSpawnableAsset() const; - - private: - AZ::Data::Asset m_networkSpawnableAsset{AZ::Data::AssetLoadBehavior::PreLoad}; - size_t m_netEntityIndex = 0; - AzFramework::EntitySpawnTicket m_netSpawnTicket; - }; -} // namespace Multiplayer diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index acfec5eb38..e0990aa785 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include @@ -46,7 +45,7 @@ namespace Multiplayer { if (auto* serializeContext = azrtti_cast(context); serializeContext != nullptr) { - serializeContext->Class()->Version(1); + serializeContext->Class()->Version(2); } } @@ -137,8 +136,6 @@ namespace Multiplayer networkSpawnableAsset.Create(networkSpawnable->GetId()); networkSpawnableAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad); - size_t netEntitiesIndexCounter = 0; - for (auto* prefabEntity : prefabNetEntities) { Instance* instance = netEntityToInstanceMap[prefabEntity]; @@ -148,30 +145,11 @@ namespace Multiplayer AZ_Assert(netEntity, "Unable to detach entity %s [%s] from the source prefab instance", prefabEntity->GetName().c_str(), entityId.ToString().c_str()); - // Net entity will need a new ID to avoid IDs collision - netEntity->SetId(AZ::Entity::MakeId()); netEntity->InvalidateDependencies(); netEntity->EvaluateDependencies(); // Insert the entity into the target net spawnable netSpawnableEntities.emplace_back(netEntity); - - // Use the old ID for the breadcrumb entity to keep parent-child relationship in the original spawnable - AZ::Entity* breadcrumbEntity = aznew AZ::Entity(entityId, netEntity->GetName()); - breadcrumbEntity->SetRuntimeActiveByDefault(netEntity->IsRuntimeActiveByDefault()); - - // Marker component is responsible to spawning entities based on the index. - NetBindMarkerComponent* netBindMarkerComponent = breadcrumbEntity->CreateComponent(); - netBindMarkerComponent->SetNetEntityIndex(netEntitiesIndexCounter); - netBindMarkerComponent->SetNetworkSpawnableAsset(networkSpawnableAsset); - - // Copy the transform component from the original entity to have the correct transform and parent-child relationship - AzFramework::TransformComponent* transformComponent = netEntity->FindComponent(); - breadcrumbEntity->CreateComponent(*transformComponent); - - instance->AddEntity(*breadcrumbEntity); - - netEntitiesIndexCounter++; } // Add net spawnable asset holder to the prefab root diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp index c40cb3677b..5d677c6101 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include namespace Multiplayer { @@ -22,16 +24,43 @@ namespace Multiplayer } } + void NetworkSpawnableHolderComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + // TransformService isn't strictly required in this component (Identity transform will be used by default) + // However we need to make sure if there's a component providing TransformService it is activated first. + dependent.push_back(AZ_CRC_CE("TransformService")); + } + NetworkSpawnableHolderComponent::NetworkSpawnableHolderComponent() { } void NetworkSpawnableHolderComponent::Activate() { + const auto agentType = GetMultiplayer()->GetAgentType(); + const bool shouldSpawnNetEntities = + (agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer); + + if(shouldSpawnNetEntities) + { + AZ::Transform rootEntityTransform = AZ::Transform::CreateIdentity(); + + if(auto* transformInterface = GetEntity()->GetTransform()) + { + rootEntityTransform = transformInterface->GetWorldTM(); + } + + INetworkEntityManager* networkEntityManager = GetNetworkEntityManager(); + AZ_Assert(networkEntityManager != nullptr, + "Network Entity Manager must be initialized before NetworkSpawnableHolderComponent is activated"); + + m_netSpawnableTicket = networkEntityManager->RequestNetSpawnableInstantiation(m_networkSpawnableAsset, rootEntityTransform); + } } void NetworkSpawnableHolderComponent::Deactivate() { + m_netSpawnableTicket.reset(); } void NetworkSpawnableHolderComponent::SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset) @@ -39,7 +68,7 @@ namespace Multiplayer m_networkSpawnableAsset = networkSpawnableAsset; } - AZ::Data::Asset NetworkSpawnableHolderComponent::GetNetworkSpawnableAsset() + AZ::Data::Asset NetworkSpawnableHolderComponent::GetNetworkSpawnableAsset() const { return m_networkSpawnableAsset; } diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h index d369bbefd3..c95a1a5442 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace Multiplayer { @@ -21,11 +22,12 @@ namespace Multiplayer public: AZ_COMPONENT(NetworkSpawnableHolderComponent, "{B0E3ADEE-FCB4-4A32-8D4F-6920F1CB08E4}"); - static void Reflect(AZ::ReflectContext* context); - NetworkSpawnableHolderComponent();; ~NetworkSpawnableHolderComponent() override = default; + static void Reflect(AZ::ReflectContext* context); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + //! AZ::Component overrides. //! @{ void Activate() override; @@ -33,9 +35,10 @@ namespace Multiplayer //! @} void SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset); - AZ::Data::Asset GetNetworkSpawnableAsset(); + AZ::Data::Asset GetNetworkSpawnableAsset() const; private: AZ::Data::Asset m_networkSpawnableAsset{ AZ::Data::AssetLoadBehavior::PreLoad }; + AZStd::unique_ptr m_netSpawnableTicket; }; } // namespace Multiplayer diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp new file mode 100644 index 0000000000..316f85c214 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -0,0 +1,390 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + using namespace testing; + using namespace ::UnitTest; + + /* + * Test NetBindComponent activation. This must work before more complicated tests. + */ + TEST_F(HierarchyTests, On_Client_NetBindComponent_Activate) + { + AZStd::unique_ptr entity = AZStd::make_unique(); + entity->CreateComponent(); + SetupEntity(entity, NetEntityId{ 1 }, NetEntityRole::Client); + entity->Activate(); + + StopEntity(entity); + + entity->Deactivate(); + } + + /* + * Hierarchy test - a child entity on a client delaying activation until its hierarchical parent has been activated + */ + TEST_F(HierarchyTests, On_Client_EntityReplicator_DontActivate_BeforeParent) + { + // Create a child entity that will be tested for activation inside a hierarchy + AZStd::unique_ptr childEntity = AZStd::make_unique(); + CreateEntityWithChildHierarchy(childEntity); + SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client); + // child entity is not activated on purpose here, we are about to test conditional activation check + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(childEntity, NetEntityId{ 1 }); + SetHierarchyRootFieldOnNetworkHierarchyChild(childEntity, NetEntityId{ 1 }); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(childEntity.get(), m_networkEntityTracker.get()); + EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); + entityReplicator.Initialize(childHandle); + + // Entity replicator should not be ready to activate the entity because its parent does not exist + EXPECT_EQ(entityReplicator.IsReadyToActivate(), false); + } + + TEST_F(HierarchyTests, On_Client_EntityReplicator_DontActivate_Inner_Root_Before_Top_Root) + { + // Create a child entity that will be tested for activation inside a hierarchy + AZStd::unique_ptr innerRootEntity = AZStd::make_unique(); + CreateEntityWithRootHierarchy(innerRootEntity); + SetupEntity(innerRootEntity, NetEntityId{ 2 }, NetEntityRole::Client); + // child entity is not activated on purpose here, we are about to test conditional activation check + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(innerRootEntity, NetEntityId{ 1 }); + SetHierarchyRootFieldOnNetworkHierarchyChild(innerRootEntity, NetEntityId{ 1 }); + + // Create an entity replicator for the child entity + const NetworkEntityHandle innerRootHandle(innerRootEntity.get(), m_networkEntityTracker.get()); + EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, innerRootHandle); + entityReplicator.Initialize(innerRootHandle); + + // Entity replicator should not be ready to activate the entity because its parent does not exist + EXPECT_EQ(entityReplicator.IsReadyToActivate(), false); + } + + TEST_F(HierarchyTests, On_Client_Not_In_Hierarchy_EntityReplicator_Ignores_Parent) + { + // Create a child entity that will be tested for activation inside a hierarchy + AZStd::unique_ptr childEntity = AZStd::make_unique(); + CreateEntityWithChildHierarchy(childEntity); + SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client); + // child entity is not activated on purpose here, we are about to test conditional activation check + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(childEntity, NetEntityId{ 1 }); + SetHierarchyRootFieldOnNetworkHierarchyChild(childEntity, InvalidNetEntityId); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(childEntity.get(), m_networkEntityTracker.get()); + EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); + entityReplicator.Initialize(childHandle); + + // Entity replicator should not be ready to activate the entity because its parent does not exist + EXPECT_EQ(entityReplicator.IsReadyToActivate(), true); + } + + /* + * Hierarchy test - a child entity on a client allowing activation when its hierarchical parent is active + */ + TEST_F(HierarchyTests, On_Client_EntityReplicator_Activates_AfterParent) + { + AZStd::unique_ptr childEntity = AZStd::make_unique(); + CreateEntityWithChildHierarchy(childEntity); + SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client); + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(childEntity, NetEntityId{ 1 }); + SetHierarchyRootFieldOnNetworkHierarchyChild(childEntity, NetEntityId{ 1 }); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(childEntity.get(), m_networkEntityTracker.get()); + EntityReplicator childEntityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); + childEntityReplicator.Initialize(childHandle); + + // Now let's create a parent entity and activate it + AZStd::unique_ptr parentEntity = AZStd::make_unique(); + CreateEntityWithRootHierarchy(parentEntity); + SetupEntity(parentEntity, NetEntityId{ 1 }, NetEntityRole::Client); + + // Create an entity replicator for the parent entity + const NetworkEntityHandle parentHandle(parentEntity.get(), m_networkEntityTracker.get()); + ON_CALL(*m_mockNetworkEntityManager, GetEntity(_)).WillByDefault(Return(parentHandle)); + EntityReplicator parentEntityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, parentHandle); + parentEntityReplicator.Initialize(parentHandle); + + parentEntity->Activate(); + + // The child should be ready to be activated + EXPECT_EQ(childEntityReplicator.IsReadyToActivate(), true); + + StopEntity(parentEntity); + + parentEntity->Deactivate(); + } + + /* + * Parent -> Child + */ + class ClientSimpleHierarchyTests : public HierarchyTests + { + public: + const NetEntityId RootNetEntityId = NetEntityId{ 1 }; + const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; + + void SetUp() override + { + HierarchyTests::SetUp(); + + m_root = AZStd::make_unique(1, "root", RootNetEntityId, EntityInfo::Role::Root); + m_child = AZStd::make_unique(2, "child", ChildNetEntityId, EntityInfo::Role::Child); + + CreateSimpleHierarchy(*m_root, *m_child); + + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + // now the two entities are under one hierarchy + } + + void TearDown() override + { + m_child.reset(); + m_root.reset(); + + HierarchyTests::TearDown(); + } + + void CreateSimpleHierarchy(EntityInfo& root, EntityInfo& child) + { + PopulateHierarchicalEntity(root); + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Client); + + PopulateHierarchicalEntity(child); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Client); + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(child.m_entity, root.m_netId); + SetHierarchyRootFieldOnNetworkHierarchyChild(child.m_entity, root.m_netId); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); + child.m_replicator->Initialize(childHandle); + + // Create an entity replicator for the root entity + const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get()); + root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, rootHandle); + root.m_replicator->Initialize(rootHandle); + + root.m_entity->Activate(); + child.m_entity->Activate(); + } + + void SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(const AZStd::unique_ptr& entity, NetEntityId value) + { + /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ + constexpr int totalBits = 1 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::Count*/; + constexpr int inHierarchyBit = 0 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::hierarchyRoot_DirtyFlag*/; + + ReplicationRecord currentRecord(NetEntityRole::Client); + currentRecord.m_authorityToClient.AddBits(totalBits); + currentRecord.m_authorityToClient.SetBit(inHierarchyBit, true); + + constexpr uint32_t bufferSize = 100; + AZStd::array buffer = {}; + NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); + inSerializer.Serialize(reinterpret_cast(value), + "hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ + AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + + NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); + + ReplicationRecord notifyRecord = currentRecord; + + entity->FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); + entity->FindComponent()->NotifyStateDeltaChanges(notifyRecord); + } + + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + }; + + TEST_F(ClientSimpleHierarchyTests, Client_Activates_Hierarchy_From_Network_Fields) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchicalRoot(), + m_root->m_entity.get() + ); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 2) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() + ); + } + } + + TEST_F(ClientSimpleHierarchyTests, Client_Detaches_Child_When_Server_Detaches) + { + // simulate server detaching child entity + SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchicalRoot(), + nullptr + ); + } + + TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Updated_Event_On_Child_Detached_On_Server) + { + MockNetworkHierarchyCallbackHandler mock; + EXPECT_CALL(mock, OnNetworkHierarchyUpdated(m_root->m_entity->GetId())); + + m_root->m_entity->FindComponent()->BindNetworkHierarchyChangedEventHandler(mock.m_changedHandler); + + // simulate server detaching a child entity + SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId); + } + + TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Leave_Event_On_Child_Detached_On_Server) + { + MockNetworkHierarchyCallbackHandler mock; + EXPECT_CALL(mock, OnNetworkHierarchyLeave); + + m_child->m_entity->FindComponent()->BindNetworkHierarchyLeaveEventHandler(mock.m_leaveHandler); + + // simulate server detaching a child entity + SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId); + } + + /* + * Parent -> Child -> ChildOfChild + */ + class ClientDeepHierarchyTests : public ClientSimpleHierarchyTests + { + public: + const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 }; + + void SetUp() override + { + ClientSimpleHierarchyTests::SetUp(); + + m_childOfChild = AZStd::make_unique((3), "child of child", ChildOfChildNetEntityId, EntityInfo::Role::Child); + + CreateDeepHierarchyOnClient(*m_childOfChild); + + m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); + } + + void TearDown() override + { + m_childOfChild.reset(); + + ClientSimpleHierarchyTests::TearDown(); + } + + void CreateDeepHierarchyOnClient(EntityInfo& childOfChild) + { + PopulateHierarchicalEntity(childOfChild); + SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Client); + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(childOfChild.m_entity, m_childOfChild->m_netId); + SetHierarchyRootFieldOnNetworkHierarchyChild(childOfChild.m_entity, m_root->m_netId); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childOfChildHandle(childOfChild.m_entity.get(), m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childOfChildHandle); + childOfChild.m_replicator->Initialize(childOfChildHandle); + + childOfChild.m_entity->Activate(); + } + + AZStd::unique_ptr m_childOfChild; + }; + + TEST_F(ClientDeepHierarchyTests, Client_Activates_Hierarchy_From_Network_Fields) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchicalRoot(), + m_root->m_entity.get() + ); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild->m_entity.get() + ); + } + } +} diff --git a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h new file mode 100644 index 0000000000..2deac1aa27 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h @@ -0,0 +1,414 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + using namespace testing; + using namespace ::UnitTest; + + class NetworkHierarchyCallbacks + { + public: + virtual ~NetworkHierarchyCallbacks() = default; + virtual void OnNetworkHierarchyLeave() = 0; + virtual void OnNetworkHierarchyUpdated(const AZ::EntityId& hierarchyRootId) = 0; + }; + + class MockNetworkHierarchyCallbackHandler : public NetworkHierarchyCallbacks + { + public: + MockNetworkHierarchyCallbackHandler() + : m_leaveHandler([this]() { OnNetworkHierarchyLeave(); }) + , m_changedHandler([this](const AZ::EntityId& rootId) { OnNetworkHierarchyUpdated(rootId); }) + { + } + + NetworkHierarchyLeaveEvent::Handler m_leaveHandler; + NetworkHierarchyChangedEvent::Handler m_changedHandler; + + MOCK_METHOD0(OnNetworkHierarchyLeave, void()); + MOCK_METHOD1(OnNetworkHierarchyUpdated, void(const AZ::EntityId&)); + }; + + class HierarchyTests + : public AllocatorsFixture + { + public: + void SetUp() override + { + SetupAllocator(); + AZ::NameDictionary::Create(); + + m_mockComponentApplicationRequests = AZStd::make_unique>(); + AZ::Interface::Register(m_mockComponentApplicationRequests.get()); + + ON_CALL(*m_mockComponentApplicationRequests, AddEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::AddEntity)); + ON_CALL(*m_mockComponentApplicationRequests, FindEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::FindEntity)); + + // register components involved in testing + m_serializeContext = AZStd::make_unique(); + + m_transformDescriptor.reset(AzFramework::TransformComponent::CreateDescriptor()); + m_transformDescriptor->Reflect(m_serializeContext.get()); + + m_netBindDescriptor.reset(NetBindComponent::CreateDescriptor()); + m_netBindDescriptor->Reflect(m_serializeContext.get()); + + m_hierarchyRootDescriptor.reset(NetworkHierarchyRootComponent::CreateDescriptor()); + m_hierarchyRootDescriptor->Reflect(m_serializeContext.get()); + + m_hierarchyChildDescriptor.reset(NetworkHierarchyChildComponent::CreateDescriptor()); + m_hierarchyChildDescriptor->Reflect(m_serializeContext.get()); + + m_netTransformDescriptor.reset(NetworkTransformComponent::CreateDescriptor()); + m_netTransformDescriptor->Reflect(m_serializeContext.get()); + + m_mockMultiplayer = AZStd::make_unique>(); + AZ::Interface::Register(m_mockMultiplayer.get()); + + EXPECT_NE(AZ::Interface::Get(), nullptr); + + // Create space for replication stats + // Without Multiplayer::RegisterMultiplayerComponents() the stats go to invalid id, which is fine for unit tests + GetMultiplayer()->GetStats().ReserveComponentStats(Multiplayer::InvalidNetComponentId, 50, 0); + + m_mockNetworkEntityManager = AZStd::make_unique>(); + + ON_CALL(*m_mockNetworkEntityManager, AddEntityToEntityMap(_, _)).WillByDefault(Invoke(this, &HierarchyTests::AddEntityToEntityMap)); + ON_CALL(*m_mockNetworkEntityManager, GetEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::GetEntity)); + ON_CALL(*m_mockNetworkEntityManager, GetNetEntityIdById(_)).WillByDefault(Invoke(this, &HierarchyTests::GetNetEntityIdById)); + + m_mockTime = AZStd::make_unique>(); + AZ::Interface::Register(m_mockTime.get()); + + m_mockNetworkTime = AZStd::make_unique>(); + AZ::Interface::Register(m_mockNetworkTime.get()); + + ON_CALL(*m_mockMultiplayer, GetNetworkEntityManager()).WillByDefault(Return(m_mockNetworkEntityManager.get())); + EXPECT_NE(AZ::Interface::Get()->GetNetworkEntityManager(), nullptr); + + const IpAddress address("localhost", 1, ProtocolType::Udp); + m_mockConnection = AZStd::make_unique>(ConnectionId{ 1 }, address, ConnectionRole::Connector); + m_mockConnectionListener = AZStd::make_unique(); + + m_networkEntityTracker = AZStd::make_unique(); + ON_CALL(*m_mockNetworkEntityManager, GetNetworkEntityTracker()).WillByDefault(Return(m_networkEntityTracker.get())); + + m_networkEntityAuthorityTracker = AZStd::make_unique(*m_mockNetworkEntityManager); + ON_CALL(*m_mockNetworkEntityManager, GetNetworkEntityAuthorityTracker()).WillByDefault(Return(m_networkEntityAuthorityTracker.get())); + + m_entityReplicationManager = AZStd::make_unique(*m_mockConnection, *m_mockConnectionListener, EntityReplicationManager::Mode::LocalClientToRemoteServer); + + m_console.reset(aznew AZ::Console()); + AZ::Interface::Register(m_console.get()); + m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead()); + + m_multiplayerComponentRegistry = AZStd::make_unique(); + ON_CALL(*m_mockNetworkEntityManager, GetMultiplayerComponentRegistry()).WillByDefault(Return(m_multiplayerComponentRegistry.get())); + RegisterMultiplayerComponents(); + } + + void TearDown() override + { + m_multiplayerComponentRegistry.reset(); + + AZ::Interface::Unregister(m_console.get()); + m_console.reset(); + + m_networkEntityMap.clear(); + m_entities.clear(); + + m_entityReplicationManager.reset(); + + m_mockConnection.reset(); + m_mockConnectionListener.reset(); + m_networkEntityTracker.reset(); + m_networkEntityAuthorityTracker.reset(); + + AZ::Interface::Unregister(m_mockNetworkTime.get()); + AZ::Interface::Unregister(m_mockTime.get()); + AZ::Interface::Unregister(m_mockMultiplayer.get()); + AZ::Interface::Unregister(m_mockComponentApplicationRequests.get()); + + m_mockTime.reset(); + + m_mockNetworkEntityManager.reset(); + m_mockMultiplayer.reset(); + + m_transformDescriptor.reset(); + m_netTransformDescriptor.reset(); + m_hierarchyRootDescriptor.reset(); + m_hierarchyChildDescriptor.reset(); + m_netBindDescriptor.reset(); + m_serializeContext.reset(); + m_mockComponentApplicationRequests.reset(); + + AZ::NameDictionary::Destroy(); + TeardownAllocator(); + } + + AZStd::unique_ptr m_console; + + AZStd::unique_ptr> m_mockComponentApplicationRequests; + AZStd::unique_ptr m_serializeContext; + AZStd::unique_ptr m_transformDescriptor; + AZStd::unique_ptr m_netBindDescriptor; + AZStd::unique_ptr m_hierarchyRootDescriptor; + AZStd::unique_ptr m_hierarchyChildDescriptor; + AZStd::unique_ptr m_netTransformDescriptor; + + AZStd::unique_ptr> m_mockMultiplayer; + AZStd::unique_ptr m_mockNetworkEntityManager; + AZStd::unique_ptr> m_mockTime; + AZStd::unique_ptr> m_mockNetworkTime; + + AZStd::unique_ptr> m_mockConnection; + AZStd::unique_ptr m_mockConnectionListener; + AZStd::unique_ptr m_networkEntityTracker; + AZStd::unique_ptr m_networkEntityAuthorityTracker; + + AZStd::unique_ptr m_entityReplicationManager; + + AZStd::unique_ptr m_multiplayerComponentRegistry;; + + mutable AZStd::map m_networkEntityMap; + + NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) + { + m_networkEntityMap[netEntityId] = entity; + return NetworkEntityHandle(entity, netEntityId, m_networkEntityTracker.get()); + } + + ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const + { + AZ::Entity* entity = m_networkEntityMap[netEntityId]; + return ConstNetworkEntityHandle(entity, m_networkEntityTracker.get()); + } + + NetEntityId GetNetEntityIdById(const AZ::EntityId& entityId) const + { + for (const auto& pair : m_networkEntityMap) + { + if (pair.second->GetId() == entityId) + { + return pair.first; + } + } + + return InvalidNetEntityId; + } + + AZStd::map m_entities; + + bool AddEntity(AZ::Entity* entity) + { + m_entities[entity->GetId()] = entity; + return true; + } + + AZ::Entity* FindEntity(AZ::EntityId entityId) + { + const auto iterator = m_entities.find(entityId); + if (iterator != m_entities.end()) + { + return iterator->second; + } + + return nullptr; + } + + void SetupEntity(const AZStd::unique_ptr& entity, NetEntityId netId, NetEntityRole role) + { + const auto netBindComponent = entity->FindComponent(); + EXPECT_NE(netBindComponent, nullptr); + netBindComponent->PreInit(entity.get(), PrefabEntityId{ AZ::Name("test"), 1 }, netId, role); + entity->Init(); + } + + static void StopEntity(const AZStd::unique_ptr& entity) + { + const auto netBindComponent = entity->FindComponent(); + EXPECT_NE(netBindComponent, nullptr); + netBindComponent->StopEntity(); + } + + static void StopAndDeactivateEntity(AZStd::unique_ptr& entity) + { + if (entity) + { + StopEntity(entity); + entity->Deactivate(); + entity.reset(); + } + } + + void CreateEntityWithRootHierarchy(AZStd::unique_ptr& rootEntity) + { + rootEntity->CreateComponent(); + rootEntity->CreateComponent(); + rootEntity->CreateComponent(); + rootEntity->CreateComponent(); + } + + void CreateEntityWithChildHierarchy(AZStd::unique_ptr& childEntity) + { + childEntity->CreateComponent(); + childEntity->CreateComponent(); + childEntity->CreateComponent(); + childEntity->CreateComponent(); + } + + void SetParentIdOnNetworkTransform(const AZStd::unique_ptr& entity, NetEntityId netParentId) + { + /* Derived from NetworkTransformComponent.AutoComponent.xml */ + constexpr int totalBits = 6 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::Count*/; + constexpr int parentIdBit = 4 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::parentEntityId_DirtyFlag*/; + + ReplicationRecord currentRecord; + currentRecord.m_authorityToClient.AddBits(totalBits); + currentRecord.m_authorityToClient.SetBit(parentIdBit, true); + + constexpr uint32_t bufferSize = 100; + AZStd::array buffer = {}; + NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); + inSerializer.Serialize(reinterpret_cast(netParentId), + "parentEntityId", /* Derived from NetworkTransformComponent.AutoComponent.xml */ + AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + + NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); + + ReplicationRecord notifyRecord = currentRecord; + entity->FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); + entity->FindComponent()->NotifyStateDeltaChanges(notifyRecord); + } + + template + void SetHierarchyRootFieldOnNetworkHierarchyChild(const AZStd::unique_ptr& entity, NetEntityId value) + { + /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ + constexpr int totalBits = 1 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::Count*/; + constexpr int inHierarchyBit = 0 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::hierarchyRoot_DirtyFlag*/; + + ReplicationRecord currentRecord; + currentRecord.m_authorityToClient.AddBits(totalBits); + currentRecord.m_authorityToClient.SetBit(inHierarchyBit, true); + + constexpr uint32_t bufferSize = 100; + AZStd::array buffer = {}; + NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); + inSerializer.Serialize(reinterpret_cast(value), + "hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ + AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + + NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); + + ReplicationRecord notifyRecord = currentRecord; + entity->FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); + entity->FindComponent()->NotifyStateDeltaChanges(notifyRecord); + } + + struct EntityInfo + { + enum class Role + { + Root, + Child, + None + }; + + EntityInfo(AZ::u64 entityId, const char* entityName, NetEntityId netId, Role role) + : m_entity(AZStd::make_unique(AZ::EntityId(entityId), entityName)) + , m_netId(netId) + , m_role(role) + { + } + + ~EntityInfo() + { + StopAndDeactivateEntity(m_entity); + } + + AZStd::unique_ptr m_entity; + NetEntityId m_netId; + AZStd::unique_ptr m_replicator; + Role m_role = Role::None; + }; + + void PopulateHierarchicalEntity(const EntityInfo& entityInfo) + { + entityInfo.m_entity->CreateComponent(); + entityInfo.m_entity->CreateComponent(); + entityInfo.m_entity->CreateComponent(); + switch (entityInfo.m_role) + { + case EntityInfo::Role::Root: + entityInfo.m_entity->CreateComponent(); + break; + case EntityInfo::Role::Child: + entityInfo.m_entity->CreateComponent(); + break; + case EntityInfo::Role::None: + break; + } + } + + void CreateDeepHierarchy(EntityInfo& root, EntityInfo& child, EntityInfo& childOfChild) + { + PopulateHierarchicalEntity(root); + PopulateHierarchicalEntity(child); + PopulateHierarchicalEntity(childOfChild); + + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Authority); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority); + SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Authority); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childOfChildHandle(childOfChild.m_entity.get(), m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChildHandle); + childOfChild.m_replicator->Initialize(childOfChildHandle); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); + child.m_replicator->Initialize(childHandle); + + // Create an entity replicator for the root entity + const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get()); + root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle); + root.m_replicator->Initialize(rootHandle); + + root.m_entity->Activate(); + child.m_entity->Activate(); + childOfChild.m_entity->Activate(); + } + }; +} diff --git a/Gems/Multiplayer/Code/Tests/MainTools.cpp b/Gems/Multiplayer/Code/Tests/MainTools.cpp index 89b2492bc6..53dfad62d1 100644 --- a/Gems/Multiplayer/Code/Tests/MainTools.cpp +++ b/Gems/Multiplayer/Code/Tests/MainTools.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -30,7 +29,6 @@ namespace Multiplayer { AZStd::vector descriptors({ NetBindComponent::CreateDescriptor(), - NetBindMarkerComponent::CreateDescriptor(), NetworkSpawnableHolderComponent::CreateDescriptor() }); diff --git a/Gems/Multiplayer/Code/Tests/MockInterfaces.h b/Gems/Multiplayer/Code/Tests/MockInterfaces.h new file mode 100644 index 0000000000..42e034d234 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/MockInterfaces.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class MockMultiplayer : public Multiplayer::IMultiplayer + { + public: + MOCK_CONST_METHOD0(GetCurrentBlendFactor, float ()); + MOCK_CONST_METHOD0(GetAgentType, Multiplayer::MultiplayerAgentType()); + MOCK_METHOD1(InitializeMultiplayer, void(Multiplayer::MultiplayerAgentType)); + MOCK_METHOD2(StartHosting, bool(uint16_t, bool)); + MOCK_METHOD2(Connect, bool(AZStd::string, uint16_t)); + MOCK_METHOD1(Terminate, void(AzNetworking::DisconnectReason)); + MOCK_METHOD1(AddClientDisconnectedHandler, void(AZ::Event<>::Handler&)); + MOCK_METHOD1(AddConnectionAcquiredHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddSessionInitHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddSessionShutdownHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(SendReadyForEntityUpdates, void(bool)); + MOCK_CONST_METHOD0(GetCurrentHostTimeMs, AZ::TimeMs()); + MOCK_METHOD0(GetNetworkTime, Multiplayer::INetworkTime* ()); + MOCK_METHOD0(GetNetworkEntityManager, Multiplayer::INetworkEntityManager* ()); + MOCK_METHOD1(SetFilterEntityManager, void(Multiplayer::IFilterEntityManager*)); + MOCK_METHOD0(GetFilterEntityManager, Multiplayer::IFilterEntityManager* ()); + }; + + class MockNetworkEntityManager : public Multiplayer::INetworkEntityManager + { + public: + MOCK_METHOD2(RequestNetSpawnableInstantiation, AZStd::unique_ptr (const AZ::Data::Asset&, const AZ::Transform&)); + MOCK_METHOD4( + CreateEntitiesImmediate, + EntityList (const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ::Transform&, Multiplayer::AutoActivate)); + MOCK_CONST_METHOD1(GetNetEntityIdById, Multiplayer::NetEntityId (const AZ::EntityId&)); + MOCK_METHOD0(GetNetworkEntityTracker, Multiplayer::NetworkEntityTracker* ()); + MOCK_METHOD0(GetNetworkEntityAuthorityTracker, Multiplayer::NetworkEntityAuthorityTracker* ()); + MOCK_METHOD0(GetMultiplayerComponentRegistry, Multiplayer::MultiplayerComponentRegistry* ()); + MOCK_CONST_METHOD0(GetHostId, Multiplayer::HostId()); + MOCK_METHOD3(CreateEntitiesImmediate, EntityList(const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ:: + Transform&)); + MOCK_METHOD5(CreateEntitiesImmediate, EntityList(const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityId, Multiplayer:: + NetEntityRole, Multiplayer::AutoActivate, const AZ::Transform&)); + MOCK_METHOD3(SetupNetEntity, void(AZ::Entity*, Multiplayer::PrefabEntityId, Multiplayer::NetEntityRole)); + MOCK_CONST_METHOD1(GetEntity, Multiplayer::ConstNetworkEntityHandle(Multiplayer::NetEntityId)); + MOCK_CONST_METHOD0(GetEntityCount, uint32_t()); + MOCK_METHOD2(AddEntityToEntityMap, Multiplayer::NetworkEntityHandle(Multiplayer::NetEntityId, AZ::Entity*)); + MOCK_METHOD1(MarkForRemoval, void(const Multiplayer::ConstNetworkEntityHandle&)); + MOCK_CONST_METHOD1(IsMarkedForRemoval, bool(const Multiplayer::ConstNetworkEntityHandle&)); + MOCK_METHOD1(ClearEntityFromRemovalList, void(const Multiplayer::ConstNetworkEntityHandle&)); + MOCK_METHOD0(ClearAllEntities, void()); + MOCK_METHOD1(AddEntityMarkedDirtyHandler, void(AZ::Event<>::Handler&)); + MOCK_METHOD1(AddEntityNotifyChangesHandler, void(AZ::Event<>::Handler&)); + MOCK_METHOD1(AddEntityExitDomainHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddControllersActivatedHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddControllersDeactivatedHandler, void(AZ::Event::Handler&)); + MOCK_METHOD0(NotifyEntitiesDirtied, void()); + MOCK_METHOD0(NotifyEntitiesChanged, void()); + MOCK_METHOD2(NotifyControllersActivated, void(const Multiplayer::ConstNetworkEntityHandle&, Multiplayer::EntityIsMigrating)); + MOCK_METHOD2(NotifyControllersDeactivated, void(const Multiplayer::ConstNetworkEntityHandle&, Multiplayer::EntityIsMigrating)); + MOCK_METHOD1(HandleLocalRpcMessage, void(Multiplayer::NetworkEntityRpcMessage&)); + }; + + class MockConnectionListener : public AzNetworking::IConnectionListener + { + public: + MOCK_METHOD3(ValidateConnect, ConnectResult(const IpAddress&, const IPacketHeader&, ISerializer&)); + MOCK_METHOD1(OnConnect, void(IConnection*)); + MOCK_METHOD3(OnPacketReceived, PacketDispatchResult (IConnection*, const IPacketHeader&, ISerializer&)); + MOCK_METHOD2(OnPacketLost, void(IConnection*, PacketId)); + MOCK_METHOD3(OnDisconnect, void(IConnection*, DisconnectReason, TerminationEndpoint)); + }; + + class MockTime : public AZ::ITime + { + public: + MOCK_CONST_METHOD0(GetElapsedTimeMs, AZ::TimeMs()); + }; + + class MockNetworkTime : public Multiplayer::INetworkTime + { + public: + MOCK_METHOD2(ForceSetTime, void (Multiplayer::HostFrameId, AZ::TimeMs)); + MOCK_CONST_METHOD0(GetHostBlendFactor, float ()); + MOCK_METHOD1(AlterBlendFactor, void (float)); + MOCK_CONST_METHOD0(IsTimeRewound, bool()); + MOCK_CONST_METHOD0(GetHostFrameId, Multiplayer::HostFrameId()); + MOCK_CONST_METHOD0(GetUnalteredHostFrameId, Multiplayer::HostFrameId()); + MOCK_METHOD0(IncrementHostFrameId, void()); + MOCK_CONST_METHOD0(GetHostTimeMs, AZ::TimeMs()); + MOCK_CONST_METHOD0(GetRewindingConnectionId, AzNetworking::ConnectionId()); + MOCK_CONST_METHOD1(GetHostFrameIdForRewindingConnection, Multiplayer::HostFrameId(AzNetworking::ConnectionId)); + MOCK_METHOD4(AlterTime, void (Multiplayer::HostFrameId, AZ::TimeMs, float, AzNetworking::ConnectionId)); + MOCK_METHOD1(SyncEntitiesToRewindState, void(const AZ::Aabb&)); + MOCK_METHOD0(ClearRewoundEntities, void()); + }; + + class MockComponentApplicationRequests : public AZ::ComponentApplicationRequests + { + public: + MOCK_METHOD1(RegisterComponentDescriptor, void(const AZ::ComponentDescriptor*)); + MOCK_METHOD1(UnregisterComponentDescriptor, void(const AZ::ComponentDescriptor*)); + MOCK_METHOD0(GetApplication, AZ::ComponentApplication* ()); + MOCK_METHOD1(RegisterEntityAddedEventHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(RegisterEntityRemovedEventHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(RegisterEntityActivatedEventHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(RegisterEntityDeactivatedEventHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(SignalEntityActivated, void(AZ::Entity*)); + MOCK_METHOD1(SignalEntityDeactivated, void(AZ::Entity*)); + MOCK_METHOD1(AddEntity, bool(AZ::Entity*)); + MOCK_METHOD1(RemoveEntity, bool(AZ::Entity*)); + MOCK_METHOD1(DeleteEntity, bool(const AZ::EntityId&)); + MOCK_METHOD1(FindEntity, AZ::Entity* (const AZ::EntityId&)); + MOCK_METHOD1(GetEntityName, AZStd::string(const AZ::EntityId&)); + MOCK_METHOD1(EnumerateEntities, void(const EntityCallback&)); + MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ()); + MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ()); + MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ()); + MOCK_CONST_METHOD0(GetAppRoot, const char* ()); + MOCK_CONST_METHOD0(GetEngineRoot, const char* ()); + MOCK_CONST_METHOD0(GetExecutableFolder, const char* ()); + MOCK_METHOD0(GetDrillerManager, AZ::Debug::DrillerManager* ()); + MOCK_METHOD1(ResolveModulePath, void(AZ::OSString&)); + MOCK_METHOD0(GetAzCommandLine, AZ::CommandLine* ()); + MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&)); + }; + + class MockSerializer : public ISerializer + { + public: + MOCK_CONST_METHOD0(IsValid, bool ()); + MOCK_CONST_METHOD0(GetSerializerMode, SerializerMode ()); + MOCK_METHOD2(Serialize, bool (bool&, const char*)); + MOCK_METHOD4(Serialize, bool (char&, const char*, char, char)); + MOCK_METHOD4(Serialize, bool (int8_t&, const char*, int8_t, int8_t)); + MOCK_METHOD4(Serialize, bool (int16_t&, const char*, int16_t, int16_t)); + MOCK_METHOD4(Serialize, bool (int32_t&, const char*, int32_t, int32_t)); + MOCK_METHOD4(Serialize, bool (int64_t&, const char*, int64_t, int64_t)); + MOCK_METHOD4(Serialize, bool (uint8_t&, const char*, uint8_t, uint8_t)); + MOCK_METHOD4(Serialize, bool (uint16_t&, const char*, uint16_t, uint16_t)); + MOCK_METHOD4(Serialize, bool (uint32_t&, const char*, uint32_t, uint32_t)); + MOCK_METHOD4(Serialize, bool (uint64_t&, const char*, uint64_t, uint64_t)); + MOCK_METHOD4(Serialize, bool (float&, const char*, float, float)); + MOCK_METHOD4(Serialize, bool (double&, const char*, double, double)); + MOCK_METHOD5(SerializeBytes, bool (uint8_t*, uint32_t, bool, uint32_t&, const char*)); + MOCK_METHOD2(BeginObject, bool (const char*, const char*)); + MOCK_METHOD2(EndObject, bool (const char*, const char*)); + MOCK_CONST_METHOD0(GetBuffer, const uint8_t* ()); + MOCK_CONST_METHOD0(GetCapacity, uint32_t ()); + MOCK_CONST_METHOD0(GetSize, uint32_t ()); + MOCK_METHOD0(ClearTrackedChangesFlag, void ()); + MOCK_CONST_METHOD0(GetTrackedChangesFlag, bool ()); + }; +} + diff --git a/Gems/Multiplayer/Code/Tests/RewindableObjectTests.cpp b/Gems/Multiplayer/Code/Tests/RewindableObjectTests.cpp index b8d60a94e8..04de971f0d 100644 --- a/Gems/Multiplayer/Code/Tests/RewindableObjectTests.cpp +++ b/Gems/Multiplayer/Code/Tests/RewindableObjectTests.cpp @@ -57,6 +57,35 @@ namespace UnitTest } } + TEST_F(RewindableObjectTests, CurrentPreviousTests) + { + Multiplayer::RewindableObject test(0); + + for (uint32_t i = 0; i < RewindableBufferFrames; ++i) + { + test = i; + EXPECT_EQ(i, test); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + } + + { + // Test that Get/GetPrevious return different value when not on the owning connection + Multiplayer::ScopedAlterTime time(static_cast(RewindableBufferFrames - 1), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); + EXPECT_EQ(RewindableBufferFrames - 1, test.Get()); + EXPECT_EQ(RewindableBufferFrames - 2, test.GetPrevious()); + } + + // Test that Get/GetPrevious return the unaltered frame on the owning conection + Multiplayer::GetNetworkTime()->AlterTime(static_cast(RewindableBufferFrames - 1), AZ::TimeMs{ 0 }, 1.f, AzNetworking::ConnectionId(0)); + { + Multiplayer::ScopedAlterTime time(static_cast(RewindableBufferFrames - 1), AZ::TimeMs{ 0 }, 1.f, AzNetworking::ConnectionId(0)); + test.SetOwningConnectionId(AzNetworking::ConnectionId(0)); + EXPECT_EQ(RewindableBufferFrames - 1, test.Get()); + EXPECT_EQ(RewindableBufferFrames - 1, test.GetPrevious()); + } + Multiplayer::GetNetworkTime()->AlterTime(static_cast(RewindableBufferFrames), AZ::TimeMs(0), 1.f, AzNetworking::InvalidConnectionId); + } + TEST_F(RewindableObjectTests, OverflowTests) { Multiplayer::RewindableObject test(0); diff --git a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp new file mode 100644 index 0000000000..385a4b83ae --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp @@ -0,0 +1,1233 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + using namespace testing; + using namespace ::UnitTest; + + /* + * Parent -> Child + */ + class ServerSimpleHierarchyTests : public HierarchyTests + { + public: + void SetUp() override + { + HierarchyTests::SetUp(); + + m_root = AZStd::make_unique(1, "root", NetEntityId{ 1 }, EntityInfo::Role::Root); + m_child = AZStd::make_unique(2, "child", NetEntityId{ 2 }, EntityInfo::Role::Child); + + CreateSimpleHierarchy(*m_root, *m_child); + + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + // now the two entities are under one hierarchy + } + + void TearDown() override + { + m_child.reset(); + m_root.reset(); + + HierarchyTests::TearDown(); + } + + void CreateSimpleHierarchy(EntityInfo& root, EntityInfo& child) + { + PopulateHierarchicalEntity(root); + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Authority); + + PopulateHierarchicalEntity(child); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); + child.m_replicator->Initialize(childHandle); + + // Create an entity replicator for the root entity + const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get()); + root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle); + root.m_replicator->Initialize(rootHandle); + + root.m_entity->Activate(); + child.m_entity->Activate(); + } + + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + }; + + TEST_F(ServerSimpleHierarchyTests, Server_Sets_Appropriate_Network_Fields_For_Clients) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + NetEntityId{ 1 } + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Is_Top_Level_Root) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->IsHierarchicalChild(), + false + ); + } + + TEST_F(ServerSimpleHierarchyTests, Child_Has_Root_Set) + { + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + NetEntityId{ 1 } + ); + } + + TEST_F(ServerSimpleHierarchyTests, Child_Has_Root_Cleared_On_Detach) + { + // now detach the child + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Has_Child_Reference) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Has_Child_References_Removed_On_Detach) + { + // now detach the child + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Deactivates_Child_Has_No_References_To_Root) + { + StopEntity(m_root->m_entity); + m_root->m_entity->Deactivate(); + m_root->m_entity.reset(); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerSimpleHierarchyTests, Child_Deactivates_Root_Has_No_References_To_Child) + { + m_child.reset(); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Deactivates_IsHierarchyEnabled_Is_False) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->IsHierarchyEnabled(), + true + ); + + StopEntity(m_root->m_entity); + m_root->m_entity->Deactivate(); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->IsHierarchyEnabled(), + false + ); + + m_root->m_entity.reset(); + } + + TEST_F(ServerSimpleHierarchyTests, Child_Deactivates_IsHierarchyEnabled_Is_False) + { + EXPECT_EQ( + m_child->m_entity->FindComponent()->IsHierarchyEnabled(), + true + ); + + StopEntity(m_child->m_entity); + m_child->m_entity->Deactivate(); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->IsHierarchyEnabled(), + false + ); + + m_child->m_entity.reset(); + } + + /* + * Parent -> Child -> ChildOfChild + */ + class ServerDeepHierarchyTests : public HierarchyTests + { + public: + const NetEntityId RootNetEntityId = NetEntityId{ 1 }; + const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; + const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 }; + + void SetUp() override + { + HierarchyTests::SetUp(); + + m_root = AZStd::make_unique((1), "root", RootNetEntityId, EntityInfo::Role::Root); + m_child = AZStd::make_unique((2), "child", ChildNetEntityId, EntityInfo::Role::Child); + m_childOfChild = AZStd::make_unique((3), "child of child", ChildOfChildNetEntityId, EntityInfo::Role::Child); + + CreateDeepHierarchy(*m_root, *m_child, *m_childOfChild); + + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChild.reset(); + m_child.reset(); + m_root.reset(); + + HierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + AZStd::unique_ptr m_childOfChild; + }; + + TEST_F(ServerDeepHierarchyTests, Root_Is_Top_Level_Root) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->IsHierarchicalChild(), + false + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_Child_References) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild->m_entity.get() + ); + } + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_Child_Of_Child_Reference_Removed_On_Detach) + { + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_All_References_Removed_On_Detach_Of_Mid_Child) + { + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_All_References_If_Mid_Child_Added_With_Child) + { + m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); + // reconnect + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_All_References_If_Child_Of_Child_Added) + { + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + // reconnect + m_childOfChild->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerDeepHierarchyTests, Child_Of_Child_Points_To_Root_After_Attach) + { + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + // reconnect + m_childOfChild->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, All_New_Children_Point_To_Root_If_Mid_Child_Added_With_Child) + { + m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); + // reconnect + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, Children_Clear_Reference_To_Root_After_Mid_Child_Detached) + { + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, Child_Of_Child_Clears_Reference_To_Root_After_Detached) + { + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Deactivates_Children_Have_No_References_To_Root) + { + m_root.reset(); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, Child_Of_Child_Deactivates_Root_Removes_References_To_It) + { + m_childOfChild.reset(); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerDeepHierarchyTests, Testing_Limiting_Hierarchy_Maximum_Size) + { + uint32_t currentMaxLimit = 0; + m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); + m_console->PerformCommand("bg_hierarchyEntityMaxLimit 2"); + + // remake the hierarchy + m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + + m_console->PerformCommand((AZStd::string("bg_hierarchyEntityMaxLimit ") + AZStd::to_string(currentMaxLimit)).c_str()); + m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); + } + + /* + * Parent -> Child -> Child Of Child + * -> Child2 -> Child Of Child2 + * -> Child2 Of Child2 + */ + class ServerBranchedHierarchyTests : public HierarchyTests + { + public: + const NetEntityId RootNetEntityId = NetEntityId{ 1 }; + const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; + const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 }; + const NetEntityId Child2NetEntityId = NetEntityId{ 4 }; + const NetEntityId ChildOfChild2NetEntityId = NetEntityId{ 5 }; + const NetEntityId Child2OfChild2NetEntityId = NetEntityId{ 6 }; + + void SetUp() override + { + HierarchyTests::SetUp(); + + m_root = AZStd::make_unique((1), "root", RootNetEntityId, EntityInfo::Role::Root); + m_child = AZStd::make_unique((2), "child", ChildNetEntityId, EntityInfo::Role::Child); + m_childOfChild = AZStd::make_unique((3), "child of child", ChildOfChildNetEntityId, EntityInfo::Role::Child); + m_child2 = AZStd::make_unique((4), "child2", Child2NetEntityId, EntityInfo::Role::Child); + m_childOfChild2 = AZStd::make_unique((5), "child of child2", ChildOfChild2NetEntityId, EntityInfo::Role::Child); + m_child2OfChild2 = AZStd::make_unique((6), "child2 of child2", Child2OfChild2NetEntityId, EntityInfo::Role::Child); + + CreateBranchedHierarchy(*m_root, *m_child, *m_childOfChild, + *m_child2, *m_childOfChild2, *m_child2OfChild2); + + m_child2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + m_child2OfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_child2OfChild2.reset(); + m_childOfChild2.reset(); + m_child2.reset(); + m_childOfChild.reset(); + m_child.reset(); + m_root.reset(); + + HierarchyTests::TearDown(); + } + + + void CreateBranchedHierarchy(EntityInfo& root, EntityInfo& child, EntityInfo& childOfChild, + EntityInfo& child2, EntityInfo& childOfChild2, EntityInfo& child2OfChild2) + { + PopulateHierarchicalEntity(root); + PopulateHierarchicalEntity(child); + PopulateHierarchicalEntity(childOfChild); + PopulateHierarchicalEntity(child2); + PopulateHierarchicalEntity(childOfChild2); + PopulateHierarchicalEntity(child2OfChild2); + + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Authority); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority); + SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Authority); + SetupEntity(child2.m_entity, child2.m_netId, NetEntityRole::Authority); + SetupEntity(childOfChild2.m_entity, childOfChild2.m_netId, NetEntityRole::Authority); + SetupEntity(child2OfChild2.m_entity, child2OfChild2.m_netId, NetEntityRole::Authority); + + // Create entity replicators + const NetworkEntityHandle childOfChild2Handle(childOfChild2.m_entity.get(), m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChild2Handle); + childOfChild.m_replicator->Initialize(childOfChild2Handle); + + const NetworkEntityHandle child2OfChild2Handle(child2OfChild2.m_entity.get(), m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, child2OfChild2Handle); + childOfChild.m_replicator->Initialize(child2OfChild2Handle); + + const NetworkEntityHandle child2Handle(child2.m_entity.get(), m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, child2Handle); + child.m_replicator->Initialize(child2Handle); + + const NetworkEntityHandle childOfChildHandle(childOfChild.m_entity.get(), m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChildHandle); + childOfChild.m_replicator->Initialize(childOfChildHandle); + + const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); + child.m_replicator->Initialize(childHandle); + + const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get()); + root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle); + root.m_replicator->Initialize(rootHandle); + + root.m_entity->Activate(); + child.m_entity->Activate(); + childOfChild.m_entity->Activate(); + child2.m_entity->Activate(); + childOfChild2.m_entity->Activate(); + child2OfChild2.m_entity->Activate(); + } + + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + AZStd::unique_ptr m_childOfChild; + AZStd::unique_ptr m_child2; + AZStd::unique_ptr m_childOfChild2; + AZStd::unique_ptr m_child2OfChild2; + }; + + TEST_F(ServerBranchedHierarchyTests, Sanity_Check) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 6) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[3], + m_child2->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[4], + m_child2OfChild2->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[5], + m_childOfChild2->m_entity.get() + ); + } + } + + TEST_F(ServerBranchedHierarchyTests, Detach_Child_While_Child2_Remains_Attached) + { + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 4 + ); + + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 4) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child2->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_child2OfChild2->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[3], + m_childOfChild2->m_entity.get() + ); + } + + EXPECT_EQ( + m_child2->m_entity->FindComponent()->GetHierarchicalRoot(), + m_root->m_entity.get() + ); + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchicalRoot(), + nullptr + ); + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchicalRoot(), + nullptr + ); + } + + TEST_F(ServerBranchedHierarchyTests, Detach_Child_Then_Attach_To_Child2) + { + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_child->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + /* + * Sets up 2 deep hierarchies. + */ + class ServerHierarchyOfHierarchyTests : public ServerDeepHierarchyTests + { + public: + const NetEntityId Root2NetEntityId = NetEntityId{ 4 }; + const NetEntityId Child2NetEntityId = NetEntityId{ 5 }; + const NetEntityId ChildOfChild2NetEntityId = NetEntityId{ 6 }; + + void SetUp() override + { + ServerDeepHierarchyTests::SetUp(); + + m_root2 = AZStd::make_unique((4), "root 2", Root2NetEntityId, EntityInfo::Role::Root); + m_child2 = AZStd::make_unique((5), "child 2", Child2NetEntityId, EntityInfo::Role::Child); + m_childOfChild2 = AZStd::make_unique((6), "child of child 2", ChildOfChild2NetEntityId, EntityInfo::Role::Child); + + CreateDeepHierarchy(*m_root2, *m_child2, *m_childOfChild2); + + m_child2->m_entity->FindComponent()->SetParent(m_root2->m_entity->GetId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChild2.reset(); + m_child2.reset(); + m_root2.reset(); + + ServerDeepHierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_root2; + AZStd::unique_ptr m_child2; + AZStd::unique_ptr m_childOfChild2; + }; + + TEST_F(ServerHierarchyOfHierarchyTests, Hierarchies_Are_Not_Related) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild->m_entity.get() + ); + } + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + + if (m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root2->m_entity.get() + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child2->m_entity.get() + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild2->m_entity.get() + ); + } + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Is_Not_Top_Level_Root) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->IsHierarchicalChild(), + false + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), + true + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_References_All_When_Another_Hierarchy_Attached_At_Root) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_References_All_When_Another_Hierarchy_Attached_At_Child) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_References_All_When_Another_Hierarchy_Attached_At_Child_Of_Child) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_References_Top_Root_When_Another_Hierarchy_Attached_At_Root) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), + true + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_References_Top_Root_When_Another_Hierarchy_Attached_At_Child) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), + true + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_References_Top_Root_When_Another_Hierarchy_Attached_At_Child_Of_Child) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), + true + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Doesnt_Keep_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 0 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Detachment_From_Top_Root) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + // detach + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + if (m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root2->m_entity.get() + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child2->m_entity.get() + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild2->m_entity.get() + ); + } + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Detachment_From_Child_Of_Child) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Top_Root_Deactivates) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_root.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Child_Of_Top_Root_Deactivates) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_child.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Child_Of_Child_Deactivates) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_childOfChild.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Stress_Test_Inner_Root_Has_Child_References_After_Detachment_From_Child_Of_Child) + { + for (int i = 0; i < 100; ++i) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + } + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Detachment_Of_Child_Of_Child_In_Inner_Hierarchy) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_childOfChild2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 5 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Attachment_Of_Child_Of_Child_In_Inner_Hierarchy) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_childOfChild2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + // re-connect + m_childOfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Child_Of_Child_Changed_Hierarchies) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_childOfChild2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + // connect to a different hierarchy + m_childOfChild2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Detachment_Of_Child_In_Inner_Hierarchy) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_child2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 4 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Child_Changed_Hierarchies) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_child2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + // connect to a different hierarchy + m_child2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_No_Child_References_After_All_Children_Moved_To_Another_Hierarchy) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_child2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + // detach + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Child_Deactivated_Top_Root_Has_No_Child_Reference_To_It) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_child2.reset(); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 4 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Testing_Limiting_Hierarchy_Maximum_Size) + { + uint32_t currentMaxLimit = 0; + m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); + m_console->PerformCommand("bg_hierarchyEntityMaxLimit 2"); + + // remake the top level hierarchy + m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + + m_console->PerformCommand((AZStd::string("bg_hierarchyEntityMaxLimit ") + AZStd::to_string(currentMaxLimit)).c_str()); + m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); + } + + /* + * Parent -> Child -> ChildOfChild (not marked as in a hierarchy) + */ + class ServerMixedDeepHierarchyTests : public HierarchyTests + { + public: + void SetUp() override + { + HierarchyTests::SetUp(); + + m_root = AZStd::make_unique((1), "root", NetEntityId{ 1 }, EntityInfo::Role::Root); + m_child = AZStd::make_unique((2), "child", NetEntityId{ 2 }, EntityInfo::Role::Child); + m_childOfChild = AZStd::make_unique((3), "child of child", NetEntityId{ 3 }, EntityInfo::Role::None); + + CreateDeepHierarchy(*m_root, *m_child, *m_childOfChild); + + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChild.reset(); + m_child.reset(); + m_root.reset(); + + HierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + AZStd::unique_ptr m_childOfChild; + }; + + TEST_F(ServerMixedDeepHierarchyTests, Top_Root_Ignores_Non_Hierarchical_Entities) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerMixedDeepHierarchyTests, Detaching_Non_Hierarchical_Entity_Has_No_Effect_On_Top_Root) + { + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerMixedDeepHierarchyTests, Attaching_Non_Hierarchical_Entity_Has_No_Effect_On_Top_Root) + { + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + /* + * 1st hierarchy: Parent -> Child -> ChildOfChild (not marked as in a hierarchy) + * 2nd hierarchy: Parent2 -> Child2 (not marked as in a hierarchy) -> ChildOfChild2 + */ + class ServerMixedHierarchyOfHierarchyTests : public ServerMixedDeepHierarchyTests + { + public: + void SetUp() override + { + ServerMixedDeepHierarchyTests::SetUp(); + + m_root2 = AZStd::make_unique((4), "root 2", NetEntityId{ 4 }, EntityInfo::Role::Root); + m_child2 = AZStd::make_unique((5), "child 2", NetEntityId{ 5 }, EntityInfo::Role::None); + m_childOfChild2 = AZStd::make_unique((6), "child of child 2", NetEntityId{ 6 }, EntityInfo::Role::Child); + + CreateDeepHierarchy(*m_root2, *m_child2, *m_childOfChild2); + + m_child2->m_entity->FindComponent()->SetParent(m_root2->m_entity->GetId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChild2.reset(); + m_child2.reset(); + m_root2.reset(); + + ServerMixedDeepHierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_root2; + AZStd::unique_ptr m_child2; + AZStd::unique_ptr m_childOfChild2; + }; + + TEST_F(ServerMixedHierarchyOfHierarchyTests, Sanity_Check_Ingore_Children_Without_Hierarchy_Components) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerMixedHierarchyOfHierarchyTests, Adding_Mixed_Hierarchy_Ingores_Children_Without_Hierarchy_Components) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerMixedHierarchyOfHierarchyTests, Attaching_Hierarchy_To_Non_Hierarchical_Entity_Does_Not_Merge_Hierarchies) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), + false + ); + } + + /* + * Sets up a hierarchy with 3 roots, 2 of them being inner roots. + */ + class ServerHierarchyWithThreeRoots : public ServerHierarchyOfHierarchyTests + { + public: + const NetEntityId Root3NetEntityId = NetEntityId{ 7 }; + const NetEntityId Child3NetEntityId = NetEntityId{ 8 }; + const NetEntityId ChildOfChild3NetEntityId = NetEntityId{ 9 }; + + void SetUp() override + { + ServerHierarchyOfHierarchyTests::SetUp(); + + m_root3 = AZStd::make_unique((7), "root 3", Root3NetEntityId, EntityInfo::Role::Root); + m_child3 = AZStd::make_unique((8), "child 3", Child3NetEntityId, EntityInfo::Role::Child); + m_childOfChild3 = AZStd::make_unique((9), "child of child 3", ChildOfChild3NetEntityId, EntityInfo::Role::Child); + + CreateDeepHierarchy(*m_root3, *m_child3, *m_childOfChild3); + + m_child3->m_entity->FindComponent()->SetParent(m_root3->m_entity->GetId()); + m_childOfChild3->m_entity->FindComponent()->SetParent(m_child3->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChild3.reset(); + m_child3.reset(); + m_root3.reset(); + + ServerHierarchyOfHierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_root3; + AZStd::unique_ptr m_child3; + AZStd::unique_ptr m_childOfChild3; + }; + + TEST_F(ServerHierarchyWithThreeRoots, Top_Root_Active_Then_Inner_Roots_Have_No_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 0 + ); + EXPECT_EQ( + m_root3->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 0 + ); + } + + TEST_F(ServerHierarchyWithThreeRoots, Top_Root_Deactivates_Inner_Roots_Have_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_root.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + EXPECT_EQ( + m_root3->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyWithThreeRoots, Child_Of_Top_Root_Deactivates_Inner_Roots_Have_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_child.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + EXPECT_EQ( + m_root3->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyWithThreeRoots, Child_Of_Child_Of_Top_Root_Deactivates_Inner_Roots_Have_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_childOfChild.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + EXPECT_EQ( + m_root3->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } +} diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index 0b2adb1530..7782444a6e 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -15,20 +15,31 @@ set(FILES Include/Multiplayer/MultiplayerTypes.h Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h Include/Multiplayer/Components/MultiplayerComponent.h - Include/Multiplayer/Components/MultiplayerController.h Include/Multiplayer/Components/MultiplayerComponentRegistry.h + Include/Multiplayer/Components/MultiplayerController.h Include/Multiplayer/Components/NetBindComponent.h + Include/Multiplayer/Components/NetworkHierarchyChildComponent.h + Include/Multiplayer/Components/NetworkHierarchyRootComponent.h + Include/Multiplayer/Components/NetworkHierarchyBus.h + Include/Multiplayer/Components/NetworkCharacterComponent.h + Include/Multiplayer/Components/NetworkHitVolumesComponent.h + Include/Multiplayer/Components/NetworkRigidBodyComponent.h Include/Multiplayer/Components/NetworkTransformComponent.h Include/Multiplayer/ConnectionData/IConnectionData.h Include/Multiplayer/EntityDomains/IEntityDomain.h - Include/Multiplayer/NetworkEntity/INetworkEntityManager.h + Include/Multiplayer/IMultiplayer.h + Include/Multiplayer/IMultiplayerTools.h Include/Multiplayer/INetworkSpawnableLibrary.h + Include/Multiplayer/MultiplayerConstants.h + Include/Multiplayer/MultiplayerStats.h + Include/Multiplayer/MultiplayerTypes.h + Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h Include/Multiplayer/NetworkEntity/IFilterEntityManager.h - Include/Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h - Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h + Include/Multiplayer/NetworkEntity/INetworkEntityManager.h Include/Multiplayer/NetworkEntity/NetworkEntityHandle.h Include/Multiplayer/NetworkEntity/NetworkEntityHandle.inl - Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h + Include/Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h + Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h Include/Multiplayer/NetworkInput/IMultiplayerComponentInput.h Include/Multiplayer/NetworkInput/NetworkInput.h Include/Multiplayer/NetworkTime/INetworkTime.h @@ -40,23 +51,30 @@ set(FILES Include/Multiplayer/NetworkTime/RewindableObject.inl Include/Multiplayer/Physics/PhysicsUtils.h Include/Multiplayer/ReplicationWindows/IReplicationWindow.h - Source/MultiplayerSystemComponent.cpp - Source/MultiplayerSystemComponent.h - Source/MultiplayerStats.cpp - Source/AutoGen/AutoComponent_Header.jinja - Source/AutoGen/AutoComponent_Source.jinja - Source/AutoGen/AutoComponent_Common.jinja - Source/AutoGen/AutoComponentTypes_Header.jinja - Source/AutoGen/AutoComponentTypes_Source.jinja + Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja + Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja + Include/Multiplayer/AutoGen/AutoComponent_Common.jinja + Include/Multiplayer/AutoGen/AutoComponent_Header.jinja + Include/Multiplayer/AutoGen/AutoComponent_Source.jinja Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml Source/AutoGen/Multiplayer.AutoPackets.xml Source/AutoGen/MultiplayerEditor.AutoPackets.xml + Source/AutoGen/NetworkCharacterComponent.AutoComponent.xml + Source/AutoGen/NetworkHitVolumesComponent.AutoComponent.xml + Source/AutoGen/NetworkRigidBodyComponent.AutoComponent.xml Source/AutoGen/NetworkTransformComponent.AutoComponent.xml + Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml + Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml Source/Components/LocalPredictionPlayerInputComponent.cpp Source/Components/MultiplayerComponent.cpp - Source/Components/MultiplayerController.cpp Source/Components/MultiplayerComponentRegistry.cpp + Source/Components/MultiplayerController.cpp Source/Components/NetBindComponent.cpp + Source/Components/NetworkHierarchyChildComponent.cpp + Source/Components/NetworkHierarchyRootComponent.cpp + Source/Components/NetworkCharacterComponent.cpp + Source/Components/NetworkHitVolumesComponent.cpp + Source/Components/NetworkRigidBodyComponent.cpp Source/Components/NetworkTransformComponent.cpp Source/ConnectionData/ClientToServerConnectionData.cpp Source/ConnectionData/ClientToServerConnectionData.h @@ -68,6 +86,9 @@ set(FILES Source/Editor/MultiplayerEditorConnection.h Source/EntityDomains/FullOwnershipEntityDomain.cpp Source/EntityDomains/FullOwnershipEntityDomain.h + Source/MultiplayerStats.cpp + Source/MultiplayerSystemComponent.cpp + Source/MultiplayerSystemComponent.h Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp Source/NetworkEntity/EntityReplication/EntityReplicationManager.h Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -83,13 +104,13 @@ set(FILES Source/NetworkEntity/NetworkEntityHandle.cpp Source/NetworkEntity/NetworkEntityManager.cpp Source/NetworkEntity/NetworkEntityManager.h - Source/NetworkEntity/NetworkSpawnableLibrary.cpp - Source/NetworkEntity/NetworkSpawnableLibrary.h Source/NetworkEntity/NetworkEntityRpcMessage.cpp Source/NetworkEntity/NetworkEntityTracker.cpp Source/NetworkEntity/NetworkEntityTracker.h Source/NetworkEntity/NetworkEntityTracker.inl Source/NetworkEntity/NetworkEntityUpdateMessage.cpp + Source/NetworkEntity/NetworkSpawnableLibrary.cpp + Source/NetworkEntity/NetworkSpawnableLibrary.h Source/NetworkInput/NetworkInput.cpp Source/NetworkInput/NetworkInputArray.cpp Source/NetworkInput/NetworkInputArray.h @@ -101,11 +122,8 @@ set(FILES Source/NetworkInput/NetworkInputMigrationVector.h Source/NetworkTime/NetworkTime.cpp Source/NetworkTime/NetworkTime.h - Source/Pipeline/NetBindMarkerComponent.cpp - Source/Pipeline/NetBindMarkerComponent.h Source/Pipeline/NetworkSpawnableHolderComponent.cpp Source/Pipeline/NetworkSpawnableHolderComponent.h - Source/Physics/PhysicsUtils.cpp Source/ReplicationWindows/NullReplicationWindow.cpp Source/ReplicationWindows/NullReplicationWindow.h Source/ReplicationWindows/ServerToClientReplicationWindow.cpp diff --git a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake index f385a21600..3f4fcc9efa 100644 --- a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake @@ -8,6 +8,10 @@ set(FILES Tests/Main.cpp + Tests/MockInterfaces.h + Tests/ClientHierarchyTests.cpp + Tests/ServerHierarchyTests.cpp + Tests/CommonHierarchySetup.h Tests/IMultiplayerConnectionMock.h Tests/MultiplayerSystemTests.cpp Tests/RewindableContainerTests.cpp diff --git a/Gems/Multiplayer/gem.json b/Gems/Multiplayer/gem.json index 8abbddea5f..47dbddfcbd 100644 --- a/Gems/Multiplayer/gem.json +++ b/Gems/Multiplayer/gem.json @@ -5,8 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Multiplayer Gem provides a public API for multiplayer functionality such as connecting and hosting.", - "canonical_tags": ["Gem"], - "user_tags": ["Multiplayer", "Network", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Multiplayer", + "Network", + "Framework" + ], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "dependencies": [ + "CertificateManager", + "Atom_Feature_Common", + "ImGui" + ] } diff --git a/Gems/MultiplayerCompression/Code/Source/LZ4Compressor.h b/Gems/MultiplayerCompression/Code/Source/LZ4Compressor.h index 640cf03ee4..f7fec70813 100644 --- a/Gems/MultiplayerCompression/Code/Source/LZ4Compressor.h +++ b/Gems/MultiplayerCompression/Code/Source/LZ4Compressor.h @@ -31,13 +31,13 @@ namespace MultiplayerCompression LZ4Compressor() = default; const char* GetName() const { return CompressorName; } - AzNetworking::CompressorType GetType() const { return CompressorType; }; + AzNetworking::CompressorType GetType() const override { return CompressorType; }; - bool Init() { return true; } - size_t GetMaxChunkSize(size_t maxCompSize) const; - size_t GetMaxCompressedBufferSize(size_t uncompSize) const; + bool Init() override { return true; } + size_t GetMaxChunkSize(size_t maxCompSize) const override; + size_t GetMaxCompressedBufferSize(size_t uncompSize) const override; - AzNetworking::CompressorError Compress(const void* uncompData, size_t uncompSize, void* compData, size_t compDataSize, size_t& compSize); - AzNetworking::CompressorError Decompress(const void* compData, size_t compDataSize, void* uncompData, size_t uncompDataSize, size_t& consumedSize, size_t& uncompSize); + AzNetworking::CompressorError Compress(const void* uncompData, size_t uncompSize, void* compData, size_t compDataSize, size_t& compSize) override; + AzNetworking::CompressorError Decompress(const void* compData, size_t compDataSize, void* uncompData, size_t uncompDataSize, size_t& consumedSize, size_t& uncompSize) override; }; } diff --git a/Gems/MultiplayerCompression/gem.json b/Gems/MultiplayerCompression/gem.json index 178cca0ffe..7dd31476e3 100644 --- a/Gems/MultiplayerCompression/gem.json +++ b/Gems/MultiplayerCompression/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Multiplayer Compression Gem provides an open source Compressor for use with AzNetworking's transport layer.", - "canonical_tags": ["Gem"], - "user_tags": ["Multiplayer", "Network", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Multiplayer", + "Network", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/multiplayer/multiplayer-compression/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/multiplayer/multiplayer-compression/", + "dependencies": [] } diff --git a/Gems/NvCloth/gem.json b/Gems/NvCloth/gem.json index 49ccbf32f1..019ce23742 100644 --- a/Gems/NvCloth/gem.json +++ b/Gems/NvCloth/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The NVIDIA Cloth Gem provides functionality to create fast, realistic cloth simulation with the NVIDIA Cloth library.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-cloth/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-cloth/", + "dependencies": [ + "CommonFeaturesAtom", + "EMotionFX" + ] } diff --git a/Gems/PBSreferenceMaterials/gem.json b/Gems/PBSreferenceMaterials/gem.json index 563dd31a38..feddc1e465 100644 --- a/Gems/PBSreferenceMaterials/gem.json +++ b/Gems/PBSreferenceMaterials/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The PBS Reference Materials Gem provides physically based reference materials for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Sample", "Assets"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Sample", + "Assets" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/pbs-reference-materials/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/pbs-reference-materials/", + "dependencies": [] } diff --git a/Gems/PhysX/Code/Include/PhysX/EditorColliderComponentRequestBus.h b/Gems/PhysX/Code/Include/PhysX/EditorColliderComponentRequestBus.h index 2cfb48effa..20b02b7098 100644 --- a/Gems/PhysX/Code/Include/PhysX/EditorColliderComponentRequestBus.h +++ b/Gems/PhysX/Code/Include/PhysX/EditorColliderComponentRequestBus.h @@ -82,4 +82,18 @@ namespace PhysX }; using EditorColliderComponentRequestBus = AZ::EBus; + + /// + /// This is a Bus in order to communicate the status of the meshes of the collider and avoid dependencies with the rigidbody + /// + class EditorColliderValidationRequests : public AZ::ComponentBus + { + public: + /// Checks if the the mesh in the collider is correct with the current state of the Rigidbody! + virtual void ValidateRigidBodyMeshGeometryType() = 0; + }; + + using EditorColliderValidationRequestBus = AZ::EBus; } + + diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index 4aa240eaae..566903d84a 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -118,11 +118,10 @@ namespace PhysX AZ::u32 EditorProxyShapeConfig::OnShapeTypeChanged() { - //reset the physics asset if the shape type was Physics Asset - if (m_shapeType != Physics::ShapeType::PhysicsAsset && - m_lastShapeType == Physics::ShapeType::PhysicsAsset) + // reset the physics asset if the shape type was Physics Asset + if (m_shapeType != Physics::ShapeType::PhysicsAsset && m_lastShapeType == Physics::ShapeType::PhysicsAsset) { - //clean up any reference to a physics assets, and re-initialize to an empty Pipeline::MeshAsset asset. + // clean up any reference to a physics assets, and re-initialize to an empty Pipeline::MeshAsset asset. m_physicsAsset.m_pxAsset.Reset(); m_physicsAsset.m_pxAsset = AZ::Data::Asset(AZ::Data::AssetLoadBehavior::QueueLoad); @@ -212,6 +211,7 @@ namespace PhysX ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_shapeConfiguration, "Shape Configuration", "Configuration of the shape") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorColliderComponent::OnConfigurationChanged) + ->Attribute(AZ::Edit::Attributes::RemoveNotify, &EditorColliderComponent::ValidateRigidBodyMeshGeometryType) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_componentModeDelegate, "Component Mode", "Collider Component Mode") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_colliderDebugDraw, @@ -383,6 +383,7 @@ namespace PhysX ColliderShapeRequestBus::Handler::BusConnect(GetEntityId()); AZ::Render::MeshComponentNotificationBus::Handler::BusConnect(GetEntityId()); EditorColliderComponentRequestBus::Handler::BusConnect(AZ::EntityComponentIdPair(GetEntityId(), GetId())); + EditorColliderValidationRequestBus::Handler::BusConnect(GetEntityId()); m_nonUniformScaleChangedHandler = AZ::NonUniformScaleChangedEvent::Handler( [this](const AZ::Vector3& scale) {OnNonUniformScaleChanged(scale); }); AZ::NonUniformScaleRequestBus::Event(GetEntityId(), &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, @@ -427,6 +428,7 @@ namespace PhysX m_colliderDebugDraw.Disconnect(); AZ::Data::AssetBus::MultiHandler::BusDisconnect(); m_nonUniformScaleChangedHandler.Disconnect(); + EditorColliderValidationRequestBus::Handler::BusDisconnect(); EditorColliderComponentRequestBus::Handler::BusDisconnect(); AZ::Render::MeshComponentNotificationBus::Handler::BusDisconnect(); ColliderShapeRequestBus::Handler::BusDisconnect(); @@ -466,6 +468,7 @@ namespace PhysX UpdateShapeConfigurationScale(); CreateStaticEditorCollider(); + ValidateRigidBodyMeshGeometryType(); m_colliderDebugDraw.ClearCachedGeometry(); @@ -768,15 +771,16 @@ namespace PhysX { m_componentWarnings.clear(); m_configuration.m_materialSelection.SetMaterialSlots(Physics::MaterialSelection::SlotsArray()); + AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( + &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); } - AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); } void EditorColliderComponent::ValidateRigidBodyMeshGeometryType() { const PhysX::EditorRigidBodyComponent* entityRigidbody = m_entity->FindComponent(); - if (m_shapeConfiguration.m_physicsAsset.m_configuration.GetShapeType() == Physics::ShapeType::PhysicsAsset && entityRigidbody) + if (m_shapeConfiguration.m_physicsAsset.m_pxAsset && (m_shapeConfiguration.m_shapeType == Physics::ShapeType::PhysicsAsset) && entityRigidbody) { AZStd::vector> shapes; Utils::GetShapesFromAsset(m_shapeConfiguration.m_physicsAsset.m_configuration, m_configuration, m_hasNonUniformScale, @@ -784,17 +788,32 @@ namespace PhysX if (shapes.empty()) { + m_componentWarnings.clear(); + AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( + &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); return; } - //We grab the first shape to check if it is a triangle mesh. - auto shape = AZStd::rtti_pointer_cast(shapes[0]); + //We check if the shapes are triangle meshes, if any mesh is a triangle mesh we activate the warning. + bool shapeIsTriangleMesh = false; - if (shape && - shape->GetPxShape()->getGeometryType() == physx::PxGeometryType::eTRIANGLEMESH && - entityRigidbody->GetRigidBody() && - entityRigidbody->GetRigidBody()->IsKinematic() == false) + for (const auto& shape : shapes) { + auto current_shape = AZStd::rtti_pointer_cast(shape); + if (current_shape && + current_shape->GetPxShape()->getGeometryType() == physx::PxGeometryType::eTRIANGLEMESH && + entityRigidbody->GetRigidBody() && + entityRigidbody->GetRigidBody()->IsKinematic() == false) + { + shapeIsTriangleMesh = true; + break; + } + } + + if (shapeIsTriangleMesh) + { + m_componentWarnings.clear(); + AZStd::string assetPath = m_shapeConfiguration.m_physicsAsset.m_configuration.m_asset.GetHint().c_str(); const size_t lastSlash = assetPath.rfind('/'); if (lastSlash != AZStd::string::npos) @@ -816,6 +835,10 @@ namespace PhysX { m_componentWarnings.clear(); } + + AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( + &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); + } void EditorColliderComponent::OnAssetReloaded(AZ::Data::Asset asset) diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.h b/Gems/PhysX/Code/Source/EditorColliderComponent.h index 50cf9d0c8b..773dd0a9b8 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.h @@ -58,8 +58,8 @@ namespace PhysX //! Proxy container for only displaying a specific shape configuration depending on the shapeType selected. struct EditorProxyShapeConfig { - AZ_CLASS_ALLOCATOR(PhysX::EditorProxyShapeConfig, AZ::SystemAllocator, 0); - AZ_RTTI(PhysX::EditorProxyShapeConfig, "{531FB42A-42A9-4234-89BA-FD349EF83D0C}"); + AZ_CLASS_ALLOCATOR(EditorProxyShapeConfig, AZ::SystemAllocator, 0); + AZ_RTTI(EditorProxyShapeConfig, "{531FB42A-42A9-4234-89BA-FD349EF83D0C}"); static void Reflect(AZ::ReflectContext* context); EditorProxyShapeConfig() = default; @@ -106,6 +106,7 @@ namespace PhysX , private PhysX::ColliderShapeRequestBus::Handler , private AZ::Render::MeshComponentNotificationBus::Handler , private PhysX::EditorColliderComponentRequestBus::Handler + , private PhysX::EditorColliderValidationRequestBus::Handler , private AzPhysics::SimulatedBodyComponentRequestsBus::Handler { public: @@ -144,7 +145,7 @@ namespace PhysX void OnDeselected() override; // DisplayCallback - void Display(AzFramework::DebugDisplayRequests& debugDisplay) const; + void Display(AzFramework::DebugDisplayRequests& debugDisplay) const override; void DisplayMeshCollider(AzFramework::DebugDisplayRequests& debugDisplay) const; void DisplayUnscaledPrimitiveCollider(AzFramework::DebugDisplayRequests& debugDisplay) const; void DisplayScaledPrimitiveCollider(AzFramework::DebugDisplayRequests& debugDisplay) const; @@ -197,6 +198,9 @@ namespace PhysX void SetAssetScale(const AZ::Vector3& scale) override; AZ::Vector3 GetAssetScale() override; + // PhysX::EditorColliderValidationRequestBus overrides ... + void ValidateRigidBodyMeshGeometryType() override; + AZ::Transform GetColliderLocalTransform() const; EditorProxyShapeConfig m_shapeConfiguration; @@ -223,8 +227,6 @@ namespace PhysX void BuildDebugDrawMesh() const; - void ValidateRigidBodyMeshGeometryType(); - AZ::ComponentDescriptor::StringWarningArray GetComponentWarnings() const { return m_componentWarnings; }; using ComponentModeDelegate = AzToolsFramework::ComponentModeFramework::ComponentModeDelegate; diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp index ce812a9f31..447c2755c6 100644 --- a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp @@ -286,6 +286,9 @@ namespace PhysX } CreateEditorWorldRigidBody(); + PhysX::EditorColliderValidationRequestBus::Event( + GetEntityId(), &PhysX::EditorColliderValidationRequestBus::Events::ValidateRigidBodyMeshGeometryType); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(GetEntityId()); } diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h index 47da6eb772..792d3c4327 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h @@ -129,7 +129,7 @@ namespace PhysX void OnShapeChanged(LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons changeReason) override; // DisplayCallback - void Display(AzFramework::DebugDisplayRequests& debugDisplay) const; + void Display(AzFramework::DebugDisplayRequests& debugDisplay) const override; // ColliderShapeRequestBus AZ::Aabb GetColliderShapeAabb() override; diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h index a5b64c9c76..02de77568e 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h @@ -108,7 +108,7 @@ namespace PhysX // CharacterControllerRequestBus void Resize(float height) override; float GetHeight() override; - void SetHeight(float height); + void SetHeight(float height) override; float GetRadius() override; void SetRadius(float radius) override; float GetHalfSideExtent() override; diff --git a/Gems/PhysX/Code/Source/Pipeline/HeightFieldAssetHandler.h b/Gems/PhysX/Code/Source/Pipeline/HeightFieldAssetHandler.h index eb99a7e306..b1f55027b8 100644 --- a/Gems/PhysX/Code/Source/Pipeline/HeightFieldAssetHandler.h +++ b/Gems/PhysX/Code/Source/Pipeline/HeightFieldAssetHandler.h @@ -52,7 +52,7 @@ namespace PhysX // AZ::AssetTypeInfoBus AZ::Data::AssetType GetAssetType() const override; void GetAssetTypeExtensions(AZStd::vector& extensions) override; - const char* GetAssetTypeDisplayName() const; + const char* GetAssetTypeDisplayName() const override; const char* GetBrowserIcon() const override; const char* GetGroup() const override; AZ::Uuid GetComponentTypeId() const override; diff --git a/Gems/PhysX/Code/Source/Pipeline/MeshAssetHandler.h b/Gems/PhysX/Code/Source/Pipeline/MeshAssetHandler.h index ccaddc0c86..0cb8a58b65 100644 --- a/Gems/PhysX/Code/Source/Pipeline/MeshAssetHandler.h +++ b/Gems/PhysX/Code/Source/Pipeline/MeshAssetHandler.h @@ -48,7 +48,7 @@ namespace PhysX // AZ::AssetTypeInfoBus AZ::Data::AssetType GetAssetType() const override; void GetAssetTypeExtensions(AZStd::vector& extensions) override; - const char* GetAssetTypeDisplayName() const; + const char* GetAssetTypeDisplayName() const override; const char* GetBrowserIcon() const override; const char* GetGroup() const override; AZ::Uuid GetComponentTypeId() const override; diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersBenchmarks.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersBenchmarks.cpp index 200cbeafde..cd9e3c0395 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersBenchmarks.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersBenchmarks.cpp @@ -85,8 +85,7 @@ namespace PhysX::Benchmarks class PhysXCharactersBenchmarkFixture : public PhysX::Benchmarks::PhysXBaseBenchmarkFixture { - public: - virtual void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { PhysX::Benchmarks::PhysXBaseBenchmarkFixture::SetUpInternal(); //need to get the Physics::System to be able to spawn the rigid bodies @@ -95,12 +94,31 @@ namespace PhysX::Benchmarks m_terrainEntity = PhysX::TestUtils::CreateFlatTestTerrain(m_testSceneHandle, CharacterConstants::TerrainSize, CharacterConstants::TerrainSize); } - virtual void TearDown([[maybe_unused]] const ::benchmark::State& state) override + void internalTearDown() { m_terrainEntity = nullptr; PhysX::Benchmarks::PhysXBaseBenchmarkFixture::TearDownInternal(); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } + + void TearDown(const benchmark::State&) override + { + internalTearDown(); + } + void TearDown(benchmark::State&) override + { + internalTearDown(); + } + protected: // PhysXBaseBenchmarkFixture Overrides ... AzPhysics::SceneConfiguration GetDefaultSceneConfiguration() override diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersRagdollBenchmarks.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersRagdollBenchmarks.cpp index 8febfcddfc..c82d222e80 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersRagdollBenchmarks.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersRagdollBenchmarks.cpp @@ -70,8 +70,7 @@ namespace PhysX::Benchmarks class PhysXCharactersRagdollBenchmarkFixture : public PhysX::Benchmarks::PhysXBaseBenchmarkFixture { - public: - virtual void SetUp([[maybe_unused]] const ::benchmark::State& state) override + void internalSetUp() { PhysX::Benchmarks::PhysXBaseBenchmarkFixture::SetUpInternal(); //need to get the Physics::System to be able to spawn the rigid bodies @@ -80,12 +79,31 @@ namespace PhysX::Benchmarks m_terrainEntity = PhysX::TestUtils::CreateFlatTestTerrain(m_testSceneHandle, RagdollConstants::TerrainSize, RagdollConstants::TerrainSize); } - virtual void TearDown([[maybe_unused]] const ::benchmark::State& state) override + void internalTearDown() { m_terrainEntity = nullptr; PhysX::Benchmarks::PhysXBaseBenchmarkFixture::TearDownInternal(); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } + + void TearDown(const benchmark::State&) override + { + internalTearDown(); + } + void TearDown(benchmark::State&) override + { + internalTearDown(); + } + protected: // PhysXBaseBenchmarkFixture Overrides ... AzPhysics::SceneConfiguration GetDefaultSceneConfiguration() override diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXJointBenchmarks.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXJointBenchmarks.cpp index 17468b5cc6..de8a9d2146 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXJointBenchmarks.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXJointBenchmarks.cpp @@ -183,18 +183,35 @@ namespace PhysX::Benchmarks class PhysXJointBenchmarkFixture : public PhysXBaseBenchmarkFixture { - public: - virtual void SetUp([[maybe_unused]] const ::benchmark::State &state) override + void internalSetUp() { PhysXBaseBenchmarkFixture::SetUpInternal(); } - virtual void TearDown([[maybe_unused]] const ::benchmark::State &state) override + void internalTearDown() { PhysXBaseBenchmarkFixture::TearDownInternal(); } - protected: + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } + + void TearDown(const benchmark::State&) override + { + internalTearDown(); + } + void TearDown(benchmark::State&) override + { + internalTearDown(); + } + // PhysXBaseBenchmarkFixture Interface --------- AzPhysics::SceneConfiguration GetDefaultSceneConfiguration() override { diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXRigidBodyBenchmarks.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXRigidBodyBenchmarks.cpp index 0117e9737b..58cbd0da23 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXRigidBodyBenchmarks.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXRigidBodyBenchmarks.cpp @@ -136,8 +136,8 @@ namespace PhysX::Benchmarks class PhysXRigidbodyBenchmarkFixture : public PhysXBaseBenchmarkFixture { - public: - virtual void SetUp([[maybe_unused]] const ::benchmark::State &state) override + protected: + virtual void internalSetUp() { PhysXBaseBenchmarkFixture::SetUpInternal(); //need to get the Physics::System to be able to spawn the rigid bodies @@ -146,11 +146,29 @@ namespace PhysX::Benchmarks m_terrainEntity = PhysX::TestUtils::CreateFlatTestTerrain(m_testSceneHandle, RigidBodyConstants::TerrainSize, RigidBodyConstants::TerrainSize); } - virtual void TearDown([[maybe_unused]] const ::benchmark::State &state) override + virtual void internalTearDown() { m_terrainEntity = nullptr; PhysXBaseBenchmarkFixture::TearDownInternal(); } + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } + + void TearDown(const benchmark::State&) override + { + internalTearDown(); + } + void TearDown(benchmark::State&) override + { + internalTearDown(); + } protected: // PhysXBaseBenchmarkFixture Interface --------- @@ -312,10 +330,9 @@ namespace PhysX::Benchmarks class PhysXRigidbodyCollisionsBenchmarkFixture : public PhysXRigidbodyBenchmarkFixture { - public: - void SetUp(const ::benchmark::State& state) override + void internalSetUp() override { - PhysXRigidbodyBenchmarkFixture::SetUp(state); + PhysXRigidbodyBenchmarkFixture::internalSetUp(); m_collisionBeginCount = 0; m_collisionPersistCount = 0; @@ -346,11 +363,30 @@ namespace PhysX::Benchmarks m_defaultScene->RegisterSceneCollisionEventHandler(m_onSceneCollisionHandler); } - void TearDown(const ::benchmark::State& state) override + void internalTearDown() override { m_onSceneCollisionHandler.Disconnect(); - PhysXRigidbodyBenchmarkFixture::TearDown(state); + PhysXRigidbodyBenchmarkFixture::internalTearDown(); + } + + public: + void SetUp(const benchmark::State&) override + { + internalSetUp(); + } + void SetUp(benchmark::State&) override + { + internalSetUp(); + } + + void TearDown(const benchmark::State&) override + { + internalTearDown(); + } + void TearDown(benchmark::State&) override + { + internalTearDown(); } protected: diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXSceneQueryBenchmarks.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXSceneQueryBenchmarks.cpp index 4a6cbae99b..9a8a28a85e 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXSceneQueryBenchmarks.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXSceneQueryBenchmarks.cpp @@ -47,14 +47,12 @@ namespace PhysX::Benchmarks , public PhysX::GenericPhysicsFixture { - public: - //! Spawns box entities in unique locations in 1/8 of sphere with all non-negative dimensions between radii[2, max radius]. //! Accepts 2 parameters from \state. //! //! \state.range(0) - number of box entities to spawn //! \state.range(1) - max radius - void SetUp(const ::benchmark::State& state) override + void internalSetUp(const ::benchmark::State& state) { PhysX::GenericPhysicsFixture::SetUpInternal(); @@ -100,13 +98,32 @@ namespace PhysX::Benchmarks } } - void TearDown([[maybe_unused]] const ::benchmark::State& state) override + void internalTearDown() { m_boxes.clear(); m_entities.clear(); PhysX::GenericPhysicsFixture::TearDownInternal(); } + public: + void SetUp(const benchmark::State& state) override + { + internalSetUp(state); + } + void SetUp(benchmark::State& state) override + { + internalSetUp(state); + } + + void TearDown(const benchmark::State&) override + { + internalTearDown(); + } + void TearDown(benchmark::State&) override + { + internalTearDown(); + } + protected: std::vector m_entities; std::vector m_boxes; diff --git a/Gems/PhysX/Code/Tests/TestColliderComponent.h b/Gems/PhysX/Code/Tests/TestColliderComponent.h index 4df96a9ff1..91d3f16122 100644 --- a/Gems/PhysX/Code/Tests/TestColliderComponent.h +++ b/Gems/PhysX/Code/Tests/TestColliderComponent.h @@ -49,7 +49,7 @@ namespace UnitTest m_componentModeDelegate.Disconnect(); } - void SetColliderOffset(const AZ::Vector3& offset) { m_offset = offset; } + void SetColliderOffset(const AZ::Vector3& offset) override { m_offset = offset; } AZ::Vector3 GetColliderOffset() override { return m_offset; } void SetColliderRotation(const AZ::Quaternion& rotation) override { m_rotation = rotation; } AZ::Quaternion GetColliderRotation() override { return m_rotation; } diff --git a/Gems/PhysX/gem.json b/Gems/PhysX/gem.json index 0fbd3e44f9..bacbcf2dee 100644 --- a/Gems/PhysX/gem.json +++ b/Gems/PhysX/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The PhysX Gem provides physics simulation with NVIDIA PhysX including static and dynamic rigid body simulation, force regions, ragdolls, and dynamic PhysX joints.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx/", + "dependencies": [ + "LmbrCentral", + "CommonFeaturesAtom" + ] } diff --git a/Gems/PhysXDebug/gem.json b/Gems/PhysXDebug/gem.json index f7877cabdd..ece0774210 100644 --- a/Gems/PhysXDebug/gem.json +++ b/Gems/PhysXDebug/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The PhysX Debug Gem provides debugging functionality and visualizations for NVIDIA PhysX in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "Debug"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "Debug" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-debug/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-debug/", + "dependencies": [ + "PhysX", + "ImGui" + ] } diff --git a/Gems/PhysXSamples/gem.json b/Gems/PhysXSamples/gem.json index e48dc6991b..0c84a29f47 100644 --- a/Gems/PhysXSamples/gem.json +++ b/Gems/PhysXSamples/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The PhysX Samples Gem provides sample assets and scripts that demonstrate PhysX Gem features in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "Sample"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "Sample" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-samples/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-samples/", + "dependencies": [] } diff --git a/Gems/Prefab/PrefabBuilder/gem.json b/Gems/Prefab/PrefabBuilder/gem.json index ad6062ae3b..ba78f96358 100644 --- a/Gems/Prefab/PrefabBuilder/gem.json +++ b/Gems/Prefab/PrefabBuilder/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Prefab Builder Gem provides an Asset Processor module for prefabs, which are complex assets built by combining smaller entities.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Utility", "Core"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Utility", + "Core" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/prefab/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/prefab/", + "dependencies": [] } diff --git a/Gems/Presence/Code/Source/PresenceSystemComponent.h b/Gems/Presence/Code/Source/PresenceSystemComponent.h index cc3ab48bae..bc4ec01906 100644 --- a/Gems/Presence/Code/Source/PresenceSystemComponent.h +++ b/Gems/Presence/Code/Source/PresenceSystemComponent.h @@ -43,7 +43,7 @@ namespace Presence //////////////////////////////////////////////////////////////////////// //! PresenceRequestBus interface implementation void SetPresence(const SetPresenceParams& params) override; - void QueryPresence(const QueryPresenceParams& params); + void QueryPresence(const QueryPresenceParams& params) override; public: //////////////////////////////////////////////////////////////////////// diff --git a/Gems/Presence/gem.json b/Gems/Presence/gem.json index 49d937946d..a70953ae4e 100644 --- a/Gems/Presence/gem.json +++ b/Gems/Presence/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Presence Gem provides a target platform agnostic interface for Presence services.", - "canonical_tags": ["Gem"], - "user_tags": ["Network", "Gameplay", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Network", + "Gameplay", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/presence/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/presence/", + "dependencies": [] } diff --git a/Gems/PrimitiveAssets/gem.json b/Gems/PrimitiveAssets/gem.json index d1789d46fe..4ad3cb62ad 100644 --- a/Gems/PrimitiveAssets/gem.json +++ b/Gems/PrimitiveAssets/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Primitive Assets Gem provides primitive shape mesh assets with physics enabled.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Sample", "Debug"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Sample", + "Debug" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/primitive-assets/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/primitive-assets/", + "dependencies": [] } diff --git a/Gems/PythonAssetBuilder/Code/Tests/PythonBuilderTestShared.h b/Gems/PythonAssetBuilder/Code/Tests/PythonBuilderTestShared.h index 3a16e28ee0..eb01d5011b 100644 --- a/Gems/PythonAssetBuilder/Code/Tests/PythonBuilderTestShared.h +++ b/Gems/PythonAssetBuilder/Code/Tests/PythonBuilderTestShared.h @@ -37,7 +37,7 @@ namespace UnitTest return response; } - AssetBuilderSDK::ProcessJobResponse OnProcessJobRequest(const AssetBuilderSDK::ProcessJobRequest& request) + AssetBuilderSDK::ProcessJobResponse OnProcessJobRequest(const AssetBuilderSDK::ProcessJobRequest& request) override { if (request.m_sourceFileUUID.IsNull()) { @@ -49,12 +49,12 @@ namespace UnitTest return response; } - void OnShutdown() + void OnShutdown() override { ++m_onShutdownCount; } - void OnCancel() + void OnCancel() override { ++m_onCancelCount; } diff --git a/Gems/PythonAssetBuilder/gem.json b/Gems/PythonAssetBuilder/gem.json index 1a76d7d2c2..ce30ba9e82 100644 --- a/Gems/PythonAssetBuilder/gem.json +++ b/Gems/PythonAssetBuilder/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Python Asset Builder Gem provides functionality to implement custom asset builders in Python for Asset Processor.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Assets", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Assets", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/python-asset-builder/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/python-asset-builder/", + "dependencies": [ + "EditorPythonBindings" + ] } diff --git a/Gems/QtForPython/gem.json b/Gems/QtForPython/gem.json index 1519d46c52..f83be43342 100644 --- a/Gems/QtForPython/gem.json +++ b/Gems/QtForPython/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Qt for Python Gem provides the PySide2 Python libraries to manage Qt widgets.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "UI", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "UI", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/qt-for-python/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/qt-for-python/", + "dependencies": [ + "EditorPythonBindings" + ] } diff --git a/Gems/SaveData/Code/Source/Platform/Android/SaveData_SystemComponent_Android.cpp b/Gems/SaveData/Code/Source/Platform/Android/SaveData_SystemComponent_Android.cpp index 26760ca002..cf083e26ca 100644 --- a/Gems/SaveData/Code/Source/Platform/Android/SaveData_SystemComponent_Android.cpp +++ b/Gems/SaveData/Code/Source/Platform/Android/SaveData_SystemComponent_Android.cpp @@ -59,7 +59,7 @@ namespace SaveData //////////////////////////////////////////////////////////////////////////////////////////// //! The absolute path to the application's save data dircetory. - AZStd::string m_saveDataDircetoryPathAbsolute = nullptr; + AZStd::string m_saveDataDircetoryPathAbsolute; }; //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/SaveData/Code/Source/Platform/Common/Apple/SaveData_SystemComponent_Apple.mm b/Gems/SaveData/Code/Source/Platform/Common/Apple/SaveData_SystemComponent_Apple.mm index 882723de13..e4c1f8a0d8 100644 --- a/Gems/SaveData/Code/Source/Platform/Common/Apple/SaveData_SystemComponent_Apple.mm +++ b/Gems/SaveData/Code/Source/Platform/Common/Apple/SaveData_SystemComponent_Apple.mm @@ -60,7 +60,7 @@ namespace SaveData //////////////////////////////////////////////////////////////////////////////////////////// //! The absolute path to the application's save data dircetory. - AZStd::string m_saveDataDircetoryPathAbsolute = nullptr; + AZStd::string m_saveDataDircetoryPathAbsolute; }; //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/SaveData/Code/Source/Platform/Windows/SaveData_SystemComponent_Windows.cpp b/Gems/SaveData/Code/Source/Platform/Windows/SaveData_SystemComponent_Windows.cpp index 2e473cfea7..4c182ecd17 100644 --- a/Gems/SaveData/Code/Source/Platform/Windows/SaveData_SystemComponent_Windows.cpp +++ b/Gems/SaveData/Code/Source/Platform/Windows/SaveData_SystemComponent_Windows.cpp @@ -62,7 +62,7 @@ namespace SaveData //////////////////////////////////////////////////////////////////////////////////////////// //! The absolute path to the application's save data dircetory. - AZStd::string m_saveDataDircetoryPathAbsolute = nullptr; + AZStd::string m_saveDataDircetoryPathAbsolute; }; //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/SaveData/gem.json b/Gems/SaveData/gem.json index 3892cc80c2..333b4682d2 100644 --- a/Gems/SaveData/gem.json +++ b/Gems/SaveData/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Save Data Gem provides a platform independent API to save and load persistent user data in Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["Utility", "Gameplay"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Utility", + "Gameplay" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/save-data/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/save-data/", + "dependencies": [] } diff --git a/Gems/SceneLoggingExample/Code/Behaviors/LoggingGroupBehavior.h b/Gems/SceneLoggingExample/Code/Behaviors/LoggingGroupBehavior.h index ae1989a284..a3ed7c1b20 100644 --- a/Gems/SceneLoggingExample/Code/Behaviors/LoggingGroupBehavior.h +++ b/Gems/SceneLoggingExample/Code/Behaviors/LoggingGroupBehavior.h @@ -28,8 +28,8 @@ namespace SceneLoggingExample ~LoggingGroupBehavior() override = default; - void Activate(); - void Deactivate(); + void Activate() override; + void Deactivate() override; static void Reflect(AZ::ReflectContext* context); void GetCategoryAssignments(CategoryRegistrationList& categories, const AZ::SceneAPI::Containers::Scene& scene) override; diff --git a/Gems/SceneLoggingExample/Code/Processors/LoadingTrackingProcessor.h b/Gems/SceneLoggingExample/Code/Processors/LoadingTrackingProcessor.h index d6480b7dcd..959098cb97 100644 --- a/Gems/SceneLoggingExample/Code/Processors/LoadingTrackingProcessor.h +++ b/Gems/SceneLoggingExample/Code/Processors/LoadingTrackingProcessor.h @@ -34,7 +34,7 @@ namespace SceneLoggingExample RequestingApplication requester) override; AZ::SceneAPI::Events::LoadingResult LoadAsset(AZ::SceneAPI::Containers::Scene& scene, const AZStd::string& path, const AZ::Uuid& guid, RequestingApplication requester) override; - void FinalizeAssetLoading(AZ::SceneAPI::Containers::Scene& scene, RequestingApplication requester); + void FinalizeAssetLoading(AZ::SceneAPI::Containers::Scene& scene, RequestingApplication requester) override; AZ::SceneAPI::Events::ProcessingResult UpdateManifest(AZ::SceneAPI::Containers::Scene& scene, ManifestAction action, RequestingApplication requester) override; diff --git a/Gems/SceneLoggingExample/gem.json b/Gems/SceneLoggingExample/gem.json index ff7def1b32..16961b9c5b 100644 --- a/Gems/SceneLoggingExample/gem.json +++ b/Gems/SceneLoggingExample/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Scene Logging Example Gem demonstrates the basics of extending the Open 3D Engine Scene API by adding additional logging to the pipeline.", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Sample"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Sample" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/scene-logging-example/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/scene-logging-example/", + "dependencies": [] } diff --git a/Gems/SceneProcessing/Code/Source/Config/SettingsObjects/FileSoftNameSetting.h b/Gems/SceneProcessing/Code/Source/Config/SettingsObjects/FileSoftNameSetting.h index d6dcdb9de9..15c407a0cb 100644 --- a/Gems/SceneProcessing/Code/Source/Config/SettingsObjects/FileSoftNameSetting.h +++ b/Gems/SceneProcessing/Code/Source/Config/SettingsObjects/FileSoftNameSetting.h @@ -68,7 +68,7 @@ namespace AZ const char* virtualType, bool inclusive, std::initializer_list graphTypes); ~FileSoftNameSetting() override = default; - bool IsVirtualType(const SceneAPI::Containers::Scene& scene, SceneAPI::Containers::SceneGraph::NodeIndex node) const; + bool IsVirtualType(const SceneAPI::Containers::Scene& scene, SceneAPI::Containers::SceneGraph::NodeIndex node) const override; static void Reflect(AZ::ReflectContext* context); diff --git a/Gems/SceneProcessing/Code/Source/Config/Widgets/GraphTypeSelector.h b/Gems/SceneProcessing/Code/Source/Config/Widgets/GraphTypeSelector.h index 19ab410384..813f791310 100644 --- a/Gems/SceneProcessing/Code/Source/Config/Widgets/GraphTypeSelector.h +++ b/Gems/SceneProcessing/Code/Source/Config/Widgets/GraphTypeSelector.h @@ -32,7 +32,7 @@ namespace AZ QWidget* CreateGUI(QWidget* parent) override; u32 GetHandlerName() const override; - bool AutoDelete() const; + bool AutoDelete() const override; bool IsDefaultHandler() const override; diff --git a/Gems/SceneProcessing/gem.json b/Gems/SceneProcessing/gem.json index a6c2cefbd6..de1dfeb23f 100644 --- a/Gems/SceneProcessing/gem.json +++ b/Gems/SceneProcessing/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Scene Processing Gem provides Scene Settings, a tool you can use to specify the default settings for processing asset files for actors, meshes, motions, and PhysX.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Tools", "Core"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Tools", + "Core" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/scene-processing/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/scene-processing/", + "dependencies": [] } diff --git a/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAssetTracker.h b/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAssetTracker.h index 6101f292a1..4850e669ff 100644 --- a/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAssetTracker.h +++ b/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAssetTracker.h @@ -86,7 +86,7 @@ namespace ScriptCanvasEditor void UpdateFileState(AZ::Data::AssetId assetId, Tracker::ScriptCanvasFileState state) override; AssetTrackerRequests::AssetList GetUnsavedAssets() override; - AssetTrackerRequests::AssetList GetAssets(); + AssetTrackerRequests::AssetList GetAssets() override; AssetTrackerRequests::AssetList GetAssetsIf(AZStd::function pred = []() { return true; }) override; AZ::EntityId GetSceneEntityIdFromEditorEntityId(AZ::Data::AssetId assetId, AZ::EntityId editorEntityId) override; diff --git a/Gems/ScriptCanvas/Code/Editor/Framework/ScriptCanvasTraceUtilities.h b/Gems/ScriptCanvas/Code/Editor/Framework/ScriptCanvasTraceUtilities.h index e9a850a2a4..a87f4450c2 100644 --- a/Gems/ScriptCanvas/Code/Editor/Framework/ScriptCanvasTraceUtilities.h +++ b/Gems/ScriptCanvas/Code/Editor/Framework/ScriptCanvasTraceUtilities.h @@ -128,15 +128,15 @@ namespace ScriptCanvasEditor } } - bool OnPreAssert(const char*, int, const char*, const char*) { return suppressPreAssert; } - bool OnAssert(const char*) { return suppressAssert; } - bool OnException(const char*) { return suppressException; } - bool OnPreError(const char*, const char*, int, const char*, const char*) { return suppressPreError; } - bool OnError(const char*, const char*) { return suppressError; } - bool OnPreWarning(const char*, const char*, int, const char*, const char*) { return suppressPreWarning; } - bool OnWarning(const char*, const char*) { return suppressWarning; } - bool OnPrintf(const char*, const char*) { return suppressPrintf; } - bool OnOutput(const char*, const char*) { return suppressAllOutput; } + bool OnPreAssert(const char*, int, const char*, const char*) override { return suppressPreAssert; } + bool OnAssert(const char*) override { return suppressAssert; } + bool OnException(const char*) override { return suppressException; } + bool OnPreError(const char*, const char*, int, const char*, const char*) override { return suppressPreError; } + bool OnError(const char*, const char*) override { return suppressError; } + bool OnPreWarning(const char*, const char*, int, const char*, const char*) override { return suppressPreWarning; } + bool OnWarning(const char*, const char*) override { return suppressWarning; } + bool OnPrintf(const char*, const char*) override { return suppressPrintf; } + bool OnOutput(const char*, const char*) override { return suppressAllOutput; } void SuppressPreAssert(bool suppress) override { suppressPreAssert = suppress; } void SuppressAssert(bool suppress)override { suppressAssert = suppress; } diff --git a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/DynamicSlotComponent.h b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/DynamicSlotComponent.h index 5f1372d8c3..a30e6c999f 100644 --- a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/DynamicSlotComponent.h +++ b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/DynamicSlotComponent.h @@ -33,9 +33,9 @@ namespace ScriptCanvasEditor ~DynamicSlotComponent() override = default; // AZ::Component - void Init(); - void Activate(); - void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; //// // GraphCanvas::SceneMemberNotificationBus diff --git a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/MappingComponent.h b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/MappingComponent.h index f5345f8bc8..fdc5ebc9fa 100644 --- a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/MappingComponent.h +++ b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/MappingComponent.h @@ -31,8 +31,8 @@ namespace ScriptCanvasEditor SceneMemberMappingComponent(const AZ::EntityId& sourceId); ~SceneMemberMappingComponent() = default; - void Activate(); - void Deactivate(); + void Activate() override; + void Deactivate() override; // SceneMemberMappingConfigurationRequestBus void ConfigureMapping(const AZ::EntityId& scriptCanvasMemberId) override; @@ -68,8 +68,8 @@ namespace ScriptCanvasEditor SlotMappingComponent(const AZ::EntityId& sourceId); ~SlotMappingComponent() = default; - void Activate(); - void Deactivate(); + void Activate() override; + void Deactivate() override; // GraphCanvas::NodeNotificationBus void OnAddedToScene(const AZ::EntityId&) override; diff --git a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/NodeDescriptors/NodeDescriptorComponent.h b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/NodeDescriptors/NodeDescriptorComponent.h index c885c26624..1c42d1bdb0 100644 --- a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/NodeDescriptors/NodeDescriptorComponent.h +++ b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/NodeDescriptors/NodeDescriptorComponent.h @@ -33,9 +33,9 @@ namespace ScriptCanvasEditor ~NodeDescriptorComponent() override = default; // Component - void Init(); - void Activate(); - void Deactivate(); + void Init() override; + void Activate() override; + void Deactivate() override; //// // NodeDescriptorBus::Handler diff --git a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/NodeDescriptors/ScriptEventReceiverEventNodeDescriptorComponent.h b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/NodeDescriptors/ScriptEventReceiverEventNodeDescriptorComponent.h index 7a991fc791..4a75432ad9 100644 --- a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/NodeDescriptors/ScriptEventReceiverEventNodeDescriptorComponent.h +++ b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/NodeDescriptors/ScriptEventReceiverEventNodeDescriptorComponent.h @@ -74,7 +74,7 @@ namespace ScriptCanvasEditor //// // ScriptEventReceiveNodeDescriptorNotifications - void OnScriptEventReloaded(const AZ::Data::Asset& asset); + void OnScriptEventReloaded(const AZ::Data::Asset& asset) override; //// protected: diff --git a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/NodeDescriptors/VariableNodeDescriptorComponent.h b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/NodeDescriptors/VariableNodeDescriptorComponent.h index 9a14a7ca23..be5eacc397 100644 --- a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/NodeDescriptors/VariableNodeDescriptorComponent.h +++ b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/Components/NodeDescriptors/VariableNodeDescriptorComponent.h @@ -52,7 +52,7 @@ namespace ScriptCanvasEditor //// // VariableNodeDescriptorBus - ScriptCanvas::VariableId GetVariableId() const; + ScriptCanvas::VariableId GetVariableId() const override; //// protected: diff --git a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/DataInterfaces/ScriptCanvasColorDataInterface.h b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/DataInterfaces/ScriptCanvasColorDataInterface.h index b7db5a4e08..37d5ba0181 100644 --- a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/DataInterfaces/ScriptCanvasColorDataInterface.h +++ b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/DataInterfaces/ScriptCanvasColorDataInterface.h @@ -131,7 +131,7 @@ namespace ScriptCanvasEditor return "vectorized"; } - virtual AZStd::string GetElementStyle(int index) const + AZStd::string GetElementStyle(int index) const override { return AZStd::string::format("vector_%i", index); } diff --git a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/DataInterfaces/ScriptCanvasVariableDataInterface.h b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/DataInterfaces/ScriptCanvasVariableDataInterface.h index 7b93dc3a71..29dc17deca 100644 --- a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/DataInterfaces/ScriptCanvasVariableDataInterface.h +++ b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/DataInterfaces/ScriptCanvasVariableDataInterface.h @@ -88,12 +88,12 @@ namespace ScriptCanvasEditor //// // GeneralEditorNotifications - void OnUndoRedoBegin() + void OnUndoRedoBegin() override { ScriptCanvas::GraphVariableManagerNotificationBus::Handler::BusDisconnect(); } - void OnUndoRedoEnd() + void OnUndoRedoEnd() override { FinalizeActivation(); } @@ -250,7 +250,7 @@ namespace ScriptCanvasEditor } // SystemTickBus - void OnSystemTick() + void OnSystemTick() override { AZ::SystemTickBus::Handler::BusDisconnect(); AssignIndex(m_variableTypeModel.GetDefaultIndex()); @@ -440,7 +440,7 @@ namespace ScriptCanvasEditor } // SystemTickBus - void OnSystemTick() + void OnSystemTick() override { AZ::SystemTickBus::Handler::BusDisconnect(); AssignIndex(m_variableTypeModel.GetDefaultIndex()); @@ -449,7 +449,7 @@ namespace ScriptCanvasEditor //// // NodeNotificationBus - void OnSlotDisplayTypeChanged(const ScriptCanvas::SlotId& slotId, [[maybe_unused]] const ScriptCanvas::Data::Type& slotType) + void OnSlotDisplayTypeChanged(const ScriptCanvas::SlotId& slotId, [[maybe_unused]] const ScriptCanvas::Data::Type& slotType) override { if (slotId == GetSlotId()) { @@ -482,7 +482,7 @@ namespace ScriptCanvasEditor //// // EndpointNotificationBus - void OnEndpointReferenceChanged(const ScriptCanvas::VariableId& variableId) + void OnEndpointReferenceChanged(const ScriptCanvas::VariableId& variableId) override { ScriptCanvas::VariableNotificationBus::Handler::BusDisconnect(); ScriptCanvas::VariableNotificationBus::Handler::BusConnect(ScriptCanvas::GraphScopedVariableId(GetScriptCanvasId(), variableId)); @@ -490,7 +490,7 @@ namespace ScriptCanvasEditor SignalValueChanged(); } - void OnSlotRecreated() + void OnSlotRecreated() override { ScriptCanvas::Slot* slot = GetSlot(); diff --git a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/DataInterfaces/ScriptCanvasVectorDataInterface.h b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/DataInterfaces/ScriptCanvasVectorDataInterface.h index 717022c19d..6d76d5504a 100644 --- a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/DataInterfaces/ScriptCanvasVectorDataInterface.h +++ b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/DataInterfaces/ScriptCanvasVectorDataInterface.h @@ -103,7 +103,7 @@ namespace ScriptCanvasEditor return "???"; } - AZStd::string GetStyle() const + AZStd::string GetStyle() const override { return "vectorized"; } diff --git a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/PropertyInterfaces/ScriptCanvasEnumComboBoxPropertyDataInterface.h b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/PropertyInterfaces/ScriptCanvasEnumComboBoxPropertyDataInterface.h index 6faac13659..c283fc8802 100644 --- a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/PropertyInterfaces/ScriptCanvasEnumComboBoxPropertyDataInterface.h +++ b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/PropertyInterfaces/ScriptCanvasEnumComboBoxPropertyDataInterface.h @@ -50,7 +50,7 @@ namespace ScriptCanvasEditor return m_comboBoxModel.GetIndexForValue(dataValue); } - QString GetDisplayString() const + QString GetDisplayString() const override { int32_t dataValue = GetValue(); return m_comboBoxModel.GetNameForValue(dataValue); diff --git a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/PropertyInterfaces/ScriptCanvasPropertyDataInterface.h b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/PropertyInterfaces/ScriptCanvasPropertyDataInterface.h index 768773e676..f69315bc86 100644 --- a/Gems/ScriptCanvas/Code/Editor/GraphCanvas/PropertyInterfaces/ScriptCanvasPropertyDataInterface.h +++ b/Gems/ScriptCanvas/Code/Editor/GraphCanvas/PropertyInterfaces/ScriptCanvasPropertyDataInterface.h @@ -177,7 +177,7 @@ namespace ScriptCanvasEditor return m_comboBoxModel.GetIndexForValue(dataValue); } - QString GetDisplayString() const + QString GetDisplayString() const override { DataType dataValue = this->GetValue(); return m_comboBoxModel.GetNameForValue(dataValue); diff --git a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasAsset.h b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasAsset.h index 9942e349c5..fdc8cbc1d8 100644 --- a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasAsset.h +++ b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasAsset.h @@ -81,8 +81,8 @@ namespace ScriptCanvasEditor ScriptCanvas::Graph* GetScriptCanvasGraph() const; using Description = ScriptCanvasAssetDescription; - ScriptCanvas::ScriptCanvasData& GetScriptCanvasData(); - const ScriptCanvas::ScriptCanvasData& GetScriptCanvasData() const; + ScriptCanvas::ScriptCanvasData& GetScriptCanvasData() override; + const ScriptCanvas::ScriptCanvasData& GetScriptCanvasData() const override; }; } diff --git a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h index cb6689091b..6f73e84493 100644 --- a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h +++ b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/EditorGraph.h @@ -248,8 +248,8 @@ namespace ScriptCanvasEditor GraphCanvas::GraphId GetGraphCanvasGraphId() const override; - AZStd::unordered_map< AZ::EntityId, GraphCanvas::EntitySaveDataContainer* > GetGraphCanvasSaveData(); - void UpdateGraphCanvasSaveData(const AZStd::unordered_map< AZ::EntityId, GraphCanvas::EntitySaveDataContainer* >& saveData); + AZStd::unordered_map< AZ::EntityId, GraphCanvas::EntitySaveDataContainer* > GetGraphCanvasSaveData() override; + void UpdateGraphCanvasSaveData(const AZStd::unordered_map< AZ::EntityId, GraphCanvas::EntitySaveDataContainer* >& saveData) override; NodeIdPair CreateCustomNode(const AZ::Uuid& typeId, const AZ::Vector2& position) override; @@ -325,7 +325,7 @@ namespace ScriptCanvasEditor } protected: - void PostRestore(const UndoData& restoredData); + void PostRestore(const UndoData& restoredData) override; void UnregisterToast(const GraphCanvas::ToastId& toastId); diff --git a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h index 89ee0f3b55..fb6e109156 100644 --- a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h +++ b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Components/GraphUpgrade.h @@ -82,7 +82,7 @@ namespace ScriptCanvasEditor StateMachine* GetStateMachine() override { return m_stateMachine; } - virtual int GetStateId() const { return Traits::StateID(); } + int GetStateId() const override { return Traits::StateID(); } static int StateID() { return Traits::StateID(); } diff --git a/Gems/ScriptCanvas/Code/Editor/Settings.h b/Gems/ScriptCanvas/Code/Editor/Settings.h index b282e114ac..7eff40ec43 100644 --- a/Gems/ScriptCanvas/Code/Editor/Settings.h +++ b/Gems/ScriptCanvas/Code/Editor/Settings.h @@ -40,7 +40,7 @@ namespace ScriptCanvasEditor ScriptCanvasConstructPresets(); ~ScriptCanvasConstructPresets() override = default; - void InitializeConstructType(GraphCanvas::ConstructType constructType); + void InitializeConstructType(GraphCanvas::ConstructType constructType) override; }; class EditorWorkspace diff --git a/Gems/ScriptCanvas/Code/Editor/Static/Include/ScriptCanvas/View/EditCtrls/GenericLineEditCtrl.h b/Gems/ScriptCanvas/Code/Editor/Static/Include/ScriptCanvas/View/EditCtrls/GenericLineEditCtrl.h index 9b83819860..950fa8ef2e 100644 --- a/Gems/ScriptCanvas/Code/Editor/Static/Include/ScriptCanvas/View/EditCtrls/GenericLineEditCtrl.h +++ b/Gems/ScriptCanvas/Code/Editor/Static/Include/ScriptCanvas/View/EditCtrls/GenericLineEditCtrl.h @@ -59,7 +59,7 @@ namespace ScriptCanvasEditor void onChildLineEditValueChange(const QString& value); protected: - virtual void focusInEvent(QFocusEvent* e); + void focusInEvent(QFocusEvent* e) override; private: QLineEdit* m_pLineEdit; diff --git a/Gems/ScriptCanvas/Code/Editor/SystemComponent.h b/Gems/ScriptCanvas/Code/Editor/SystemComponent.h index ba850401d6..3e18262c98 100644 --- a/Gems/ScriptCanvas/Code/Editor/SystemComponent.h +++ b/Gems/ScriptCanvas/Code/Editor/SystemComponent.h @@ -65,7 +65,7 @@ namespace ScriptCanvasEditor //////////////////////////////////////////////////////////////////////// // SystemRequestBus::Handler... void AddAsyncJob(AZStd::function&& jobFunc) override; - void GetEditorCreatableTypes(AZStd::unordered_set& outCreatableTypes); + void GetEditorCreatableTypes(AZStd::unordered_set& outCreatableTypes) override; void CreateEditorComponentsOnEntity(AZ::Entity* entity, const AZ::Data::AssetType& assetType) override; //////////////////////////////////////////////////////////////////////// @@ -110,7 +110,7 @@ namespace ScriptCanvasEditor //////////////////////////////////////////////////////////////////////// // IUpgradeRequests... - void ClearGraphsThatNeedUpgrade() + void ClearGraphsThatNeedUpgrade() override { m_assetsThatNeedManualUpgrade.clear(); } diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/CanvasWidget.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/CanvasWidget.h index 2ebc71f785..9abb41aacc 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/CanvasWidget.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/CanvasWidget.h @@ -61,7 +61,7 @@ namespace ScriptCanvasEditor protected: - void resizeEvent(QResizeEvent *ev); + void resizeEvent(QResizeEvent *ev) override; void OnClicked(); diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/AssetWindowSession/LoggingAssetDataAggregator.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/AssetWindowSession/LoggingAssetDataAggregator.h index ae92586c4d..71efcbb017 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/AssetWindowSession/LoggingAssetDataAggregator.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/AssetWindowSession/LoggingAssetDataAggregator.h @@ -27,15 +27,15 @@ namespace ScriptCanvasEditor bool IsCapturingData() const override { return false; } protected: - void Visit(ScriptCanvas::AnnotateNodeSignal&); - void Visit(ScriptCanvas::ExecutionThreadEnd&); - void Visit(ScriptCanvas::ExecutionThreadBeginning&); - void Visit(ScriptCanvas::GraphActivation&); - void Visit(ScriptCanvas::GraphDeactivation&); - void Visit(ScriptCanvas::NodeStateChange&); - void Visit(ScriptCanvas::InputSignal&); - void Visit(ScriptCanvas::OutputSignal&); - void Visit(ScriptCanvas::VariableChange&); + void Visit(ScriptCanvas::AnnotateNodeSignal&) override; + void Visit(ScriptCanvas::ExecutionThreadEnd&) override; + void Visit(ScriptCanvas::ExecutionThreadBeginning&) override; + void Visit(ScriptCanvas::GraphActivation&) override; + void Visit(ScriptCanvas::GraphDeactivation&) override; + void Visit(ScriptCanvas::NodeStateChange&) override; + void Visit(ScriptCanvas::InputSignal&) override; + void Visit(ScriptCanvas::OutputSignal&) override; + void Visit(ScriptCanvas::VariableChange&) override; private: diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingDataAggregator.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingDataAggregator.h index d26939a217..8efeba4613 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingDataAggregator.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingDataAggregator.h @@ -38,8 +38,8 @@ namespace ScriptCanvasEditor void OnCurrentTargetChanged() override; //// - bool CanCaptureData() const; - bool IsCapturingData() const; + bool CanCaptureData() const override; + bool IsCapturingData() const override; void StartCaptureData(); void StopCaptureData(); diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingWindowSession.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingWindowSession.h index ba2e5b4a56..3fa0cf8d01 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingWindowSession.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingWindowSession.h @@ -97,8 +97,8 @@ namespace ScriptCanvasEditor //// // AzToolsFramework::EditorEntityContextNotificationBus::Handler - void OnStartPlayInEditorBegin(); - void OnStopPlayInEditor(); + void OnStartPlayInEditorBegin() override; + void OnStopPlayInEditor() override; //// // ScriptCavnas::Debugger::ServiceNotificationsBus diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h index 0e4d35e270..9892245fa4 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h @@ -88,7 +88,7 @@ namespace ScriptCanvasEditor AZ::NamedEntityId FindNamedEntityId(const AZ::EntityId& entityId) override; //// - virtual bool IsCapturingData() const = 0; + bool IsCapturingData() const override = 0; virtual bool CanCaptureData() const = 0; // Should be bus methods, but don't want to copy data diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LoggingWindowTreeItems.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LoggingWindowTreeItems.h index 751c1c30b7..862b231f3a 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LoggingWindowTreeItems.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/LoggingPanel/LoggingWindowTreeItems.h @@ -115,7 +115,7 @@ namespace ScriptCanvasEditor protected: - bool OnMatchesFilter([[maybe_unused]] const DebugLogFilter& treeFilter) { return true; } + bool OnMatchesFilter([[maybe_unused]] const DebugLogFilter& treeFilter) override { return true; } UpdatePolicy m_updatePolicy; QTimer m_additionTimer; diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/FunctionNodePaletteTreeItemTypes.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/FunctionNodePaletteTreeItemTypes.h index b03efa5813..479fed1097 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/FunctionNodePaletteTreeItemTypes.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/FunctionNodePaletteTreeItemTypes.h @@ -59,8 +59,8 @@ namespace ScriptCanvasEditor FunctionPaletteTreeItem(const char* name, const ScriptCanvas::Grammar::FunctionSourceId& sourceId, AZ::Data::Asset asset); ~FunctionPaletteTreeItem() = default; - GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const; - QVariant OnData(const QModelIndex& index, int role) const; + GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override; + QVariant OnData(const QModelIndex& index, int role) const override; ScriptCanvas::Grammar::FunctionSourceId GetFunctionSourceId() const; AZ::Data::AssetId GetSourceAssetId() const; diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp index 390dbaf0fb..b29992f9b1 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp @@ -1322,7 +1322,7 @@ namespace ScriptCanvasEditor if (seperator == AZStd::string_view::npos) { - categoryTrail = nullptr; + categoryTrail = {}; } else { diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.h index c3b37369ca..f47a224564 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.h @@ -232,8 +232,8 @@ namespace ScriptCanvasEditor ScriptEventsEventNodePaletteTreeItem(const AZ::Data::AssetId& m_assetId, const ScriptEvents::Method& methodDefinition, const ScriptCanvas::EBusEventId& eventId); ~ScriptEventsEventNodePaletteTreeItem() = default; - GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const; - QVariant OnData(const QModelIndex& index, int role) const; + GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override; + QVariant OnData(const QModelIndex& index, int role) const override; ScriptCanvas::EBusBusId GetBusIdentifier() const; ScriptCanvas::EBusEventId GetEventIdentifier() const; diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/SpecializedNodePaletteTreeItemTypes.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/SpecializedNodePaletteTreeItemTypes.h index cfc056e705..244e2bf645 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/SpecializedNodePaletteTreeItemTypes.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/SpecializedNodePaletteTreeItemTypes.h @@ -25,7 +25,7 @@ namespace ScriptCanvasEditor CreateCommentNodeMimeEvent() = default; ~CreateCommentNodeMimeEvent() = default; - NodeIdPair ConstructNode(const AZ::EntityId& sceneId, const AZ::Vector2& scenePosition); + NodeIdPair ConstructNode(const AZ::EntityId& sceneId, const AZ::Vector2& scenePosition) override; bool ExecuteEvent(const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& sceneId) override; }; @@ -54,7 +54,7 @@ namespace ScriptCanvasEditor CreateNodeGroupMimeEvent() = default; ~CreateNodeGroupMimeEvent() = default; - NodeIdPair ConstructNode(const GraphCanvas::GraphId& sceneId, const AZ::Vector2& scenePosition); + NodeIdPair ConstructNode(const GraphCanvas::GraphId& sceneId, const AZ::Vector2& scenePosition) override; bool ExecuteEvent(const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const GraphCanvas::GraphId& sceneId) override; }; diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/VariablePanel/GraphVariablesTableView.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/VariablePanel/GraphVariablesTableView.h index 27923fbe63..962aadb8c8 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/VariablePanel/GraphVariablesTableView.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/VariablePanel/GraphVariablesTableView.h @@ -160,7 +160,7 @@ namespace ScriptCanvasEditor //// // GraphCanvas::SceneNotifications - void OnSelectionChanged(); + void OnSelectionChanged() override; //// void ApplyPreferenceSort(); diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.h index d11d2f0052..ba5ff7f97f 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.h @@ -272,7 +272,7 @@ namespace ScriptCanvasEditor private: // UIRequestBus QMainWindow* GetMainWindow() override { return qobject_cast(this); } - void OpenValidationPanel(); + void OpenValidationPanel() override; // // Undo Handlers @@ -299,9 +299,9 @@ namespace ScriptCanvasEditor bool ContainsGraph(const GraphCanvas::GraphId& graphId) const override; bool CloseGraph(const GraphCanvas::GraphId& graphId) override; - void CustomizeConnectionEntity(AZ::Entity* connectionEntity); + void CustomizeConnectionEntity(AZ::Entity* connectionEntity) override; - void ShowAssetPresetsMenu(GraphCanvas::ConstructType constructType); + void ShowAssetPresetsMenu(GraphCanvas::ConstructType constructType) override; GraphCanvas::ContextMenuAction::SceneReaction ShowSceneContextMenuWithGroup(const QPoint& screenPoint, const QPointF& scenePoint, AZ::EntityId groupTarget) override; @@ -327,8 +327,8 @@ namespace ScriptCanvasEditor //// //! ScriptCanvas::BatchOperationsNotificationBus - void OnCommandStarted(AZ::Crc32 commandTag); - void OnCommandFinished(AZ::Crc32 commandTag); + void OnCommandStarted(AZ::Crc32 commandTag) override; + void OnCommandFinished(AZ::Crc32 commandTag) override; // File menu void OnFileNew(); @@ -427,7 +427,7 @@ namespace ScriptCanvasEditor QVariant GetTabData(const AZ::Data::AssetId& assetId); //! GeneralRequestBus - AZ::Outcome OpenScriptCanvasAssetId(const AZ::Data::AssetId& assetId); + AZ::Outcome OpenScriptCanvasAssetId(const AZ::Data::AssetId& assetId) override; AZ::Outcome OpenScriptCanvasAsset(AZ::Data::AssetId scriptCanvasAssetId, int tabIndex = -1) override; AZ::Outcome OpenScriptCanvasAsset(const ScriptCanvasMemoryAsset& scriptCanvasAsset, int tabIndex = -1); int CloseScriptCanvasAsset(const AZ::Data::AssetId& assetId) override; @@ -497,7 +497,7 @@ namespace ScriptCanvasEditor float GetEdgePanningScrollSpeed() const override; GraphCanvas::EditorConstructPresets* GetConstructPresets() const override; - const GraphCanvas::ConstructTypePresetBucket* GetConstructTypePresetBucket(GraphCanvas::ConstructType constructType) const; + const GraphCanvas::ConstructTypePresetBucket* GetConstructTypePresetBucket(GraphCanvas::ConstructType constructType) const override; GraphCanvas::Styling::ConnectionCurveType GetConnectionCurveType() const override; GraphCanvas::Styling::ConnectionCurveType GetDataConnectionCurveType() const override; diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/ScriptCanvasContextMenus.h b/Gems/ScriptCanvas/Code/Editor/View/Windows/ScriptCanvasContextMenus.h index f95ca6c80b..e8e2a8ee4f 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/ScriptCanvasContextMenus.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/ScriptCanvasContextMenus.h @@ -61,7 +61,10 @@ namespace ScriptCanvasEditor bool IsInSubMenu() const override; AZStd::string GetSubMenuPath() const override; + using GraphCanvas::SceneContextMenuAction::RefreshAction; void RefreshAction(const GraphCanvas::GraphId& graphId, const AZ::EntityId& targetId) override; + + using GraphCanvas::SceneContextMenuAction::TriggerAction; GraphCanvas::ContextMenuAction::SceneReaction TriggerAction(const GraphCanvas::GraphId& graphId, const AZ::Vector2& scenePos) override; }; @@ -77,7 +80,10 @@ namespace ScriptCanvasEditor GraphCanvas::ActionGroupId GetActionGroupId() const override; + using GraphCanvas::ContextMenuAction::RefreshAction; void RefreshAction(const GraphCanvas::GraphId& graphId, const AZ::EntityId& targetId) override; + + using GraphCanvas::ContextMenuAction::TriggerAction; GraphCanvas::ContextMenuAction::SceneReaction TriggerAction(const GraphCanvas::GraphId& graphId, const AZ::Vector2& scenePos) override; private: @@ -110,7 +116,10 @@ namespace ScriptCanvasEditor GraphCanvas::ActionGroupId GetActionGroupId() const override; + using SlotManipulationMenuAction::RefreshAction; void RefreshAction(const GraphCanvas::GraphId& graphId, const AZ::EntityId& targetId) override; + + using SlotManipulationMenuAction::TriggerAction; GraphCanvas::ContextMenuAction::SceneReaction TriggerAction(const GraphCanvas::GraphId& graphId, const AZ::Vector2& scenePos) override; private: @@ -127,7 +136,10 @@ namespace ScriptCanvasEditor ExposeSlotMenuAction(QObject* parent); virtual ~ExposeSlotMenuAction() = default; + using GraphCanvas::SlotContextMenuAction::RefreshAction; void RefreshAction(const GraphCanvas::GraphId& graphId, const AZ::EntityId& targetId) override; + + using GraphCanvas::SlotContextMenuAction::TriggerAction; GraphCanvas::ContextMenuAction::SceneReaction TriggerAction(const GraphCanvas::GraphId& graphId, const AZ::Vector2& scenePos) override; protected: @@ -145,7 +157,10 @@ namespace ScriptCanvasEditor virtual ~SetDataSlotTypeMenuAction() = default; static bool IsSupportedSlotType(const AZ::EntityId& slotId); + using GraphCanvas::SlotContextMenuAction::RefreshAction; void RefreshAction(const GraphCanvas::GraphId& graphId, const AZ::EntityId& targetId) override; + + using GraphCanvas::SlotContextMenuAction::TriggerAction; GraphCanvas::ContextMenuAction::SceneReaction TriggerAction(const GraphCanvas::GraphId& graphId, const AZ::Vector2& scenePos) override; private: @@ -164,7 +179,10 @@ namespace ScriptCanvasEditor CreateAzEventHandlerSlotMenuAction(QObject* parent); + using GraphCanvas::SlotContextMenuAction::RefreshAction; void RefreshAction(const GraphCanvas::GraphId& graphId, const AZ::EntityId& targetId) override; + + using GraphCanvas::SlotContextMenuAction::TriggerAction; GraphCanvas::ContextMenuAction::SceneReaction TriggerAction(const GraphCanvas::GraphId& graphId, const AZ::Vector2& scenePos) override; static const AZ::BehaviorMethod* FindBehaviorMethodWithAzEventReturn(const GraphCanvas::GraphId& graphId, AZ::EntityId targetId); @@ -239,7 +257,10 @@ namespace ScriptCanvasEditor RenameFunctionDefinitionNodeAction(NodeDescriptorComponent* descriptor, QObject* parent); virtual ~RenameFunctionDefinitionNodeAction() = default; + using GraphCanvas::NodeContextMenuAction::RefreshAction; void RefreshAction(const GraphCanvas::GraphId& graphId, const AZ::EntityId& targetId) override; + + using GraphCanvas::NodeContextMenuAction::TriggerAction; GraphCanvas::ContextMenuAction::SceneReaction TriggerAction(const GraphCanvas::GraphId& graphId, const AZ::Vector2& scenePos) override; NodeDescriptorComponent* m_descriptor; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.h index bb1ea21f30..46e8a103bb 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.h @@ -109,11 +109,11 @@ namespace ScriptCanvas //! NOTE: There can be multiple Graph components on the same entity so calling FindComponent may not not return this GraphComponent AZ::Entity* GetGraphEntity() const override { return GetEntity(); } - Graph* GetGraph() { return this; } + Graph* GetGraph() override { return this; } GraphData* GetGraphData() override { return &m_graphData; } const GraphData* GetGraphDataConst() const override { return &m_graphData; } - const VariableData* GetVariableDataConst() const { return const_cast(this)->GetVariableData(); } + const VariableData* GetVariableDataConst() const override { return const_cast(this)->GetVariableData(); } bool AddGraphData(const GraphData&) override; void RemoveGraphData(const GraphData&) override; @@ -131,7 +131,7 @@ namespace ScriptCanvas /////////////////////////////////////////////////////////// // StatusRequestBus - void ValidateGraph(ValidationResults& validationEvents); + void ValidateGraph(ValidationResults& validationEvents) override; void ReportValidationResults(ValidationResults&) override { } //// diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h index 27f0b3862d..a54f330e18 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h @@ -69,14 +69,14 @@ namespace ScriptCanvas { public: /// Called right before we start reading from the instance pointed by classPtr. - void OnReadBegin(void* objectPtr) + void OnReadBegin(void* objectPtr) override { t_Class* deserializedObject = reinterpret_cast(objectPtr); deserializedObject->OnReadBegin(); } /// Called after we are done reading from the instance pointed by classPtr. - void OnReadEnd(void* objectPtr) + void OnReadEnd(void* objectPtr) override { t_Class* deserializedObject = reinterpret_cast(objectPtr); deserializedObject->OnReadEnd(); @@ -178,8 +178,8 @@ namespace ScriptCanvas NodePropertyInterface() = default; public: - AZ_RTTI(NodePropertyInterface, "{265A2163-D3AE-4C4E-BDCC-37BA0084BF88}"); + virtual ~NodePropertyInterface() = default; virtual Data::Type GetDataType() = 0; @@ -217,14 +217,14 @@ namespace ScriptCanvas AZ_RTTI((TypedNodePropertyInterface, "{24248937-86FB-406C-8DD5-023B10BD0B60}", DataType), NodePropertyInterface); TypedNodePropertyInterface() = default; - ~TypedNodePropertyInterface() = default; + virtual ~TypedNodePropertyInterface() = default; void SetPropertyReference(DataType* dataReference) { m_dataType = dataReference; } - virtual Data::Type GetDataType() override + Data::Type GetDataType() override { return Data::FromAZType(azrtti_typeid()); } @@ -283,7 +283,7 @@ namespace ScriptCanvas AZ_RTTI((TypedComboBoxNodePropertyInterface, "{24248937-86FB-406C-8DD5-023B10BD0B60}", DataType), TypedNodePropertyInterface, ComboBoxPropertyInterface); TypedComboBoxNodePropertyInterface() = default; - ~TypedComboBoxNodePropertyInterface() = default; + virtual ~TypedComboBoxNodePropertyInterface() = default; // TypedNodePropertyInterface void ResetToDefault() override @@ -310,7 +310,7 @@ namespace ScriptCanvas } // ComboBoxPropertyInterface - int GetSelectedIndex() const + int GetSelectedIndex() const override { int counter = -1; @@ -328,7 +328,7 @@ namespace ScriptCanvas return counter; } - void SetSelectedIndex(int index) + void SetSelectedIndex(int index) override { if (index >= 0 || index < m_displaySet.size()) { @@ -354,6 +354,7 @@ namespace ScriptCanvas { public: AZ_RTTI(EnumComboBoxNodePropertyInterface, "{7D46B998-9E05-401A-AC92-37A90BAF8F60}", TypedComboBoxNodePropertyInterface); + virtual ~EnumComboBoxNodePropertyInterface() = default; // No way of identifying Enum types properly yet. Going to fake a BCO object type for now. static const AZ::Uuid k_EnumUUID; @@ -512,13 +513,13 @@ namespace ScriptCanvas //! Node internal initialization, for custom init, use OnInit - void Init() override final; + void Init() final; //! Node internal activation and housekeeping, for custom activation configuration use OnActivate - void Activate() override final; + void Activate() final; //! Node internal deactivation and housekeeping, for custom deactivation configuration use OnDeactivate - void Deactivate() override final; + void Deactivate() final; void PostActivate(); @@ -590,7 +591,7 @@ namespace ScriptCanvas NodeDisabledFlag GetNodeDisabledFlag() const; void SetNodeDisabledFlag(NodeDisabledFlag disabledFlag); - bool RemoveVariableReferences(const AZStd::unordered_set< ScriptCanvas::VariableId >& variableIds); + bool RemoveVariableReferences(const AZStd::unordered_set< ScriptCanvas::VariableId >& variableIds) override; //// Slot* GetSlotByName(AZStd::string_view slotName) const; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ClientTransceiver.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ClientTransceiver.h index 23406fbf56..78e293c4b0 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ClientTransceiver.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ClientTransceiver.h @@ -78,7 +78,7 @@ namespace ScriptCanvas ////////////////////////////////////////////////////////////////////////// // TargetManagerClient void DesiredTargetConnected(bool connected) override; - void DesiredTargetChanged(AZ::u32 newId, AZ::u32 oldId); + void DesiredTargetChanged(AZ::u32 newId, AZ::u32 oldId) override; void TargetJoinedNetwork(AzFramework::TargetInfo info) override; void TargetLeftNetwork(AzFramework::TargetInfo info) override; ////////////////////////////////////////////////////////////////////////// diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/InvalidVariableTypeEvent.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/InvalidVariableTypeEvent.h index 85ce975968..602e6626a1 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/InvalidVariableTypeEvent.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/InvalidVariableTypeEvent.h @@ -31,12 +31,12 @@ namespace ScriptCanvas SetDescription(AZStd::string::format("Variable with id %s has an invalid type.", variableId.ToString().c_str())); } - bool CanAutoFix() const + bool CanAutoFix() const override { return true; } - AZStd::string GetIdentifier() const + AZStd::string GetIdentifier() const override { return DataValidationIds::InvalidVariableTypeId; } @@ -46,12 +46,12 @@ namespace ScriptCanvas return m_variableId; } - AZ::Crc32 GetIdCrc() const + AZ::Crc32 GetIdCrc() const override { return DataValidationIds::InvalidVariableTypeCrc; } - AZStd::string_view GetTooltip() const + AZStd::string_view GetTooltip() const override { return "Invalid type for variable, auto fixing will remove all invalid variable nodes."; } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/ScopedDataConnectionEvent.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/ScopedDataConnectionEvent.h index 09c372da9a..0b2150448b 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/ScopedDataConnectionEvent.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/ScopedDataConnectionEvent.h @@ -59,17 +59,17 @@ namespace ScriptCanvas , targetNode.GetNodeName().data())); } - bool CanAutoFix() const + bool CanAutoFix() const override { return false; } - AZStd::string GetIdentifier() const + AZStd::string GetIdentifier() const override { return DataValidationIds::ScopedDataConnectionId; } - AZ::Crc32 GetIdCrc() const + AZ::Crc32 GetIdCrc() const override { return DataValidationIds::ScopedDataConnectionCrc; } @@ -79,7 +79,7 @@ namespace ScriptCanvas return m_connectionId; } - AZStd::string_view GetTooltip() const + AZStd::string_view GetTooltip() const override { return "Out of Scope Data Connection"; } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/ScriptEventVersionMismatch.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/ScriptEventVersionMismatch.h index b23c241967..c0443f9cc2 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/ScriptEventVersionMismatch.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/ScriptEventVersionMismatch.h @@ -37,17 +37,17 @@ namespace ScriptCanvas SetDescription("The Script Event asset this node uses has changed. This node is no longer valid. You can fix this by deleting this node, re-adding it and reconnecting it."); } - bool CanAutoFix() const + bool CanAutoFix() const override { return false; } - AZStd::string GetIdentifier() const + AZStd::string GetIdentifier() const override { return DataValidationIds::ScriptEventVersionMismatchId; } - AZ::Crc32 GetIdCrc() const + AZ::Crc32 GetIdCrc() const override { return DataValidationIds::ScriptEventVersionMismatchCrc; } @@ -57,7 +57,7 @@ namespace ScriptCanvas return m_definition; } - AZStd::string_view GetTooltip() const + AZStd::string_view GetTooltip() const override { return "The Script Event asset has changed, you can fix this problem by deleting the out of date node and re-adding it to your graph."; } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/UnknownEndpointEvent.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/UnknownEndpointEvent.h index b654448dc6..36f12527a2 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/UnknownEndpointEvent.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/UnknownEndpointEvent.h @@ -86,7 +86,7 @@ namespace ScriptCanvas return DataValidationIds::UnknownSourceEndpointCrc; } - AZStd::string_view GetTooltip() const + AZStd::string_view GetTooltip() const override { return "Unknown Source Endpoint"; } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/ExecutionValidation/UnusedNodeEvent.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/ExecutionValidation/UnusedNodeEvent.h index a49998e555..d2c028985f 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/ExecutionValidation/UnusedNodeEvent.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/ExecutionValidation/UnusedNodeEvent.h @@ -60,7 +60,7 @@ namespace ScriptCanvas } // HighlightEntityEffect - AZ::EntityId GetHighlightTarget() const + AZ::EntityId GetHighlightTarget() const override { return m_nodeId; } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/ParsingValidation/ParsingValidations.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/ParsingValidation/ParsingValidations.h index ac8f41862e..a862cca2eb 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/ParsingValidation/ParsingValidations.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/ValidationEvents/ParsingValidation/ParsingValidations.h @@ -43,7 +43,7 @@ namespace ScriptCanvas } // HighlightEntityEffect - AZ::EntityId GetHighlightTarget() const + AZ::EntityId GetHighlightTarget() const override { return m_nodeId; } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Internal/Nodes/StringFormatted.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Internal/Nodes/StringFormatted.h index 205ae4ea60..2a327bd37d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Internal/Nodes/StringFormatted.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Internal/Nodes/StringFormatted.h @@ -40,7 +40,7 @@ namespace ScriptCanvas return true; } - AZ::Outcome GetDependencies() const + AZ::Outcome GetDependencies() const override { return AZ::Success(DependencyReport{}); } @@ -49,8 +49,6 @@ namespace ScriptCanvas AZ_INLINE const NamedSlotIdMap& GetNamedSlotIdMap() const { return m_formatSlotMap; } AZ_INLINE const int GetPostDecimalPrecision() const { return m_numericPrecision; } - - protected: // This is a map that binds the index into m_unresolvedString to the SlotId that needs to be checked for a valid datum. diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/EBusEventHandler.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/EBusEventHandler.h index c6d4a8b512..05419f3ec3 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/EBusEventHandler.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/EBusEventHandler.h @@ -102,7 +102,7 @@ namespace ScriptCanvas AZ::Outcome GetFunctionCallName(const Slot* /*slot*/) const override; bool IsEBusAddressed() const override; - AZStd::optional GetEventIndex(AZStd::string eventName) const; + AZStd::optional GetEventIndex(AZStd::string eventName) const override; const EBusEventEntry* FindEvent(const AZStd::string& name) const; AZStd::string GetEBusName() const override; bool IsAutoConnected() const override; @@ -138,7 +138,7 @@ namespace ScriptCanvas void SetAutoConnectToGraphOwner(bool enabled); - void OnDeserialize(); + void OnDeserialize() override; #if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// void OnWriteEnd(); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp index 7e3f5f7d24..ca57824b78 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp @@ -652,8 +652,13 @@ namespace ScriptCanvas return {}; } - bool Method::GetBehaviorContextClassMethod(const AZStd::string&, const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const + bool Method::GetBehaviorContextClassMethod(const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const { + if (m_lookupName.empty() && m_className.empty()) + { + return false; + } + AZStd::string prettyClassName; AZStd::string methodName = m_lookupName; @@ -749,15 +754,14 @@ namespace ScriptCanvas AZStd::tuple Method::LookupMethod() const { using TupleType = AZStd::tuple; - AZStd::string methodName = m_lookupName; - + AZStd::string prettyClassName; const AZ::BehaviorClass* bcClass{}; const AZ::BehaviorMethod* method{}; EventType eventType; - if (GetBehaviorContextClassMethod(m_lookupName, bcClass, method, eventType)) + if (GetBehaviorContextClassMethod(bcClass, method, eventType)) { return TupleType{ method, m_methodType, eventType, bcClass }; } @@ -776,7 +780,7 @@ namespace ScriptCanvas const AZ::BehaviorMethod* method{}; EventType eventType; - if (GetBehaviorContextClassMethod(m_lookupName, bcClass, method, eventType)) + if (GetBehaviorContextClassMethod(bcClass, method, eventType)) { m_eventType = eventType; ConfigureMethod(*method, bcClass); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h index 5584212048..8a8dc09856 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h @@ -107,7 +107,7 @@ namespace ScriptCanvas SlotId GetBusSlotId() const; - void OnDeserialize(); + void OnDeserialize() override; #if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// void OnWriteEnd(); @@ -168,7 +168,7 @@ namespace ScriptCanvas AZ_INLINE void SetWarnOnMissingFunction(bool enabled) { m_warnOnMissingFunction = enabled; } - bool GetBehaviorContextClassMethod(const AZStd::string& name, const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const; + bool GetBehaviorContextClassMethod(const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const; private: friend struct ScriptCanvas::BehaviorContextMethodHelper; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/Cycle.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/Cycle.h index 17c203626f..7edd83794a 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/Cycle.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/Cycle.h @@ -36,9 +36,9 @@ namespace ScriptCanvas void OnConfigured() override; void ConfigureVisualExtensions() override; - bool CanDeleteSlot(const SlotId& slotId) const; + bool CanDeleteSlot(const SlotId& slotId) const override; - SlotId HandleExtension(AZ::Crc32 extensionId); + SlotId HandleExtension(AZ::Crc32 extensionId) override; AZ::Outcome GetDependencies() const override; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/IsNull.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/IsNull.h index 71ea8870c2..24bcd8e5e5 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/IsNull.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/IsNull.h @@ -29,7 +29,7 @@ namespace ScriptCanvas IsNull(); - AZ::Outcome GetDependencies() const; + AZ::Outcome GetDependencies() const override; bool IsIfBranch() const override; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/OrderedSequencer.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/OrderedSequencer.h index 10517502a5..0547f59db0 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/OrderedSequencer.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/OrderedSequencer.h @@ -30,13 +30,13 @@ namespace ScriptCanvas OrderedSequencer(); - bool CanDeleteSlot(const SlotId& slotId) const; + bool CanDeleteSlot(const SlotId& slotId) const override; AZ::Outcome GetDependencies() const override; ConstSlotsOutcome GetSlotsInExecutionThreadByTypeImpl(const Slot& executionSlot, CombinedSlotType targetSlotType, const Slot* /*executionChildSlot*/) const override; - SlotId HandleExtension(AZ::Crc32 extensionId); + SlotId HandleExtension(AZ::Crc32 extensionId) override; void OnInit() override; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/TargetedSequencer.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/TargetedSequencer.h index a2c4d93ef8..f4f590751a 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/TargetedSequencer.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/TargetedSequencer.h @@ -33,9 +33,9 @@ namespace ScriptCanvas void OnConfigured() override; void ConfigureVisualExtensions() override; - bool CanDeleteSlot(const SlotId& slotId) const; + bool CanDeleteSlot(const SlotId& slotId) const override; - SlotId HandleExtension(AZ::Crc32 extensionId); + SlotId HandleExtension(AZ::Crc32 extensionId) override; // Script Canvas Translation... bool IsSwitchStatement() const override { return true; } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/WeightedRandomSequencer.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/WeightedRandomSequencer.h index f9cbaac652..b51327b65d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/WeightedRandomSequencer.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/WeightedRandomSequencer.h @@ -39,7 +39,7 @@ namespace ScriptCanvas void OnInit() override; void ConfigureVisualExtensions() override; - bool OnValidateNode(ValidationResults& validationResults); + bool OnValidateNode(ValidationResults& validationResults) override; SlotId HandleExtension(AZ::Crc32 extensionId) override; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/SystemComponent.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/SystemComponent.h index f208e3b9d4..7dbde3224e 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/SystemComponent.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/SystemComponent.h @@ -74,7 +74,7 @@ namespace ScriptCanvas ScriptCanvasId FindScriptCanvasId(AZ::Entity* graphEntity) override; ScriptCanvas::Node* GetNode(const AZ::EntityId&, const AZ::Uuid&) override; ScriptCanvas::Node* CreateNodeOnEntity(const AZ::EntityId& entityId, ScriptCanvasId scriptCanvasId, const AZ::Uuid& nodeType) override; - SystemComponentConfiguration GetSystemComponentConfiguration() + SystemComponentConfiguration GetSystemComponentConfiguration() override { SystemComponentConfiguration configuration; configuration.m_maxIterationsForInfiniteLoopDetection = m_infiniteLoopDetectionMaxIterations; diff --git a/Gems/ScriptCanvas/Code/scriptcanvasgem_debugger_files.cmake b/Gems/ScriptCanvas/Code/scriptcanvasgem_debugger_files.cmake index 38182dc251..0a68a9b175 100644 --- a/Gems/ScriptCanvas/Code/scriptcanvasgem_debugger_files.cmake +++ b/Gems/ScriptCanvas/Code/scriptcanvasgem_debugger_files.cmake @@ -35,6 +35,7 @@ set(FILES Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/DynamicDataTypeEvent.h Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/InvalidExpressionEvent.h Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/InvalidRandomSignalEvent.h + Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/InvalidVariableTypeEvent.h Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/ScopedDataConnectionEvent.h Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/SlotReferenceEvent.h Include/ScriptCanvas/Debugger/ValidationEvents/DataValidation/UnknownEndpointEvent.h diff --git a/Gems/ScriptCanvas/gem.json b/Gems/ScriptCanvas/gem.json index b6b5522d7d..621ea42961 100644 --- a/Gems/ScriptCanvas/gem.json +++ b/Gems/ScriptCanvas/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Gem provides Open 3D Engine's visual scripting environment, Script Canvas.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas/", + "dependencies": [ + "ScriptEvents", + "ExpressionEvaluation", + "GraphCanvas" + ] } diff --git a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationActions/ScriptCanvasActions/CreateElementsActions.h b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationActions/ScriptCanvasActions/CreateElementsActions.h index d2f1feb0d1..c9eda4c0bc 100644 --- a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationActions/ScriptCanvasActions/CreateElementsActions.h +++ b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationActions/ScriptCanvasActions/CreateElementsActions.h @@ -139,7 +139,7 @@ namespace ScriptCanvasDeveloper protected: - void OnActionsComplete(); + void OnActionsComplete() override; private: @@ -222,7 +222,7 @@ namespace ScriptCanvasDeveloper CreateGroupAction(GraphCanvas::EditorId editorGraph, GraphCanvas::GraphId graphId, CreationType creationType = CreationType::Hotkey); ~CreateGroupAction() override = default; - void SetupAction(); + void SetupAction() override; // GraphCanvas::SceneNotificationBus::Handler void OnNodeAdded(const AZ::EntityId& groupId, bool isPaste = false) override; @@ -236,7 +236,7 @@ namespace ScriptCanvasDeveloper void SetupToolbarAction(); void SetupHotkeyAction(); - void OnActionsComplete(); + void OnActionsComplete() override; private: diff --git a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationActions/ScriptCanvasActions/ElementInteractions.h b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationActions/ScriptCanvasActions/ElementInteractions.h index 4a83ab2637..b7498ff56e 100644 --- a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationActions/ScriptCanvasActions/ElementInteractions.h +++ b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationActions/ScriptCanvasActions/ElementInteractions.h @@ -32,7 +32,7 @@ namespace ScriptCanvasDeveloper bool IsMissingPrecondition() override; EditorAutomationAction* GenerateMissingPreconditionAction() override; - void SetupAction(); + void SetupAction() override; private: @@ -66,9 +66,9 @@ namespace ScriptCanvasDeveloper ActionReport GenerateReport() const override; // SceneNotificaitonBus - void OnNodeRemoved(const AZ::EntityId& nodeId); + void OnNodeRemoved(const AZ::EntityId& nodeId) override; - void OnConnectionRemoved(const AZ::EntityId& connectionId); + void OnConnectionRemoved(const AZ::EntityId& connectionId) override; //// protected: @@ -98,7 +98,7 @@ namespace ScriptCanvasDeveloper MouseToNodePropertyEditorAction(GraphCanvas::SlotId slotId); ~MouseToNodePropertyEditorAction() override = default; - void SetupAction(); + void SetupAction() override; private: diff --git a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationActions/ScriptCanvasActions/VariableActions.h b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationActions/ScriptCanvasActions/VariableActions.h index 2ce49ba3ed..a8d7165feb 100644 --- a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationActions/ScriptCanvasActions/VariableActions.h +++ b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationActions/ScriptCanvasActions/VariableActions.h @@ -96,7 +96,7 @@ namespace ScriptCanvasDeveloper bool IsMissingPrecondition() override; EditorAutomationAction* GenerateMissingPreconditionAction() override; - void SetupAction(); + void SetupAction() override; // GraphCanvas::SceneNotificationBus void OnNodeAdded(const AZ::EntityId& nodeId, bool isPaste) override; @@ -146,7 +146,7 @@ namespace ScriptCanvasDeveloper ShowGraphVariablesAction() = default; ~ShowGraphVariablesAction() override = default; - void SetupAction(); + void SetupAction() override; ActionReport GenerateReport() const override; }; diff --git a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationStates/CreateElementsStates.h b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationStates/CreateElementsStates.h index 156933063f..58779e91f6 100644 --- a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationStates/CreateElementsStates.h +++ b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationStates/CreateElementsStates.h @@ -131,7 +131,7 @@ namespace ScriptCanvasDeveloper QString m_nodeName; - AutomationStateModelId m_endpointId = nullptr; + AutomationStateModelId m_endpointId; AutomationStateModelId m_scenePointId; AutomationStateModelId m_nodeOutputId; diff --git a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationStates/EditorViewStates.h b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationStates/EditorViewStates.h index fa7db8ce9f..daba20b8c8 100644 --- a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationStates/EditorViewStates.h +++ b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/EditorAutomation/EditorAutomationStates/EditorViewStates.h @@ -70,7 +70,7 @@ namespace ScriptCanvasDeveloper FindViewCenterState(AutomationStateModelId outputId); ~FindViewCenterState() override = default; - void OnCustomAction(); + void OnCustomAction() override; private: diff --git a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/Mock.h b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/Mock.h index 221aa501a9..41eddbfc02 100644 --- a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/Mock.h +++ b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/Mock.h @@ -118,7 +118,7 @@ namespace ScriptCanvasDeveloper private: //// ScriptCanvasEditor::EditorGraphNotificationBus - void OnGraphCanvasNodeDisplayed(AZ::EntityId graphCanvasEntityId); + void OnGraphCanvasNodeDisplayed(AZ::EntityId graphCanvasEntityId) override; //// AZStd::string m_nodeTitle; diff --git a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/WrapperMock.h b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/WrapperMock.h index 4ffafbd2a5..4b0155ec29 100644 --- a/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/WrapperMock.h +++ b/Gems/ScriptCanvasDeveloper/Code/Editor/Include/ScriptCanvasDeveloperEditor/WrapperMock.h @@ -52,7 +52,7 @@ namespace ScriptCanvasDeveloper void OnActionNameChanged(); - void OnClear(); + void OnClear() override; void OnNodeDisplayed(const GraphCanvas::NodeId& graphCanvasNodeId) override; private: diff --git a/Gems/ScriptCanvasDeveloper/Code/Editor/Source/AutomationActions/DynamicSlotFullCreation.cpp b/Gems/ScriptCanvasDeveloper/Code/Editor/Source/AutomationActions/DynamicSlotFullCreation.cpp index 83ece831eb..090622646a 100644 --- a/Gems/ScriptCanvasDeveloper/Code/Editor/Source/AutomationActions/DynamicSlotFullCreation.cpp +++ b/Gems/ScriptCanvasDeveloper/Code/Editor/Source/AutomationActions/DynamicSlotFullCreation.cpp @@ -39,13 +39,14 @@ namespace ScriptCanvasDeveloperEditor { public: - DynamicSlotFullCreationInterface(DeveloperUtils::ConnectionStyle connectionStyle) + DynamicSlotFullCreationInterface(DeveloperUtils::ConnectionStyle connectionStyle) { m_chainConfig.m_connectionStyle = connectionStyle; m_chainConfig.m_skipHandlers = true; } + virtual ~DynamicSlotFullCreationInterface() = default; - void SetupInterface(const AZ::EntityId& activeGraphCanvasGraphId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId) + void SetupInterface(const AZ::EntityId& activeGraphCanvasGraphId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId) override { m_graphCanvasGraphId = activeGraphCanvasGraphId; m_scriptCanvasId = scriptCanvasId; @@ -142,12 +143,12 @@ namespace ScriptCanvasDeveloperEditor } } - bool ShouldProcessItem([[maybe_unused]] const GraphCanvas::NodePaletteTreeItem* nodePaletteTreeItem) const + bool ShouldProcessItem([[maybe_unused]] const GraphCanvas::NodePaletteTreeItem* nodePaletteTreeItem) const override { return !m_availableVariableIds.empty(); } - void ProcessItem(const GraphCanvas::NodePaletteTreeItem* nodePaletteTreeItem) + void ProcessItem(const GraphCanvas::NodePaletteTreeItem* nodePaletteTreeItem) override { AZStd::unordered_set createdPairs; GraphCanvas::GraphCanvasMimeEvent* mimeEvent = nodePaletteTreeItem->CreateMimeEvent(); diff --git a/Gems/ScriptCanvasDeveloper/Code/Editor/Source/AutomationActions/VariableListFullCreation.cpp b/Gems/ScriptCanvasDeveloper/Code/Editor/Source/AutomationActions/VariableListFullCreation.cpp index c443c57010..4b457e99e2 100644 --- a/Gems/ScriptCanvasDeveloper/Code/Editor/Source/AutomationActions/VariableListFullCreation.cpp +++ b/Gems/ScriptCanvasDeveloper/Code/Editor/Source/AutomationActions/VariableListFullCreation.cpp @@ -37,9 +37,7 @@ namespace ScriptCanvasDeveloperEditor m_variableNameFormat += " %i"; } - ~VariablePaletteFullCreationInterface() - { - } + virtual ~VariablePaletteFullCreationInterface() = default; void SetupInterface([[maybe_unused]] const AZ::EntityId& graphCanvasId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId) { diff --git a/Gems/ScriptCanvasDeveloper/Code/Editor/Source/EditorAutomationTestDialog.h b/Gems/ScriptCanvasDeveloper/Code/Editor/Source/EditorAutomationTestDialog.h index 84597a991c..865738eadc 100644 --- a/Gems/ScriptCanvasDeveloper/Code/Editor/Source/EditorAutomationTestDialog.h +++ b/Gems/ScriptCanvasDeveloper/Code/Editor/Source/EditorAutomationTestDialog.h @@ -101,7 +101,7 @@ namespace ScriptCanvasDeveloper void RunTest(QModelIndex index); // SystemTickBus - void OnSystemTick(); + void OnSystemTick() override; //// // EditorAutomationTestDialogRequestBus::Handler diff --git a/Gems/ScriptCanvasDeveloper/Code/Editor/Source/EditorAutomationTests/GraphCreationTests.h b/Gems/ScriptCanvasDeveloper/Code/Editor/Source/EditorAutomationTests/GraphCreationTests.h index c09f219da0..c02d5cf081 100644 --- a/Gems/ScriptCanvasDeveloper/Code/Editor/Source/EditorAutomationTests/GraphCreationTests.h +++ b/Gems/ScriptCanvasDeveloper/Code/Editor/Source/EditorAutomationTests/GraphCreationTests.h @@ -55,8 +55,8 @@ namespace ScriptCanvasDeveloper protected: - void OnSetupStateActions(EditorAutomationActionRunner& actionRunner); - void OnStateActionsComplete(); + void OnSetupStateActions(EditorAutomationActionRunner& actionRunner) override; + void OnStateActionsComplete() override; private: diff --git a/Gems/ScriptCanvasDeveloper/gem.json b/Gems/ScriptCanvasDeveloper/gem.json index d7b0aefad3..ee1bfcec84 100644 --- a/Gems/ScriptCanvasDeveloper/gem.json +++ b/Gems/ScriptCanvasDeveloper/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Developer Gem provides a suite of utility features for the development and debugging of Script Canvas systems.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Utility", "Debug"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Utility", + "Debug" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-developer/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-developer/", + "dependencies": [ + "ScriptCanvas", + "GraphCanvas" + ] } diff --git a/Gems/ScriptCanvasPhysics/Code/Tests/ScriptCanvasPhysicsTest.cpp b/Gems/ScriptCanvasPhysics/Code/Tests/ScriptCanvasPhysicsTest.cpp index ac6109ce30..70d8857b1c 100644 --- a/Gems/ScriptCanvasPhysics/Code/Tests/ScriptCanvasPhysicsTest.cpp +++ b/Gems/ScriptCanvasPhysics/Code/Tests/ScriptCanvasPhysicsTest.cpp @@ -175,7 +175,7 @@ namespace ScriptCanvasPhysicsTests MOCK_CONST_METHOD0(GetNativePointer, void*()); }; - class MockShape + class MockShape : public Physics::Shape { public: @@ -203,10 +203,12 @@ namespace ScriptCanvasPhysicsTests MOCK_METHOD1(SetContactOffset, void(float)); }; - class MockPhysicsMaterial + class MockPhysicsMaterial : public Physics::Material { public: + virtual ~MockPhysicsMaterial() = default; + MOCK_CONST_METHOD0(GetSurfaceType, AZ::Crc32()); MOCK_CONST_METHOD0(GetSurfaceTypeName, const AZStd::string&()); MOCK_METHOD1(SetSurfaceTypeName, void(const AZStd::string&)); diff --git a/Gems/ScriptCanvasPhysics/gem.json b/Gems/ScriptCanvasPhysics/gem.json index 46e2ef1194..417fa6893a 100644 --- a/Gems/ScriptCanvasPhysics/gem.json +++ b/Gems/ScriptCanvasPhysics/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Script Canvas Physics Gem provides Script Canvas nodes for physics scene queries such as raycasts.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Physics", "Simulation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Physics", + "Simulation" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-physics/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-physics/", + "dependencies": [ + "ScriptCanvas" + ] } diff --git a/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestUtilities.h b/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestUtilities.h index 56ecdc33a1..8272e647a3 100644 --- a/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestUtilities.h +++ b/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestUtilities.h @@ -548,7 +548,7 @@ namespace ScriptCanvasTests bool DestroyEntityById(AZ::EntityId entityId) override; AZ::Entity* CloneEntity(const AZ::Entity& sourceEntity) override; void ResetContext() override; - AZ::EntityId FindLoadedEntityIdMapping(const AZ::EntityId& staticId) const; + AZ::EntityId FindLoadedEntityIdMapping(const AZ::EntityId& staticId) const override; //// void AddEntity(AZ::EntityId entityId); diff --git a/Gems/ScriptCanvasTesting/Code/Source/ScriptCanvasTestBus.cpp b/Gems/ScriptCanvasTesting/Code/Source/ScriptCanvasTestBus.cpp index 7ab7fa2540..c8db1169ed 100644 --- a/Gems/ScriptCanvasTesting/Code/Source/ScriptCanvasTestBus.cpp +++ b/Gems/ScriptCanvasTesting/Code/Source/ScriptCanvasTestBus.cpp @@ -237,7 +237,7 @@ namespace ScriptCanvasTesting return result; } - void Void(AZStd::string_view value) + void Void(AZStd::string_view value) override { Call(FN_Void, value); } diff --git a/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_MethodOverload.cpp b/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_MethodOverload.cpp index 13f6657d62..d7e5bbfb02 100644 --- a/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_MethodOverload.cpp +++ b/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_MethodOverload.cpp @@ -99,7 +99,7 @@ public: } } - void ConfigureSlots() + void ConfigureSlots() override { ScriptCanvas::SlotExecution::Ins ins; { diff --git a/Gems/ScriptCanvasTesting/gem.json b/Gems/ScriptCanvasTesting/gem.json index 303eab8c7e..c45d2ac165 100644 --- a/Gems/ScriptCanvasTesting/gem.json +++ b/Gems/ScriptCanvasTesting/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Testing Gem provides a framework for testing for and with Script Canvas.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Debug", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Debug", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-testing/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-testing/", + "dependencies": [ + "ScriptCanvas", + "GraphCanvas", + "ScriptEvents" + ] } diff --git a/Gems/ScriptEvents/Code/Source/Editor/ScriptEventsSystemEditorComponent.h b/Gems/ScriptEvents/Code/Source/Editor/ScriptEventsSystemEditorComponent.h index 4365873b3a..9463b2a047 100644 --- a/Gems/ScriptEvents/Code/Source/Editor/ScriptEventsSystemEditorComponent.h +++ b/Gems/ScriptEvents/Code/Source/Editor/ScriptEventsSystemEditorComponent.h @@ -46,7 +46,7 @@ namespace ScriptEventsEditor // AssetEditorValidationRequestBus::Handler AZ::Outcome IsAssetDataValid(const AZ::Data::Asset& asset) override; - void PreAssetSave(AZ::Data::Asset asset); + void PreAssetSave(AZ::Data::Asset asset) override; void BeforePropertyEdit(AzToolsFramework::InstanceDataNode* node, AZ::Data::Asset asset) override; void SetSaveAsBinary(bool saveAsBinary) { m_saveAsBinary = saveAsBinary; } diff --git a/Gems/ScriptEvents/gem.json b/Gems/ScriptEvents/gem.json index 2b7d74ef3f..386d9b5614 100644 --- a/Gems/ScriptEvents/gem.json +++ b/Gems/ScriptEvents/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Script Events Gem provides a framework for creating event assets usable from any scripting solution in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Framework", "Gameplay"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Framework", + "Gameplay" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-events/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-events/", + "dependencies": [] } diff --git a/Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweenerModule.cpp b/Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweenerModule.cpp index 8d5057e17f..20f59b94a9 100644 --- a/Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweenerModule.cpp +++ b/Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweenerModule.cpp @@ -39,7 +39,7 @@ namespace ScriptedEntityTweener }; } - void OnSystemEvent(ESystemEvent systemEvent, UINT_PTR wparam, UINT_PTR lparam) + void OnSystemEvent(ESystemEvent systemEvent, UINT_PTR wparam, UINT_PTR lparam) override { CryHooksModule::OnSystemEvent(systemEvent, wparam, lparam); diff --git a/Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweenerSystemComponent.cpp b/Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweenerSystemComponent.cpp index f1f4977164..67a5038aec 100644 --- a/Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweenerSystemComponent.cpp +++ b/Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweenerSystemComponent.cpp @@ -47,7 +47,7 @@ namespace ScriptedEntityTweener Call(FN_RemoveCallback, callbackId); } - void OnTimelineAnimationStart(int timelineId, const AZ::Uuid& uuid, const AZStd::string& componentName, const AZStd::string& propertyName) + void OnTimelineAnimationStart(int timelineId, const AZ::Uuid& uuid, const AZStd::string& componentName, const AZStd::string& propertyName) override { Call(FN_OnTimelineAnimationStart, timelineId, uuid, componentName, propertyName); } diff --git a/Gems/ScriptedEntityTweener/gem.json b/Gems/ScriptedEntityTweener/gem.json index a0f558356b..c51f05df9a 100644 --- a/Gems/ScriptedEntityTweener/gem.json +++ b/Gems/ScriptedEntityTweener/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Scripted Entity Tweener Gem provides a script driven animation system for Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "UI", "Animation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "UI", + "Animation" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/scripted-entity-tweener/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/scripted-entity-tweener/", + "dependencies": [] } diff --git a/Gems/SliceFavorites/gem.json b/Gems/SliceFavorites/gem.json index 3ae37e2145..84f42fc1a2 100644 --- a/Gems/SliceFavorites/gem.json +++ b/Gems/SliceFavorites/gem.json @@ -5,7 +5,11 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "Add the ability to favorite a slice to allow easy access and instantiation", - "user_tags": ["Editor", "Slices"], + "user_tags": [ + "Editor", + "Slices" + ], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "dependencies": [] } diff --git a/Gems/StartingPointCamera/gem.json b/Gems/StartingPointCamera/gem.json index 5a9aa0df9b..613eb76267 100644 --- a/Gems/StartingPointCamera/gem.json +++ b/Gems/StartingPointCamera/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Camera Gem provides the behaviors used with the Camera Framework Gem to define a camera rig.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/starting-point-camera/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/starting-point-camera/", + "dependencies": [ + "CameraFramework", + "LmbrCentral" + ] } diff --git a/Gems/StartingPointInput/gem.json b/Gems/StartingPointInput/gem.json index e65f271fe5..d2641ea27b 100644 --- a/Gems/StartingPointInput/gem.json +++ b/Gems/StartingPointInput/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Input Gem provides functionality to map low-level input events to high-level actions.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-input/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-input/", + "dependencies": [ + "ScriptCanvas" + ] } diff --git a/Gems/StartingPointMovement/gem.json b/Gems/StartingPointMovement/gem.json index 70e1d105c1..7def6da768 100644 --- a/Gems/StartingPointMovement/gem.json +++ b/Gems/StartingPointMovement/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Movement Gem provides a series of Lua scripts that listen and respond to input events and trigger transform operations such as translation and rotation.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-movement/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-movement/", + "dependencies": [] } diff --git a/Gems/SurfaceData/Code/Source/Components/SurfaceDataShapeComponent.h b/Gems/SurfaceData/Code/Source/Components/SurfaceDataShapeComponent.h index 31b960c2b6..f2c478ed27 100644 --- a/Gems/SurfaceData/Code/Source/Components/SurfaceDataShapeComponent.h +++ b/Gems/SurfaceData/Code/Source/Components/SurfaceDataShapeComponent.h @@ -64,7 +64,7 @@ namespace SurfaceData ////////////////////////////////////////////////////////////////////////// // SurfaceDataProviderRequestBus - void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const; + void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override; ////////////////////////////////////////////////////////////////////////// // SurfaceDataModifierRequestBus diff --git a/Gems/SurfaceData/gem.json b/Gems/SurfaceData/gem.json index 57e4d8fb70..51a134d5df 100644 --- a/Gems/SurfaceData/gem.json +++ b/Gems/SurfaceData/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Surface Data Gem provides functionality to emit signals or tags from surfaces such as meshes and terrain.", - "canonical_tags": ["Gem"], - "user_tags": ["Environment", "Utility", "Design"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Environment", + "Utility", + "Design" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/surface-data/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/surface-data/", + "dependencies": [ + "LmbrCentral", + "Atom_RPI", + "Atom_Feature_Common" + ] } diff --git a/Gems/Terrain/Code/CMakeLists.txt b/Gems/Terrain/Code/CMakeLists.txt index 0feacdaf71..fc07294dab 100644 --- a/Gems/Terrain/Code/CMakeLists.txt +++ b/Gems/Terrain/Code/CMakeLists.txt @@ -83,6 +83,15 @@ endif() ################################################################################ # See if globally, tests are supported if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + ly_add_target( + NAME Terrain.Mocks HEADERONLY + NAMESPACE Gem + FILES_CMAKE + terrain_mocks_files.cmake + INCLUDE_DIRECTORIES + INTERFACE + Mocks + ) ly_add_target( NAME Terrain.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} NAMESPACE Gem @@ -97,6 +106,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) PRIVATE AZ::AzTest AZ::AzFramework + Gem::LmbrCentral.Mocks + Gem::Terrain.Mocks Gem::Terrain.Static ) @@ -120,6 +131,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) BUILD_DEPENDENCIES PRIVATE AZ::AzTest + Gem::LmbrCentral.Mocks + Gem::Terrain.Mocks Gem::Terrain.Editor ) diff --git a/Gems/Terrain/Code/Tests/TerrainMocks.h b/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h similarity index 57% rename from Gems/Terrain/Code/Tests/TerrainMocks.h rename to Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h index 03a2eccf1d..ea0413be9a 100644 --- a/Gems/Terrain/Code/Tests/TerrainMocks.h +++ b/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h @@ -11,59 +11,9 @@ #include #include -#include namespace UnitTest { - static const AZ::Uuid BoxShapeComponentTypeId = "{5EDF4B9E-0D3D-40B8-8C91-5142BCFC30A6}"; - - class MockBoxShapeComponent - : public AZ::Component - { - public: - AZ_COMPONENT(MockBoxShapeComponent, BoxShapeComponentTypeId) - static void Reflect([[maybe_unused]] AZ::ReflectContext* context) - { - } - - void Activate() override - { - } - - void Deactivate() override - { - } - - bool ReadInConfig([[maybe_unused]] const AZ::ComponentConfig* baseConfig) override - { - return true; - } - - bool WriteOutConfig([[maybe_unused]] AZ::ComponentConfig* outBaseConfig) const override - { - return true; - } - - private: - static void GetProvidedServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& provided) - { - provided.push_back(AZ_CRC("ShapeService", 0xe86aa5fe)); - provided.push_back(AZ_CRC("BoxShapeService", 0x946a0032)); - provided.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); - } - - static void GetIncompatibleServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& incompatible) - { - } - - static void GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) - { - } - - static void GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) - { - } - }; class MockTerrainSystemService : private Terrain::TerrainSystemServiceRequestBus::Handler { @@ -106,4 +56,40 @@ namespace UnitTest MOCK_METHOD2(OnTerrainDataChanged, void(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask)); }; + class MockTerrainAreaHeightRequests : public Terrain::TerrainAreaHeightRequestBus::Handler + { + public: + MockTerrainAreaHeightRequests(AZ::EntityId entityId) + { + Terrain::TerrainAreaHeightRequestBus::Handler::BusConnect(entityId); + } + + ~MockTerrainAreaHeightRequests() + { + Terrain::TerrainAreaHeightRequestBus::Handler::BusDisconnect(); + } + + MOCK_METHOD3(GetHeight, void( + const AZ::Vector3& inPosition, + AZ::Vector3& outPosition, + bool& terrainExists)); + + }; + + class MockTerrainSpawnerRequests : public Terrain::TerrainSpawnerRequestBus::Handler + { + public: + MockTerrainSpawnerRequests(AZ::EntityId entityId) + { + Terrain::TerrainSpawnerRequestBus::Handler::BusConnect(entityId); + } + + ~MockTerrainSpawnerRequests() + { + Terrain::TerrainSpawnerRequestBus::Handler::BusDisconnect(); + } + + MOCK_METHOD2(GetPriority, void(AZ::u32& outLayer, AZ::u32& outPriority)); + MOCK_METHOD0(GetUseGroundPlane, bool()); + }; } diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp index 4d0cb6576d..4ea16018d1 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp @@ -142,11 +142,15 @@ namespace Terrain return false; } - float TerrainHeightGradientListComponent::GetHeight(float x, float y) + void TerrainHeightGradientListComponent::GetHeight( + const AZ::Vector3& inPosition, + AZ::Vector3& outPosition, + bool& terrainExists) { float maxSample = 0.0f; + terrainExists = false; - GradientSignal::GradientSampleParams params(AZ::Vector3(x, y, 0.0f)); + GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f)); // Right now, when the list contains multiple entries, we will use the highest point from each gradient. // This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value @@ -155,49 +159,20 @@ namespace Terrain // make this list a prioritized list from top to bottom for any points that overlap. for (auto& gradientId : m_configuration.m_gradientEntities) { + // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain + // to *not* exist at a specific point. + terrainExists = true; + float sample = 0.0f; - GradientSignal::GradientRequestBus::EventResult(sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); + GradientSignal::GradientRequestBus::EventResult( + sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); maxSample = AZ::GetMax(maxSample, sample); } const float height = AZ::Lerp(m_cachedShapeBounds.GetMin().GetZ(), m_cachedShapeBounds.GetMax().GetZ(), maxSample); - - return AZ::GetClamp(height, m_cachedMinWorldHeight, m_cachedMaxWorldHeight); + outPosition.SetZ(AZ::GetClamp(height, m_cachedMinWorldHeight, m_cachedMaxWorldHeight)); } - void TerrainHeightGradientListComponent::GetHeight( - const AZ::Vector3& inPosition, - AZ::Vector3& outPosition, - [[maybe_unused]] AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter = - AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT) - { - const float height = GetHeight(inPosition.GetX(), inPosition.GetY()); - outPosition.SetZ(height); - } - - void TerrainHeightGradientListComponent::GetNormal( - const AZ::Vector3& inPosition, - AZ::Vector3& outNormal, - [[maybe_unused]] AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter = - AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT) - { - const float x = inPosition.GetX(); - const float y = inPosition.GetY(); - - if ((x >= m_cachedShapeBounds.GetMin().GetX()) && (x <= m_cachedShapeBounds.GetMax().GetX()) && - (y >= m_cachedShapeBounds.GetMin().GetY()) && (y <= m_cachedShapeBounds.GetMax().GetY())) - { - AZ::Vector2 fRange = (m_cachedHeightQueryResolution / 2.0f) + AZ::Vector2(0.05f); - - AZ::Vector3 v1(x - fRange.GetX(), y - fRange.GetY(), GetHeight(x - fRange.GetX(), y - fRange.GetY())); - AZ::Vector3 v2(x - fRange.GetX(), y + fRange.GetY(), GetHeight(x - fRange.GetX(), y + fRange.GetY())); - AZ::Vector3 v3(x + fRange.GetX(), y - fRange.GetY(), GetHeight(x + fRange.GetX(), y - fRange.GetY())); - AZ::Vector3 v4(x + fRange.GetX(), y + fRange.GetY(), GetHeight(x + fRange.GetX(), y + fRange.GetY())); - outNormal = (v3 - v2).Cross(v4 - v1).GetNormalized(); - } - } - - void TerrainHeightGradientListComponent::OnCompositionChanged() { RefreshMinMaxHeights(); diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h index 509f003afa..6c3fd7b820 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h @@ -64,14 +64,7 @@ namespace Terrain TerrainHeightGradientListComponent() = default; ~TerrainHeightGradientListComponent() = default; - void GetHeight( - const AZ::Vector3& inPosition, - AZ::Vector3& outPosition, - AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter) override; - void GetNormal( - const AZ::Vector3& inPosition, - AZ::Vector3& outNormal, - AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter) override; + void GetHeight(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists) override; ////////////////////////////////////////////////////////////////////////// // AZ::Component interface implementation @@ -91,11 +84,7 @@ namespace Terrain private: TerrainHeightGradientListConfig m_configuration; - /////////////////////////////////////////// - void GetNormalSynchronous(float x, float y, AZ::Vector3& normal); - void RefreshMinMaxHeights(); - float GetHeight(float x, float y); float m_cachedMinWorldHeight{ 0.0f }; float m_cachedMaxWorldHeight{ 0.0f }; diff --git a/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.h b/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.h index a742eab78c..d9c7893c77 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.h @@ -56,7 +56,7 @@ namespace Terrain ////////////////////////////////////////////////////////////////////////// // SurfaceDataProviderRequestBus - void GetSurfacePoints(const AZ::Vector3& inPosition, SurfaceData::SurfacePointList& surfacePointList) const; + void GetSurfacePoints(const AZ::Vector3& inPosition, SurfaceData::SurfacePointList& surfacePointList) const override; ////////////////////////////////////////////////////////////////////////// // AzFramework::Terrain::TerrainDataNotificationBus diff --git a/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp index afaf0470b4..5357ccce32 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp @@ -13,8 +13,6 @@ #include #include -#include -#include #include namespace Terrain @@ -34,8 +32,6 @@ namespace Terrain ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ; } - - Terrain::TerrainFeatureProcessor::Reflect(context); } } @@ -49,9 +45,8 @@ namespace Terrain incompatible.push_back(AZ_CRC_CE("TerrainService")); } - void TerrainSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + void TerrainSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) { - required.push_back(AZ_CRC_CE("RPISystem")); } void TerrainSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) @@ -68,14 +63,11 @@ namespace Terrain // every time an entity is added or removed to a level. If this ever changes, the Terrain System ownership could move into // the level component. m_terrainSystem = new TerrainSystem(); - AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); } void TerrainSystemComponent::Deactivate() { delete m_terrainSystem; m_terrainSystem = nullptr; - - AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); } } diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp index 89ffa6364f..e140129563 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp @@ -95,6 +95,8 @@ namespace Terrain AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId()); AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + + RefreshCachedWireframeGrid(AZ::Aabb::CreateNull()); } void TerrainWorldDebuggerComponent::Deactivate() @@ -285,13 +287,13 @@ namespace Terrain AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( z00, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x, y, - AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT, &terrainExists); + AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( z01, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x, y1, - AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT, &terrainExists); + AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( z10, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x1, y, - AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT, &terrainExists); + AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); sector.m_lineVertices.push_back(AZ::Vector3(x, y, z00)); sector.m_lineVertices.push_back(AZ::Vector3(x1, y, z10)); diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp new file mode 100644 index 0000000000..3bf34fa019 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp @@ -0,0 +1,213 @@ +/* + * 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 + +namespace Terrain +{ + void TerrainWorldRendererConfig::Reflect(AZ::ReflectContext* context) + { + Terrain::TerrainFeatureProcessor::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class()->Version(1); + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("Terrain World Renderer Component", "Enables terrain rendering") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC_CE("Level") })) + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true); + } + } + } + + void TerrainWorldRendererComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainRendererService")); + } + + void TerrainWorldRendererComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainRendererService")); + } + + void TerrainWorldRendererComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainService")); + } + + void TerrainWorldRendererComponent::Reflect(AZ::ReflectContext* context) + { + TerrainWorldRendererConfig::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class()->Version(0)->Field( + "Configuration", &TerrainWorldRendererComponent::m_configuration); + } + } + + TerrainWorldRendererComponent::TerrainWorldRendererComponent(const TerrainWorldRendererConfig& configuration) + : m_configuration(configuration) + { + } + + TerrainWorldRendererComponent::~TerrainWorldRendererComponent() + { + if (m_terrainRendererActive) + { + Deactivate(); + } + } + + AZ::RPI::Scene* TerrainWorldRendererComponent::GetScene() const + { + // Find the entity context for the entity ID. + AzFramework::EntityContextId entityContextId = AzFramework::EntityContextId::CreateNull(); + AzFramework::EntityIdContextQueryBus::EventResult( + entityContextId, GetEntityId(), &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId); + + return AZ::RPI::Scene::GetSceneForEntityContextId(entityContextId); + } + + void TerrainWorldRendererComponent::Activate() + { + // On component activation, register the terrain feature processor with Atom and the scene related to this entity context. + + AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); + + if (AZ::RPI::Scene* scene = GetScene(); scene) + { + m_terrainFeatureProcessor = scene->EnableFeatureProcessor(); + } + + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + m_terrainRendererActive = true; + } + + void TerrainWorldRendererComponent::Deactivate() + { + // On component deactivation, unregister the feature processor and remove it from the default scene. + + m_terrainRendererActive = false; + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); + + if (AZ::RPI::Scene* scene = GetScene(); scene) + { + if (scene->GetFeatureProcessor()) + { + scene->DisableFeatureProcessor(); + } + } + m_terrainFeatureProcessor = nullptr; + + AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); + } + + bool TerrainWorldRendererComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (auto config = azrtti_cast(baseConfig)) + { + m_configuration = *config; + return true; + } + return false; + } + + bool TerrainWorldRendererComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto config = azrtti_cast(outBaseConfig)) + { + *config = m_configuration; + return true; + } + return false; + } + + void TerrainWorldRendererComponent::OnTerrainDataDestroyBegin() + { + // If the terrain is being destroyed, remove all existing terrain data from the feature processor. + + if (m_terrainFeatureProcessor) + { + m_terrainFeatureProcessor->RemoveTerrainData(); + } + } + + void TerrainWorldRendererComponent::OnTerrainDataChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion, [[maybe_unused]] TerrainDataChangedMask dataChangedMask) + { + // Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus). + // We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions + // that create false detection of cyclic dependencies when multiple requests occur on different threads simultaneously. + // (One case where this was previously able to occur was in rapid updating of the Preview widget on the + // GradientSurfaceDataComponent in the Editor when moving the threshold sliders back and forth rapidly) + auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false); + typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex); + + AZ::Vector2 queryResolution = AZ::Vector2(1.0f); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); + + AZ::Aabb worldBounds = AZ::Aabb::CreateNull(); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + worldBounds, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb); + + + AZ::Transform transform = AZ::Transform::CreateTranslation(worldBounds.GetCenter()); + + uint32_t width = aznumeric_cast( + (float)worldBounds.GetXExtent() / queryResolution.GetX()); + uint32_t height = aznumeric_cast( + (float)worldBounds.GetYExtent() / queryResolution.GetY()); + AZStd::vector pixels; + pixels.resize_no_construct(width * height); + const uint32_t pixelDataSize = width * height * sizeof(float); + memset(pixels.data(), 0, pixelDataSize); + + for (uint32_t y = 0; y < height; y++) + { + for (uint32_t x = 0; x < width; x++) + { + bool terrainExists = true; + float terrainHeight = 0.0f; + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + terrainHeight, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, + (x * queryResolution.GetX()) + worldBounds.GetMin().GetX(), + (y * queryResolution.GetY()) + worldBounds.GetMin().GetY(), + AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, + &terrainExists); + + pixels[(y * width) + x] = + (terrainHeight - worldBounds.GetMin().GetZ()) / worldBounds.GetExtents().GetZ(); + } + } + + if (m_terrainFeatureProcessor) + { + m_terrainFeatureProcessor->UpdateTerrainData(transform, worldBounds, queryResolution.GetX(), width, height, pixels); + } + } + +} diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h new file mode 100644 index 0000000000..a1e9498be8 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h @@ -0,0 +1,75 @@ +/* + * 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 LmbrCentral +{ + template + class EditorWrappedComponentBase; +} + +namespace AZ::RPI +{ + class Scene; +} + +namespace Terrain +{ + class TerrainFeatureProcessor; + + class TerrainWorldRendererConfig + : public AZ::ComponentConfig + { + public: + AZ_CLASS_ALLOCATOR(TerrainWorldRendererConfig, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainWorldRendererConfig, "{08C5863C-092D-4A69-8226-4978E4F6E343}", AZ::ComponentConfig); + static void Reflect(AZ::ReflectContext* context); + }; + + + class TerrainWorldRendererComponent + : public AZ::Component + , public AzFramework::Terrain::TerrainDataNotificationBus::Handler + { + public: + template + friend class LmbrCentral::EditorWrappedComponentBase; + AZ_COMPONENT(TerrainWorldRendererComponent, "{3B0DB71E-5944-437C-8C88-70F8B405BFC7}"); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void Reflect(AZ::ReflectContext* context); + + TerrainWorldRendererComponent(const TerrainWorldRendererConfig& configuration); + TerrainWorldRendererComponent() = default; + ~TerrainWorldRendererComponent() override; + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; + bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; + + protected: + void OnTerrainDataDestroyBegin() override; + void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; + + AZ::RPI::Scene* GetScene() const; + + private: + TerrainWorldRendererConfig m_configuration; + bool m_terrainRendererActive{ false }; + TerrainFeatureProcessor* m_terrainFeatureProcessor{ nullptr }; + }; +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.cpp b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.cpp new file mode 100644 index 0000000000..2d42585fa5 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.cpp @@ -0,0 +1,56 @@ +/* + * 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 Terrain +{ + void EditorTerrainWorldRendererComponent::Reflect(AZ::ReflectContext* context) + { + BaseClassType::Reflect(context); + + AZ::SerializeContext* serializeContext = azrtti_cast(context); + + if (serializeContext) + { + serializeContext->Class() + ->Version(0) + ; + + if (auto editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Terrain World Renderer", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Terrain") + ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/TerrainWorldRenderer.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/TerrainWorldRenderer.svg") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC_CE("Level") })) + ; + } + } + } + + + void EditorTerrainWorldRendererComponent::Init() + { + BaseClassType::Init(); + } + + void EditorTerrainWorldRendererComponent::Activate() + { + BaseClassType::Activate(); + } + + AZ::u32 EditorTerrainWorldRendererComponent::ConfigurationChanged() + { + return BaseClassType::ConfigurationChanged(); + } +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.h b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.h new file mode 100644 index 0000000000..92ce648988 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.h @@ -0,0 +1,38 @@ +/* + * 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 Terrain +{ + class EditorTerrainWorldRendererComponent + : public LmbrCentral::EditorWrappedComponentBase + { + public: + using BaseClassType = LmbrCentral::EditorWrappedComponentBase; + AZ_EDITOR_COMPONENT(EditorTerrainWorldRendererComponent, "{7BEFF763-89A6-4EDA-B199-B049A8E757AF}", BaseClassType); + static void Reflect(AZ::ReflectContext* context); + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Init() override; + void Activate() override; + AZ::u32 ConfigurationChanged() override; + + protected: + using BaseClassType::m_configuration; + using BaseClassType::m_component; + using BaseClassType::m_visible; + + private: + }; +} diff --git a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp index 0ba416cbb7..5305087694 100644 --- a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp +++ b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace Terrain { @@ -25,6 +26,7 @@ namespace Terrain Terrain::EditorTerrainSystemComponent::CreateDescriptor(), Terrain::EditorTerrainWorldComponent::CreateDescriptor(), Terrain::EditorTerrainWorldDebuggerComponent::CreateDescriptor(), + Terrain::EditorTerrainWorldRendererComponent::CreateDescriptor(), }); } diff --git a/Gems/Terrain/Code/Source/TerrainModule.cpp b/Gems/Terrain/Code/Source/TerrainModule.cpp index 29b2542472..24b87b4ab3 100644 --- a/Gems/Terrain/Code/Source/TerrainModule.cpp +++ b/Gems/Terrain/Code/Source/TerrainModule.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ namespace Terrain TerrainSystemComponent::CreateDescriptor(), TerrainWorldComponent::CreateDescriptor(), TerrainWorldDebuggerComponent::CreateDescriptor(), + TerrainWorldRendererComponent::CreateDescriptor(), TerrainHeightGradientListComponent::CreateDescriptor(), TerrainLayerSpawnerComponent::CreateDescriptor(), TerrainSurfaceDataSystemComponent::CreateDescriptor(), diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp index 674af376e4..46ac677dd1 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp @@ -12,10 +12,6 @@ #include #include -#include -#include -#include - using namespace Terrain; bool TerrainLayerPriorityComparator::operator()(const AZ::EntityId& layer1id, const AZ::EntityId& layer2id) const @@ -114,18 +110,6 @@ void TerrainSystem::Deactivate() m_terrainSettingsDirty = true; m_requestedSettings.m_systemActive = false; - if (auto rpi = AZ::RPI::RPISystemInterface::Get(); rpi) - { - if (auto defaultScene = rpi->GetDefaultScene(); defaultScene) - { - const AZ::RPI::Scene* scene = defaultScene.get(); - if (auto terrainFeatureProcessor = scene->GetFeatureProcessor(); terrainFeatureProcessor) - { - terrainFeatureProcessor->RemoveTerrainData(); - } - } - } - AzFramework::Terrain::TerrainDataNotificationBus::Broadcast( &AzFramework::Terrain::TerrainDataNotificationBus::Events::OnTerrainDataDestroyEnd); } @@ -152,27 +136,69 @@ AZ::Vector2 TerrainSystem::GetTerrainHeightQueryResolution() const return m_currentSettings.m_heightQueryResolution; } +void TerrainSystem::ClampPosition(float x, float y, AZ::Vector2& outPosition, AZ::Vector2& normalizedDelta) const +{ + // Given an input position, clamp the values to our terrain grid, where it will always go to the terrain grid point + // at a lower value, whether positive or negative. Ex: 3.3 -> 3, -3.3 -> -4 + // Also, return the normalized delta as a value of [0-1) describing what fraction of a grid point the value moved. + + // Scale the position by the query resolution, so that integer values represent exact steps on the grid, + // and fractional values are the amount in-between each grid point, in the range [0-1). + AZ::Vector2 normalizedPosition = AZ::Vector2(x, y) / m_currentSettings.m_heightQueryResolution; + normalizedDelta = AZ::Vector2( + normalizedPosition.GetX() - floor(normalizedPosition.GetX()), normalizedPosition.GetY() - floor(normalizedPosition.GetY())); + + // Remove the fractional part, then scale back down into world space. + outPosition = (normalizedPosition - normalizedDelta) * m_currentSettings.m_heightQueryResolution; +} + float TerrainSystem::GetHeightSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const { bool terrainExists = false; - - AZ::Vector3 inPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ()); - AZ::Vector3 outPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ()); + float height = m_currentSettings.m_worldBounds.GetMin().GetZ(); AZStd::shared_lock lock(m_areaMutex); - for (auto& [areaId, areaBounds] : m_registeredAreas) + switch (sampler) { - inPosition.SetZ(areaBounds.GetMin().GetZ()); - if (areaBounds.Contains(inPosition)) + // Get the value at the requested location, using the terrain grid to bilinear filter between sample grid points. + case AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR: { - Terrain::TerrainAreaHeightRequestBus::Event( - areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, sampler); + // pos0 contains one corner of our grid square, pos1 contains the opposite corner, and normalizedDelta is the fractional + // amount the position exists between those corners. + // Ex: (3.3, 4.4) would have a pos0 of (3, 4), a pos1 of (4, 5), and a delta of (0.3, 0.4). + AZ::Vector2 normalizedDelta; + AZ::Vector2 pos0; + ClampPosition(x, y, pos0, normalizedDelta); + const AZ::Vector2 pos1 = pos0 + m_currentSettings.m_heightQueryResolution; - terrainExists = true; - - break; + const float heightX0Y0 = GetTerrainAreaHeight(pos0.GetX(), pos0.GetY(), terrainExists); + const float heightX1Y0 = GetTerrainAreaHeight(pos1.GetX(), pos0.GetY(), terrainExists); + const float heightX0Y1 = GetTerrainAreaHeight(pos0.GetX(), pos1.GetY(), terrainExists); + const float heightX1Y1 = GetTerrainAreaHeight(pos1.GetX(), pos1.GetY(), terrainExists); + const float heightXY0 = AZ::Lerp(heightX0Y0, heightX1Y0, normalizedDelta.GetX()); + const float heightXY1 = AZ::Lerp(heightX0Y1, heightX1Y1, normalizedDelta.GetX()); + height = AZ::Lerp(heightXY0, heightXY1, normalizedDelta.GetY()); } + break; + + //! Clamp the input point to the terrain sample grid, then get the height at the given grid location. + case AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP: + { + AZ::Vector2 normalizedDelta; + AZ::Vector2 clampedPosition; + ClampPosition(x, y, clampedPosition, normalizedDelta); + + height = GetTerrainAreaHeight(clampedPosition.GetX(), clampedPosition.GetY(), terrainExists); + } + break; + + //! Directly get the value at the location, regardless of terrain sample grid density. + case AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT: + [[fallthrough]]; + default: + height = GetTerrainAreaHeight(x, y, terrainExists); + break; } if (terrainExistsPtr) @@ -181,7 +207,30 @@ float TerrainSystem::GetHeightSynchronous(float x, float y, Sampler sampler, boo } return AZ::GetClamp( - outPosition.GetZ(), m_currentSettings.m_worldBounds.GetMin().GetZ(), m_currentSettings.m_worldBounds.GetMax().GetZ()); + height, m_currentSettings.m_worldBounds.GetMin().GetZ(), m_currentSettings.m_worldBounds.GetMax().GetZ()); +} + +float TerrainSystem::GetTerrainAreaHeight(float x, float y, bool& terrainExists) const +{ + AZ::Vector3 inPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ()); + float height = m_currentSettings.m_worldBounds.GetMin().GetZ(); + + AZStd::shared_lock lock(m_areaMutex); + + for (auto& [areaId, areaBounds] : m_registeredAreas) + { + inPosition.SetZ(areaBounds.GetMin().GetZ()); + if (areaBounds.Contains(inPosition)) + { + AZ::Vector3 outPosition; + Terrain::TerrainAreaHeightRequestBus::Event( + areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, terrainExists); + height = outPosition.GetZ(); + break; + } + } + + return height; } float TerrainSystem::GetHeight(AZ::Vector3 position, Sampler sampler, bool* terrainExistsPtr) const @@ -203,24 +252,24 @@ bool TerrainSystem::GetIsHoleFromFloats(float x, float y, Sampler sampler) const AZ::Vector3 TerrainSystem::GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const { - bool terrainExists = false; - - AZ::Vector3 inPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ()); - AZ::Vector3 outNormal = AZ::Vector3::CreateAxisZ(); - AZStd::shared_lock lock(m_areaMutex); - for (auto& [areaId, areaBounds] : m_registeredAreas) - { - inPosition.SetZ(areaBounds.GetMin().GetZ()); - if (areaBounds.Contains(inPosition)) - { - Terrain::TerrainAreaHeightRequestBus::Event( - areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetNormal, inPosition, outNormal, sampler); - terrainExists = true; - break; - } - } + bool terrainExists = false; + + AZ::Vector3 outNormal = AZ::Vector3::CreateAxisZ(); + + const AZ::Vector2 range = (m_currentSettings.m_heightQueryResolution / 2.0f); + const AZ::Vector2 left (x - range.GetX(), y); + const AZ::Vector2 right(x + range.GetX(), y); + const AZ::Vector2 up (x, y - range.GetY()); + const AZ::Vector2 down (x, y + range.GetY()); + + AZ::Vector3 v1(up.GetX(), up.GetY(), GetHeightSynchronous(up.GetX(), up.GetY(), sampler, &terrainExists)); + AZ::Vector3 v2(left.GetX(), left.GetY(), GetHeightSynchronous(left.GetX(), left.GetY(), sampler, &terrainExists)); + AZ::Vector3 v3(right.GetX(), right.GetY(), GetHeightSynchronous(right.GetX(), right.GetY(), sampler, &terrainExists)); + AZ::Vector3 v4(down.GetX(), down.GetY(), GetHeightSynchronous(down.GetX(), down.GetY(), sampler, &terrainExists)); + + outNormal = (v3 - v2).Cross(v4 - v1).GetNormalized(); if (terrainExistsPtr) { @@ -442,73 +491,6 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) m_currentSettings = m_requestedSettings; } - if (m_currentSettings.m_systemActive && m_terrainHeightDirty) - { - AZStd::shared_lock lock(m_areaMutex); - - // Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus). - // We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions - // that create false detection of cyclic dependencies when multiple requests occur on different threads simultaneously. - // (One case where this was previously able to occur was in rapid updating of the Preview widget on the - // GradientSurfaceDataComponent in the Editor when moving the threshold sliders back and forth rapidly) - auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false); - typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex); - - AZ::Transform transform = AZ::Transform::CreateTranslation(m_currentSettings.m_worldBounds.GetCenter()); - - uint32_t width = aznumeric_cast( - (float)m_currentSettings.m_worldBounds.GetXExtent() / m_currentSettings.m_heightQueryResolution.GetX()); - uint32_t height = aznumeric_cast( - (float)m_currentSettings.m_worldBounds.GetYExtent() / m_currentSettings.m_heightQueryResolution.GetY()); - AZStd::vector pixels; - pixels.resize_no_construct(width * height); - const uint32_t pixelDataSize = width * height * sizeof(float); - memset(pixels.data(), 0, pixelDataSize); - - for (uint32_t y = 0; y < height; y++) - { - for (uint32_t x = 0; x < width; x++) - { - // Find the first terrain layer that covers this position. This will be the highest priority, so others can be ignored. - for (auto& [areaId, areaBounds] : m_registeredAreas) - { - AZ::Vector3 inPosition( - (x * m_currentSettings.m_heightQueryResolution.GetX()) + m_currentSettings.m_worldBounds.GetMin().GetX(), - (y * m_currentSettings.m_heightQueryResolution.GetY()) + m_currentSettings.m_worldBounds.GetMin().GetY(), - areaBounds.GetMin().GetZ()); - - if (!areaBounds.Contains(inPosition)) - { - continue; - } - - AZ::Vector3 outPosition; - const AzFramework::Terrain::TerrainDataRequestBus::Events::Sampler sampleFilter = - AzFramework::Terrain::TerrainDataRequestBus::Events::Sampler::DEFAULT; - - Terrain::TerrainAreaHeightRequestBus::Event( - areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, sampleFilter); - - pixels[(y * width) + x] = (outPosition.GetZ() - m_currentSettings.m_worldBounds.GetMin().GetZ()) / - m_currentSettings.m_worldBounds.GetExtents().GetZ(); - - break; - } - } - } - - - const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); - auto terrainFeatureProcessor = scene->GetFeatureProcessor(); - - AZ_Assert(terrainFeatureProcessor, "Unable to find a TerrainFeatureProcessor."); - if (terrainFeatureProcessor) - { - terrainFeatureProcessor->UpdateTerrainData( - transform, m_currentSettings.m_worldBounds, m_currentSettings.m_heightQueryResolution.GetX(), width, height, pixels); - } - } - if (terrainSettingsChanged || m_terrainHeightDirty) { // Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus). diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h index c52165da6b..a9240e02a6 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h @@ -94,8 +94,11 @@ namespace Terrain float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; private: + void ClampPosition(float x, float y, AZ::Vector2& outPosition, AZ::Vector2& normalizedDelta) const; + float GetHeightSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const; - AZ::Vector3 GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const; + float GetTerrainAreaHeight(float x, float y, bool& terrainExists) const; + AZ::Vector3 GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const; // AZ::TickBus::Handler overrides ... void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystemBus.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystemBus.h index 1ba63a8f84..cda6d65a1e 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystemBus.h +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystemBus.h @@ -65,27 +65,8 @@ namespace Terrain virtual ~TerrainAreaHeightRequests() = default; - enum SurfacePointDataMask - { - POSITION = 0x01, - NORMAL = 0x02, - SURFACE_WEIGHTS = 0x04, - - DEFAULT = POSITION | NORMAL | SURFACE_WEIGHTS - }; - // Synchronous single input location. The Vector3 input position versions are defined to ignore the input Z value. - - virtual void GetHeight( - const AZ::Vector3& inPosition, - AZ::Vector3& outPosition, - AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter = - AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT) = 0; - virtual void GetNormal( - const AZ::Vector3& inPosition, - AZ::Vector3& outNormal, - AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter = - AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT) = 0; + virtual void GetHeight(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists) = 0; }; using TerrainAreaHeightRequestBus = AZ::EBus; diff --git a/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp b/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp index f0533322c8..ee4b18e2fb 100644 --- a/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp +++ b/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp @@ -15,7 +15,12 @@ #include #include -#include +#include +#include + +using ::testing::NiceMock; +using ::testing::AtLeast; +using ::testing::_; using ::testing::NiceMock; using ::testing::AtLeast; @@ -29,7 +34,7 @@ protected: AZStd::unique_ptr m_entity; Terrain::TerrainLayerSpawnerComponent* m_layerSpawnerComponent; - UnitTest::MockBoxShapeComponent* m_shapeComponent; + UnitTest::MockAxisAlignedBoxShapeComponent* m_shapeComponent; AZStd::unique_ptr> m_terrainSystem; void SetUp() override @@ -67,7 +72,7 @@ protected: m_layerSpawnerComponent = m_entity->CreateComponent(config); m_app.RegisterComponentDescriptor(m_layerSpawnerComponent->CreateDescriptor()); - m_shapeComponent = m_entity->CreateComponent(); + m_shapeComponent = m_entity->CreateComponent(); m_app.RegisterComponentDescriptor(m_shapeComponent->CreateDescriptor()); ASSERT_TRUE(m_layerSpawnerComponent); diff --git a/Gems/Terrain/Code/Tests/MockAxisAlignedBoxShapeComponent.h b/Gems/Terrain/Code/Tests/MockAxisAlignedBoxShapeComponent.h new file mode 100644 index 0000000000..aeaaa609c4 --- /dev/null +++ b/Gems/Terrain/Code/Tests/MockAxisAlignedBoxShapeComponent.h @@ -0,0 +1,45 @@ +/* + * 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 UnitTest +{ + class MockAxisAlignedBoxShapeComponent + : public AZ::Component + { + public: + AZ_COMPONENT(MockAxisAlignedBoxShapeComponent, "{77CBEED3-FAA3-4BC7-85A9-1A2BFC37BC2A}"); + + static void Reflect([[maybe_unused]] AZ::ReflectContext* context) + { + } + + void Activate() override + { + } + + void Deactivate() override + { + } + + private: + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("ShapeService")); + provided.push_back(AZ_CRC_CE("BoxShapeService")); + provided.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); + } + }; +} diff --git a/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp b/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp index 3e557c66a6..e4fb0f348e 100644 --- a/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp +++ b/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp @@ -9,22 +9,41 @@ #include #include -#include - #include -#include + +#include +#include +#include + +#include +#include using ::testing::AtLeast; +using ::testing::FloatNear; +using ::testing::FloatEq; +using ::testing::IsFalse; +using ::testing::Ne; using ::testing::NiceMock; +using ::testing::Return; class TerrainSystemTest : public ::testing::Test { protected: - AZ::ComponentApplication m_app; + // Defines a structure for defining both an XY position and the expected height for that position. + struct HeightTestPoint + { + AZ::Vector2 m_testLocation; + float m_expectedHeight; + }; - AZStd::unique_ptr m_entity; + AZ::ComponentApplication m_app; AZStd::unique_ptr m_terrainSystem; + AZStd::unique_ptr> m_boxShapeRequests; + AZStd::unique_ptr> m_shapeRequests; + AZStd::unique_ptr> m_terrainAreaHeightRequests; + + void SetUp() override { AZ::ComponentApplication::Descriptor appDesc; @@ -38,31 +57,99 @@ protected: void TearDown() override { m_terrainSystem.reset(); + m_boxShapeRequests.reset(); + m_shapeRequests.reset(); + m_terrainAreaHeightRequests.reset(); m_app.Destroy(); } - void CreateEntity() + AZStd::unique_ptr CreateEntity() { - m_entity = AZStd::make_unique(); - m_entity->Init(); - - ASSERT_TRUE(m_entity); + return AZStd::make_unique(); } - void ResetEntity() + void ActivateEntity(AZ::Entity* entity) { - m_entity->Deactivate(); - m_entity->Reset(); + entity->Init(); + EXPECT_EQ(AZ::Entity::State::Init, entity->GetState()); + + entity->Activate(); + EXPECT_EQ(AZ::Entity::State::Active, entity->GetState()); + } + + template + AZ::Component* CreateComponent(AZ::Entity* entity, const Configuration& config) + { + m_app.RegisterComponentDescriptor(Component::CreateDescriptor()); + return entity->CreateComponent(config); + } + + template + AZ::Component* CreateComponent(AZ::Entity* entity) + { + m_app.RegisterComponentDescriptor(Component::CreateDescriptor()); + return entity->CreateComponent(); + } + + // Create a terrain system with reasonable defaults for testing, but with the ability to override the defaults + // on a test-by-test basis. + void CreateAndActivateTerrainSystem( + AZ::Vector2 queryResolution = AZ::Vector2(1.0f), + AZ::Aabb worldBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-128.0f), AZ::Vector3(128.0f))) + { + // Create the terrain system and give it one tick to fully initialize itself. + m_terrainSystem = AZStd::make_unique(); + m_terrainSystem->SetTerrainAabb(worldBounds); + m_terrainSystem->SetTerrainHeightQueryResolution(queryResolution); + m_terrainSystem->Activate(); + AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.f, AZ::ScriptTimePoint{}); + } + + + AZStd::unique_ptr CreateAndActivateMockTerrainLayerSpawner( + const AZ::Aabb& spawnerBox, + const AZStd::function& mockHeights) + { + // Create the base entity with a mock box shape, Terrain Layer Spawner, and height provider. + auto entity = CreateEntity(); + CreateComponent(entity.get()); + CreateComponent(entity.get()); + + m_boxShapeRequests = AZStd::make_unique>(entity->GetId()); + m_shapeRequests = AZStd::make_unique>(entity->GetId()); + + // Set up the box shape to return whatever spawnerBox was passed in. + ON_CALL(*m_shapeRequests, GetEncompassingAabb).WillByDefault(Return(spawnerBox)); + + // Set up a mock height provider to use the passed-in mock height function to generate a height. + m_terrainAreaHeightRequests = AZStd::make_unique>(entity->GetId()); + ON_CALL(*m_terrainAreaHeightRequests, GetHeight) + .WillByDefault( + [mockHeights](const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists) + { + // By default, set the outPosition to the input position and terrain to always exist. + outPosition = inPosition; + terrainExists = true; + // Let the test function modify these values based on the needs of the specific test. + mockHeights(outPosition, terrainExists); + }); + + ActivateEntity(entity.get()); + return entity; } }; TEST_F(TerrainSystemTest, TrivialCreateDestroy) { + // Trivially verify that the terrain system can successfully be constructed and destructed without errors. + m_terrainSystem = AZStd::make_unique(); } TEST_F(TerrainSystemTest, TrivialActivateDeactivate) { + // Verify that the terrain system can be activated and deactivated without errors. + m_terrainSystem = AZStd::make_unique(); m_terrainSystem->Activate(); m_terrainSystem->Deactivate(); @@ -70,6 +157,8 @@ TEST_F(TerrainSystemTest, TrivialActivateDeactivate) TEST_F(TerrainSystemTest, CreateEventsCalledOnActivation) { + // Verify that when the terrain system is activated, the OnTerrainDataCreate* ebus notifications are generated. + NiceMock mockTerrainListener; EXPECT_CALL(mockTerrainListener, OnTerrainDataCreateBegin()).Times(AtLeast(1)); EXPECT_CALL(mockTerrainListener, OnTerrainDataCreateEnd()).Times(AtLeast(1)); @@ -80,6 +169,8 @@ TEST_F(TerrainSystemTest, CreateEventsCalledOnActivation) TEST_F(TerrainSystemTest, DestroyEventsCalledOnDeactivation) { + // Verify that when the terrain system is deactivated, the OnTerrainDataDestroy* ebus notifications are generated. + NiceMock mockTerrainListener; EXPECT_CALL(mockTerrainListener, OnTerrainDataDestroyBegin()).Times(AtLeast(1)); EXPECT_CALL(mockTerrainListener, OnTerrainDataDestroyEnd()).Times(AtLeast(1)); @@ -89,4 +180,296 @@ TEST_F(TerrainSystemTest, DestroyEventsCalledOnDeactivation) m_terrainSystem->Deactivate(); } +TEST_F(TerrainSystemTest, TerrainDoesNotExistWhenNoTerrainLayerSpawnersAreRegistered) +{ + // For the terrain system, terrain should only exist where terrain layer spawners are present. + // Verify that in the active terrain system, if there are no terrain layer spawners, any arbitrary point + // will return false for terrainExists, returns a height equal to the min world bounds of the terrain system, and returns + // a normal facing up the Z axis. + + // Create and activate the terrain system with our testing defaults for world bounds and query resolution. + CreateAndActivateTerrainSystem(); + + AZ::Aabb worldBounds = m_terrainSystem->GetTerrainAabb(); + + // Loop through several points within the world bounds, including on the edges, and verify that they all return false for + // terrainExists with default heights and normals. + for (float y = worldBounds.GetMin().GetY(); y <= worldBounds.GetMax().GetY(); y += (worldBounds.GetExtents().GetY() / 4.0f)) + { + for (float x = worldBounds.GetMin().GetX(); x <= worldBounds.GetMax().GetX(); x += (worldBounds.GetExtents().GetX() / 4.0f)) + { + AZ::Vector3 position(x, y, 0.0f); + bool terrainExists = true; + float height = m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); + EXPECT_FALSE(terrainExists); + EXPECT_FLOAT_EQ(height, worldBounds.GetMin().GetZ()); + + terrainExists = true; + AZ::Vector3 normal = m_terrainSystem->GetNormal( + position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); + EXPECT_FALSE(terrainExists); + EXPECT_EQ(normal, AZ::Vector3::CreateAxisZ()); + + bool isHole = m_terrainSystem->GetIsHoleFromFloats( + position.GetX(), position.GetY(), AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT); + EXPECT_TRUE(isHole); + } + } +} + +TEST_F(TerrainSystemTest, TerrainExistsOnlyWithinTerrainLayerSpawnerBounds) +{ + // Verify that the presence of a TerrainLayerSpawner causes terrain to exist in (and *only* in) the box where the TerrainLayerSpawner + // is defined. + + // The terrain system should only query Heights from the TerrainAreaHeightRequest bus within the + // TerrainLayerSpawner region, and so those values should only get returned from GetHeight for queries inside that region. + + // Create a mock terrain layer spawner that uses a box of (0,0,5) - (10,10,15) and always returns a height of 5. + constexpr float spawnerHeight = 5.0f; + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(spawnerHeight); + terrainExists = true; + }); + + // Verify that terrain exists within the layer spawner bounds, and doesn't exist outside of it. + + // Create and activate the terrain system with our testing defaults for world bounds and query resolution. + CreateAndActivateTerrainSystem(); + + // Create a box that's twice as big as the layer spawner box. Loop through it and verify that points within the layer box contain + // terrain and the expected height & normal values, and points outside the layer box don't contain terrain. + const AZ::Aabb encompassingBox = + AZ::Aabb::CreateFromMinMax(spawnerBox.GetMin() - (spawnerBox.GetExtents() / 2.0f), + spawnerBox.GetMax() + (spawnerBox.GetExtents() / 2.0f)); + + for (float y = encompassingBox.GetMin().GetY(); y < encompassingBox.GetMax().GetY(); y += 1.0f) + { + for (float x = encompassingBox.GetMin().GetX(); x < encompassingBox.GetMax().GetX(); x += 1.0f) + { + AZ::Vector3 position(x, y, 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &heightQueryTerrainExists); + bool isHole = m_terrainSystem->GetIsHoleFromFloats( + position.GetX(), position.GetY(), AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT); + + if (spawnerBox.Contains(AZ::Vector3(position.GetX(), position.GetY(), spawnerBox.GetMin().GetZ()))) + { + EXPECT_TRUE(heightQueryTerrainExists); + EXPECT_FALSE(isHole); + EXPECT_FLOAT_EQ(height, spawnerHeight); + } + else + { + EXPECT_FALSE(heightQueryTerrainExists); + EXPECT_TRUE(isHole); + } + } + } +} + +TEST_F(TerrainSystemTest, TerrainHeightQueriesWithExactSamplersIgnoreQueryGrid) +{ + // Verify that when using the "EXACT" height sampler, the returned heights come directly from the height provider at the exact + // requested location, instead of the position being quantized to the height query grid. + + // Create a mock terrain layer spawner that uses a box of (0,0,5) - (10,10,15) and generates a height based on a sine wave + // using a frequency of 1m and an amplitude of 10m. i.e. Heights will range between -10 to 10 meters, but will have a value of 0 + // every 0.5 meters. The sine wave value is based on the absolute X position only, for simplicity. + constexpr float amplitudeMeters = 10.0f; + constexpr float frequencyMeters = 1.0f; + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(amplitudeMeters * sin(AZ::Constants::TwoPi * (position.GetX() / frequencyMeters))); + terrainExists = true; + }); + + // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution that exactly matches + // the frequency of our sine wave. If our height queries rely on the query resolution, we should always get a value of 0. + const AZ::Vector2 queryResolution(frequencyMeters); + CreateAndActivateTerrainSystem(queryResolution); + + // Test an arbitrary set of points that should all produce non-zero heights with the EXACT sampler. They're not aligned with the + // query resolution, or with the 0 points on the sine wave. + const AZ::Vector2 nonZeroPoints[] = { AZ::Vector2(0.3f), AZ::Vector2(2.8f), AZ::Vector2(5.9f), AZ::Vector2(7.7f) }; + for (auto& nonZeroPoint : nonZeroPoints) + { + AZ::Vector3 position(nonZeroPoint.GetX(), nonZeroPoint.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &heightQueryTerrainExists); + + // We've chosen a bunch of places on the sine wave that should return a non-zero positive or negative value. + constexpr float epsilon = 0.0001f; + EXPECT_GT(fabsf(height), epsilon); + } + + // Test an arbitrary set of points that should all produce zero heights with the EXACT sampler, since they align with 0 points on the + // sine wave, regardless of whether or not they align to the query resolution. + const AZ::Vector2 zeroPoints[] = { AZ::Vector2(0.5f), AZ::Vector2(1.0f), AZ::Vector2(5.0f), AZ::Vector2(7.5f) }; + for (auto& zeroPoint : zeroPoints) + { + AZ::Vector3 position(zeroPoint.GetX(), zeroPoint.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &heightQueryTerrainExists); + + constexpr float epsilon = 0.0001f; + EXPECT_NEAR(height, 0.0f, epsilon); + } +} + +TEST_F(TerrainSystemTest, TerrainHeightQueriesWithClampSamplersUseQueryGrid) +{ + // Verify that when using the "CLAMP" height sampler, the requested location is quantized to the height query grid before fetching + // the height. + + // Create a mock terrain layer spawner that uses a box of (-10,-10,-5) - (10,10,15) and generates a height equal + // to the X + Y position, so if either one doesn't get clamped we'll get an unexpected result. + 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) + { + 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 0.25 meter intervals. + const AZ::Vector2 queryResolution(0.25f); + CreateAndActivateTerrainSystem(queryResolution); + + // Test some points and verify that the results always go "downward", whether they're in positive or negative space. + // (Z contains the the expected result for convenience). + const HeightTestPoint testPoints[] = + { + { AZ::Vector2(0.0f, 0.0f), 0.0f }, // Should return a height of 0.00 + 0.00 + { AZ::Vector2(0.3f, 0.3f), 0.5f }, // Should return a height of 0.25 + 0.25 + { AZ::Vector2(2.8f, 2.8f), 5.5f }, // Should return a height of 2.75 + 2.75 + { AZ::Vector2(5.5f, 5.5f), 11.0f }, // Should return a height of 5.50 + 5.50 + { AZ::Vector2(7.7f, 7.7f), 15.0f }, // Should return a height of 7.50 + 7.50 + + { AZ::Vector2(-0.3f, -0.3f), -1.0f }, // Should return a height of -0.50 + -0.50 + { AZ::Vector2(-2.8f, -2.8f), -6.0f }, // Should return a height of -3.00 + -3.00 + { AZ::Vector2(-5.5f, -5.5f), -11.0f }, // Should return a height of -5.50 + -5.50 + { AZ::Vector2(-7.7f, -7.7f), -15.5f } // Should return a height of -7.75 + -7.75 + }; + for (auto& testPoint : testPoints) + { + const float expectedHeight = testPoint.m_expectedHeight; + + AZ::Vector3 position(testPoint.m_testLocation.GetX(), testPoint.m_testLocation.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP, &heightQueryTerrainExists); + + constexpr float epsilon = 0.0001f; + EXPECT_NEAR(height, expectedHeight, epsilon); + } +} + +TEST_F(TerrainSystemTest, TerrainHeightQueriesWithBilinearSamplersUseQueryGridToInterpolate) +{ + // Verify that when using the "BILINEAR" height sampler, the heights are interpolated from points sampled from the query grid. + + // Create a mock terrain layer spawner that uses a box of (-10,-10,-5) - (10,10,15) and generates a height equal + // to the X + Y position, so we'll have heights that look like this on our grid: + // 0 *---* 1 + // | | + // 1 *---* 2 + // However, everywhere inside the grid box, we'll generate heights much larger than X + Y. It will have no effect on exact grid + // points, but it will noticeably affect the expected height values if any points get sampled in-between grid points. + + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f); + const float amplitudeMeters = 10.0f; + const float frequencyMeters = 1.0f; + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [amplitudeMeters, frequencyMeters](AZ::Vector3& position, bool& terrainExists) + { + // Our generated height will be X + Y. + float expectedHeight = position.GetX() + position.GetY(); + + // If either X or Y aren't evenly divisible by the query frequency, add a scaled value to our generated height. + // This will show up as an unexpected height "spike" if it gets used in any bilinear filter queries. + float unexpectedVariance = amplitudeMeters * + (fmodf(position.GetX(), frequencyMeters) + fmodf(position.GetY(), frequencyMeters)); + position.SetZ(expectedHeight + unexpectedVariance); + terrainExists = true; + }); + + // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 1 meter intervals. + const AZ::Vector2 queryResolution(frequencyMeters); + CreateAndActivateTerrainSystem(queryResolution); + + // Test some points and verify that the results are the expected bilinear filtered result, + // whether they're in positive or negative space. + // (Z contains the the expected result for convenience). + const HeightTestPoint testPoints[] = { + + // Queries directly on grid points. These should return values of X + Y. + { AZ::Vector2(0.0f, 0.0f), 0.0f }, // Should return a height of 0 + 0 + { AZ::Vector2(1.0f, 0.0f), 1.0f }, // Should return a height of 1 + 0 + { AZ::Vector2(0.0f, 1.0f), 1.0f }, // Should return a height of 0 + 1 + { AZ::Vector2(1.0f, 1.0f), 2.0f }, // Should return a height of 1 + 1 + { AZ::Vector2(3.0f, 5.0f), 8.0f }, // Should return a height of 3 + 5 + + { AZ::Vector2(-1.0f, 0.0f), -1.0f }, // Should return a height of -1 + 0 + { AZ::Vector2(0.0f, -1.0f), -1.0f }, // Should return a height of 0 + -1 + { AZ::Vector2(-1.0f, -1.0f), -2.0f }, // Should return a height of -1 + -1 + { AZ::Vector2(-3.0f, -5.0f), -8.0f }, // Should return a height of -3 + -5 + + // Queries that are on a grid edge (one axis on the grid, the other somewhere in-between). + // These should just be a linear interpolation of the points, so it should still be X + Y. + + { AZ::Vector2(0.25f, 0.0f), 0.25f }, // Should return a height of -0.25 + 0 + { AZ::Vector2(3.75f, 0.0f), 3.75f }, // Should return a height of -3.75 + 0 + { AZ::Vector2(0.0f, 0.25f), 0.25f }, // Should return a height of 0 + -0.25 + { AZ::Vector2(0.0f, 3.75f), 3.75f }, // Should return a height of 0 + -3.75 + + { AZ::Vector2(2.0f, 3.75f), 5.75f }, // Should return a height of -2 + -3.75 + { AZ::Vector2(2.25f, 4.0f), 6.25f }, // Should return a height of -2.25 + -4 + + { AZ::Vector2(-0.25f, 0.0f), -0.25f }, // Should return a height of -0.25 + 0 + { AZ::Vector2(-3.75f, 0.0f), -3.75f }, // Should return a height of -3.75 + 0 + { AZ::Vector2(0.0f, -0.25f), -0.25f }, // Should return a height of 0 + -0.25 + { AZ::Vector2(0.0f, -3.75f), -3.75f }, // Should return a height of 0 + -3.75 + + { AZ::Vector2(-2.0f, -3.75f), -5.75f }, // Should return a height of -2 + -3.75 + { AZ::Vector2(-2.25f, -4.0f), -6.25f }, // Should return a height of -2.25 + -4 + + // Queries inside a grid square (both axes are in-between grid points) + // This is a full bilinear interpolation, but because we're using X + Y for our heights, the interpolated values + // should *still* be X + Y assuming the points were sampled correctly from the grid points. + + { AZ::Vector2(3.25f, 5.25f), 8.5f }, // Should return a height of 3.25 + 5.25 + { AZ::Vector2(7.71f, 9.74f), 17.45f }, // Should return a height of 7.71 + 9.74 + + { AZ::Vector2(-3.25f, -5.25f), -8.5f }, // Should return a height of -3.25 + -5.25 + { AZ::Vector2(-7.71f, -9.74f), -17.45f }, // Should return a height of -7.71 + -9.74 + }; + + // Loop through every test point and validate it. + for (auto& testPoint : testPoints) + { + const float expectedHeight = testPoint.m_expectedHeight; + + AZ::Vector3 position(testPoint.m_testLocation.GetX(), testPoint.m_testLocation.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, &heightQueryTerrainExists); + + // Verify that our height query returned the bilinear filtered result we expect. + constexpr float epsilon = 0.0001f; + EXPECT_NEAR(height, expectedHeight, epsilon); + } +} diff --git a/Gems/Terrain/Code/terrain_editor_shared_files.cmake b/Gems/Terrain/Code/terrain_editor_shared_files.cmake index 9868f7d310..d4719f46d5 100644 --- a/Gems/Terrain/Code/terrain_editor_shared_files.cmake +++ b/Gems/Terrain/Code/terrain_editor_shared_files.cmake @@ -15,6 +15,8 @@ set(FILES Source/EditorComponents/EditorTerrainWorldComponent.h Source/EditorComponents/EditorTerrainWorldDebuggerComponent.cpp Source/EditorComponents/EditorTerrainWorldDebuggerComponent.h + Source/EditorComponents/EditorTerrainWorldRendererComponent.cpp + Source/EditorComponents/EditorTerrainWorldRendererComponent.h Source/EditorComponents/EditorTerrainSystemComponent.cpp Source/EditorComponents/EditorTerrainSystemComponent.h Source/EditorTerrainModule.cpp diff --git a/Gems/Terrain/Code/terrain_files.cmake b/Gems/Terrain/Code/terrain_files.cmake index 6a76dd7141..2c013fa332 100644 --- a/Gems/Terrain/Code/terrain_files.cmake +++ b/Gems/Terrain/Code/terrain_files.cmake @@ -19,6 +19,8 @@ set(FILES Source/Components/TerrainWorldComponent.h Source/Components/TerrainWorldDebuggerComponent.cpp Source/Components/TerrainWorldDebuggerComponent.h + Source/Components/TerrainWorldRendererComponent.cpp + Source/Components/TerrainWorldRendererComponent.h Source/TerrainRenderer/TerrainFeatureProcessor.cpp Source/TerrainRenderer/TerrainFeatureProcessor.h Source/TerrainSystem/TerrainSystem.cpp diff --git a/Gems/Terrain/Code/terrain_mocks_files.cmake b/Gems/Terrain/Code/terrain_mocks_files.cmake new file mode 100644 index 0000000000..2aedd1c5d8 --- /dev/null +++ b/Gems/Terrain/Code/terrain_mocks_files.cmake @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + Mocks/Terrain/MockTerrain.h +) diff --git a/Gems/Terrain/Code/terrain_tests_files.cmake b/Gems/Terrain/Code/terrain_tests_files.cmake index 6d1cf97fd9..3ce1d05003 100644 --- a/Gems/Terrain/Code/terrain_tests_files.cmake +++ b/Gems/Terrain/Code/terrain_tests_files.cmake @@ -7,8 +7,8 @@ # set(FILES - Tests/TerrainMocks.h Tests/TerrainTest.cpp Tests/TerrainSystemTest.cpp Tests/LayerSpawnerTests.cpp + Tests/MockAxisAlignedBoxShapeComponent.h ) diff --git a/Gems/Terrain/gem.json b/Gems/Terrain/gem.json index c01ea7e534..cd72a91708 100644 --- a/Gems/Terrain/gem.json +++ b/Gems/Terrain/gem.json @@ -4,7 +4,21 @@ "license": "Apache-2.0 Or MIT", "origin": "Open 3D Engine - o3de.org", "summary": "The Terrain Gem is an experimental terrain system. The terrain system maps height, color, and surface data to regions of the world, provides gradient-based and shape-based authoring tools and workflows, includes specialized rendering for efficient display, and integrates with physics for physical simulation.", - "canonical_tags": [ "Gem" ], - "user_tags": [ "Environment", "Tools", "Design", "Terrain" ], - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/terrain/" + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Environment", + "Tools", + "Design", + "Terrain" + ], + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/terrain/", + "dependencies": [ + "Atom_RPI", + "Atom", + "GradientSignal", + "SurfaceData", + "LmbrCentral" + ] } diff --git a/Gems/TestAssetBuilder/gem.json b/Gems/TestAssetBuilder/gem.json index 1e0b84894a..ba7568005f 100644 --- a/Gems/TestAssetBuilder/gem.json +++ b/Gems/TestAssetBuilder/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Test Asset Builder Gem is used to feature test Asset Processor.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Debug", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Debug", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/test-asset-builder/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/test-asset-builder/", + "dependencies": [] } diff --git a/Gems/TextureAtlas/gem.json b/Gems/TextureAtlas/gem.json index 832dee0e62..345e085359 100644 --- a/Gems/TextureAtlas/gem.json +++ b/Gems/TextureAtlas/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Texture Atlas Gem provides the formatting for texture atlases from 2D textures for LyShine.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Assets", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Assets", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/texture-atlas/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/texture-atlas/", + "dependencies": [ + "Atom_RPI", + "ImageProcessingAtom" + ] } diff --git a/Gems/TickBusOrderViewer/gem.json b/Gems/TickBusOrderViewer/gem.json index 5ea936960a..dc5cb6f66c 100644 --- a/Gems/TickBusOrderViewer/gem.json +++ b/Gems/TickBusOrderViewer/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Tick Bus Order Viewer Gem provides a console variable that displays the order of runtime tick events.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Simulation", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Simulation", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/tick-bus-order-viewer/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/tick-bus-order-viewer/", + "dependencies": [] } diff --git a/Gems/Twitch/Code/Source/TwitchReflection.cpp b/Gems/Twitch/Code/Source/TwitchReflection.cpp index 618fb6814a..510d1d94f5 100644 --- a/Gems/Twitch/Code/Source/TwitchReflection.cpp +++ b/Gems/Twitch/Code/Source/TwitchReflection.cpp @@ -595,137 +595,137 @@ namespace Twitch StartChannelCommercial, ResetChannelStreamKey); - void UserIDNotify(const StringValue& userID) + void UserIDNotify(const StringValue& userID) override { Call(FN_UserIDNotify, userID); } - void OAuthTokenNotify(const StringValue& token) + void OAuthTokenNotify(const StringValue& token) override { Call(FN_OAuthTokenNotify, token); } - void GetUser(const UserInfoValue& result) + void GetUser(const UserInfoValue& result) override { Call(FN_GetUser, result); } - void ResetFriendsNotificationCountNotify(const Int64Value& result) + void ResetFriendsNotificationCountNotify(const Int64Value& result) override { Call(FN_ResetFriendsNotificationCountNotify, result); } - void GetFriendNotificationCount(const Int64Value& result) + void GetFriendNotificationCount(const Int64Value& result) override { Call(FN_GetFriendNotificationCount, result); } - void GetFriendRecommendations(const FriendRecommendationValue& result) + void GetFriendRecommendations(const FriendRecommendationValue& result) override { Call(FN_GetFriendRecommendations, result); } - void GetFriends(const GetFriendValue& result) + void GetFriends(const GetFriendValue& result) override { Call(FN_GetFriends, result); } - void GetFriendStatus(const FriendStatusValue& result) + void GetFriendStatus(const FriendStatusValue& result) override { Call(FN_GetFriendStatus, result); } - void AcceptFriendRequest(const Int64Value& result) + void AcceptFriendRequest(const Int64Value& result) override { Call(FN_AcceptFriendRequest, result); } - void GetFriendRequests(const FriendRequestValue& result) + void GetFriendRequests(const FriendRequestValue& result) override { Call(FN_GetFriendRequests, result); } - void CreateFriendRequest(const Int64Value& result) + void CreateFriendRequest(const Int64Value& result) override { Call(FN_CreateFriendRequest, result); } - void DeclineFriendRequest(const Int64Value& result) + void DeclineFriendRequest(const Int64Value& result) override { Call(FN_DeclineFriendRequest, result); } - void UpdatePresenceStatus(const Int64Value& result) + void UpdatePresenceStatus(const Int64Value& result) override { Call(FN_UpdatePresenceStatus, result); } - void GetPresenceStatusofFriends(const PresenceStatusValue& result) + void GetPresenceStatusofFriends(const PresenceStatusValue& result) override { Call(FN_GetPresenceStatusofFriends, result); } - void GetPresenceSettings(const PresenceSettingsValue& result) + void GetPresenceSettings(const PresenceSettingsValue& result) override { Call(FN_GetPresenceSettings, result); } - void UpdatePresenceSettings(const PresenceSettingsValue& result) + void UpdatePresenceSettings(const PresenceSettingsValue& result) override { Call(FN_UpdatePresenceSettings, result); } - void GetChannelbyID(const ChannelInfoValue& result) + void GetChannelbyID(const ChannelInfoValue& result) override { Call(FN_GetChannelbyID, result); } - void GetChannel(const ChannelInfoValue& result) + void GetChannel(const ChannelInfoValue& result) override { Call(FN_GetChannel, result); } - void UpdateChannel(const ChannelInfoValue& result) + void UpdateChannel(const ChannelInfoValue& result) override { Call(FN_UpdateChannel, result); } - void GetChannelEditors(const UserInfoListValue& result) + void GetChannelEditors(const UserInfoListValue& result) override { Call(FN_GetChannelEditors, result); } - void GetChannelFollowers(const FollowerResultValue& result) + void GetChannelFollowers(const FollowerResultValue& result) override { Call(FN_GetChannelFollowers, result); } - void GetChannelTeams(const ChannelTeamValue& result) + void GetChannelTeams(const ChannelTeamValue& result) override { Call(FN_GetChannelTeams, result); } - void GetChannelSubscribers(const SubscriberValue& result) + void GetChannelSubscribers(const SubscriberValue& result) override { Call(FN_GetChannelSubscribers, result); } - void CheckChannelSubscriptionbyUser(const SubscriberbyUserValue& result) + void CheckChannelSubscriptionbyUser(const SubscriberbyUserValue& result) override { Call(FN_CheckChannelSubscriptionbyUser, result); } - void GetChannelVideos(const VideoReturnValue& result) + void GetChannelVideos(const VideoReturnValue& result) override { Call(FN_GetChannelVideos, result); } - void StartChannelCommercial(const StartChannelCommercialValue& result) + void StartChannelCommercial(const StartChannelCommercialValue& result) override { Call(FN_StartChannelCommercial, result); } - void ResetChannelStreamKey(const ChannelInfoValue& result) + void ResetChannelStreamKey(const ChannelInfoValue& result) override { Call(FN_ResetChannelStreamKey, result); } diff --git a/Gems/Twitch/gem.json b/Gems/Twitch/gem.json index 6f875e9171..f45433bc3f 100644 --- a/Gems/Twitch/gem.json +++ b/Gems/Twitch/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Twitch Gem provides access to the Twitch API v5 SDK including social functions, channels, and other APIs.", - "canonical_tags": ["Gem"], - "user_tags": ["Network", "SDK", "Multiplayer"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Network", + "SDK", + "Multiplayer" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/twitch/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/twitch/", + "dependencies": [ + "HttpRequestor" + ] } diff --git a/Gems/UiBasics/gem.json b/Gems/UiBasics/gem.json index d1e06eb15a..9a1e16a462 100644 --- a/Gems/UiBasics/gem.json +++ b/Gems/UiBasics/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The UI Basics Gem provides a collection of basic UI prefabs such as image, text, and button, that can be used with LyShine, the Open 3D Engine runtime User Interface system and editor.", - "canonical_tags": ["Gem"], - "user_tags": ["UI", "Assets", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "UI", + "Assets", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/ui-basics/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/ui-basics/", + "dependencies": [] } diff --git a/Gems/Vegetation/Code/Include/Vegetation/Editor/EditorAreaComponentBase.h b/Gems/Vegetation/Code/Include/Vegetation/Editor/EditorAreaComponentBase.h index 60ef04b46e..87a1546ad0 100644 --- a/Gems/Vegetation/Code/Include/Vegetation/Editor/EditorAreaComponentBase.h +++ b/Gems/Vegetation/Code/Include/Vegetation/Editor/EditorAreaComponentBase.h @@ -63,7 +63,7 @@ namespace Vegetation AZ::Aabb GetPreviewBounds() const override; bool GetConstrainToShape() const override; - GradientSignal::GradientPreviewContextPriority GetPreviewContextPriority() const; + GradientSignal::GradientPreviewContextPriority GetPreviewContextPriority() const override; ////////////////////////////////////////////////////////////////////////// // AzToolsFramework::EntitySelectionEvents::Bus::Handler diff --git a/Gems/Vegetation/Code/Tests/DynamicSliceInstanceSpawnerTests.cpp b/Gems/Vegetation/Code/Tests/DynamicSliceInstanceSpawnerTests.cpp index 01a2e059e6..9838a7b6a1 100644 --- a/Gems/Vegetation/Code/Tests/DynamicSliceInstanceSpawnerTests.cpp +++ b/Gems/Vegetation/Code/Tests/DynamicSliceInstanceSpawnerTests.cpp @@ -191,7 +191,7 @@ namespace UnitTest AZ::Data::AssetHandler::LoadResult LoadAssetData( const AZ::Data::Asset& asset, AZStd::shared_ptr stream, - [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) + [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) override { MockAssetData* temp = reinterpret_cast(asset.GetData()); temp->SetStatus(AZ::Data::AssetData::AssetStatus::Ready); diff --git a/Gems/Vegetation/Code/Tests/PrefabInstanceSpawnerTests.cpp b/Gems/Vegetation/Code/Tests/PrefabInstanceSpawnerTests.cpp index ed48aec134..b0d88f2d63 100644 --- a/Gems/Vegetation/Code/Tests/PrefabInstanceSpawnerTests.cpp +++ b/Gems/Vegetation/Code/Tests/PrefabInstanceSpawnerTests.cpp @@ -185,7 +185,7 @@ namespace UnitTest AZ::Data::AssetHandler::LoadResult LoadAssetData( const AZ::Data::Asset& asset, AZStd::shared_ptr stream, - [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) + [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) override { MockAssetData* temp = reinterpret_cast(asset.GetData()); temp->SetStatus(AZ::Data::AssetData::AssetStatus::Ready); diff --git a/Gems/Vegetation/Code/Tests/VegetationMocks.h b/Gems/Vegetation/Code/Tests/VegetationMocks.h index 97eaa50b69..ee620c81a2 100644 --- a/Gems/Vegetation/Code/Tests/VegetationMocks.h +++ b/Gems/Vegetation/Code/Tests/VegetationMocks.h @@ -268,7 +268,7 @@ namespace UnitTest } } - void GetSystemConfig(AZ::ComponentConfig* config) const + void GetSystemConfig(AZ::ComponentConfig* config) const override { if (azrtti_typeid(m_areaSystemConfig) == azrtti_typeid(*config)) { diff --git a/Gems/Vegetation/gem.json b/Gems/Vegetation/gem.json index 3119d67560..9dbcc3450b 100644 --- a/Gems/Vegetation/gem.json +++ b/Gems/Vegetation/gem.json @@ -5,9 +5,21 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Vegetation Gem provides tools to place natural-looking vegetation in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Environment", "Tools", "Design"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Environment", + "Tools", + "Design" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/vegetation/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/vegetation/", + "dependencies": [ + "LmbrCentral", + "SurfaceData", + "CommonFeaturesAtom", + "GradientSignal" + ] } diff --git a/Gems/VideoPlaybackFramework/gem.json b/Gems/VideoPlaybackFramework/gem.json index 15b200c8db..9f491f47cb 100644 --- a/Gems/VideoPlaybackFramework/gem.json +++ b/Gems/VideoPlaybackFramework/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Video Playback Framework Gem provides the interface to play back video.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/video-playback-framework/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/video-playback-framework/", + "dependencies": [] } diff --git a/Gems/VirtualGamepad/gem.json b/Gems/VirtualGamepad/gem.json index 472e8b93fe..c6305f1754 100644 --- a/Gems/VirtualGamepad/gem.json +++ b/Gems/VirtualGamepad/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Virtual Gamepad Gem provides controls that emulate a gamepad on touch screen devices.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/virtual-gamepad/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/virtual-gamepad/", + "dependencies": [] } diff --git a/Gems/WhiteBox/gem.json b/Gems/WhiteBox/gem.json index 0e44e2fe5e..81e0ad5f88 100644 --- a/Gems/WhiteBox/gem.json +++ b/Gems/WhiteBox/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The White Box Gem provides White Box rapid design components for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Design", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Design", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/design/white-box/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/design/white-box/", + "dependencies": [ + "Atom_RPI", + "Atom_Feature_Common", + "CommonFeaturesAtom" + ] } diff --git a/cmake/3rdParty.cmake b/cmake/3rdParty.cmake index f70ed1aa02..4b7f929279 100644 --- a/cmake/3rdParty.cmake +++ b/cmake/3rdParty.cmake @@ -6,6 +6,17 @@ # # +define_property(TARGET PROPERTY LY_SYSTEM_LIBRARY + BRIEF_DOCS "Defines a 3rdParty library as a system library" + FULL_DOCS [[ + Property which is set on third party targets that should be considered + as provided by the system. Such targets are excluded from the runtime + dependencies considerations, and are not distributed as part of the + O3DE SDK package. Instead, users of the SDK are expected to install + such a third party library themselves. + ]] +) + # Do not overcomplicate searching for the 3rdParty path, if it is not easy to find, # the user should define it. @@ -79,9 +90,10 @@ endfunction() # "fileA\nMy/Output/Subfolder/lib" # "fileB\nMy/Output/Subfolder/bin" # +# \arg:SYSTEM If specified, the library is considered a system library, and is not copied to the build output directory function(ly_add_external_target) - set(options) + set(options SYSTEM) set(oneValueArgs NAME VERSION 3RDPARTY_DIRECTORY PACKAGE 3RDPARTY_ROOT_DIRECTORY OUTPUT_SUBDIRECTORY) set(multiValueArgs HEADER_CHECK COMPILE_DEFINITIONS INCLUDE_DIRECTORIES BUILD_DEPENDENCIES RUNTIME_DEPENDENCIES) @@ -300,6 +312,10 @@ function(ly_add_external_target) ) endif() + if(ly_add_external_target_SYSTEM) + set_target_properties(3rdParty::${NAME_WITH_NAMESPACE} PROPERTIES LY_SYSTEM_LIBRARY TRUE) + endif() + endif() endfunction() @@ -327,4 +343,4 @@ if(NOT INSTALLED_ENGINE) ly_include_cmake_file_list(cmake/3rdParty/cmake_files.cmake) ly_get_absolute_pal_filename(pal_3rdparty_dir ${CMAKE_CURRENT_SOURCE_DIR}/cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}) ly_include_cmake_file_list(${pal_3rdparty_dir}/cmake_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake) -endif() \ No newline at end of file +endif() diff --git a/cmake/3rdParty/FindX11.cmake b/cmake/3rdParty/FindX11.cmake new file mode 100644 index 0000000000..98cc57e40e --- /dev/null +++ b/cmake/3rdParty/FindX11.cmake @@ -0,0 +1,29 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +# Open a new scope so we can make changes to CMAKE_MODULE_PATH, and restore it +# when we're done +function(FindX11) + # O3DE's FindX11.cmake is a wrapper for the one that CMake provides. Remove + # our current directory from CMAKE_MODULE_PATH to avoid recursive includes + list(REMOVE_ITEM CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + + find_package(X11 COMPONENTS ${X11_FIND_COMPONENTS} QUIET) + + foreach(component IN LISTS X11_FIND_COMPONENTS) + ly_add_external_target( + SYSTEM + PACKAGE X11 + NAME ${component} + VERSION "" + BUILD_DEPENDENCIES + X11::${component} + ) + endforeach() +endfunction() +FindX11() diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index 429665f04a..65e046c8ba 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -9,7 +9,6 @@ # shared by other platforms: ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev11-multiplatform TARGETS assimplib PACKAGE_HASH 1a9113788b893ef4a2ee63ac01eb71b981a92894a5a51175703fa225f5804dec) -ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023) ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) @@ -31,7 +30,7 @@ ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev6-linux ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-linux TARGETS Lua PACKAGE_HASH 1adc812abe3dd0dbb2ca9756f81d8f0e0ba45779ac85bf1d8455b25c531a38b0) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-linux TARGETS PhysX PACKAGE_HASH a110249cbef4f266b0002c4ee9a71f59f373040cefbe6b82f1e1510c811edde6) ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-linux TARGETS etc2comp PACKAGE_HASH 9283aa5db5bb7fb90a0ddb7a9f3895317c8ebe8044943124bbb3673a41407430) -ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-linux TARGETS mcpp PACKAGE_HASH 0aa713f3f2c156cb2f17d9b800aed8acf9df5ab167c48b679853ecb040da9a67) +ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-linux TARGETS mcpp PACKAGE_HASH df7a998d0bc3fedf44b5bdebaf69ddad6033355b71a590e8642445ec77bc6c41) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-linux TARGETS mikkelsen PACKAGE_HASH 5973b1e71a64633588eecdb5b5c06ca0081f7be97230f6ef64365cbda315b9c8) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-linux TARGETS googletest PACKAGE_HASH 7b7ad330f369450c316a4c4592d17fbb4c14c731c95bd8f37757203e8c2bbc1b) ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-linux TARGETS GoogleBenchmark PACKAGE_HASH 4038878f337fc7e0274f0230f71851b385b2e0327c495fc3dd3d1c18a807928d) @@ -44,5 +43,6 @@ ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-linux ly_associate_package(PACKAGE_NAME azslc-1.7.23-rev2-linux TARGETS azslc PACKAGE_HASH 1ba84d8321a566d35a1e9aa7400211ba8e6d1c11c08e4be3c93e6e74b8f7aef1) ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-linux TARGETS zlib PACKAGE_HASH 16f3b9e11cda525efb62144f354c1cfc30a5def9eff020dbe49cb00ee7d8234f) ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-linux TARGETS squish-ccr PACKAGE_HASH 85fecafbddc6a41a27c5f59ed4a5dfb123a94cb4666782cf26e63c0a4724c530) +ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-linux TARGETS astc-encoder PACKAGE_HASH 2ba97a06474d609945f0ab4419af1f6bbffdd294ca6b869f5fcebec75c573c0f) ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-linux TARGETS ISPCTexComp PACKAGE_HASH 065fd12abe4247dde247330313763cf816c3375c221da030bdec35024947f259) ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-linux TARGETS lz4 PACKAGE_HASH 5de3dbd3e2a3537c6555d759b3c5bb98e5456cf85c74ff6d046f809b7087290d) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index 4900a9d77e..acddb879d4 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -9,7 +9,6 @@ # shared by other platforms: ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev11-multiplatform TARGETS assimplib PACKAGE_HASH 1a9113788b893ef4a2ee63ac01eb71b981a92894a5a51175703fa225f5804dec) -ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023) ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) @@ -33,7 +32,7 @@ ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev5-mac ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev6-mac TARGETS Lua PACKAGE_HASH b9079fd35634774c9269028447562c6b712dbc83b9c64975c095fd423ff04c08) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-mac TARGETS PhysX PACKAGE_HASH 5e092a11d5c0a50c4dd99bb681a04b566a4f6f29aa08443d9bffc8dc12c27c8e) ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-mac TARGETS etc2comp PACKAGE_HASH 1966ab101c89db7ecf30984917e0a48c0d02ee0e4d65b798743842b9469c0818) -ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-mac TARGETS mcpp PACKAGE_HASH 48a9c5197bf72843fb9ac44825501ee16bbe3e72e086a32b8c9c05bf47db12ab) +ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-mac TARGETS mcpp PACKAGE_HASH be9558905c9c49179ef3d7d84f0a5472415acdf7fe2d76eb060d9431723ddf2e) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-mac TARGETS mikkelsen PACKAGE_HASH 83af99ca8bee123684ad254263add556f0cf49486c0b3e32e6d303535714e505) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-mac TARGETS googletest PACKAGE_HASH cbf020d5ef976c5db8b6e894c6c63151ade85ed98e7c502729dd20172acae5a8) ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-mac TARGETS GoogleBenchmark PACKAGE_HASH ad25de0146769c91e179953d845de2bec8ed4a691f973f47e3eb37639381f665) @@ -42,6 +41,7 @@ ly_associate_package(PACKAGE_NAME qt-5.15.2-rev5-mac ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-mac TARGETS libsamplerate PACKAGE_HASH b912af40c0ac197af9c43d85004395ba92a6a859a24b7eacd920fed5854a97fe) ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-mac TARGETS zlib PACKAGE_HASH 21714e8a6de4f2523ee92a7f52d51fbee29c5f37ced334e00dc3c029115b472e) ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-mac TARGETS squish-ccr PACKAGE_HASH 155bfbfa17c19a9cd2ef025de14c5db598f4290045d5b0d83ab58cb345089a77) +ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-mac TARGETS astc-encoder PACKAGE_HASH 96f6ea8c3e45ec7fe525230c7c53ca665c8300d8e28456cc19bb3159ce6f8dcc) ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-mac TARGETS ISPCTexComp PACKAGE_HASH 8a4e93277b8face6ea2fd57c6d017bdb55643ed3d6387110bc5f6b3b884dd169) ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-mac TARGETS lz4 PACKAGE_HASH 891ff630bf34f7ab1d8eaee2ea0a8f1fca89dbdc63fca41ee592703dd488a73b) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 3855b7d712..d56a03f515 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -9,7 +9,6 @@ # shared by other platforms: ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev11-multiplatform TARGETS assimplib PACKAGE_HASH 1a9113788b893ef4a2ee63ac01eb71b981a92894a5a51175703fa225f5804dec) -ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023) ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246) @@ -34,7 +33,7 @@ ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev4-windows ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-windows TARGETS Lua PACKAGE_HASH 136faccf1f73891e3fa3b95f908523187792e56f5b92c63c6a6d7e72d1158d40) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-windows TARGETS PhysX PACKAGE_HASH 0c5ffbd9fa588e5cf7643721a7cfe74d0fe448bf82252d39b3a96d06dfca2298) ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-windows TARGETS etc2comp PACKAGE_HASH fc9ae937b2ec0d42d5e7d0e9e8c80e5e4d257673fb33bc9b7d6db76002117123) -ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-windows TARGETS mcpp PACKAGE_HASH 511672598fa319bfb8db87f965b59abff1620bb7c1dcf7669e039a8acd8d3ff8) +ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-windows TARGETS mcpp PACKAGE_HASH 794789aba639bfe2f4e8fcb4424d679933dd6290e523084aa0a4e287ac44acb2) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-windows TARGETS mikkelsen PACKAGE_HASH 872c4d245a1c86139aa929f2b465b63ea4ea55b04ced50309135dd4597457a4e) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-windows TARGETS googletest PACKAGE_HASH 7e8f03ae8a01563124e3daa06386f25a2b311c10bb95bff05cae6c41eff83837) ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-windows TARGETS GoogleBenchmark PACKAGE_HASH 0c94ca69ae8e7e4aab8e90032b5c82c5964410429f3dd9dbb1f9bf4fe032b1d4) @@ -49,5 +48,6 @@ ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-windows ly_associate_package(PACKAGE_NAME Crashpad-0.8.0-rev1-windows TARGETS Crashpad PACKAGE_HASH d162aa3070147bc0130a44caab02c5fe58606910252caf7f90472bd48d4e31e2) ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-windows TARGETS zlib PACKAGE_HASH 9afab1d67641ed8bef2fb38fc53942da47f2ab339d9e77d3d20704a48af2da0b) ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-windows TARGETS squish-ccr PACKAGE_HASH 5c3d9fa491e488ccaf802304ad23b932268a2b2846e383f088779962af2bfa84) +ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-windows TARGETS astc-encoder PACKAGE_HASH 3addc6fc1a7eb0d6b7f3d530e962af967e6d92b3825ef485da243346357cf78e) ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-windows TARGETS ISPCTexComp PACKAGE_HASH b6fa6ea28a2808a9a5524c72c37789c525925e435770f2d94eb2d387360fa2d0) ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-windows TARGETS lz4 PACKAGE_HASH 4ea457b833cd8cfaf8e8e06ed6df601d3e6783b606bdbc44a677f77e19e0db16) diff --git a/cmake/AzAutoGen.py b/cmake/AzAutoGen.py index 51c46b53d8..19b9711325 100755 --- a/cmake/AzAutoGen.py +++ b/cmake/AzAutoGen.py @@ -71,10 +71,10 @@ def SearchPaths(filename, paths=[]): return None def ComputeOutputPath(inputFiles, projectDir, outputDir): - commonInputPath = os.path.commonprefix(inputFiles) # If we've globbed many source files, this finds the common prefix + commonInputPath = os.path.commonpath(inputFiles) # If we've globbed many source files, this finds the common path if os.path.isfile(commonInputPath): # If the commonInputPath resolves to an actual file, slice off the filename commonInputPath = os.path.dirname(commonInputPath) - commonPath = os.path.commonprefix([commonInputPath, projectDir]) # Finds the common path between the data source files and our project directory (//depot/dev/Code/Framework/AzCore/) + commonPath = os.path.commonpath([commonInputPath, projectDir]) # Finds the common path between the data source files and our project directory (//depot/dev/Code/Framework/AzCore/) inputRelativePath = os.path.relpath(commonInputPath, commonPath) # Computes the relative path for the project source directory (Code/Framework/AzCore/AutoGen/) return os.path.join(outputDir, inputRelativePath) # Returns a suitable output directory (//depot/dev/Generated/Code/Framework/AzCore/AutoGen/) diff --git a/cmake/Install.cmake b/cmake/Install.cmake index 6f50c78fd7..adcd28fa37 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -152,3 +152,20 @@ function(ly_install_run_code CODE) ) endfunction() + +#! ly_install_run_script: specifies path to script to be added to the install process (will run at install time) +# +# \notes: +# - refer to cmake's install(SCRIPT documentation for more information +# +function(ly_install_run_script SCRIPT) + + if(NOT LY_INSTALL_ENABLED) + return() + endif() + + install(SCRIPT ${SCRIPT} + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} # use the default for the time being + ) + +endfunction() \ No newline at end of file diff --git a/cmake/LYPython.cmake b/cmake/LYPython.cmake index eeb9f97a55..a8095fbc95 100644 --- a/cmake/LYPython.cmake +++ b/cmake/LYPython.cmake @@ -21,14 +21,17 @@ include(cmake/LySet.cmake) # CMAKE_HOST_SYSTEM_NAME is "Windows", "Darwin", or "Linux" in our cases.. if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux" ) ly_set(LY_PYTHON_VERSION 3.7.10) + ly_set(LY_PYTHON_VERSION_MAJOR_MINOR 3.7) ly_set(LY_PYTHON_PACKAGE_NAME python-3.7.10-rev2-linux) ly_set(LY_PYTHON_PACKAGE_HASH 6b9cf455e6190ec38836194f4454bb9db6bfc6890b4baff185cc5520aa822f05) elseif (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin" ) ly_set(LY_PYTHON_VERSION 3.7.10) + ly_set(LY_PYTHON_VERSION_MAJOR_MINOR 3.7) ly_set(LY_PYTHON_PACKAGE_NAME python-3.7.10-rev1-darwin) ly_set(LY_PYTHON_PACKAGE_HASH 3f65801894e4e44b5faa84dd85ef80ecd772dcf728cdd2d668a6e75978a32695) elseif (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows" ) ly_set(LY_PYTHON_VERSION 3.7.10) + ly_set(LY_PYTHON_VERSION_MAJOR_MINOR 3.7) ly_set(LY_PYTHON_PACKAGE_NAME python-3.7.10-rev2-windows) ly_set(LY_PYTHON_PACKAGE_HASH 06d97488a2dbabe832ecfa832a42d3e8a7163ba95e975f032727331b0f49d280) endif() diff --git a/cmake/LYTestWrappers.cmake b/cmake/LYTestWrappers.cmake index a305f0be38..87649c5be6 100644 --- a/cmake/LYTestWrappers.cmake +++ b/cmake/LYTestWrappers.cmake @@ -231,7 +231,7 @@ function(ly_add_test) # For test projects that are custom targets, pass a props file that sets the project as "Console" so # it leaves the console open when it finishes - set_target_properties(${unaliased_test_name} PROPERTIES VS_USER_PROPS "${LY_ROOT_FOLDER}/cmake/Platform/Common/TestProject.props") + set_target_properties(${unaliased_test_name} PROPERTIES VS_USER_PROPS "${LY_ROOT_FOLDER}/cmake/Platform/Common/MSVC/TestProject.props") # Include additional dependencies if (ly_add_test_RUNTIME_DEPENDENCIES) diff --git a/cmake/LYWrappers.cmake b/cmake/LYWrappers.cmake index 8e5633b5e6..d388b03b75 100644 --- a/cmake/LYWrappers.cmake +++ b/cmake/LYWrappers.cmake @@ -477,11 +477,23 @@ function(ly_parse_third_party_dependencies ly_THIRD_PARTY_LIBRARIES) if(${dependency_namespace} STREQUAL "3rdParty") if (NOT TARGET ${dependency}) list(GET dependency_list 1 dependency_package) + list(LENGTH dependency_list dependency_list_length) ly_download_associated_package(${dependency_package}) - find_package(${dependency_package} REQUIRED MODULE) + if (dependency_list_length GREATER 2) + # There's an optional interface specified + list(GET dependency_list 2 component) + list(APPEND packages_with_components ${dependency_package}) + list(APPEND ${dependency_package}_components ${component}) + else() + find_package(${dependency_package} REQUIRED MODULE) + endif() endif() endif() endforeach() + + foreach(dependency IN LISTS packages_with_components) + find_package(${dependency} REQUIRED MODULE COMPONENTS ${${dependency}_components}) + endforeach() endfunction() #! ly_configure_target_platform_properties: Configures any platform specific properties on target diff --git a/cmake/Platform/Common/Clang/Configurations_clang.cmake b/cmake/Platform/Common/Clang/Configurations_clang.cmake index 8b6c7e611b..2a311d6079 100644 --- a/cmake/Platform/Common/Clang/Configurations_clang.cmake +++ b/cmake/Platform/Common/Clang/Configurations_clang.cmake @@ -16,14 +16,23 @@ ly_append_configurations_options( -Wall -Werror - # Disabled warnings (please do not disable any others without first consulting ly-warnings) + ################### + # Disabled warnings (please do not disable any others without first consulting sig-build) + ################### + -Wno-inconsistent-missing-override # unfortunately there is no warning in MSVC to detect missing overrides, + # MSVC's static analyzer can, but that is a different run that most developers are not aware of. A pass + # was done to fix all hits. Leaving this disabled until there is a matching warning in MSVC. + -Wrange-loop-analysis -Wno-unknown-warning-option # used as a way to mark warnings that are MSVC only - -Wno-inconsistent-missing-override -Wno-parentheses -Wno-reorder -Wno-switch -Wno-undefined-var-template + + ################### + # Enabled warnings (that are disabled by default) + ################### COMPILATION_DEBUG -O0 # No optimization diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index fdd3256d78..8fb2effe29 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -21,15 +21,21 @@ define_property(TARGET PROPERTY LY_INSTALL_GENERATE_RUN_TARGET ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core) -cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE runtime_output_directory) -cmake_path(RELATIVE_PATH CMAKE_LIBRARY_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE library_output_directory) - if(LY_MONOLITHIC_GAME) set(LY_BUILD_PERMUTATION Monolithic) else() set(LY_BUILD_PERMUTATION Default) endif() +cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE runtime_output_directory) +cmake_path(RELATIVE_PATH CMAKE_LIBRARY_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE library_output_directory) +# Get the output folders, archive is always the same, but runtime/library can be in subfolders defined per target +cmake_path(RELATIVE_PATH CMAKE_ARCHIVE_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE archive_output_directory) + +cmake_path(APPEND archive_output_directory "${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") +cmake_path(APPEND library_output_directory "${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") +cmake_path(APPEND runtime_output_directory "${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") + #! ly_setup_target: Setup the data needed to re-create the cmake target commands for a single target function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_target_source_dir) # De-alias target name @@ -78,9 +84,6 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar endforeach() endif() - # Get the output folders, archive is always the same, but runtime/library can be in subfolders defined per target - cmake_path(RELATIVE_PATH CMAKE_ARCHIVE_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE archive_output_directory) - get_target_property(target_runtime_output_directory ${TARGET_NAME} RUNTIME_OUTPUT_DIRECTORY) if(target_runtime_output_directory) cmake_path(RELATIVE_PATH target_runtime_output_directory BASE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_runtime_output_subdirectory) @@ -91,10 +94,6 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar cmake_path(RELATIVE_PATH target_library_output_directory BASE_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_library_output_subdirectory) endif() - cmake_path(APPEND archive_output_directory "${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") - cmake_path(APPEND library_output_directory "${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") - cmake_path(APPEND runtime_output_directory "${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") - if(COMMAND ly_install_target_override) # Mac needs special handling because of a cmake issue ly_install_target_override(TARGET ${TARGET_NAME} @@ -372,6 +371,10 @@ function(ly_setup_o3de_install) COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + if(COMMAND ly_post_install_steps) + ly_post_install_steps() + endif() + endfunction() #! ly_setup_cmake_install: install the "cmake" folder @@ -528,7 +531,7 @@ endfunction()" # of baking the path. This is needed so `cmake --install --prefix ` works regardless of the CMAKE_INSTALL_PREFIX # used to generate the solution. # CMAKE_INSTALL_PREFIX is still used when building the INSTALL target - set(install_output_folder "\${CMAKE_INSTALL_PREFIX}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") + set(install_output_folder "\${CMAKE_INSTALL_PREFIX}/${runtime_output_directory}") set(target_file_dir "${install_output_folder}/${target_runtime_output_subdirectory}") ly_get_runtime_dependencies(runtime_dependencies ${target}) foreach(runtime_dependency ${runtime_dependencies}) diff --git a/cmake/Platform/Common/MSVC/CodeAnalysis.ruleset b/cmake/Platform/Common/MSVC/CodeAnalysis.ruleset new file mode 100644 index 0000000000..2a248b216b --- /dev/null +++ b/cmake/Platform/Common/MSVC/CodeAnalysis.ruleset @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake index 237db5a2ce..a4d8533626 100644 --- a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake +++ b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake @@ -13,7 +13,7 @@ endif() unset(minimum_supported_toolset) include(cmake/Platform/Common/Configurations_common.cmake) -include(cmake/Platform/Common/VisualStudio_common.cmake) +include(cmake/Platform/Common/MSVC/VisualStudio_common.cmake) # Verify that it wasn't invoked with an unsupported target/host architecture. Currently only supports x64/x64 if(CMAKE_VS_PLATFORM_NAME AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "x64") @@ -35,13 +35,22 @@ ly_append_configurations_options( /WX # Warnings as errors /permissive- # Conformance with standard - # Disabling some warnings + ################### + # Disabled warnings (please do not disable any others without first consulting sig-build) + ################### /wd4201 # nonstandard extension used: nameless struct/union. This actually became part of the C++11 std, MS has an open issue: https://developercommunity.visualstudio.com/t/warning-level-4-generates-a-bogus-warning-c4201-no/103064 - # Enabling warnings that are disabled by default from /W4 + ################### + # Enabled warnings (that are disabled by default from /W4) + ################### # https://docs.microsoft.com/en-us/cpp/preprocessor/compiler-warnings-that-are-off-by-default?view=vs-2019 + /we4263 # 'function': member function does not override any base class virtual member function + /we4264 # 'virtual_function': no override available for virtual member function from base 'class'; function is hidden + /we4265 # 'class': class has virtual functions, but destructor is not virtual + /we4266 # 'function': no override available for virtual member function from base 'type'; function is hidden /we4296 # 'operator': expression is always false /we4426 # optimization flags changed after including header, may be due to #pragma optimize() + /we4437 # dynamic_cast from virtual base 'class1' to 'class2' could fail in some contexts #/we4619 # #pragma warning: there is no warning number 'number'. Unfortunately some versions of MSVC 16.X dont filter this warning coming from external headers and Qt has a bad warning in QtCore/qvector.h(340,12) /we4774 # 'string' : format string expected in argument number is not a string literal /we4777 # 'function' : format string 'string' requires an argument of type 'type1', but variadic argument number has type 'type2 @@ -49,7 +58,6 @@ ly_append_configurations_options( /we5032 # detected #pragma warning(push) with no corresponding #pragma warning(pop) /we5233 # explicit lambda capture 'identifier' is not used - /Zc:forScope # Force Conformance in for Loop Scope /diagnostics:caret # Compiler diagnostic options: includes the column where the issue was found and places a caret (^) under the location in the line of code where the issue was detected. /Zc:__cplusplus diff --git a/cmake/Platform/Common/Directory.Build.props b/cmake/Platform/Common/MSVC/Directory.Build.props similarity index 84% rename from cmake/Platform/Common/Directory.Build.props rename to cmake/Platform/Common/MSVC/Directory.Build.props index 76e4b28922..b8e9b716d9 100644 --- a/cmake/Platform/Common/Directory.Build.props +++ b/cmake/Platform/Common/MSVC/Directory.Build.props @@ -14,6 +14,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT @VCPKG_CONFIGURATION_MAPPING@ false + $(MSBuildThisFileDirectory)CodeAnalysis.ruleset @@ -21,8 +22,10 @@ SPDX-License-Identifier: Apache-2.0 OR MIT handles external headers in MSVC, we can remove that code and this --> TurnOffAllWarnings + + true - \ No newline at end of file + diff --git a/cmake/Platform/Common/TestProject.props b/cmake/Platform/Common/MSVC/TestProject.props similarity index 100% rename from cmake/Platform/Common/TestProject.props rename to cmake/Platform/Common/MSVC/TestProject.props diff --git a/cmake/Platform/Common/VisualStudio_common.cmake b/cmake/Platform/Common/MSVC/VisualStudio_common.cmake similarity index 88% rename from cmake/Platform/Common/VisualStudio_common.cmake rename to cmake/Platform/Common/MSVC/VisualStudio_common.cmake index 9124758b18..843b3dcd9d 100644 --- a/cmake/Platform/Common/VisualStudio_common.cmake +++ b/cmake/Platform/Common/MSVC/VisualStudio_common.cmake @@ -15,4 +15,4 @@ foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) endforeach() configure_file("${CMAKE_CURRENT_LIST_DIR}/Directory.Build.props" "${CMAKE_BINARY_DIR}/Directory.Build.props" @ONLY) - +file(COPY "${CMAKE_CURRENT_LIST_DIR}/CodeAnalysis.ruleset" DESTINATION "${CMAKE_BINARY_DIR}") diff --git a/cmake/Platform/Common/RuntimeDependencies_common.cmake b/cmake/Platform/Common/RuntimeDependencies_common.cmake index 95d91da7f7..60e55453f8 100644 --- a/cmake/Platform/Common/RuntimeDependencies_common.cmake +++ b/cmake/Platform/Common/RuntimeDependencies_common.cmake @@ -70,12 +70,23 @@ function(ly_get_runtime_dependencies ly_RUNTIME_DEPENDENCIES ly_TARGET) # link dependencies are not runtime dependencies (we dont have anything to copy) however, we need to traverse # them since them or some dependency downstream could have something to copy over - foreach(link_dependency ${link_dependencies}) - if(NOT ${link_dependency} MATCHES "^::@") # Skip wraping produced when targets are not created in the same directory (https://cmake.org/cmake/help/latest/prop_tgt/LINK_LIBRARIES.html) - unset(dependencies) - ly_get_runtime_dependencies(dependencies ${link_dependency}) - list(APPEND all_runtime_dependencies ${dependencies}) + foreach(link_dependency IN LISTS link_dependencies) + if(${link_dependency} MATCHES "^::@") + # Skip wraping produced when targets are not created in the same directory + # (https://cmake.org/cmake/help/latest/prop_tgt/LINK_LIBRARIES.html) + continue() endif() + + if(TARGET ${link_dependency} AND link_dependency MATCHES "^3rdParty::") + get_target_property(is_system_library ${link_dependency} LY_SYSTEM_LIBRARY) + if(is_system_library) + continue() + endif() + endif() + + unset(dependencies) + ly_get_runtime_dependencies(dependencies ${link_dependency}) + list(APPEND all_runtime_dependencies ${dependencies}) endforeach() # For manual dependencies, we want to copy over the dependency and traverse them diff --git a/cmake/Platform/Linux/PAL_linux.cmake b/cmake/Platform/Linux/PAL_linux.cmake index c088163dca..e74adb287e 100644 --- a/cmake/Platform/Linux/PAL_linux.cmake +++ b/cmake/Platform/Linux/PAL_linux.cmake @@ -35,7 +35,7 @@ else() endif() # Set the default asset type for deployment -set(LY_ASSET_DEPLOY_ASSET_TYPE "pc" CACHE STRING "Set the asset type for deployment.") +set(LY_ASSET_DEPLOY_ASSET_TYPE "linux" CACHE STRING "Set the asset type for deployment.") # Set the python cmd tool ly_set(LY_PYTHON_CMD ${CMAKE_CURRENT_SOURCE_DIR}/python/python.sh) diff --git a/cmake/Platform/Mac/Configurations_mac.cmake b/cmake/Platform/Mac/Configurations_mac.cmake index 33a58dbbda..b1c50b751d 100644 --- a/cmake/Platform/Mac/Configurations_mac.cmake +++ b/cmake/Platform/Mac/Configurations_mac.cmake @@ -29,9 +29,7 @@ else() endif() # Signing -# The "-o linker-signed" flag is required as a work-around for the following CMake issue: -# https://gitlab.kitware.com/cmake/cmake/-/issues/21854 -ly_set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--deep -o linker-signed") +ly_set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--deep") # Generate scheme files for Xcode ly_set(CMAKE_XCODE_GENERATE_SCHEME TRUE) diff --git a/cmake/Platform/Mac/InstallUtils_mac.cmake.in b/cmake/Platform/Mac/InstallUtils_mac.cmake.in new file mode 100644 index 0000000000..d73c4db459 --- /dev/null +++ b/cmake/Platform/Mac/InstallUtils_mac.cmake.in @@ -0,0 +1,170 @@ +# +# 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 +# +# + +function(fixup_qt_framework lib_name framework_path) + + file(REMOVE_RECURSE + ${framework_path}/Headers + ${framework_path}/Resources + ${framework_path}/${lib_name} + ${framework_path}/Versions/Current + ${framework_path}/Versions/5/Headers + ) + + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink 5 Current + WORKING_DIRECTORY ${framework_path}/Versions + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/${lib_name} ${lib_name} + WORKING_DIRECTORY ${framework_path} + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Resources Resources + WORKING_DIRECTORY ${framework_path} + ) + +endfunction() + +function(fixup_python_framework framework_path) + + file(REMOVE_RECURSE + ${framework_path}/Versions/Current + ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/Headers + ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/Python + ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/python@LY_PYTHON_VERSION_MAJOR_MINOR@/test + ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/python@LY_PYTHON_VERSION_MAJOR_MINOR@/site-packages/scipy/io/tests + ${framework_path}/Python + ${framework_path}/Resources + ${framework_path}/Headers + ) + + file(GLOB_RECURSE exe_file_list "${framework_path}/**/*.exe") + if(exe_file_list) + file(REMOVE_RECURSE ${exe_file_list}) + endif() + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink include/python@LY_PYTHON_VERSION_MAJOR_MINOR@m Headers + WORKING_DIRECTORY ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@ + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink @LY_PYTHON_VERSION_MAJOR_MINOR@ Current + WORKING_DIRECTORY ${framework_path}/Versions/ + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Python Python + WORKING_DIRECTORY ${framework_path} + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Headers Headers + WORKING_DIRECTORY ${framework_path} + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Resources Resources + WORKING_DIRECTORY ${framework_path} + ) + file(CHMOD ${framework_path}/Versions/Current/Python + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + ) + +endfunction() + +function(codesign_file file entitlement_file) + + if (NOT @LY_ENABLE_HARDENED_RUNTIME@) + return() + endif() + + if(EXISTS ${entitlement_file}) + + execute_process(COMMAND "/usr/bin/codesign" "--force" "--sign" "@LY_CODE_SIGN_IDENTITY@" "--deep" "-o" "runtime" "--timestamp" "--entitlements" "${entitlement_file}" "${file}" + TIMEOUT 300 + OUTPUT_VARIABLE codesign_out + RESULT_VARIABLE codesign_ret + ) + else() + execute_process(COMMAND "/usr/bin/codesign" "--force" "--sign" "@LY_CODE_SIGN_IDENTITY@" "--deep" "-o" "runtime" "--timestamp" "${file}" + TIMEOUT 300 + OUTPUT_VARIABLE codesign_out + RESULT_VARIABLE codesign_ret + ) + endif() + + if(NOT ${codesign_ret} EQUAL "0") + message(FATAL_ERROR "Codesign operation for ${file_path} returned ${codesign_ret} with message ${codesign_out}") + endif() + +endfunction() + +function(codesign_python_framework_binaries framework_path) + + if (NOT @LY_ENABLE_HARDENED_RUNTIME@) + return() + endif() + + # The codesign "--deep" flag will only codesign binaries in folders with specific names. + # We need to codesign all the binaries that the "--deep" flag will miss. + file(GLOB_RECURSE files + LIST_DIRECTORIES false + "${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/bin/**" + "${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/**" + "${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/Resources/**") + + foreach(file ${files}) + if(NOT EXISTS ${file}) + file(REMOVE ${file}) + continue() + endif() + cmake_path(SET path_var "${file}") + cmake_path(GET path_var EXTENSION LAST_ONLY extension) + set(should_codesign FALSE) + set(extension_skip_list ".dylib" ".so" ".7m") + if (NOT extension) + set(should_codesign TRUE) + elseif(extension IN_LIST extension_skip_list) + set(should_codesign TRUE) + endif() + if(${should_codesign}) + codesign_file("${file}" "@LY_ROOT_FOLDER@/python/Platform/Mac/PythonEntitlements.plist") + endif() + endforeach() + +endfunction() + +function(ly_copy source_file target_directory) + + if("${source_file}" MATCHES "\\.[Ff]ramework[^\\.]") + + # fixup origin to copy the whole Framework folder + string(REGEX REPLACE "(.*\\.[Ff]ramework).*" "\\1" source_file "${source_file}") + + endif() + get_filename_component(target_filename "${source_file}" NAME) + file(COPY "${source_file}" DESTINATION "${target_directory}" FILE_PERMISSIONS @LY_COPY_PERMISSIONS@ FOLLOW_SYMLINK_CHAIN) + + # Our Qt and Python frameworks aren't in the correct bundle format to be codesigned. + if("${target_filename}" MATCHES "(Qt[^.]+)\\.[Ff]ramework") + fixup_qt_framework(${CMAKE_MATCH_1} "${target_directory}/${target_filename}") + # For some Qt frameworks(QtCore), signing the bundle doesn't work because of bundle + # format issues(despite the fixes above). But once we've patched the framework above, there's + # only one executable that we need to sign so we can do it directly. + set(target_filename "${target_filename}/Versions/5/${CMAKE_MATCH_1}") + elseif("${target_filename}" MATCHES "Python.framework") + fixup_python_framework("${target_directory}/${target_filename}") + codesign_python_framework_binaries("${target_directory}/${target_filename}") + endif() + codesign_file("${target_directory}/${target_filename}" "none") + +endfunction() + +function(ly_download_and_codesign_sdk_python) + execute_process(COMMAND ${CMAKE_COMMAND} -DPAL_PLATFORM_NAME=Mac -DLY_3RDPARTY_PATH=${CMAKE_INSTALL_PREFIX}/python -P ${CMAKE_INSTALL_PREFIX}/python/get_python.cmake + WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX} + ) + fixup_python_framework(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework) + codesign_python_framework_binaries(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework) + codesign_file(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework @LY_ROOT_FOLDER@/python/Platform/Mac/PythonEntitlements.plist) +endfunction() + +function(ly_codesign_sdk) + codesign_file(${LY_INSTALL_PATH_ORIGINAL}/O3DE_SDK.app "none") +endfunction() + + diff --git a/cmake/Platform/Mac/Install_mac.cmake b/cmake/Platform/Mac/Install_mac.cmake index 8f07b1bfde..bdc2300131 100644 --- a/cmake/Platform/Mac/Install_mac.cmake +++ b/cmake/Platform/Mac/Install_mac.cmake @@ -6,6 +6,8 @@ # # +include(cmake/Platform/Common/Install_common.cmake) + # This is used to generate a setreg file which will be placed inside the bundle # for targets that request it(eg. AssetProcessor/Editor). This is the relative path # to the bundle from the installed engine's root. This will be used to compute the @@ -16,7 +18,7 @@ set(installed_binaries_path_template [[ "AzCore": { "Runtime": { "FilePaths": { - "InstalledBinariesFolder": "bin/Mac/$" + "InstalledBinariesFolder": "@runtime_output_directory@" } } } @@ -24,15 +26,20 @@ set(installed_binaries_path_template [[ }]] ) -unset(target_conf_dir) -foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) - string(TOUPPER ${conf} UCONF) - string(APPEND target_conf_dir $<$:${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${UCONF}}>) -endforeach() +# This setreg file will be used by all of our installed app bundles to locate installed +# runtime dependencies. It contains the path to binary install directory relative to +# the installed engine root. +string(CONFIGURE "${installed_binaries_path_template}" configured_setreg_file) +file(GENERATE + OUTPUT ${CMAKE_BINARY_DIR}/runtime_install/$/BinariesInstallPath.setreg + CONTENT "${configured_setreg_file}" +) -set(installed_binaries_setreg_path ${target_conf_dir}/Registry/installed_binaries_path.setreg) - -file(GENERATE OUTPUT ${installed_binaries_setreg_path} CONTENT ${installed_binaries_path_template}) +# ly_install_run_script isn't defined yet so we use install(SCRIPT) directly. +# This needs to be done here because it needs to update the install prefix +# before cmake does anything else in the install process. +configure_file(${LY_ROOT_FOLDER}/cmake/Platform/Mac/PreInstallSteps_mac.cmake.in ${CMAKE_BINARY_DIR}/runtime_install/PreInstallSteps_mac.cmake @ONLY) +install(SCRIPT ${CMAKE_BINARY_DIR}/runtime_install/PreInstallSteps_mac.cmake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) #! ly_install_target_override: Mac specific target installation function(ly_install_target_override) @@ -70,33 +77,62 @@ function(ly_install_target_override) COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + set(install_relative_binaries_path "${ly_platform_install_target_RUNTIME_DIR}/${ly_platform_install_target_RUNTIME_SUBDIR}") + if (${is_bundle}) set_property(TARGET ${ly_platform_install_target_TARGET} PROPERTY RESOURCE ${cached_resources_dir}) + set(runtime_output_filename "$.app") + else() + set(runtime_output_filename "$") + endif() + + get_target_property(target_type ${ly_platform_install_target_TARGET} TYPE) + if(target_type IN_LIST LY_TARGET_TYPES_WITH_RUNTIME_OUTPUTS) + get_target_property(entitlement_file ${ly_platform_install_target_TARGET} ENTITLEMENT_FILE_PATH) + if (NOT entitlement_file) + set(entitlement_file "none") + endif() + + ly_file_read(${LY_ROOT_FOLDER}/cmake/Platform/Mac/runtime_install_mac.cmake.in template_file) + string(CONFIGURE "${template_file}" configured_template_file @ONLY) + file(GENERATE + OUTPUT ${CMAKE_BINARY_DIR}/runtime_install/$/${ly_platform_install_target_TARGET}.cmake + CONTENT "${configured_template_file}" + ) endif() -endfunction() - -#! ly_install_add_install_path_setreg: Adds the install path setreg file as a dependency -function(ly_install_add_install_path_setreg NAME) - set_property(TARGET ${NAME} APPEND PROPERTY INTERFACE_LY_TARGET_FILES "${installed_binaries_setreg_path}\nRegistry") endfunction() #! ly_install_code_function_override: Mac specific copy function to handle frameworks function(ly_install_code_function_override) - install(CODE -"function(ly_copy source_file target_directory) - if(\"\${source_file}\" MATCHES \"\\\\.[Ff]ramework[^\\\\.]\") - - # fixup origin to copy the whole Framework folder - string(REGEX REPLACE \"(.*\\\\.[Ff]ramework).*\" \"\\\\1\" source_file \"\${source_file}\") - get_filename_component(target_filename \"\${source_file}\" NAME) - - endif() - file(COPY \"\${source_file}\" DESTINATION \"\${target_directory}\" FILE_PERMISSIONS ${LY_COPY_PERMISSIONS}) -endfunction()" - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - ) + configure_file(${LY_ROOT_FOLDER}/cmake/Platform/Mac/InstallUtils_mac.cmake.in ${CMAKE_BINARY_DIR}/runtime_install/InstallUtils_mac.cmake @ONLY) + ly_install_run_script(${CMAKE_BINARY_DIR}/runtime_install/InstallUtils_mac.cmake) + +endfunction() + +#! ly_post_install_steps: Any additional platform specific post install steps +function(ly_post_install_steps) + + # On Mac, after CMake is done installing, the code signatures on all our built binaries will be invalid. + # We need to now codesign each dynamic library, executable, and app bundle. It's specific to each target + # because there could potentially be different entitlements for different targets. + get_property(all_targets GLOBAL PROPERTY LY_ALL_TARGETS) + foreach(alias_target IN LISTS all_targets) + ly_de_alias_target(${alias_target} target) + # Exclude targets that dont produce runtime outputs + get_target_property(target_type ${target} TYPE) + if(NOT target_type IN_LIST LY_TARGET_TYPES_WITH_RUNTIME_OUTPUTS) + continue() + endif() + + ly_install_run_script(${CMAKE_BINARY_DIR}/runtime_install/$/${target}.cmake) + endforeach() + + ly_install_run_code(" + ly_download_and_codesign_sdk_python() + ly_codesign_sdk() + set(CMAKE_INSTALL_PREFIX ${LY_INSTALL_PATH_ORIGINAL}) + ") endfunction() -include(cmake/Platform/Common/Install_common.cmake) diff --git a/cmake/Platform/Mac/LYWrappers_mac.cmake b/cmake/Platform/Mac/LYWrappers_mac.cmake index 578f6fe041..245d3bb7c1 100644 --- a/cmake/Platform/Mac/LYWrappers_mac.cmake +++ b/cmake/Platform/Mac/LYWrappers_mac.cmake @@ -6,6 +6,16 @@ # # +set(LY_ENABLE_HARDENED_RUNTIME OFF CACHE BOOL "Enable hardened runtime capability for Mac builds. This should be ON when building the engine for notarization/distribution.") + +define_property(TARGET PROPERTY ENTITLEMENT_FILE_PATH + BRIEF_DOCS "Path to the entitlement file" + FULL_DOCS [[ + On MacOS, entitlements are used to grant certain privileges + to applications at runtime. Use this propery to specify the + path to a .plist file containing entitlements. + ]] +) function(ly_apply_platform_properties target) @@ -14,6 +24,18 @@ function(ly_apply_platform_properties target) INSTALL_RPATH "@executable_path/;@executable_path/../Frameworks" ) + get_property(is_imported TARGET ${target} PROPERTY IMPORTED) + if((NOT is_imported) AND (LY_ENABLE_HARDENED_RUNTIME)) + get_property(target_type TARGET ${target} PROPERTY TYPE) + set(runtime_types_list "MODULE_LIBRARY" "SHARED_LIBRARY" "EXECUTABLE") + if (target_type IN_LIST runtime_types_list) + set_target_properties(${target} PROPERTIES + XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES + XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS NO + ) + endif() + endif() + endfunction() diff --git a/cmake/Platform/Mac/PAL_mac.cmake b/cmake/Platform/Mac/PAL_mac.cmake index b415daf44a..561ce86570 100644 --- a/cmake/Platform/Mac/PAL_mac.cmake +++ b/cmake/Platform/Mac/PAL_mac.cmake @@ -37,5 +37,12 @@ endif() # Set the default asset type for deployment set(LY_ASSET_DEPLOY_ASSET_TYPE "mac" CACHE STRING "Set the asset type for deployment.") +# Set the deployment target for MacOS +set(LY_MAC_DEPLOYMENT_TARGET "11.0" CACHE STRING "Mac Deployment Target") +set(CMAKE_OSX_DEPLOYMENT_TARGET ${LY_MAC_DEPLOYMENT_TARGET}) + # Set the python cmd tool ly_set(LY_PYTHON_CMD ${CMAKE_CURRENT_SOURCE_DIR}/python/python.sh) + +# Only x86_64 is currently supported on Mac +ly_set(CMAKE_OSX_ARCHITECTURES "x86_64") diff --git a/cmake/Platform/Mac/PreInstallSteps_mac.cmake.in b/cmake/Platform/Mac/PreInstallSteps_mac.cmake.in new file mode 100644 index 0000000000..5033aadd6e --- /dev/null +++ b/cmake/Platform/Mac/PreInstallSteps_mac.cmake.in @@ -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 +# +# + +cmake_minimum_required(VERSION 3.20) + +# The O3DE SDK will be shipped as an app bundle. So we create an O3DE_SDK.app directory +# and install SDK into the app's Contents/Engine directory. +set(LY_INSTALL_PATH_ORIGINAL ${CMAKE_INSTALL_PREFIX}) + +file(INSTALL @LY_ROOT_FOLDER@/Code/Tools/BundleLauncher/info.plist + DESTINATION ${CMAKE_INSTALL_PREFIX}/O3DE_SDK.app/Contents +) + +# This SDK launcher will install python site-packages and then launch the ProjectManager +# when a user double clicks on the SDK from Finder. We're only going to need one version +# of the SDK launcher regardless of what configs of the engine are installed. +if (EXISTS @CMAKE_BINARY_DIR@/bin/profile/O3DE_SDK) + set(sdk_launcher_config profile) +elseif (EXISTS @CMAKE_BINARY_DIR@/bin/debug/O3DE_SDK) + set(sdk_launcher_config debug) +elseif (EXISTS @CMAKE_BINARY_DIR@/bin/release/O3DE_SDK) + set(sdk_launcher_config release) +endif() +file(INSTALL @CMAKE_BINARY_DIR@/bin/${sdk_launcher_config}/O3DE_SDK + DESTINATION ${CMAKE_INSTALL_PREFIX}/O3DE_SDK.app/Contents/MacOS + USE_SOURCE_PERMISSIONS +) +file(INSTALL @CMAKE_BINARY_DIR@/runtime_install/${sdk_launcher_config}/BinariesInstallPath.setreg + DESTINATION ${CMAKE_INSTALL_PREFIX}/O3DE_SDK.app/Contents/MacOS/Registry +) + +# We need to update the CMAKE_INSTALL_PREFIX so that the engine is installed inside the app bundle. +file(MAKE_DIRECTORY ${CMAKE_INSTALL_PREFIX}/O3DE_SDK.app/Contents/Engine) +set(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/O3DE_SDK.app/Contents/Engine) \ No newline at end of file diff --git a/cmake/Platform/Mac/runtime_dependencies_mac.cmake.in b/cmake/Platform/Mac/runtime_dependencies_mac.cmake.in index d65578f82a..11551f608f 100644 --- a/cmake/Platform/Mac/runtime_dependencies_mac.cmake.in +++ b/cmake/Platform/Mac/runtime_dependencies_mac.cmake.in @@ -73,8 +73,8 @@ function(ly_copy source_file target_directory) return() endif() - # fixup the destination so it ends up in Contents/Plugins - string(REGEX REPLACE "(.*\\.app/Contents)/MacOS" "\\1/plugins" target_directory "${target_directory}") + # fixup the destination so it ends up in Contents/PlugIns + string(REGEX REPLACE "(.*\\.app/Contents)/MacOS" "\\1/PlugIns" target_directory "${target_directory}") set(local_plugin_dirs ${plugin_dirs}) list(APPEND local_plugin_dirs "${target_directory}") @@ -212,7 +212,6 @@ if(@target_file_dir@ MATCHES ".app/Contents/MacOS") file(REMOVE_RECURSE ${remove_file_list}) endif() - endif() else() # Non-bundle case diff --git a/cmake/Platform/Mac/runtime_install_mac.cmake.in b/cmake/Platform/Mac/runtime_install_mac.cmake.in new file mode 100644 index 0000000000..65b1ede77b --- /dev/null +++ b/cmake/Platform/Mac/runtime_install_mac.cmake.in @@ -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 +# +# + +cmake_path(SET file_path "${CMAKE_INSTALL_PREFIX}/@install_relative_binaries_path@/@runtime_output_filename@") +cmake_path(GET file_path EXTENSION LAST_ONLY file_ext) + +if(file_ext STREQUAL .app) + + file(INSTALL @CMAKE_BINARY_DIR@/runtime_install/$/BinariesInstallPath.setreg + DESTINATION ${file_path}/Contents/MacOS/Registry + ) + + if(EXISTS "${file_path}/Contents/Frameworks/Python.framework") + codesign_python_framework_binaries("${file_path}/Contents/Frameworks/Python.framework") + endif() + +else() + + find_program(LY_INSTALL_NAME_TOOL install_name_tool) + if (NOT LY_INSTALL_NAME_TOOL) + message(FATAL_ERROR "Unable to locate 'install_name_tool'") + endif() + + execute_process(COMMAND + ${LY_INSTALL_NAME_TOOL} -add_rpath @loader_path ${file_path}) + +endif() + +codesign_file("${file_path}" "@entitlement_file@") diff --git a/cmake/Platform/Windows/platform_windows_files.cmake b/cmake/Platform/Windows/platform_windows_files.cmake index a1e26bd992..fcc47ab6eb 100644 --- a/cmake/Platform/Windows/platform_windows_files.cmake +++ b/cmake/Platform/Windows/platform_windows_files.cmake @@ -7,10 +7,12 @@ # set(FILES - ../Common/Directory.Build.props - ../Common/VisualStudio_common.cmake ../Common/Configurations_common.cmake ../Common/MSVC/Configurations_msvc.cmake + ../Common/MSVC/CodeAnalysis.ruleset + ../Common/MSVC/Directory.Build.props + ../Common/MSVC/TestProject.props + ../Common/MSVC/VisualStudio_common.cmake ../Common/Install_common.cmake ../Common/LYWrappers_default.cmake ../Common/TargetIncludeSystemDirectories_unsupported.cmake diff --git a/cmake/Platform/iOS/Toolchain_ios.cmake b/cmake/Platform/iOS/Toolchain_ios.cmake index 49f0b23461..719c492ebb 100644 --- a/cmake/Platform/iOS/Toolchain_ios.cmake +++ b/cmake/Platform/iOS/Toolchain_ios.cmake @@ -13,7 +13,7 @@ set(CMAKE_OSX_ARCHITECTURES arm64) set(LY_IOS_CODE_SIGNING_IDENTITY "iPhone Developer" CACHE STRING "iPhone Developer") -set(LY_IOS_DEPLOYMENT_TARGET "13.0" CACHE STRING "iOS Deployment Target") +set(LY_IOS_DEPLOYMENT_TARGET "14.0" CACHE STRING "iOS Deployment Target") set(LY_IOS_DEVELOPMENT_TEAM "CF9TGN983S" CACHE STRING "The development team ID") diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index 7f2ac6a4fd..d3f4b33b03 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -147,14 +147,24 @@ foreach(project ${LY_PROJECTS}) cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE runtime_output_directory) set(install_engine_pak_template [=[ if("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$") - set(install_output_folder "${CMAKE_INSTALL_PREFIX}/@runtime_output_directory@/@PAL_PLATFORM_NAME@/${CMAKE_INSTALL_CONFIG_NAME}") - message(STATUS "Generating ${install_output_folder}/Engine.pak from @full_directory_path@/Cache") + set(install_output_folder "${CMAKE_INSTALL_PREFIX}/@runtime_output_directory@/@PAL_PLATFORM_NAME@/${CMAKE_INSTALL_CONFIG_NAME}/@LY_BUILD_PERMUTATION@") + if(NOT DEFINED LY_ASSET_DEPLOY_ASSET_TYPE) + set(LY_ASSET_DEPLOY_ASSET_TYPE @LY_ASSET_DEPLOY_ASSET_TYPE@) + endif() + message(STATUS "Generating ${install_output_folder}/Engine.pak from @full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") file(MAKE_DIRECTORY "${install_output_folder}") - file(ARCHIVE_CREATE OUTPUT "${install_output_folder}/Engine.pak" - PATHS "@full_directory_path@/Cache" - FORMAT zip - ) - message(STATUS "${install_output_folder}/Engine.pak generated") + cmake_path(SET cache_product_path "@full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") + file(GLOB product_assets "${cache_product_path}/*") + if(product_assets) + execute_process( + COMMAND ${CMAKE_COMMAND} -E tar "cf" "${install_output_folder}/Engine.pak" --format=zip -- ${product_assets} + WORKING_DIRECTORY "${cache_product_path}" + RESULT_VARIABLE archive_creation_result + ) + if(archive_creation_result EQUAL 0) + message(STATUS "${install_output_folder}/Engine.pak generated") + endif() + endif() endif() ]=]) string(CONFIGURE "${install_engine_pak_template}" install_engine_pak_code @ONLY) diff --git a/python/Platform/Mac/PythonEntitlements.plist b/python/Platform/Mac/PythonEntitlements.plist new file mode 100644 index 0000000000..ed4892befa --- /dev/null +++ b/python/Platform/Mac/PythonEntitlements.plist @@ -0,0 +1,10 @@ + + + + + com.apple.security.cs.disable-library-validation + + com.apple.security.cs.allow-unsigned-executable-memory + + + diff --git a/python/get_python.sh b/python/get_python.sh index 248a0790fc..e8d35a311e 100755 --- a/python/get_python.sh +++ b/python/get_python.sh @@ -30,7 +30,8 @@ cd $DIR python_exitcode=$? if [ $python_exitcode == 0 ]; then echo get_python.sh: Python is already downloaded: $(./python.sh --version) - $DIR/pip.sh install -r $DIR/requirements.txt --quiet --disable-pip-version-check + $DIR/pip.sh install -r $DIR/requirements.txt --disable-pip-version-check --no-warn-script-location + $DIR/pip.sh install -e $DIR/../scripts/o3de --no-deps --disable-pip-version-check --no-warn-script-location exit 0 fi if [[ "$OSTYPE" = *"darwin"* ]]; @@ -73,5 +74,6 @@ if [ $retVal -ne 0 ]; then fi echo installing via pip... -$DIR/pip.sh install -r $DIR/requirements.txt --disable-pip-version-check +$DIR/pip.sh install -r $DIR/requirements.txt --disable-pip-version-check --no-warn-script-location +$DIR/pip.sh install -e $DIR/../scripts/o3de --no-deps --disable-pip-version-check --no-warn-script-location exit $? diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 5bc4b919fb..5fe19ddf46 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -9,6 +9,7 @@ import groovy.json.JsonOutput PIPELINE_CONFIG_FILE = 'scripts/build/Jenkins/lumberyard.json' INCREMENTAL_BUILD_SCRIPT_PATH = 'scripts/build/bootstrap/incremental_build_util.py' +PIPELINE_RETRY_ATTEMPTS = 3 EMPTY_JSON = readJSON text: '{}' @@ -502,64 +503,75 @@ def CreateTeardownStage(Map environmentVars) { def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVars, String branchName, String pipelineName, String repositoryName, String projectName, boolean onlyMountEBSVolume = false) { def nodeLabel = envVars['NODE_LABEL'] return { - node("${nodeLabel}") { - if(isUnix()) { // Has to happen inside a node - envVars['IS_UNIX'] = 1 - } - withEnv(GetEnvStringList(envVars)) { - def build_job_name = build_job.key - try { - CreateSetupStage(pipelineConfig, snapshot, repositoryName, projectName, pipelineName, branchName, platform.key, build_job.key, envVars, onlyMountEBSVolume).call() + def currentResult = '' + def currentException = '' + retry(PIPELINE_RETRY_ATTEMPTS) { + node("${nodeLabel}") { + if(isUnix()) { // Has to happen inside a node + envVars['IS_UNIX'] = 1 + } + withEnv(GetEnvStringList(envVars)) { + def build_job_name = build_job.key + try { + CreateSetupStage(pipelineConfig, snapshot, repositoryName, projectName, pipelineName, branchName, platform.key, build_job.key, envVars, onlyMountEBSVolume).call() - if(build_job.value.steps) { //this is a pipe with many steps so create all the build stages - build_job.value.steps.each { build_step -> - build_job_name = build_step - envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, platform.value.build_types[build_step].PIPELINE_ENV ?: EMPTY_JSON, pipelineName) - try { - CreateBuildStage(pipelineConfig, platform.key, build_step, envVars).call() + if(build_job.value.steps) { //this is a pipe with many steps so create all the build stages + build_job.value.steps.each { build_step -> + build_job_name = build_step + envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, platform.value.build_types[build_step].PIPELINE_ENV ?: EMPTY_JSON, pipelineName) + try { + CreateBuildStage(pipelineConfig, platform.key, build_step, envVars).call() + } + catch (Exception e) { + if (envVars['NONBLOCKING_STEP']?.toBoolean()) { + unstable(message: "Build step ${build_step} failed but it's a non-blocking step in build job ${build_job.key}") + } else { + throw e + } + } } - catch (Exception e) { - if (envVars['NONBLOCKING_STEP']?.toBoolean()) { - unstable(message: "Build step ${build_step} failed but it's a non-blocking step in build job ${build_job.key}") - } - else { - error "FAILURE: ${e}" - } + } else { + CreateBuildStage(pipelineConfig, platform.key, build_job.key, envVars).call() + } + } + catch(Exception e) { + if (e instanceof org.jenkinsci.plugins.workflow.steps.FlowInterruptedException) { + def causes = e.getCauses().toString() + if (causes.contains('RemovedNodeCause')) { + error "Node disconnected during build: ${e}" // Error raised to retry stage on a new node } } - } else { - CreateBuildStage(pipelineConfig, platform.key, build_job.key, envVars).call() + // All other errors will be raised outside the retry block + currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE' + currentException = e.toString() } - } - catch(Exception e) { - // https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/Result.java - // {SUCCESS,UNSTABLE,FAILURE,NOT_BUILT,ABORTED} - def currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE' - if (currentResult == 'FAILURE') { - currentBuild.result = 'FAILURE' - error "FAILURE: ${e}" - } else if (currentResult == 'UNSTABLE') { - currentBuild.result = 'UNSTABLE' - unstable(message: "UNSTABLE: ${e}") + finally { + def params = platform.value.build_types[build_job_name].PARAMETERS + if (env.MARS_REPO && params && params.containsKey('TEST_METRICS') && params.TEST_METRICS == 'True') { + def output_directory = params.OUTPUT_DIRECTORY + def configuration = params.CONFIGURATION + CreateTestMetricsStage(pipelineConfig, branchName, envVars, build_job_name, output_directory, configuration).call() + } + if (params && params.containsKey('TEST_RESULTS') && params.TEST_RESULTS == 'True') { + CreateExportTestResultsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call() + } + if (params && params.containsKey('TEST_SCREENSHOTS') && params.TEST_SCREENSHOTS == 'True' && currentResult == 'FAILURE') { + CreateExportTestScreenshotsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call() + } + CreateTeardownStage(envVars).call() } } - finally { - def params = platform.value.build_types[build_job_name].PARAMETERS - if (env.MARS_REPO && params && params.containsKey('TEST_METRICS') && params.TEST_METRICS == 'True') { - def output_directory = params.OUTPUT_DIRECTORY - def configuration = params.CONFIGURATION - CreateTestMetricsStage(pipelineConfig, branchName, envVars, build_job_name, output_directory, configuration).call() - } - if (params && params.containsKey('TEST_RESULTS') && params.TEST_RESULTS == 'True') { - CreateExportTestResultsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call() - } - if (params && params.containsKey('TEST_SCREENSHOTS') && params.TEST_SCREENSHOTS == 'True' && currentResult == 'FAILURE') { - CreateExportTestScreenshotsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call() - } - CreateTeardownStage(envVars).call() - } } } + // https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/Result.java + // {SUCCESS,UNSTABLE,FAILURE,NOT_BUILT,ABORTED} + if (currentResult == 'FAILURE') { + currentBuild.result = 'FAILURE' + error "FAILURE: ${currentException}" + } else if (currentResult == 'UNSTABLE') { + currentBuild.result = 'UNSTABLE' + unstable(message: "UNSTABLE: ${currentException}") + } } } diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 02bd01038a..3848e6f980 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -360,7 +360,7 @@ "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"!WIX! \"", - "EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=https://dkb1uj4hs9ikv.cloudfront.net -DLY_INSTALLER_LICENSE_URL=https://www.o3debinaries.org/license -DLY_INSTALLER_3RD_PARTY_LICENSE_URL=https://dkb1uj4hs9ikv.cloudfront.net/SPDX-Licenses.txt", + "EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=https://www.o3debinaries.org -DLY_INSTALLER_LICENSE_URL=https://www.o3debinaries.org/license", "CPACK_BUCKET": "spectra-prism-staging-us-west-2", "CMAKE_LY_PROJECTS": "", "CMAKE_TARGET": "ALL_BUILD", diff --git a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt index 21c4755a2e..2cbbe58b38 100644 --- a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt +++ b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt @@ -13,6 +13,8 @@ libxcb-xinput0 # For Qt plugins at runtime libfontconfig1-dev # For Qt plugins at runtime libcurl4-openssl-dev # For HttpRequestor libsdl2-dev # for WWise/Audio -libxkbcommon-dev +libxcb-xkb-dev # For xcb keyboard input +libxkbcommon-x11-dev # For xcb keyboard input +libxkbcommon-dev # For xcb keyboard input zlib1g-dev mesa-common-dev
Material Slot %1
Entity %1
Material Slot %1
Material %1