diff --git a/.clang-format b/.clang-format index 565f28130e..04e0284f97 100644 --- a/.clang-format +++ b/.clang-format @@ -46,7 +46,7 @@ SortIncludes: true SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true -SpaceBeforeCpp11BracedList: true +SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements diff --git a/.gitignore b/.gitignore index c3af907e97..664680c5bf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ __pycache__ AssetProcessorTemp/** [Bb]uild/** [Cc]ache/ +/install/ Editor/EditorEventLog.xml Editor/EditorLayout.xml **/*egg-info/** @@ -19,3 +20,4 @@ _savebackup/ TestResults/** *.swatches /imgui.ini +/scripts/project_manager/logs/ diff --git a/Assets/Engine/SeedAssetList.seed b/Assets/Engine/SeedAssetList.seed index 45a02f7682..77ec509721 100644 --- a/Assets/Engine/SeedAssetList.seed +++ b/Assets/Engine/SeedAssetList.seed @@ -67,106 +67,98 @@ - + - + - + - + - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -264,109 +256,101 @@ - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -387,498 +371,474 @@ - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -896,14 +856,6 @@ - - - - - - - - @@ -928,29 +880,13 @@ - - - - - - - - - - - - - - - - - + - + @@ -1451,146 +1387,146 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1699,42 +1635,42 @@ - + - + - + - + - + - + - + - + - + - + diff --git a/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_diff.tif.exportsettings b/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_diff.tif.exportsettings index 5c4c862583..b65133fbb0 100644 --- a/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_diff.tif.exportsettings +++ b/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /preset=AlbedoWithGenericAlpha /reduce="es3:2,ios:2,osx_gl:0,pc:0,provo:0" \ No newline at end of file +/autooptimizefile=0 /preset=AlbedoWithGenericAlpha /reduce="android:2,ios:2,mac:0,pc:0,provo:0" \ No newline at end of file diff --git a/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_sss.tif.exportsettings b/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_sss.tif.exportsettings index 441a11bc68..e8da408b36 100644 --- a/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_sss.tif.exportsettings +++ b/AutomatedTesting/Assets/Objects/Foliage/Textures/grass_atlas_sss.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /preset=Albedo /reduce="es3:3,ios:3,osx_gl:0,pc:0,provo:0" \ No newline at end of file +/autooptimizefile=0 /preset=Albedo /reduce="android:3,ios:3,mac:0,pc:0,provo:0" \ No newline at end of file diff --git a/AutomatedTesting/EngineFinder.cmake b/AutomatedTesting/EngineFinder.cmake index 1fdcef2b56..fbbe3d8cfe 100644 --- a/AutomatedTesting/EngineFinder.cmake +++ b/AutomatedTesting/EngineFinder.cmake @@ -20,31 +20,49 @@ if(json_error) message(FATAL_ERROR "Unable to read key 'engine' from 'project.json', error: ${json_error}") endif() -# Read the list of paths from ~.o3de/o3de_manifest.json -file(TO_CMAKE_PATH "$ENV{USERPROFILE}" home_directory) # Windows -if((NOT home_directory) OR (NOT EXISTS ${home_directory})) - file(TO_CMAKE_PATH "$ENV{HOME}" home_directory)# Unix +if(DEFINED ENV{USERPROFILE} AND EXISTS $ENV{USERPROFILE}) + set(manifest_path $ENV{USERPROFILE}/.o3de/o3de_manifest.json) # Windows +else() + set(manifest_path $ENV{HOME}/.o3de/o3de_manifest.json) # Unix endif() -if (NOT home_directory) - message(FATAL_ERROR "Cannot find user home directory, the o3de manifest cannot be found") -endif() -# Set manifest path to path in the user home directory -set(manifest_path ${home_directory}/.o3de/o3de_manifest.json) - +# Read the ~/.o3de/o3de_manifest.json file and look through the 'engines_path' object. +# Find a key that matches LY_ENGINE_NAME_TO_USE and use that as the engine path. if(EXISTS ${manifest_path}) file(READ ${manifest_path} manifest_json) - string(JSON engines_count ERROR_VARIABLE json_error LENGTH ${manifest_json} engines) + + string(JSON engines_path_count ERROR_VARIABLE json_error LENGTH ${manifest_json} engines_path) if(json_error) - message(FATAL_ERROR "Unable to read key 'engines' from '${manifest_path}', error: ${json_error}") + message(FATAL_ERROR "Unable to read key 'engines_path' from '${manifest_path}', error: ${json_error}") + endif() + + string(JSON engines_path_type ERROR_VARIABLE json_error TYPE ${manifest_json} engines_path) + if(json_error OR NOT ${engines_path_type} STREQUAL "OBJECT") + message(FATAL_ERROR "Type of 'engines_path' in '${manifest_path}' is not a JSON Object, error: ${json_error}") endif() - math(EXPR engines_count "${engines_count}-1") - foreach(engine_path_index RANGE ${engines_count}) - string(JSON engine_path ERROR_VARIABLE json_error GET ${manifest_json} engines ${engine_path_index}) - if(${json_error}) - message(FATAL_ERROR "Unable to read engines[${engine_path_index}] '${manifest_path}', error: ${json_error}") + math(EXPR engines_path_count "${engines_path_count}-1") + foreach(engine_path_index RANGE ${engines_path_count}) + string(JSON engine_name ERROR_VARIABLE json_error MEMBER ${manifest_json} engines_path ${engine_path_index}) + if(json_error) + message(FATAL_ERROR "Unable to read 'engines_path/${engine_path_index}' from '${manifest_path}', error: ${json_error}") + endif() + + if(LY_ENGINE_NAME_TO_USE STREQUAL engine_name) + string(JSON engine_path ERROR_VARIABLE json_error GET ${manifest_json} engines_path ${engine_name}) + if(json_error) + message(FATAL_ERROR "Unable to read value from 'engines_path/${engine_name}', error: ${json_error}") + endif() + + if(engine_path) + list(APPEND CMAKE_MODULE_PATH "${engine_path}/cmake") + break() + endif() endif() - list(APPEND CMAKE_MODULE_PATH "${engine_path}/cmake") endforeach() +else() + # If the user is passing CMAKE_MODULE_PATH we assume thats where we will find the engine + if(NOT CMAKE_MODULE_PATH) + message(FATAL_ERROR "Engine registration is required before configuring a project. Please register an engine by running 'scripts/o3de register --this-engine'") + endif() endif() diff --git a/AutomatedTesting/Gem/Code/CMakeLists.txt b/AutomatedTesting/Gem/Code/CMakeLists.txt index 2bcc304bde..548aa51ad1 100644 --- a/AutomatedTesting/Gem/Code/CMakeLists.txt +++ b/AutomatedTesting/Gem/Code/CMakeLists.txt @@ -28,30 +28,41 @@ ly_add_target( Gem::Atom_AtomBridge.Static ) +# if enabled, AutomatedTesting is used by all kinds of applications +ly_create_alias(NAME AutomatedTesting.Builders NAMESPACE Gem TARGETS Gem::AutomatedTesting) +ly_create_alias(NAME AutomatedTesting.Tools NAMESPACE Gem TARGETS Gem::AutomatedTesting) +ly_create_alias(NAME AutomatedTesting.Clients NAMESPACE Gem TARGETS Gem::AutomatedTesting) +ly_create_alias(NAME AutomatedTesting.Servers NAMESPACE Gem TARGETS Gem::AutomatedTesting) + ################################################################################ # Gem dependencies ################################################################################ -ly_add_project_dependencies( - PROJECT_NAME - AutomatedTesting - TARGETS - AutomatedTesting.GameLauncher - DEPENDENCIES_FILES - runtime_dependencies.cmake - ${pal_dir}/runtime_dependencies.cmake -) -if(PAL_TRAIT_BUILD_HOST_TOOLS) - ly_add_project_dependencies( - PROJECT_NAME - AutomatedTesting - TARGETS - AssetBuilder - AssetProcessor - AssetProcessorBatch - Editor - DEPENDENCIES_FILES - tool_dependencies.cmake - ${pal_dir}/tool_dependencies.cmake - ) +# The GameLauncher uses "Clients" gem variants: +ly_enable_gems(PROJECT_NAME AutomatedTesting GEM_FILE enabled_gems.cmake + TARGETS AutomatedTesting.GameLauncher + VARIANTS Clients) + +# If we build a server, then apply the gems to the server +if(PAL_TRAIT_BUILD_SERVER_SUPPORTED) + # if we're making a server, then add the "Server" gem variants to it: + ly_enable_gems(PROJECT_NAME AutomatedTesting GEM_FILE enabled_gems.cmake + TARGETS AutomatedTesting.ServerLauncher + VARIANTS Servers) + + set_property(GLOBAL APPEND PROPERTY LY_LAUNCHER_SERVER_PROJECTS AutomatedTesting) +endif() + +if (PAL_TRAIT_BUILD_HOST_TOOLS) + # The Editor uses "Tools" gem variants: + ly_enable_gems( + PROJECT_NAME AutomatedTesting GEM_FILE enabled_gems.cmake + TARGETS Editor + VARIANTS Tools) + + # The pipeline tools use "Builders" gem variants: + ly_enable_gems( + PROJECT_NAME AutomatedTesting GEM_FILE enabled_gems.cmake + TARGETS AssetBuilder AssetProcessor AssetProcessorBatch + VARIANTS Builders) endif() diff --git a/AutomatedTesting/Gem/Code/enabled_gems.cmake b/AutomatedTesting/Gem/Code/enabled_gems.cmake new file mode 100644 index 0000000000..d99d17b55e --- /dev/null +++ b/AutomatedTesting/Gem/Code/enabled_gems.cmake @@ -0,0 +1,58 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(ENABLED_GEMS + ImGui + ScriptEvents + ExpressionEvaluation + Gestures + CertificateManager + DebugDraw + SceneProcessing + GraphCanvas + InAppPurchases + AutomatedTesting + EditorPythonBindings + QtForPython + PythonAssetBuilder + Metastream + AudioSystem + Camera + EMotionFX + PhysX + CameraFramework + StartingPointMovement + StartingPointCamera + ScriptCanvas + ScriptCanvasPhysics + ScriptCanvasTesting + LyShineExamples + StartingPointInput + PhysXDebug + WhiteBox + FastNoise + SurfaceData + GradientSignal + Vegetation + GraphModel + LandscapeCanvas + NvCloth + Blast + Maestro + TextureAtlas + LmbrCentral + LyShine + HttpRequestor + Atom_AtomBridge + AWSCore + AWSClientAuth + AWSMetrics +) diff --git a/AutomatedTesting/Gem/Code/runtime_dependencies.cmake b/AutomatedTesting/Gem/Code/runtime_dependencies.cmake deleted file mode 100644 index 280c25bcf7..0000000000 --- a/AutomatedTesting/Gem/Code/runtime_dependencies.cmake +++ /dev/null @@ -1,48 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the License). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an AS IS BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -# Extracted from Game -set(GEM_DEPENDENCIES - Gem::Maestro - Gem::TextureAtlas - Gem::LmbrCentral - Gem::LyShine - Gem::HttpRequestor - Gem::ScriptEvents - Gem::ExpressionEvaluation - Gem::Gestures - Gem::CertificateManager - Gem::DebugDraw - Gem::AudioSystem - Gem::InAppPurchases - Gem::AutomatedTesting - Gem::Metastream - Gem::Camera - Gem::EMotionFX - Gem::PhysX - Gem::CameraFramework - Gem::StartingPointMovement - Gem::StartingPointCamera - Gem::ScriptCanvas - Gem::ImGui - Gem::LyShineExamples - Gem::StartingPointInput - Gem::ScriptCanvasPhysics - Gem::PhysXDebug - Gem::WhiteBox - Gem::FastNoise - Gem::SurfaceData - Gem::GradientSignal - Gem::Vegetation - Gem::Atom_AtomBridge - Gem::NvCloth - Gem::Blast -) diff --git a/AutomatedTesting/Gem/Code/tool_dependencies.cmake b/AutomatedTesting/Gem/Code/tool_dependencies.cmake deleted file mode 100644 index 1d70c02b1c..0000000000 --- a/AutomatedTesting/Gem/Code/tool_dependencies.cmake +++ /dev/null @@ -1,60 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the License). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an AS IS BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -# Extracted from Editor.xml -set(GEM_DEPENDENCIES - Gem::Maestro.Editor - Gem::TextureAtlas.Editor - Gem::LmbrCentral.Editor - Gem::LyShine.Editor - Gem::HttpRequestor - Gem::ScriptEvents.Editor - Gem::ExpressionEvaluation - Gem::Gestures - Gem::CertificateManager - Gem::DebugDraw.Editor - Gem::SceneProcessing.Editor - Gem::GraphCanvas.Editor - Gem::InAppPurchases - Gem::AutomatedTesting - Gem::EditorPythonBindings.Editor - Gem::PythonAssetBuilder.Editor - Gem::Metastream - Gem::AudioSystem.Editor - Gem::Camera.Editor - Gem::EMotionFX.Editor - Gem::PhysX.Editor - Gem::CameraFramework - Gem::StartingPointMovement - Gem::StartingPointCamera - Gem::ScriptCanvas.Editor - Gem::ScriptEvents.Editor - Gem::ImGui.Editor - Gem::LyShineExamples - Gem::StartingPointInput.Editor - Gem::ScriptCanvasPhysics - Gem::ScriptCanvasTesting.Editor - Gem::PhysXDebug.Editor - Gem::WhiteBox.Editor - Gem::FastNoise.Editor - Gem::SurfaceData.Editor - Gem::GradientSignal.Editor - Gem::Vegetation.Editor - Gem::GraphModel.Editor - Gem::LandscapeCanvas.Editor - Gem::EMotionFX.Editor - Gem::ImGui.Editor - Gem::Atom_RHI.Private - Gem::Atom_Feature_Common.Editor - Gem::Atom_AtomBridge.Editor - Gem::NvCloth.Editor - Gem::Blast.Editor -) diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/__init__.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/__init__.py new file mode 100644 index 0000000000..cdee4b5a56 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/__init__.py @@ -0,0 +1,10 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" 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 new file mode 100644 index 0000000000..04be31759d --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/aws_metrics_automation_test.py @@ -0,0 +1,237 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + +import logging +import os +import pytest +import time +import typing + +from datetime import datetime +import ly_test_tools.log.log_monitor + +from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor as asset_processor +from AWS.common.aws_utils import aws_utils +from AWS.common.aws_credentials import aws_credentials +from AWS.Windows.resource_mappings.resource_mappings import resource_mappings +from AWS.Windows.cdk.cdk import cdk +from .aws_metrics_utils import aws_metrics_utils + +AWS_METRICS_FEATURE_NAME = 'AWSMetrics' +GAME_LOG_NAME = 'Game.log' + +logger = logging.getLogger(__name__) + + +def setup(launcher: ly_test_tools.launchers.Launcher, + cdk: cdk, + asset_processor: asset_processor, + resource_mappings: resource_mappings, + context_variable: str = '') -> typing.Tuple[ly_test_tools.log.log_monitor.LogMonitor, str, str]: + """ + Set up the CDK application and start the log monitor. + :param launcher: Client launcher for running the test level. + :param cdk: CDK application for deploying the AWS resources. + :param asset_processor: asset_processor fixture. + :param resource_mappings: resource_mappings fixture. + :param context_variable: context_variable for enable optional CDK feature. + :return log monitor object, metrics file path and the metrics stack name. + """ + logger.info(f'Cdk stack names:\n{cdk.list()}') + stacks = cdk.deploy(context_variable=context_variable) + resource_mappings.populate_output_keys(stacks) + + asset_processor.start() + asset_processor.wait_for_idle() + + metrics_file_path = os.path.join(launcher.workspace.paths.project(), 'user', + AWS_METRICS_FEATURE_NAME, 'metrics.json') + remove_file(metrics_file_path) + + file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), GAME_LOG_NAME) + remove_file(file_to_monitor) + + # Initialize the log monitor. + log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor) + + return log_monitor, metrics_file_path, stacks[0] + + +def monitor_metrics_submission(log_monitor: ly_test_tools.log.log_monitor.LogMonitor) -> None: + """ + Monitor the messages and notifications for submitting metrics. + :param log_monitor: Log monitor to check the log messages. + """ + expected_lines = [ + '(Script) - Submitted metrics without buffer.', + '(Script) - Submitted metrics with buffer.', + '(Script) - Metrics is sent successfully.' + ] + + unexpected_lines = [ + '(Script) - Failed to submit metrics without buffer.', + '(Script) - Failed to submit metrics with buffer.', + '(Script) - Failed to send metrics.' + ] + + result = log_monitor.monitor_log_for_lines( + expected_lines=expected_lines, + unexpected_lines=unexpected_lines, + halt_on_unexpected=True) + + # Assert the log monitor detected expected lines and did not detect any unexpected lines. + assert result, ( + f'Log monitoring failed. Used expected_lines values: {expected_lines} & ' + f'unexpected_lines values: {unexpected_lines}') + + +def remove_file(file_path: str) -> None: + """ + Remove a local file and its directory. + :param file_path: Path to the local file. + """ + if os.path.exists(file_path): + os.remove(file_path) + + file_dir = os.path.dirname(file_path) + if os.path.exists(file_dir) and len(os.listdir(file_dir)) == 0: + os.rmdir(file_dir) + + +@pytest.mark.SUITE_periodic +@pytest.mark.usefixtures('automatic_process_killer') +@pytest.mark.parametrize('project', ['AutomatedTesting']) +@pytest.mark.parametrize('level', ['AWS/Metrics']) +@pytest.mark.parametrize('feature_name', [AWS_METRICS_FEATURE_NAME]) +@pytest.mark.parametrize('resource_mappings_filename', ['aws_resource_mappings.json']) +@pytest.mark.parametrize('profile_name', ['AWSAutomationTest']) +@pytest.mark.parametrize('region_name', ['us-west-2']) +@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests']) +@pytest.mark.parametrize('session_name', ['o3de-Automation-session']) +class TestAWSMetrics_Windows(object): + def test_AWSMetrics_RealTimeAnalytics_MetricsSentToCloudWatch(self, + level: str, + launcher: ly_test_tools.launchers.Launcher, + asset_processor: pytest.fixture, + workspace: pytest.fixture, + aws_utils: aws_utils, + aws_credentials: aws_credentials, + resource_mappings: resource_mappings, + cdk: cdk, + aws_metrics_utils: aws_metrics_utils, + ): + """ + Tests that the submitted metrics are sent to CloudWatch for real-time analytics. + """ + log_monitor, metrics_file_path, stack_name = setup(launcher, cdk, asset_processor, resource_mappings) + + # Start the Kinesis Data Analytics application for real-time analytics. + analytics_application_name = f'{stack_name}-AnalyticsApplication' + aws_metrics_utils.start_kinesis_data_analytics_application(analytics_application_name) + + launcher.args = ['+LoadLevel', level] + launcher.args.extend(['-rhi=null']) + + with launcher.start(launch_ap=False): + start_time = datetime.utcnow() + monitor_metrics_submission(log_monitor) + # Verify that operational health metrics are delivered to CloudWatch. + aws_metrics_utils.verify_cloud_watch_delivery( + 'AWS/Lambda', + 'Invocations', + [{'Name': 'FunctionName', + 'Value': f'{stack_name}-AnalyticsProcessingLambda'}], + start_time) + logger.info('Operational health metrics sent to CloudWatch.') + + aws_metrics_utils.verify_cloud_watch_delivery( + AWS_METRICS_FEATURE_NAME, + 'TotalLogins', + [], + start_time) + logger.info('Real-time metrics sent to CloudWatch.') + + # Stop the Kinesis Data Analytics application. + aws_metrics_utils.stop_kinesis_data_analytics_application(analytics_application_name) + + def test_AWSMetrics_UnauthorizedUser_RequestRejected(self, + level: str, + launcher: ly_test_tools.launchers.Launcher, + cdk: cdk, + aws_credentials: aws_credentials, + asset_processor: pytest.fixture, + resource_mappings: resource_mappings, + workspace: pytest.fixture): + """ + Tests that unauthorized users cannot send metrics events to the AWS backed backend. + """ + log_monitor, metrics_file_path, stack_name = setup(launcher, cdk, asset_processor, resource_mappings) + # Set invalid AWS credentials. + launcher.args = ['+LoadLevel', level, '+cl_awsAccessKey', 'AKIAIOSFODNN7EXAMPLE', + '+cl_awsSecretKey', 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'] + launcher.args.extend(['-rhi=null']) + + with launcher.start(launch_ap=False): + result = log_monitor.monitor_log_for_lines( + expected_lines=['(Script) - Failed to send metrics.'], + unexpected_lines=['(Script) - Metrics is sent successfully.'], + 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_AWSMetrics_BatchAnalytics_MetricsDeliveredToS3(self, + level: str, + launcher: ly_test_tools.launchers.Launcher, + cdk: cdk, + aws_credentials: aws_credentials, + asset_processor: pytest.fixture, + resource_mappings: resource_mappings, + aws_utils: aws_utils, + aws_metrics_utils: aws_metrics_utils, + workspace: pytest.fixture): + """ + Tests that the submitted metrics are sent to the data lake for batch analytics. + """ + log_monitor, metrics_file_path, stack_name = setup(launcher, cdk, asset_processor, resource_mappings, + context_variable='batch_processing=true') + + analytics_bucket_name = aws_metrics_utils.get_analytics_bucket_name(stack_name) + + launcher.args = ['+LoadLevel', level] + launcher.args.extend(['-rhi=null']) + + with launcher.start(launch_ap=False): + start_time = datetime.utcnow() + monitor_metrics_submission(log_monitor) + # Verify that operational health metrics are delivered to CloudWatch. + aws_metrics_utils.verify_cloud_watch_delivery( + 'AWS/Lambda', + 'Invocations', + [{'Name': 'FunctionName', + 'Value': f'{stack_name}-EventsProcessingLambda'}], + start_time) + logger.info('Operational health metrics sent to CloudWatch.') + + aws_metrics_utils.verify_s3_delivery(analytics_bucket_name) + logger.info('Metrics sent to S3.') + + # Run the glue crawler to populate the AWS Glue Data Catalog with tables. + aws_metrics_utils.run_glue_crawler(f'{stack_name}-EventsCrawler') + # Run named queries on the table to verify the batch analytics. + aws_metrics_utils.run_named_queries(f'{stack_name}-AthenaWorkGroup') + logger.info('Query metrics from S3 successfully.') + + # Kinesis Data Firehose buffers incoming data before it delivers it to Amazon S3. Sleep for the + # default interval (60s) to make sure that all the metrics are sent to the bucket before cleanup. + time.sleep(60) + # Empty the S3 bucket. S3 buckets can only be deleted successfully when it doesn't contain any object. + aws_metrics_utils.empty_s3_bucket(analytics_bucket_name) + diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/aws_metrics_utils.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/aws_metrics_utils.py new file mode 100644 index 0000000000..686feda3d9 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/aws_metrics_utils.py @@ -0,0 +1,252 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + +import logging +import pathlib +import pytest +import typing + +from datetime import datetime +from botocore.exceptions import WaiterError + +from AWS.common.aws_utils import AwsUtils +from .aws_metrics_waiters import KinesisAnalyticsApplicationUpdatedWaiter, \ + CloudWatchMetricsDeliveredWaiter, DataLakeMetricsDeliveredWaiter, GlueCrawlerReadyWaiter + +logging.getLogger('boto').setLevel(logging.CRITICAL) + +# Expected directory and file extension for the S3 objects. +EXPECTED_S3_DIRECTORY = 'firehose_events/' +EXPECTED_S3_OBJECT_EXTENSION = '.parquet' + + +class AWSMetricsUtils: + """ + Provide utils functions for the AWSMetrics gem to interact with the deployed resources. + """ + + def __init__(self, aws_utils: AwsUtils): + self._aws_util = aws_utils + + def start_kinesis_data_analytics_application(self, application_name: str) -> None: + """ + Start the Kenisis Data Analytics application for real-time analytics. + :param application_name: Name of the Kenisis Data Analytics application. + """ + input_id = self.get_kinesis_analytics_application_input_id(application_name) + assert input_id, 'invalid Kinesis Data Analytics application input.' + + client = self._aws_util.client('kinesisanalytics') + try: + client.start_application( + ApplicationName=application_name, + InputConfigurations=[ + { + 'Id': input_id, + 'InputStartingPositionConfiguration': { + 'InputStartingPosition': 'NOW' + } + }, + ] + ) + except client.exceptions.ResourceInUseException: + # The application has been started. + return + + try: + KinesisAnalyticsApplicationUpdatedWaiter(client, 'RUNNING').wait(application_name=application_name) + except WaiterError as e: + assert False, f'Failed to start the Kinesis Data Analytics application: {str(e)}.' + + def get_kinesis_analytics_application_input_id(self, application_name: str) -> str: + """ + Get the input ID for the Kenisis Data Analytics application. + :param application_name: Name of the Kenisis Data Analytics application. + :return: Input ID for the Kenisis Data Analytics application. + """ + client = self._aws_util.client('kinesisanalytics') + response = client.describe_application( + ApplicationName=application_name + ) + if not response: + return '' + input_descriptions = response.get('ApplicationDetail', {}).get('InputDescriptions', []) + if len(input_descriptions) != 1: + return '' + + return input_descriptions[0].get('InputId', '') + + def stop_kinesis_data_analytics_application(self, application_name: str) -> None: + """ + Stop the Kenisis Data Analytics application. + :param application_name: Name of the Kenisis Data Analytics application. + """ + client = self._aws_util.client('kinesisanalytics') + client.stop_application( + ApplicationName=application_name + ) + + try: + KinesisAnalyticsApplicationUpdatedWaiter(client, 'READY').wait(application_name=application_name) + except WaiterError as e: + assert False, f'Failed to stop the Kinesis Data Analytics application: {str(e)}.' + + def verify_cloud_watch_delivery(self, namespace: str, metrics_name: str, + dimensions: typing.List[dict], start_time: datetime) -> None: + """ + Verify that the expected metrics is delivered to CloudWatch. + :param namespace: Namespace of the metrics. + :param metrics_name: Name of the metrics. + :param dimensions: Dimensions of the metrics. + :param start_time: Start time for generating the metrics. + """ + client = self._aws_util.client('cloudwatch') + + try: + CloudWatchMetricsDeliveredWaiter(client).wait( + namespace=namespace, + metrics_name=metrics_name, + dimensions=dimensions, + start_time=start_time + ) + except WaiterError as e: + assert False, f'Failed to deliver metrics to CloudWatch: {str(e)}.' + + def verify_s3_delivery(self, analytics_bucket_name: str) -> None: + """ + Verify that metrics are delivered to S3 for batch analytics successfully. + :param analytics_bucket_name: Name of the deployed S3 bucket. + """ + client = self._aws_util.client('s3') + bucket_name = analytics_bucket_name + + try: + DataLakeMetricsDeliveredWaiter(client).wait(bucket_name=bucket_name, prefix=EXPECTED_S3_DIRECTORY) + except WaiterError as e: + assert False, f'Failed to find the S3 directory for storing metrics data: {str(e)}.' + + # Check whether the data is converted to the expected data format. + response = client.list_objects_v2( + Bucket=bucket_name, + Prefix=EXPECTED_S3_DIRECTORY + ) + assert response.get('KeyCount', 0) != 0, f'Failed to deliver metrics to the S3 bucket {bucket_name}.' + + s3_objects = response.get('Contents', []) + for s3_object in s3_objects: + key = s3_object.get('Key', '') + assert pathlib.Path(key).suffix == EXPECTED_S3_OBJECT_EXTENSION, \ + f'Invalid data format is found in the S3 bucket {bucket_name}' + + def run_glue_crawler(self, crawler_name: str) -> None: + """ + Run the Glue crawler and wait for it to finish. + :param crawler_name: Name of the Glue crawler + """ + client = self._aws_util.client('glue') + try: + client.start_crawler( + Name=crawler_name + ) + except client.exceptions.CrawlerRunningException: + # The crawler has already been started. + return + + try: + GlueCrawlerReadyWaiter(client).wait(crawler_name=crawler_name) + except WaiterError as e: + assert False, f'Failed to run the Glue crawler: {str(e)}.' + + def run_named_queries(self, work_group: str) -> None: + """ + Run the named queries under the specific Athena work group. + :param work_group: Name of the Athena work group. + """ + client = self._aws_util.client('athena') + # List all the named queries. + response = client.list_named_queries( + WorkGroup=work_group + ) + named_query_ids = response.get('NamedQueryIds', []) + + # Run each of the queries. + for named_query_id in named_query_ids: + get_named_query_response = client.get_named_query( + NamedQueryId=named_query_id + ) + named_query = get_named_query_response.get('NamedQuery', {}) + + start_query_execution_response = client.start_query_execution( + QueryString=named_query.get('QueryString', ''), + QueryExecutionContext={ + 'Database': named_query.get('Database', '') + }, + WorkGroup=work_group + ) + + # Wait for the query to finish. + state = 'RUNNING' + while state == 'QUEUED' or state == 'RUNNING': + get_query_execution_response = client.get_query_execution( + QueryExecutionId=start_query_execution_response.get('QueryExecutionId', '') + ) + + state = get_query_execution_response.get('QueryExecution', {}).get('Status', {}).get('State', '') + + assert state == 'SUCCEEDED', f'Failed to run the named query {named_query.get("Name", {})}' + + def empty_s3_bucket(self, bucket_name: str) -> None: + """ + Empty the S3 bucket following: + https://boto3.amazonaws.com/v1/documentation/api/latest/guide/migrations3.html + + :param bucket_name: Name of the S3 bucket. + """ + + s3 = self._aws_util.resource('s3') + bucket = s3.Bucket(bucket_name) + + for key in bucket.objects.all(): + key.delete() + + def get_analytics_bucket_name(self, stack_name: str) -> str: + """ + Get the name of the deployed S3 bucket. + :param stack_name: Name of the CloudFormation stack. + :return: Name of the deployed S3 bucket. + """ + + client = self._aws_util.client('cloudformation') + + response = client.describe_stack_resources( + StackName=stack_name + ) + resources = response.get('StackResources', []) + + for resource in resources: + if resource.get('ResourceType') == 'AWS::S3::Bucket': + return resource.get('PhysicalResourceId', '') + + return '' + + +@pytest.fixture(scope='function') +def aws_metrics_utils( + request: pytest.fixture, + aws_utils: pytest.fixture): + """ + Fixture for the AWS metrics util functions. + :param request: _pytest.fixtures.SubRequest class that handles getting + a pytest fixture from a pytest function/fixture. + :param aws_utils: aws_utils fixture. + """ + aws_utils_obj = AWSMetricsUtils(aws_utils) + return aws_utils_obj diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/aws_metrics_waiters.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/aws_metrics_waiters.py new file mode 100644 index 0000000000..7ce5551fd4 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/aws_metrics/aws_metrics_waiters.py @@ -0,0 +1,142 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + +import botocore.client +import logging + +from datetime import timedelta +from AWS.common.custom_waiter import CustomWaiter, WaitState + +logging.getLogger('boto').setLevel(logging.CRITICAL) + + +class KinesisAnalyticsApplicationUpdatedWaiter(CustomWaiter): + """ + Subclass of the base custom waiter class. + Wait for the Kinesis analytics application being updated to a specific status. + """ + def __init__(self, client: botocore.client, status: str): + """ + Initialize the waiter. + + :param client: Boto3 client to use. + :param status: Expected status. + """ + super().__init__( + 'KinesisAnalyticsApplicationUpdated', + 'DescribeApplication', + 'ApplicationDetail.ApplicationStatus', + {status: WaitState.SUCCESS}, + client) + + def wait(self, application_name: str): + """ + Wait for the expected status. + + :param application_name: Name of the Kinesis analytics application. + """ + self._wait(ApplicationName=application_name) + + +class GlueCrawlerReadyWaiter(CustomWaiter): + """ + Subclass of the base custom waiter class. + Wait for the Glue crawler to finish its processing. + """ + def __init__(self, client: botocore.client): + """ + Initialize the waiter. + + :param client: Boto3 client to use. + """ + super().__init__( + 'GlueCrawlerReady', + 'GetCrawler', + 'Crawler.State', + {'READY': WaitState.SUCCESS}, + client) + + def wait(self, crawler_name): + """ + Wait for the expected status. + + :param crawler_name: Name of the Glue crawler. + """ + self._wait(Name=crawler_name) + + +class DataLakeMetricsDeliveredWaiter(CustomWaiter): + """ + Subclass of the base custom waiter class. + Wait for the expected directory being created in the S3 bucket. + """ + def __init__(self, client: botocore.client): + """ + Initialize the waiter. + + :param client: Boto3 client to use. + """ + super().__init__( + 'DataLakeMetricsDelivered', + 'ListObjectsV2', + 'KeyCount > `0`', + {True: WaitState.SUCCESS}, + client) + + def wait(self, bucket_name, prefix): + """ + Wait for the expected directory being created. + + :param bucket_name: Name of the S3 bucket. + :param prefix: Name of the expected directory prefix. + """ + self._wait(Bucket=bucket_name, Prefix=prefix) + + +class CloudWatchMetricsDeliveredWaiter(CustomWaiter): + """ + Subclass of the base custom waiter class. + Wait for the expected metrics being delivered to CloudWatch. + """ + def __init__(self, client: botocore.client): + """ + Initialize the waiter. + + :param client: Boto3 client to use. + """ + super().__init__( + 'CloudWatchMetricsDelivered', + 'GetMetricStatistics', + 'length(Datapoints) > `0`', + {True: WaitState.SUCCESS}, + client) + + def wait(self, namespace, metrics_name, dimensions, start_time): + """ + Wait for the expected metrics being delivered. + + :param namespace: Namespace of the metrics. + :param metrics_name: Name of the metrics. + :param dimensions: Dimensions of the metrics. + :param start_time: Start time for generating the metrics. + """ + self._wait( + Namespace=namespace, + MetricName=metrics_name, + Dimensions=dimensions, + StartTime=start_time, + EndTime=start_time + timedelta(0, self.timeout), + Period=60, + Statistics=[ + 'SampleCount' + ], + Unit='Count' + ) diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/cdk.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/cdk.py index ea40001c31..9254c3d4eb 100644 --- a/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/cdk.py +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/cdk.py @@ -16,12 +16,15 @@ import boto3 import ly_test_tools.environment.process_utils as process_utils from typing import List +BOOTSTRAP_STACK_NAME = 'CDKToolkit' +BOOTSTRAP_STAGING_BUCKET_LOGIC_ID = 'StagingBucket' class Cdk: """ Cdk class that provides methods to run cdk application commands. Expects system to have NodeJS, AWS CLI and CDK installed globally and have their paths setup as env variables. """ + def __init__(self, cdk_path: str, project: str, account_id: str, workspace: pytest.fixture, session: boto3.session.Session): """ @@ -49,12 +52,24 @@ class Cdk: env=self._cdk_env, shell=True) + def bootstrap(self) -> None: + """ + Deploy the bootstrap stack. + """ + bootstrap_cmd = ['cdk', 'bootstrap', + f'aws://{self._cdk_env["O3DE_AWS_DEPLOY_ACCOUNT"]}/{self._cdk_env["O3DE_AWS_DEPLOY_REGION"]}'] + + process_utils.check_call( + bootstrap_cmd, + cwd=self._cdk_path, + env=self._cdk_env, + shell=True) + def list(self) -> List[str]: """ lists cdk stack names :return List of cdk stack names """ - if not self._cdk_path: return [] @@ -126,6 +141,38 @@ class Cdk: self._stacks = [] self._cdk_path = '' + @staticmethod + def remove_bootstrap_stack(aws_utils: pytest.fixture) -> None: + """ + Remove the CDK bootstrap stack. + :param aws_utils: aws_utils fixture. + """ + # Check if the bootstrap stack exists. + response = aws_utils.client('cloudformation').describe_stacks( + StackName=BOOTSTRAP_STACK_NAME + ) + stacks = response.get('Stacks', []) + if not stacks: + return + + # Clear the bootstrap staging bucket before deleting the bootstrap stack. + response = aws_utils.client('cloudformation').describe_stack_resource( + StackName=BOOTSTRAP_STACK_NAME, + LogicalResourceId=BOOTSTRAP_STAGING_BUCKET_LOGIC_ID + ) + + staging_bucket_name = response.get('StackResourceDetail', {}).get('PhysicalResourceId', '') + if staging_bucket_name: + s3 = aws_utils.resource('s3') + bucket = s3.Bucket(staging_bucket_name) + for key in bucket.objects.all(): + key.delete() + + # Delete the bootstrap stack. + aws_utils.client('cloudformation').delete_stack( + StackName=BOOTSTRAP_STACK_NAME + ) + @pytest.fixture(scope='function') def cdk( @@ -134,6 +181,7 @@ def cdk( feature_name: str, workspace: pytest.fixture, aws_utils: pytest.fixture, + bootstrap_required: bool = True, destroy_stacks_on_teardown: bool = True) -> Cdk: """ Fixture for setting up a Cdk @@ -143,6 +191,8 @@ def cdk( :param feature_name: Feature gem name to expect cdk folder in. :param workspace: ly_test_tools workspace fixture. :param aws_utils: aws_utils fixture. + :param bootstrap_required: Whether the bootstrap stack needs to be created to + provision resources the AWS CDK needs to perform the deployment. :param destroy_stacks_on_teardown: option to control calling destroy ot the end of test. :return Cdk class object. """ @@ -150,9 +200,14 @@ def cdk( cdk_path = f'{workspace.paths.engine_root()}/Gems/{feature_name}/cdk' cdk_obj = Cdk(cdk_path, project, aws_utils.assume_account_id(), workspace, aws_utils.assume_session()) + if bootstrap_required: + cdk_obj.bootstrap() + def teardown(): if destroy_stacks_on_teardown: cdk_obj.destroy() + cdk_obj.remove_bootstrap_stack(aws_utils) + request.addfinalizer(teardown) return cdk_obj diff --git a/AutomatedTesting/Gem/PythonTests/AWS/common/aws_credentials.py b/AutomatedTesting/Gem/PythonTests/AWS/common/aws_credentials.py new file mode 100644 index 0000000000..fbce772d40 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/common/aws_credentials.py @@ -0,0 +1,134 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + +import boto3 +import configparser +import logging +import os +import pytest +import typing + +logger = logging.getLogger(__name__) +logging.getLogger('boto').setLevel(logging.CRITICAL) + + +class AwsCredentials: + def __init__(self, profile_name: str): + self._profile_name = profile_name + + self._credentials_path = os.environ.get('AWS_SHARED_CREDENTIALS_FILE') + if not self._credentials_path: + # Home directory location varies based on the operating system, but is referred to using the environment + # variables %UserProfile% in Windows and $HOME or ~ (tilde) in Unix-based systems. + self._credentials_path = os.path.join(os.environ.get('UserProfile', os.path.expanduser('~')), + '.aws', 'credentials') + self._credentials_file_exists = os.path.exists(self._credentials_path) + + self._credentials = configparser.ConfigParser() + self._credentials.read(self._credentials_path) + + def get_aws_credentials(self) -> typing.Tuple[str, str, str]: + """ + Get aws credentials stored in the specific named profile. + + :return AWS credentials. + """ + access_key_id = self._get_aws_credential_attribute_value('aws_access_key_id') + secret_access_key = self._get_aws_credential_attribute_value('aws_secret_access_key') + session_token = self._get_aws_credential_attribute_value('aws_session_token') + + return access_key_id, secret_access_key, session_token + + def set_aws_credentials_by_session(self, session: boto3.Session) -> None: + """ + Set AWS credentials stored in the specific named profile using an assumed role session. + + :param session: assumed role session. + """ + credentials = session.get_credentials().get_frozen_credentials() + self.set_aws_credentials(credentials.access_key, credentials.secret_key, credentials.token) + + def set_aws_credentials(self, aws_access_key_id: str, aws_secret_access_key: str, + aws_session_token: str) -> None: + """ + Set AWS credentials stored in the specific named profile. + + :param aws_access_key_id: AWS access key id. + :param aws_secret_access_key: AWS secrete access key. + :param aws_session_token: AWS assumed role session. + """ + self._set_aws_credential_attribute_value('aws_access_key_id', aws_access_key_id) + self._set_aws_credential_attribute_value('aws_secret_access_key', aws_secret_access_key) + self._set_aws_credential_attribute_value('aws_session_token', aws_session_token) + + if (len(self._credentials.sections()) == 0) and (not self._credentials_file_exists): + os.remove(self._credentials_path) + return + + with open(self._credentials_path, 'w+') as credential_file: + self._credentials.write(credential_file) + + def _get_aws_credential_attribute_value(self, attribute_name: str) -> str: + """ + Get the value of an AWS credential attribute stored in the specific named profile. + + :param attribute_name: Name of the AWS credential attribute. + :return Value of the AWS credential attribute. + """ + try: + value = self._credentials.get(self._profile_name, attribute_name) + except configparser.NoSectionError: + # Named profile or key doesn't exist + value = None + except configparser.NoOptionError: + # Named profile doesn't have the specified attribute + value = None + + return value + + def _set_aws_credential_attribute_value(self, attribute_name: str, attribute_value: str) -> None: + """ + Set the value of an AWS credential attribute stored in the specific named profile. + + :param attribute_name: Name of the AWS credential attribute. + :param attribute_value: Value of the AWS credential attribute. + """ + if self._profile_name not in self._credentials: + self._credentials[self._profile_name] = {} + + if attribute_value is None: + self._credentials.remove_option(self._profile_name, attribute_name) + # Remove the named profile if it doesn't have any AWS credential attribute. + if len(self._credentials[self._profile_name]) == 0: + self._credentials.remove_section(self._profile_name) + else: + self._credentials[self._profile_name][attribute_name] = attribute_value + + +@pytest.fixture(scope='function') +def aws_credentials(request: pytest.fixture, aws_utils: pytest.fixture, profile_name: str): + """ + Fixture for setting up temporary AWS credentials from assume role. + + :param request: _pytest.fixtures.SubRequest class that handles getting + a pytest fixture from a pytest function/fixture. + :param aws_utils: aws_utils fixture. + :param profile_name: Named AWS profile to store temporary credentials. + """ + aws_credentials_obj = AwsCredentials(profile_name) + original_access_key, original_secret_access_key, original_token = aws_credentials_obj.get_aws_credentials() + aws_credentials_obj.set_aws_credentials_by_session(aws_utils.assume_session()) + + def teardown(): + # Reset to the named profile using the original AWS credentials + aws_credentials_obj.set_aws_credentials(original_access_key, original_secret_access_key, original_token) + request.addfinalizer(teardown) + + return aws_credentials_obj diff --git a/AutomatedTesting/Gem/PythonTests/AWS/common/aws_utils.py b/AutomatedTesting/Gem/PythonTests/AWS/common/aws_utils.py index 7a15ba0abe..ff33f58d1d 100644 --- a/AutomatedTesting/Gem/PythonTests/AWS/common/aws_utils.py +++ b/AutomatedTesting/Gem/PythonTests/AWS/common/aws_utils.py @@ -1,82 +1,90 @@ -""" -All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -its licensors. -For complete copyright and license terms please see the LICENSE at the root of this -distribution (the "License"). All use of this software is governed by the License, -or, if provided, by the license below or the license accompanying this file. Do not -remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -""" -import boto3 -import pytest -import logging - -logger = logging.getLogger(__name__) - - -class AwsUtils: - - def __init__(self, arn: str, session_name: str, region_name: str): - local_session = boto3.Session(profile_name='default') - local_sts_client = local_session.client('sts') - self._local_account_id = local_sts_client.get_caller_identity()["Account"] - logger.info(f'Local Account Id: {self._local_account_id}') - - response = local_sts_client.assume_role(RoleArn=arn, RoleSessionName=session_name) - - self._assume_session = boto3.Session(aws_access_key_id=response['Credentials']['AccessKeyId'], - aws_secret_access_key=response['Credentials']['SecretAccessKey'], - aws_session_token=response['Credentials']['SessionToken'], - region_name=region_name) - - assume_sts_client = self._assume_session.client('sts') - assume_account_id = assume_sts_client.get_caller_identity()["Account"] - logger.info(f'Assume Account Id: {assume_account_id}') - self._assume_account_id = assume_account_id - - def client(self, service: str): - """ - Get the client for a specific AWS service from configured session - :return: Client for the AWS service. - """ - return self._assume_session.client(service) - - def assume_session(self): - return self._assume_session - - def local_account_id(self): - return self._local_account_id - - def assume_account_id(self): - return self._assume_account_id - - def destroy(self) -> None: - """ - clears stored session - """ - self._assume_session = None - - -@pytest.fixture(scope='function') -def aws_utils( - request: pytest.fixture, - assume_role_arn: str, - session_name: str, - region_name: str): - """ - Fixture for setting up a Cdk - :param request: _pytest.fixtures.SubRequest class that handles getting - a pytest fixture from a pytest function/fixture. - :param assume_role_arn: Role used to fetch temporary aws credentials, configure service clients with obtained credentials. - :param session_name: Session name to set. - :param region_name: AWS account region to set for session. - :return AWSUtils class object. - """ - aws_utils_obj = AwsUtils(assume_role_arn, session_name, region_name) - - def teardown(): - aws_utils_obj.destroy() - - request.addfinalizer(teardown) - - return aws_utils_obj +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" +import boto3 +import pytest +import logging + +logger = logging.getLogger(__name__) +logging.getLogger('boto').setLevel(logging.CRITICAL) + + +class AwsUtils: + + def __init__(self, arn: str, session_name: str, region_name: str): + local_session = boto3.Session(profile_name='default') + local_sts_client = local_session.client('sts') + self._local_account_id = local_sts_client.get_caller_identity()["Account"] + logger.info(f'Local Account Id: {self._local_account_id}') + + response = local_sts_client.assume_role(RoleArn=arn, RoleSessionName=session_name) + + self._assume_session = boto3.Session(aws_access_key_id=response['Credentials']['AccessKeyId'], + aws_secret_access_key=response['Credentials']['SecretAccessKey'], + aws_session_token=response['Credentials']['SessionToken'], + region_name=region_name) + + assume_sts_client = self._assume_session.client('sts') + assume_account_id = assume_sts_client.get_caller_identity()["Account"] + logger.info(f'Assume Account Id: {assume_account_id}') + self._assume_account_id = assume_account_id + + def client(self, service: str): + """ + Get the client for a specific AWS service from configured session + :return: Client for the AWS service. + """ + return self._assume_session.client(service) + + def resource(self, service: str): + """ + Get the resource for a specific AWS service from configured session + :return: Client for the AWS service. + """ + return self._assume_session.resource(service) + + def assume_session(self): + return self._assume_session + + def local_account_id(self): + return self._local_account_id + + def assume_account_id(self): + return self._assume_account_id + + def destroy(self) -> None: + """ + clears stored session + """ + self._assume_session = None + + +@pytest.fixture(scope='function') +def aws_utils( + request: pytest.fixture, + assume_role_arn: str, + session_name: str, + region_name: str): + """ + Fixture for AWS util functions + :param request: _pytest.fixtures.SubRequest class that handles getting + a pytest fixture from a pytest function/fixture. + :param assume_role_arn: Role used to fetch temporary aws credentials, configure service clients with obtained credentials. + :param session_name: Session name to set. + :param region_name: AWS account region to set for session. + :return AWSUtils class object. + """ + aws_utils_obj = AwsUtils(assume_role_arn, session_name, region_name) + + def teardown(): + aws_utils_obj.destroy() + + request.addfinalizer(teardown) + + return aws_utils_obj diff --git a/AutomatedTesting/Gem/PythonTests/AWS/common/custom_waiter.py b/AutomatedTesting/Gem/PythonTests/AWS/common/custom_waiter.py new file mode 100644 index 0000000000..7c0a65e8a3 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/common/custom_waiter.py @@ -0,0 +1,91 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + +from enum import Enum +import botocore.client +import botocore.waiter +import logging + +logging.getLogger('boto').setLevel(logging.CRITICAL) + + +class WaitState(Enum): + SUCCESS = 'success' + FAILURE = 'failure' + + +class CustomWaiter: + """ + Base class for a custom waiter. + + Modified from: + https://docs.aws.amazon.com/code-samples/latest/catalog/python-demo_tools-custom_waiter.py.html + """ + def __init__( + self, name: str, operation: str, argument: str, + acceptors: dict, client: botocore.client, delay: int = 30, max_tries: int = 10, + matcher='path'): + """ + Subclasses should pass specific operations, arguments, and acceptors to + their superclass. + + :param name: The name of the waiter. This can be any descriptive string. + :param operation: The operation to wait for. This must match the casing of + the underlying operation model, which is typically in + CamelCase. + :param argument: The dict keys used to access the result of the operation, in + dot notation. For example, 'Job.Status' will access + result['Job']['Status']. + :param acceptors: The list of acceptors that indicate the wait is over. These + can indicate either success or failure. The acceptor values + are compared to the result of the operation after the + argument keys are applied. + :param client: The Boto3 client. + :param delay: The number of seconds to wait between each call to the operation. Default to 30 seconds. + :param max_tries: The maximum number of tries before exiting. Default to 10. + :param matcher: The kind of matcher to use. Default to 'path'. + """ + self.name = name + self.operation = operation + self.argument = argument + self.client = client + self.waiter_model = botocore.waiter.WaiterModel({ + 'version': 2, + 'waiters': { + name: { + "delay": delay, + "operation": operation, + "maxAttempts": max_tries, + "acceptors": [{ + "state": state.value, + "matcher": matcher, + "argument": argument, + "expected": expected + } for expected, state in acceptors.items()] + }}}) + self.waiter = botocore.waiter.create_waiter_with_client( + self.name, self.waiter_model, self.client) + + self._timeout = delay * max_tries + + def _wait(self, **kwargs): + """ + Starts the botocore wait loop. + + :param kwargs: Keyword arguments that are passed to the operation being polled. + """ + self.waiter.wait(**kwargs) + + @property + def timeout(self): + return self._timeout + + diff --git a/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test.py b/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test.py index 818dc23079..ecf08cfcbd 100644 --- a/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test.py +++ b/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test.py @@ -31,13 +31,13 @@ class TestPythonAssetProcessing(object): unexpected_lines = [] expected_lines = [ 'Mock asset exists', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive_1.azmodel) found', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative_1.azmodel) found', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive_1.azmodel) found', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative_1.azmodel) found', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive_1.azmodel) found', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative_1.azmodel) found', - 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center_1.azmodel) found' + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive.azmodel) found', + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative.azmodel) found', + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive.azmodel) found', + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative.azmodel) found', + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive.azmodel) found', + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative.azmodel) found', + 'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found' ] timeout = 180 halt_on_unexpected = False diff --git a/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test_case.py b/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test_case.py index cd9adfdbcf..a7907778b2 100644 --- a/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test_case.py +++ b/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test_case.py @@ -38,16 +38,16 @@ def test_azmodel_product(generatedModelAssetPath, expectedSubId): assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', generatedModelAssetPath, azModelAssetType, False) assetIdString = assetId.to_string() if (assetIdString.endswith(':' + expectedSubId) is False): - raise_and_stop(f'Asset has unexpected asset ID ({assetIdString}) for ({generatedModelAssetPath})!') + raise_and_stop(f'Asset at path {generatedModelAssetPath} has unexpected asset ID ({assetIdString}) for ({generatedModelAssetPath}), expected {expectedSubId}!') else: print(f'Expected subId for asset ({generatedModelAssetPath}) found') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive_1.azmodel', '10315ae0') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative_1.azmodel', '10661093') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive_1.azmodel', '10af8810') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative_1.azmodel', '10f8c263') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive_1.azmodel', '100ac47f') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative_1.azmodel', '105d8e0c') -test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center_1.azmodel', '1002d464') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive.azmodel', '1024be55') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative.azmodel', '1052c94e') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive.azmodel', '10130556') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative.azmodel', '1065724d') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive.azmodel', '10d16e68') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative.azmodel', '10a71973') +test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel', '10412075') azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt') diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_all_platforms_setup_fixture.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_all_platforms_setup_fixture.py index e729ee9882..9a5b93ca80 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_all_platforms_setup_fixture.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_all_platforms_setup_fixture.py @@ -34,10 +34,10 @@ def ap_all_platforms_setup_fixture(request, workspace, ap_setup_fixture) -> Dict # Specific platform cache locations resources["pc_cache_location"] = os.path.join(cache_dir, "pc") - resources["es3_cache_location"] = os.path.join(cache_dir, "es3") + resources["android_cache_location"] = os.path.join(cache_dir, "android") resources["ios_cache_location"] = os.path.join(cache_dir, "ios") - resources["osx_gl_cache_location"] = os.path.join(cache_dir, "osx_gl") + resources["mac_cache_location"] = os.path.join(cache_dir, "mac") resources["provo_cache_location"] = os.path.join(cache_dir, "provo") - resources["all_platforms"] = ["pc", "es3", "ios", "osx_gl", "provo"] + resources["all_platforms"] = ["pc", "android", "ios", "mac", "provo"] return resources diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py index 580816e7b5..34af4d9115 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py @@ -54,7 +54,7 @@ def bundler_batch_setup_fixture(request, workspace, asset_processor, timeout) -> platforms = [platform.strip() for platform in platforms.split(",")] else: # No commandline argument provided, default to mac and pc - platforms = ["pc", "osx_gl"] + platforms = ["pc", "mac"] class BundlerBatchFixture: """ @@ -162,7 +162,7 @@ def bundler_batch_setup_fixture(request, workspace, asset_processor, timeout) -> else: cmd.append(f"--{key}") if append_defaults: - cmd.append(f"--project={workspace.project}") + cmd.append(f"--project-path={workspace.project}") return cmd # ****** @@ -241,11 +241,11 @@ def bundler_batch_setup_fixture(request, workspace, asset_processor, timeout) -> def get_platform_flag(self, platform_name: str) -> int: if (platform_name == "pc"): return 1 - elif (platform_name == "es3"): + elif (platform_name == "android"): return 2 elif (platform_name == "ios"): return 4 - elif (platform_name == "osx_gl"): + elif (platform_name == "mac"): return 8 elif (platform_name == "server"): return 128 @@ -300,9 +300,9 @@ def bundler_batch_setup_fixture(request, workspace, asset_processor, timeout) -> workspace.paths.engine_root(), "Code", "Framework", - "AzFramework", - "AzFramework", - "Platform", + "AzCore", + "AzCore", + "PlatformId", "PlatformDefaults.h", ) @@ -318,7 +318,7 @@ def bundler_batch_setup_fixture(request, workspace, asset_processor, timeout) -> if start_gathering: result = get_platform.match(line) # Try the regex if result: - platform_values[result.group(1).lower()] = counter + platform_values[result.group(1).replace("_ID", "").lower()] = counter counter = counter << 1 elif "(Invalid, -1)" in line: # The line right before the first platform start_gathering = True diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/CMakeLists.txt index a2002f2d15..2e7516db27 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/CMakeLists.txt @@ -128,16 +128,5 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) RUNTIME_DEPENDENCIES AZ::AssetProcessorBatch ) - -# Need performance improvements LYN-1218 -# ly_add_pytest( -# NAME AssetPipelineTests.AssetRelocator -# PATH ${CMAKE_CURRENT_LIST_DIR}/asset_relocator_tests.py -# EXCLUDE_TEST_RUN_TARGET_FROM_IDE -# TEST_SUITE periodic -# TEST_SERIAL -# RUNTIME_DEPENDENCIES -# AZ::AssetProcessorBatch -# ) endif() diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_builder_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_builder_tests.py index e3d52e8260..fb82f180c6 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_builder_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_builder_tests.py @@ -64,6 +64,15 @@ class TestsAssetBuilder_WindowsAndMac(object): ): """ Verifying -debug parameter for AssetBuilder + + Test Steps: + 1. Create temporary workspace + 2. Launch Asset Processor GUI + 3. Add test assets to workspace + 4. Run Asset Builder with debug on an intact slice + 5. Check Asset Builder didn't fail to build + 6. Run Asset Builder with debug on a corrupted slice + 7. Verify corrupted slice produced an error """ env = ap_setup_fixture intact_slice_failed = False diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py index d236e87aa2..e1091b9b82 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py @@ -80,6 +80,8 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): def test_WindowsAndMac_RunHelpCmd_ZeroExitCode(self, workspace, bundler_batch_helper): """ Simple calls to all AssetBundlerBatch --help to make sure a non-zero exit codes are returned. + + Test will call each Asset Bundler Batch sub-command with help and will error on a non-0 exit code """ bundler_batch_helper.call_bundlerbatch(help="") bundler_batch_helper.call_seeds(help="") @@ -98,6 +100,12 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): r""" Tests that an asset list created maps dependencies correctly. testdependencieslevel\level.pak and lists of known dependencies are used for validation + + Test Steps: + 1. Create an asset list from the level.pak + 2. Create Lists of expected assets in the level.pak + 3. Add lists of expected assets to a single list + 4. Compare list of expected assets to actual assets """ helper = bundler_batch_helper @@ -300,9 +308,18 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): """ Validates destructive overwriting for asset lists and that generating debug information does not affect asset list creation + + 1. Create an asset list from seed_list + 2. Validate asset list was created + 3. Read and store contents of asset list into memory + 4. Attempt to create a new asset list in without using --allowOverwrites + 5. Verify that Asset Bundler returns false + 6. Verify that file contents of the orignally created asset list did not change from what was stored in memory + 7. Attempt to create a new asset list without debug while allowing overwrites + 8. Verify that file contents of the orignally created asset list changed from what was stored in memory """ helper = bundler_batch_helper - seed_list = os.path.join(workspace.paths.engine_root(), "Engine", "SeedAssetList.seed") # Engine seed list + seed_list = os.path.join(workspace.paths.engine_root(), "Assets", "Engine", "SeedAssetList.seed") # Engine seed list asset = r"levels\testdependencieslevel\level.pak" # Create Asset list @@ -375,9 +392,17 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): """ Validates bundle creation both through the 'bundles' and 'bundlesettings' subcommands. + + Test Steps: + 1. Create an asset list + 2. Create a bundle with the asset list and without a bundle settings file + 3. Create a bundle with the asset list and a bundle settings file + 4. Validate calling bundle doesn't perform destructive overwrite without --allowOverwrites + 5. Calling bundle again with --alowOverwrites performs destructive overwrite + 6. Validate contents of original bundle and overwritten bundle """ helper = bundler_batch_helper - seed_list = os.path.join(workspace.paths.engine_root(), "Engine", "SeedAssetList.seed") # Engine seed list + seed_list = os.path.join(workspace.paths.engine_root(), "Assets", "Engine", "SeedAssetList.seed") # Engine seed list asset = r"levels\testdependencieslevel\level.pak" # Useful bundle locations / names (2 for comparing contents) @@ -457,15 +482,25 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): """ Creates bundles using the same asset list and compares that they are created equally. Also validates that platform bundles exclude/include an expected file. (excluded for WIN, included for MAC) + + Test Steps: + 1. Create an asset list + 2. Create bundles for both PC & Mac + 3. Validate that bundles were created + 4. Verify that expected missing file is not in windows bundle + 5. Verify that expected file is in the mac bundle + 6. Create duplicate bundles with allowOverwrites + 7. Verify that files were generated + 8. Verify original bundle checksums are equal to new bundle checksums """ helper = bundler_batch_helper # fmt:off - assert "pc" in helper["platforms"] and "osx_gl" in helper["platforms"], \ + assert "pc" in helper["platforms"] and "mac" in helper["platforms"], \ "This test requires both PC and MAC platforms to be enabled. " \ - "Please rerun with commandline option: '--bundle_platforms=pc,osx_gl'" + "Please rerun with commandline option: '--bundle_platforms=pc,mac'" # fmt:on - seed_list = os.path.join(workspace.paths.engine_root(), "Engine", "SeedAssetList.seed") # Engine seed list + seed_list = os.path.join(workspace.paths.engine_root(), "Assets", "Engine", "SeedAssetList.seed") # Engine seed list # Useful bundle / asset list locations bundle_dir = os.path.dirname(helper["bundle_file"]) @@ -502,21 +537,21 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): for bundle_file in bundle_files.values(): assert os.path.isfile(bundle_file) - # This asset is created on osx_gl platform but not on windows - file_to_check = b"engineassets/shading/defaultprobe_cm.dds.5" # [use byte str because file is in binary] + # This asset is created both on mac and windows platform + file_to_check = b"engineassets/shading/defaultprobe_cm_ibldiffuse.tif.streamingimage" # [use byte str because file is in binary] # Extract the delta catalog file from pc archive. {file_to_check} SHOULD NOT be present for PC file_contents = helper.extract_file_content(bundle_files["pc"], "DeltaCatalog.xml") # fmt:off - assert file_to_check not in file_contents, \ + assert file_to_check in file_contents, \ f"{file_to_check} was found in DeltaCatalog.xml in pc bundle file {bundle_files['pc']}" # fmt:on - # Extract the delta catalog file from osx_gl archive. {file_to_check} SHOULD be present for MAC - file_contents = helper.extract_file_content(bundle_files["osx_gl"], "DeltaCatalog.xml") + # Extract the delta catalog file from mac archive. {file_to_check} SHOULD be present for MAC + file_contents = helper.extract_file_content(bundle_files["mac"], "DeltaCatalog.xml") # fmt:off assert file_to_check in file_contents, \ - f"{file_to_check} was not found in DeltaCatalog.xml in darwin bundle file {bundle_files['osx_gl']}" + f"{file_to_check} was not found in DeltaCatalog.xml in darwin bundle file {bundle_files['mac']}" # fmt:on # Gather checksums for first set of bundles @@ -571,6 +606,24 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): """ Validates that the 'seeds' subcommand can add and remove seeds and seed platforms properly. Also checks that destructive overwrites require the --allowOverwrites flag + + Test Steps: + + 1. Create a PC Seed List from a test asset + 2. Validate that seed list was generated with proper platform flag + 3. Add Mac & PC as platforms to the seed list + 4. Verify that seed has both Mac & PC platform flags + 5. Remove Mac as a platform from the seed list + 6. Verify that seed only has PC as a platform flag + 7. Attempt to add a platform without using the --platform argument + 8. Verify that asset bundler returns False and file contents did not change + 9. Add Mac platform via --addPlatformToSeeds + 10. Validate that seed has both Mac & PC platform flags + 11. Attempt to remove platform without specifying a platform + 12. Validate that seed has both Mac & PC platform flags + 13. Validate that seed list contents did not change + 14. Remove seed + 15. Validate that seed was removed from the seed list """ helper = bundler_batch_helper @@ -613,20 +666,20 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): helper.call_seeds( seedListFile=helper["seed_list_file"], addSeed=test_asset, - platform="pc,osx_gl", + platform="pc,mac", ) # Validate both mac and pc are activated for seed # fmt:off check_seed_platform(helper["seed_list_file"], test_asset, - helper["platform_values"]["pc"] + helper["platform_values"]["osx"]) + helper["platform_values"]["pc"] + helper["platform_values"]["mac"]) # fmt:on # Remove MAC platform helper.call_seeds( seedListFile=helper["seed_list_file"], removePlatformFromSeeds="", - platform="osx_gl", + platform="mac", ) # Validate only pc platform for seed. Save file contents to variable all_lines = check_seed_platform(helper["seed_list_file"], test_asset, helper["platform_values"]["pc"]) @@ -646,12 +699,12 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): helper.call_seeds( seedListFile=helper["seed_list_file"], addPlatformToSeeds="", - platform="osx_gl", + platform="mac", ) # Validate Mac platform was added back on. Save file contents # fmt:off all_lines = check_seed_platform(helper["seed_list_file"], test_asset, - helper["platform_values"]["pc"] + helper["platform_values"]["osx"]) + helper["platform_values"]["pc"] + helper["platform_values"]["mac"]) # fmt:on # Try to remove platform without specifying a platform to remove (should fail) @@ -670,7 +723,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): helper.call_seeds( seedListFile=helper["seed_list_file"], removeSeed=test_asset, - platform="pc,osx_gl", + platform="pc,mac", ) # Validate seed was removed from file @@ -692,14 +745,20 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): """ Tests asset list comparison, both by file and by comparison type. Uses a set of controlled test assets to compare resulting output asset lists + + 1. Create comparison rules files + 2. Create seed files for different sets of test assets + 3. Create assetlist files for seed files + 4. Validate assetlists were created properly + 5. Compare using comparison rules files and just command line arguments """ helper = bundler_batch_helper env = ap_setup_fixture # fmt:off - assert "pc" in helper["platforms"] and "osx_gl" in helper["platforms"], \ + assert "pc" in helper["platforms"] and "mac" in helper["platforms"], \ "This test requires both PC and MAC platforms to be enabled. " \ - "Please rerun with commandline option: '--bundle_platforms=pc,osx_gl'" + "Please rerun with commandline option: '--bundle_platforms=pc,mac'" # fmt:on # Test assets arranged in common lists: six (0-5) .txt files and .dat files @@ -717,16 +776,16 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): file_platforms = { "txtfile_0.txt": "pc", "txtfile_1.txt": "pc", - "txtfile_2.txt": "pc,osx_gl", - "txtfile_3.txt": "pc,osx_gl", - "txtfile_4.txt": "osx_gl", - "txtfile_5.txt": "osx_gl", + "txtfile_2.txt": "pc,mac", + "txtfile_3.txt": "pc,mac", + "txtfile_4.txt": "mac", + "txtfile_5.txt": "mac", "datfile_0.dat": "pc", "datfile_1.dat": "pc", - "datfile_2.dat": "pc,osx_gl", - "datfile_3.dat": "pc,osx_gl", - "datfile_4.dat": "osx_gl", - "datfile_5.dat": "osx_gl", + "datfile_2.dat": "pc,mac", + "datfile_3.dat": "pc,mac", + "datfile_4.dat": "mac", + "datfile_5.dat": "mac", } # Comparison rules files and their associated 'comparisonType' flags @@ -741,7 +800,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): # Get our test assets ready and processed utils.prepare_test_assets(env["tests_dir"], "C16877178", env["project_test_assets_dir"]) - asset_processor.batch_process(timeout=timeout, fastscan=False, platforms="pc,osx_gl") + asset_processor.batch_process(timeout=timeout, fastscan=False, platforms="pc,mac") # *** Some helper functions *** # @@ -759,7 +818,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): helper.call_assetLists( assetListFile=os.path.join(helper["test_dir"], asset_list_file_name), seedListFile=os.path.join(helper["test_dir"], seed_file_name), - platform="pc,osx_gl", + platform="pc,mac", ) def get_platform_assets(asset_name_list: List[str]) -> Dict[str, List[str]]: @@ -769,7 +828,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): for asset_name in asset_name_list: if "pc" in file_platforms[asset_name]: win_assets.append(asset_name) - if "osx_gl" in file_platforms[asset_name]: + if "mac" in file_platforms[asset_name]: mac_assets.append(asset_name) return {"win": win_assets, "mac": mac_assets} @@ -798,7 +857,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): # Get platform result file names win_asset_list_file = helper.platform_file_name(request_file, platforms["pc"]) - mac_asset_list_file = helper.platform_file_name(request_file, platforms["osx_gl"]) + mac_asset_list_file = helper.platform_file_name(request_file, platforms["mac"]) # Get expected platforms for each asset in asset_names platform_files = get_platform_assets(asset_names) @@ -879,14 +938,14 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): # fmt:on # End verify_asset_list_contents() - def run_compare_command_and_verify(platform_arg: str, expect_pc_output: bool, expect_osx_gl_output: bool) -> None: + def run_compare_command_and_verify(platform_arg: str, expect_pc_output: bool, expect_mac_output: bool) -> None: # Expected asset list to equal result of comparison expected_pc_asset_list = None - expected_osx_gl_asset_list = None + expected_mac_asset_list = None # Last output file. Use this for comparison to 'expected' output_pc_asset_list = None - output_osx_gl_asset_list = None + output_mac_asset_list = None # Add the platform to the file name to match what the Bundler will create last_output_arg = output_arg.split(",")[-1] @@ -895,10 +954,10 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): expected_pc_asset_list = os.path.join(helper["test_dir"], helper.platform_file_name(expected_asset_list, platform)) output_pc_asset_list = helper.platform_file_name(last_output_arg, platform) - if expect_osx_gl_output: - platform = platforms["osx_gl"] - expected_osx_gl_asset_list = os.path.join(helper["test_dir"], helper.platform_file_name(expected_asset_list, platform)) - output_osx_gl_asset_list = helper.platform_file_name(last_output_arg, platform) + if expect_mac_output: + platform = platforms["mac"] + expected_mac_asset_list = os.path.join(helper["test_dir"], helper.platform_file_name(expected_asset_list, platform)) + output_mac_asset_list = helper.platform_file_name(last_output_arg, platform) # Build execution command cmd = generate_compare_command(platform_arg) @@ -911,15 +970,15 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): verify_asset_list_contents(expected_pc_asset_list, output_pc_asset_list) fs.delete([output_pc_asset_list], True, True) - if expect_osx_gl_output: - verify_asset_list_contents(expected_osx_gl_asset_list, output_osx_gl_asset_list) - fs.delete([output_osx_gl_asset_list], True, True) + if expect_mac_output: + verify_asset_list_contents(expected_mac_asset_list, output_mac_asset_list) + fs.delete([output_mac_asset_list], True, True) # End run_compare_command_and_verify() # Generate command, run and validate for each platform run_compare_command_and_verify("pc", True, False) - run_compare_command_and_verify("osx_gl", False, True) - run_compare_command_and_verify("pc,osx_gl", True, True) + run_compare_command_and_verify("mac", False, True) + run_compare_command_and_verify("pc,mac", True, True) #run_compare_command_and_verify(None, True, True) # End compare_and_check() @@ -1021,6 +1080,16 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): """ Tests that assetlists are created equivalent to the output while being created, and makes sure overwriting an existing file without the --allowOverwrites fails + + Test Steps: + 1. Check that Asset List creation requires PC platform flag + 2. Create a PC Asset List using asset info file and default seed lists using --print + 3. Validate all assets output are present in the asset list + 4. Create a seed file + 5. Attempt to overwrite Asset List without using --allowOverwrites + 6. Validate that command returned an error and file contents did not change + 7. Specifying platform but not "add" or "remove" should fail + 8. Verify file Has changed """ helper = bundler_batch_helper @@ -1046,7 +1115,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): "--addDefaultSeedListFiles", "--platform=pc", "--print", - f"--project={workspace.project}" + f"--project-path={workspace.project}" ], universal_newlines=True, ) @@ -1102,7 +1171,16 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): def test_WindowsAndMac_AP_BundleProcessing_BundleProcessedAtRuntime(self, workspace, bundler_batch_helper, asset_processor, request): # fmt:on - """Test to make sure the AP GUI will process a newly created bundle file""" + """ + Test to make sure the AP GUI will process a newly created bundle file + + Test Steps: + 1. Make asset list file (used for bundle creation) + 2. Start Asset Processor GUI + 3. Make bundle in /Bundles + 4. Validate file was created in Bundles folder + 5. Make sure bundle now exists in cache + """ # Set up helpers and variables helper = bundler_batch_helper @@ -1115,7 +1193,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): bundle_result_path = os.path.join(bundles_folder, helper.platform_file_name("bundle.pak", workspace.asset_processor_platform)) - bundle_cache_path = os.path.join(workspace.paths.platform_cache(), workspace.project, + bundle_cache_path = os.path.join(workspace.paths.platform_cache(), "Bundles", helper.platform_file_name("bundle.pak", workspace.asset_processor_platform)) @@ -1131,6 +1209,8 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): addSeed=level_pak, assetListFile=helper["asset_info_file_request"], ) + + # Run Asset Processor GUI result, _ = asset_processor.gui_process() assert result, "AP GUI failed" @@ -1155,14 +1235,22 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): @pytest.mark.assetpipeline # fmt:off def test_WindowsAndMac_FilesMarkedSkip_FilesAreSkipped(self, workspace, bundler_batch_helper): + """ + Test Steps: + 1. Create an asset list with a file marked as skip + 2. Verify file was created + 3. Verify that only the expected assets are present in the created asset list + """ expected_assets = [ - "libs/particles/milestone2particles.xml", - "textures/milestone2/particles/fx_sparkstreak_01.dds" + "ui/canvases/lyshineexamples/animation/multiplesequences.uicanvas", + "ui/textures/prefab/button_normal.sprite" ] bundler_batch_helper.call_assetLists( assetListFile=bundler_batch_helper['asset_info_file_request'], - addSeed="libs/particles/milestone2particles.xml", - skip="textures/milestone2/particles/fx_launchermuzzlering_01.dds,textures/milestone2/particles/fx_launchermuzzlefront_01.dds" + addSeed="ui/canvases/lyshineexamples/animation/multiplesequences.uicanvas", + skip="ui/textures/prefab/button_disabled.sprite,ui/scripts/lyshineexamples/animation/multiplesequences.luac," + "ui/textures/prefab/tooltip_sliced.sprite,ui/scripts/lyshineexamples/unloadthiscanvasbutton.luac,fonts/vera.fontfamily,fonts/vera-italic.font," + "fonts/vera.font,fonts/vera-bold.font,fonts/vera-bold-italic.font,fonts/vera-italic.ttf,fonts/vera.ttf,fonts/vera-bold.ttf,fonts/vera-bold-italic.ttf" ) assert os.path.isfile(bundler_batch_helper["asset_info_file_result"]) assets_in_list = [] @@ -1176,6 +1264,12 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): # fmt:off def test_WindowsAndMac_AssetListSkipOneOfTwoParents_SharedDependencyIsIncluded(self, workspace, bundler_batch_helper): + """ + Test Steps: + 1. Create Asset List with a parent asset that is skipped + 2. Verify that Asset List was created + 3. Verify that only the expected assets are present in the asset list + """ expected_assets = [ "testassets/bundlerskiptest_grandparent.dynamicslice", "testassets/bundlerskiptest_parenta.dynamicslice", @@ -1204,6 +1298,13 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): @pytest.mark.assetpipeline # fmt:off def test_WindowsAndMac_AssetLists_SkipRoot_ExcludesAll(self, workspace, bundler_batch_helper): + """ + Negative scenario test that skips the same file being used as the parent seed. + + Test Steps: + 1. Create an asset list that skips the root asset + 2. Verify that asset list was not generated + """ result, _ = bundler_batch_helper.call_assetLists( assetListFile=bundler_batch_helper['asset_info_file_request'], @@ -1220,6 +1321,13 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): @pytest.mark.assetpipeline # fmt:off def test_WindowsAndMac_AssetLists_SkipUniversalWildcard_ExcludesAll(self, workspace, bundler_batch_helper): + """ + Negative scenario test that uses the all wildcard when generating an asset list. + + Test Steps: + 1. Create an Asset List while using the universal all wildcard "*" + 2. Verify that asset list was not generated + """ result, _ = bundler_batch_helper.call_assetLists( assetListFile=bundler_batch_helper['asset_info_file_request'], diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py index e329846554..0c6924f3a2 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py @@ -67,7 +67,19 @@ class TestsAssetProcessorBatch_DependenycyTests(object): libs/materialeffects/surfacetypes.xml is listed as an entry engine_dependencies.xml libs/materialeffects/surfacetypes.xml is not listed as a missing dependency in the 'assetprocessorbatch' console output + + Test Steps: + 1. Assets are pre-processed + 2. Verify that engine_dependencies.xml exists + 3. Verify engine_dependencies.xml has surfacetypes.xml present + 4. Run Missing Dependency scanner against the engine_dependenciese.xml + 5. Verify that Surfacetypes.xml is NOT in the missing depdencies output + 6. Add the schema file which allows our xml parser to understand dependencies for our engine_dependencies file + 7. Process assets + 8. Run Missing Dependency scanner against the engine_dependenciese.xml + 9. Verify that surfacetypes.xml is in the missing dependencies out """ + env = ap_setup_fixture BATCH_LOG_PATH = env["ap_batch_log_file"] asset_processor.create_temp_asset_root() @@ -137,6 +149,11 @@ class TestsAssetProcessorBatch_DependenycyTests(object): def test_WindowsMacPlatforms_BatchCheckSchema_ValidateErrorChecking(self, workspace, asset_processor, ap_setup_fixture, folder, schema): # fmt:on + """ + Test Steps: + 1. Run the Missing Dependency Scanner against everything + 2. Verify that there are no missing dependencies. + """ env = ap_setup_fixture def missing_dependency_log_lines(log) -> [str]: diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests2.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests2.py index 4f33e0df4e..f184ff2392 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests2.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests2.py @@ -60,6 +60,15 @@ class TestsAssetProcessorBatch_DependenycyTests(object): Verify that Schemas can be loaded via Gems utilizing the fonts schema :returns: None + + Test Steps: + 1. Run Missing Dependency Scanner against %fonts%.xml when no fonts are present + 2. Verify fonts are scanned + 3. Verify that missing dependencies are found for fonts + 4. Add fonts to game project + 5. Run Missing Dependency Scanner against %fonts%.xml when fonts are present + 6. Verify that same amount of fonts are scanned + 7. Verify that there are no missing dependencies. """ schema_name = "Font.xmlschema" asset_processor.create_temp_asset_root() diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests.py index 50b3af1438..3efd9e7fce 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests.py @@ -100,9 +100,17 @@ class TestsAssetProcessorBatch_AllPlatforms(object): @pytest.mark.BAT @pytest.mark.assetpipeline def test_RunAPBatch_TwoPlatforms_ExitCodeZero(self, asset_processor): + """ + Tests Process assets for PC & Mac and verifies that processing exited without error + + Test Steps: + 1. Add Mac and PC as enabled platforms + 2. Process Assets + 3. Validate that AP exited cleanly + """ asset_processor.create_temp_asset_root() asset_processor.enable_asset_processor_platform("pc") - asset_processor.enable_asset_processor_platform("osx_gl") + asset_processor.enable_asset_processor_platform("mac") result, _ = asset_processor.batch_process() assert result, "AP Batch failed" @@ -111,6 +119,14 @@ class TestsAssetProcessorBatch_AllPlatforms(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id('C1571826') def test_RunAPBatch_OnlyIncludeInvalidAssets_NoAssetsAdded(self, asset_processor, ap_setup_fixture): + """ + Tests processing invalid assets and validating that no assets were moved to the cache + + Test Steps: + 1. Create a test environment with invalid assets + 2. Run asset processor + 3. Validate that no assets were found in the cache + """ asset_processor.prepare_test_environment(ap_setup_fixture["tests_dir"], "test_ProcessAssets_OnlyIncludeInvalidAssets_NoAssetsAdded") result, _ = asset_processor.batch_process() @@ -127,6 +143,16 @@ class TestsAssetProcessorBatch_AllPlatforms(object): "recognized as failing in the logs. There appears to be a window where the AutoFailJob doesn't complete" "before the shutdown completes and the failure doesn't end up counting") def test_ProcessAssets_IncludeTwoAssetsWithSameProduct_FailingOnSecondAsset(self, asset_processor, ap_setup_fixture): + """ + Tests processing two source assets with the same product file and validates that the second source will error + + Test Steps: + 1. Create a test environment that has two source files with the same product + 2. Run asset processor + 3. Validate that 1 asset failed to process + 4. Validate that only one product file with the expected name is found in the cache + """ + asset_processor.prepare_test_environment(ap_setup_fixture["tests_dir"], "test_ProcessAssets_IncludeTwoAssetsWithSameProduct_FailingOnSecondAsset") result, output = asset_processor.batch_process(capture_output = True, expect_failure = True) @@ -143,6 +169,17 @@ class TestsAssetProcessorBatch_AllPlatforms(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id('C1587615') def test_ProcessAndDeleteCache_APBatchShouldReprocess(self, asset_processor, ap_setup_fixture): + """ + Tests processing once, deleting the generated cache, then processing again and validates the cache is created + + Test Steps: + 1. Run asset processor + 2. Compare the cache with expected output + 3. Delete Cache + 4. Compare the cache with expected output to verify that cache is gone + 5. Run asset processor with fastscan disabled + 6. Compare the cache with expected output + """ # Deleting assets from Cache will make them re-processed in AP (after start) # Copying test assets to project folder and deleting them from cache to make sure APBatch will process them @@ -174,6 +211,18 @@ class TestsAssetProcessorBatch_AllPlatforms(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id('C1591564') def test_ProcessAndChangeSource_APBatchShouldReprocess(self, asset_processor, ap_setup_fixture): + """ + Tests reprocessing of a modified asset and verifies that it was reprocessed + + Test Steps: + 1. Prepare test environment and copy test asset over + 2. Run asset processor + 3. Verify asset processed + 4. Verify asset is in cache + 4. Modify asset + 5. Re-run asset processor + 6. Verify asset was processed + """ # AP Batch Processing changed files (after start) # Copying test assets to project folder and deleting them from cache to make sure APBatch will process them @@ -208,6 +257,18 @@ class TestsAssetProcessorBatch_AllPlatforms(object): @pytest.mark.BAT @pytest.mark.assetpipeline def test_ProcessByBothApAndBatch_Md5ShouldMatch(self, asset_processor, ap_setup_fixture): + """ + Tests that a cache generated by AP GUI is the same as AP Batch + + Test Steps: + 1. Create test environment with test assets + 2. Call asset processor batch + 3. Get checksum for file cache + 4. Clean up test environment + 5. Call asset processor gui with quitonidle + 6. Get checksum for file cache + 7. Verify that checksums are equal + """ # AP Batch and AP app processed assets MD5 sums should be the same # Copying test assets to project folder and deleting them from cache to make sure APBatch will process them @@ -240,6 +301,16 @@ class TestsAssetProcessorBatch_AllPlatforms(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id('C1612446') def test_AddSameAssetsDifferentNames_ShouldProcess(self, asset_processor, ap_setup_fixture): + """ + Tests Asset Processing of duplicate assets with different names and verifies that both assets are processed + + Test Steps: + 1. Create test environment with two identical source assets with different names + 2. Run asset processor + 3. Verify that assets didn't fail to process + 4. Verify the correct number of jobs were performed + 5. Verify that product files are in the cache + """ # Feed two similar slices and texture with different names - should process without any issues # Copying test assets to project folder and deleting them from cache to make sure APBatch will process them @@ -277,6 +348,19 @@ class TestsAssetProcessorBatch_AllPlatforms(object): "recognized as failing in the logs. There appears to be a window where the AutoFailJob doesn't complete" "before the shutdown completes and the failure doesn't end up counting") def test_AddTwoTexturesWithSameName_ShouldProcessAfterRename(self, asset_processor, ap_setup_fixture): + """ + Tests processing of two textures with the same name then verifies that AP will successfully process after + renaming one of the textures + + Test Steps: + 1. Create test environment with two textures that have the same name + 2. Launch Asset Processor + 3. Validate that Asset Processor generates an error + 4. Rename texture files + 5. Run asset processor + 6. Verify that asset processor does not error + 7. Verify that expected product files are in the cache + """ # Feed two different textures with same name (but different extensions) - ap will fail # Rename one of textures and failure should go away @@ -312,6 +396,15 @@ class TestsAssetProcessorBatch_AllPlatforms(object): @pytest.mark.BAT @pytest.mark.assetpipeline def test_InvalidServerAddress_Warning_Logs(self, asset_processor): + """ + Tests running Asset Processor with an invalid server address and verifies that AP returns a warning about + an invalid server address + + Test Steps: + 1. Launch asset processor while providing an invalid server address + 2. Verify asset processor does not fail + 3. Verify that asset processor generated a warning informing the user about an invalid server address + """ asset_processor.create_temp_asset_root() # Launching AP and making sure that the warning exists @@ -327,6 +420,12 @@ class TestsAssetProcessorBatch_AllPlatforms(object): def test_AllSupportedPlatforms_IncludeValidAssets_AssetsProcessed(self, asset_processor, ap_setup_fixture): """ AssetProcessorBatch is successfully processing newly added assets + + Test Steps: + 1. Create a test environment with test assets + 2. Launch Asset Processor + 3. Verify that asset processor does not fail to process + 4. Verify assets are not missing from the cache """ env = ap_setup_fixture @@ -350,6 +449,14 @@ class TestsAssetProcessorBatch_AllPlatforms(object): def test_AllSupportedPlatforms_DeletedAssets_DeletedFromCache(self, asset_processor, ap_setup_fixture): """ AssetProcessor successfully deletes cached items when removed from project + + Test Steps: + 1. Create a test environment with test assets + 2. Run asset processor + 3. Verify expected assets are in the cache + 4. Delete test assets + 5. Run asset processor + 6. Verify expected assets are in the cache """ env = ap_setup_fixture @@ -385,6 +492,10 @@ class TestsAssetProcessorBatch_AllPlatforms(object): """ Tests that when cache is deleted (no cache) and AssetProcessorBatch runs, it successfully starts and processes assets. + + Test Steps: + 1. Run asset processor + 2. Verify asset processor exits cleanly """ asset_processor.create_temp_asset_root() @@ -402,6 +513,14 @@ class TestsAssetProcessorBatch_AllPlatforms(object): # fmt:on """ AssetProcessor successfully recovers assets from cache when deleted. + + Test Steps: + 1. Create test enviornment with test assets + 2. Run Asset Processor and verify it exits cleanly + 3. Make sure cache folder was generated + 4. Delete temp cache assets but leave database behind + 5. Run asset processor and verify it exits cleanly + 6. Verify expected files were generated in the cache """ env = ap_setup_fixture @@ -434,6 +553,14 @@ class TestsAssetProcessorBatch_AllPlatforms(object): @pytest.mark.assetpipeline # fmt:off def test_AllSupportedPlatforms_RunFastScanOnEmptyCache_FullScanRuns(self, ap_setup_fixture, asset_processor): + """ + Tests fast scan processing on an empty cache and verifies that a full analyis will be peformed + + Test Steps: + 1. Create a test environment + 2. Execute asset processor batch with fast scan enabled + 3. Verify that a full analysis is performed + """ # fmt:on env = ap_setup_fixture asset_processor.create_temp_asset_root() @@ -455,6 +582,11 @@ class TestsAssetProcessorBatch_AllPlatforms(object): """ After running the APBatch and AP GUI, Logs directory should exist (C1564055), JobLogs, Batch log, and GUI log should exist in the logs directory (C1564056) + + Test Steps: + 1. Run asset processor batch + 2. Run asset processor gui with quit on idle + 3. Verify that logs exist for both AP Batch & AP GUI """ asset_processor.create_temp_asset_root() LOG_PATH = { @@ -536,6 +668,11 @@ class TestsAssetProcessorBatch_AllPlatforms(object): """ Utilizing corrupted test assets, run the batch process to verify the AP logs the failure to process the corrupted file. + + Test Steps: + 1. Create test environment with corrupted slice + 2. Launch Asset Processor + 3. Verify that asset processor fails to process corrupted slice """ env = ap_setup_fixture error_line_found = False @@ -552,6 +689,15 @@ class TestsAssetProcessorBatch_AllPlatforms(object): @pytest.mark.BAT @pytest.mark.assetpipeline def test_validateDirectPreloadDependency_Found(self, asset_processor, ap_setup_fixture, workspace): + """ + Tests processing an asset with a circular dependency and verifies that Asset Processor will return an error + notifying the user about a circular dependency. + + Test Steps: + 1. Create test environment with an asset that has a circular dependency + 2. Launch asset processor + 3. Verify that error is returned informing the user that the asset has a circular dependency + """ env = ap_setup_fixture error_line_found = False @@ -567,6 +713,15 @@ class TestsAssetProcessorBatch_AllPlatforms(object): @pytest.mark.BAT @pytest.mark.assetpipeline def test_validateNestedPreloadDependency_Found(self, asset_processor, ap_setup_fixture, workspace): + """ + Tests processing of a nested circular dependency and verifies that Asset Processor will return an error + notifying the user about a circular depdency + + Test Steps: + 1. Create test environment with an asset that has a nested circular dependency + 2. Launch asset processor + 3. Verify that error is returned informing the user that the asset has a circular dependency + """ env = ap_setup_fixture error_line_found = False diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests_2.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests_2.py index 5c42af2139..fec5df8eb7 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests_2.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests_2.py @@ -80,6 +80,15 @@ class TestsAssetProcessorBatch_AllPlatforms(object): # fmt:on """ Tests that fast scan mode can be used and is faster than full scan mode. + + Test Steps: + 1. Ensure all assets are processed + 2. Run Asset Processor without fast scan and measure the time it takes to run + 3. Capture Full Analysis was performed and number of assets processed + 4. Run Asset Processor with full scan and measure the time it takes to run + 5. Capture Full Analysis wans't performed and number of assets processed + 6. Verify that fast scan was faster than full scan + 7. Verify that full scan scanned more assets """ asset_processor.create_temp_asset_root() @@ -111,76 +120,23 @@ class TestsAssetProcessorBatch_AllPlatforms(object): assert full_scan_time > fast_scan_time, "Fast scan was slower that full scan" assert full_scan_analysis[0] > fast_scan_analysis[0], "Full scan did not process more assets than fast scan" - @pytest.mark.test_case_id("C18787404") - @pytest.mark.BAT - @pytest.mark.assetpipeline - @pytest.mark.skip(reason="External project is currently broken.") # LY-119863 - def test_AllSupportedPlatforms_ExternalProject_APRuns(self, workspace, ap_external_project_setup_fixture): - - external_resources = ap_external_project_setup_fixture - logger.info(f"Running external project test at path {external_resources['project_dir']}") - # Delete existing "external project" build if it exists - if os.path.exists(external_resources["project_dir"]): - fs.delete([external_resources["project_dir"]], True, True) - - # fmt:off - assert not os.path.exists(external_resources["project_dir"]), \ - f'{external_resources["project_dir"]} was not deleted' - # fmt:on - - lmbr_cmd = [ - workspace.paths.lmbr(), - "projects", - "create", - external_resources["project_name"], - "--template", - "EmptyTemplate", - "--app-root", - external_resources["project_dir"], - ] - - logger.info(f"Running lmbr projects create command '{lmbr_cmd}'") - - try: - subprocess.check_call(lmbr_cmd) - except subprocess.CalledProcessError as e: - assert False, f"lmbr projects create failed\n{e.stderr}" - - logger.info("...lmbr finished") - assert os.path.exists(external_resources["project_dir"]), "Project folder was not created" - - # AssetProcessor for new External project. Uses mock workspace to emulate external project workspace - external_ap = AssetProcessor(external_resources["external_workspace"]) - - # fmt:off - assert external_ap.batch_process(fastscan=False), \ - "Asset Processor Batch failed on external project" - # fmt:on - - # Parse log looking for errors or failures - log = APLogParser(workspace.paths.ap_batch_log()) - failures, errors = log.runs[-1]["Failures"], log.runs[-1]["Errors"] - assert failures == 0, f"There were {failures} asset processing failures" - assert errors == 0, f"There were {errors} asset processing errors" - - # Check that project cache was created (DNE until AP makes it) - project_cache = os.path.join(external_resources["project_dir"], "Cache") - assert os.path.exists(project_cache), f"{project_cache} was not created by AP" - - # Clean up external project - fs.delete([external_resources["project_dir"]], True, True) - - # fmt:off - assert not os.path.exists(external_resources["project_dir"]), \ - f"{external_resources['project_dir']} was not deleted" - # fmt:on - @pytest.mark.test_case_id("C4874121") @pytest.mark.BAT @pytest.mark.assetpipeline @pytest.mark.parametrize("clear_type", ["rewrite", "delete_asset", "delete_dir"]) def test_AllSupportedPlatforms_DeleteBadAssets_BatchFailedJobsCleared( self, workspace, request, ap_setup_fixture, asset_processor, clear_type): + """ + Tests the ability of Asset Processor to recover from processing of bad assets by removing them from scan folder + + Test Steps: + 1. Create testing environment with good and multiple bad assets + 2. Run Asset Processor + 3. Verify that bad assets fail to process + 4. Fix a bad asset & delete the others + 5. Run Asset Processor + 6. Verify Asset Processor does not have any asset failues + """ env = ap_setup_fixture error_search_terms = ["WWWWWWWWWWWW"] @@ -250,6 +206,14 @@ class TestsAssetProcessorBatch_Windows(object): Verify the AP batch and Gui can run and process assets independent of the Editor We do not want or need to kill running Editors here as they can be involved in other tests or simply being run locally in this branch or another + + Test Steps: + 1. Create temporary testing environment + 2. Run asset processor GUI + 3. Verify AP GUI doesn't error + 4. Stop AP GUI + 5. Run Asset Processor Batch with Fast Scan + 5. Verify Asset Processor Batch exits cleanly """ asset_processor.create_temp_asset_root() @@ -272,6 +236,11 @@ class TestsAssetProcessorBatch_Windows(object): """ Request a run for an invalid platform "AssetProcessor: Error: Platform in config file or command line 'notaplatform'" should be present in the logs + + Test Steps: + 1. Create temporary testing environment + 2. Run Asset Processor with an invalid platform + 3. Check that asset processor returns an Error notifying the user that the invalid platform is not supported """ asset_processor.create_temp_asset_root() error_search_terms = 'AssetProcessor: Error: The list of enabled platforms in the settings registry does not contain platform ' \ diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_gui_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_gui_tests.py index ed5651755c..88fa1a77b4 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_gui_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_gui_tests.py @@ -77,6 +77,13 @@ class TestsAssetProcessorGUI_Windows(object): def test_SendInputOnControlChannel_ReceivedAndResponded(self, asset_processor): """ Test that the control channel connects and that communication works both directions + + Test Steps: + 1. Start Asset Processor + 2. Send a Ping message to Asset Processor + 3. Listen for Asset Processor response + 4. Verify Asset Processor responds + 5. Stop asset Processor """ asset_processor.create_temp_asset_root() @@ -129,7 +136,15 @@ class TestsAssetProcessorGUI_Windows(object): # fmt:on """ Asset Processor Deletes processed assets when source is removed from project folder (while running) + + Test Steps: + 1. Create a temporary test environment + 2. Run Asset Processor GUI set to stay open on idle and verify that it does not fail + 3. Verify that assets were copied to the cache + 4. Delete the source test asset directory + 5. Verify assets are deleted from the cache """ + env = ap_setup_fixture # Copy test assets to project folder and verify test assets folder exists @@ -170,7 +185,18 @@ class TestsAssetProcessorGUI_Windows(object): # fmt:on """ Processing changed files (while running) + + Test Steps: + 1. Create temporary test environment with test assets + 2. Open Asset Processor GUI with set to stay open after idle and verify it does not fail + 3. Verify contents of source asset for later comparison + 4. Verify contents of product asset for later comparison + 5. Modify contents of source asset + 6. Wait for Asset Processor to go back to idle state + 7. Verify contents of source asset are the modified version + 8. Verify contents of product asset are the modified version """ + env = ap_setup_fixture # Copy test assets to project folder and verify test assets folder exists @@ -184,7 +210,7 @@ class TestsAssetProcessorGUI_Windows(object): result, _ = asset_processor.gui_process(quitonidle=False) assert result, "AP GUI failed" - # Verify contents of test asset in project folder before modication + # Verify contents of test asset in project folder before modification with open(project_asset_path, "r") as project_asset_file: assert project_asset_file.read() == "before_state" @@ -217,7 +243,14 @@ class TestsAssetProcessorGUI_Windows(object): def test_WindowsPlatforms_RunAP_ProcessesIdle(self, asset_processor): """ Asset Processor goes idle + + Test Steps: + 1. Create a temporary testing evnironment + 2. Run Asset Processor GUI without quitonidle + 3. Verify AP Goes Idle + 4. Verify AP goes below 1% CPU usage """ + CPU_USAGE_THRESHOLD = 1.0 # CPU usage percentage delimiting idle from active CPU_USAGE_WIND_DOWN = 10 # Time allowed in seconds for idle processes to stop using CPU @@ -245,7 +278,16 @@ class TestsAssetProcessorGUI_Windows(object): ): """ Processing newly added files to project folder (while running) + + Test Steps: + 1. Create a temporary testing environment with test assets + 2. Create a secondary set of testing assets that have not been copied into the the testing environment + 3. Start Asset Processor without quitonidle + 4. While Asset Processor is running add secondary set of testing assets to the testing environment + 5. Wait for Asset Processor to go idle + 6. Verify that all assets are in the cache """ + env = ap_setup_fixture level_name = "C1564064_level" new_asset = "C1564064.scriptcanvas" @@ -316,7 +358,14 @@ class TestsAssetProcessorGUI_Windows(object): def test_WindowsPlatforms_LaunchAP_LogReportsIdle(self, asset_processor, workspace, ap_idle): """ Asset Processor creates a log entry when it goes idle + + Test Steps: + 1. Create temporary testing environment + 2. Run Asset Processor batch to pre-process assets + 3. Run Asset Processor GUI + 4. Check if Asset Processor GUI reports that it has gone idle """ + asset_processor.create_temp_asset_root() # Run batch process to ensure project assets are processed assert asset_processor.batch_process(), "AP Batch failed" @@ -331,6 +380,17 @@ class TestsAssetProcessorGUI_Windows(object): @pytest.mark.assetpipeline def test_APStopTimesOut_ExceptionThrown(self, ap_setup_fixture, asset_processor): + """ + Tests whether or not Asset Processor will Time Out + + Test Steps: + 1. Create a temporary testing environment + 2. Start the Asset Processor + 3. Copy in assets to the test environment + 4. Try to stop the Asset Processor with a timeout of 1 second (This cannot be done manually). + 5. Verify that Asset Processor times out and returns the expected error + """ + asset_processor.create_temp_asset_root() asset_processor.start() @@ -347,9 +407,20 @@ class TestsAssetProcessorGUI_Windows(object): @pytest.mark.assetpipeline def test_APStopDefaultTimeout_NoException(self, asset_processor): - # If this test fails, it means other tests using the default timeout may have issues. - # In that case, either the default timeout should either be raised, or the performance - # of AP launching should be improved. + """ + Tests the default timeout of the Asset Processor + + If this test fails, it means other tests using the default timeout may have issues. + In that case, either the default timeout should either be raised, or the performance + of AP launching should be improved. + + Test Steps: + 1. Create a temporary testing environment + 2. Start the Asset Processor + 3. Stop the asset Processor without sending a timeout to it + 4. Verify that the asset processor times out and returns the expected error + """ + asset_processor.create_temp_asset_root() asset_processor.start() ap_quit_timed_out = False diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_gui_tests_2.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_gui_tests_2.py index b25ee081a1..3fb9ae5a81 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_gui_tests_2.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_gui_tests_2.py @@ -75,10 +75,17 @@ class TestsAssetProcessorGUI_WindowsAndMac(object): @pytest.mark.test_case_id("C3540434") @pytest.mark.BAT @pytest.mark.assetpipeline - def test_WindowsAndMacPlatforms_AP_GUI_FastScanSettingCreated(self, asset_processor, fast_scan_backup): + def test_WindowsAndMacPlatforms_GUIFastScanNoSettingSet_FastScanSettingCreated(self, asset_processor, fast_scan_backup): """ Tests that a fast scan settings entry gets created for the AP if it does not exist and ensures that the entry is defaulted to fast-scan enabled + + Test Steps: + 1. Create temporary testing environment + 2. Delete existing fast scan setting if exists + 3. Run Asset Processor GUI without setting FastScan setting (default:true) and without quitonidle + 4. Wait and check to see if Windows Registry fast scan setting is created + 5. Verify that Fast Scan setting is set to true """ asset_processor.create_temp_asset_root() @@ -119,6 +126,14 @@ class TestsAssetProcessorGUI_WindowsAndMac(object): Make sure game launcher working with Asset Processor set to turbo mode Validate that no fatal errors (crashes) are reported within a certain time frame for the AP and the GameLauncher + + Test Steps: + 1. Create temporary testing environment + 2. Set fast scan to true + 3. Verify fast scan is set to true + 4. Launch game launcher + 5. Verify launcher has launched without error + 6. Verify that asset processor has launched """ CHECK_ALIVE_SECONDS = 15 @@ -166,6 +181,14 @@ class TestsAssetProcessorGUI_AllPlatforms(object): # fmt:on """ Deleting slices and uicanvases while AP is running + + Test Steps: + 1. Create temporary testing environment with test assets + 2. Launch Asset Processor and wait for it to go idle + 3. Verify product assets were created in the cache + 4. Delete test assets from the cache + 5. Wait for Asset Processor to go idle + 6. Verify product assets were regenerated in the cache """ env = ap_setup_fixture @@ -201,6 +224,15 @@ class TestsAssetProcessorGUI_AllPlatforms(object): ): """ Process slice files and uicanvas files from the additional scanfolder + + Test Steps: + 1. Create temporary testing environment + 2. Run asset processor batch + 3. Validate that product assets were generated in the cache + 4. Create an additional scan folder with assets + 5. Create additional scan folder params to pass to Asset Processor + 6. Run Asset Processor GUI with QuitOnIdle and pass in params for the additional scan folder settings + 7. Verify additional product assets from additional scan folder are present in the cache """ env = ap_setup_fixture # Copy test assets to new folder in dev folder @@ -250,6 +282,12 @@ class TestsAssetProcessorGUI_AllPlatforms(object): """ Launch AP with invalid address in bootstrap.cfg Assets should process regardless of the new address + + Test Steps: + 1. Create a temporary testing environment + 2. Set an invalid ip address in Asset Processor settings file + 3. Launch Asset Processor GUI + 4. Verify that it processes assets and exits cleanly even though it has an invalid IP. """ test_ip_address = "1.1.1.1" # an IP address without Asset Processor @@ -269,6 +307,14 @@ class TestsAssetProcessorGUI_AllPlatforms(object): def test_AllSupportedPlatforms_ModifyAssetInfo_AssetsReprocessed(self, ap_setup_fixture, asset_processor): """ Modifying assetinfo files triggers file reprocessing + + Test Steps: + 1. Create temporary testing environment with test assets + 2. Run Asset Processor GUI + 3. Verify that Asset Processor exited cleanly and product assets are in the cache + 4. Modify the .assetinfo file by adding a newline + 5. Wait for Asset Processor to go idle + 6. Verify that product files were regenerated (Time Stamp compare) """ env = ap_setup_fixture diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_relocator_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_relocator_tests.py index 2d3872bf31..30044fa9e2 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_relocator_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_relocator_tests.py @@ -85,6 +85,18 @@ class TestsAssetRelocator_WindowsAndMac(object): def test_WindowsMacPlatforms_RelocatorMoveFileWithConfirm_MoveSuccess(self, request, workspace, asset_processor, ap_setup_fixture, testId, readonly, confirm, success): + """ + Tests whether tests with Move File Confirm are successful + + Test Steps: + 1. Create temporary testing environment + 2. Set move location + 3. Determine if confirm flag is set + 4. Attempt to move the files + 5. If confirm flag set: + * Validate Move was successful + * Else: Validate move was not successful + """ env = ap_setup_fixture copied_asset = '' @@ -141,6 +153,11 @@ class TestsAssetRelocator_WindowsAndMac(object): User should be warned that LeaveEmptyFolders needs to be used with the move or delete command :return: None + + Test Steps: + 1. Create temporary testing environment + 2. Attempt to move with --LeaveEmptyFolders set + 3. Verify user is given a message that command requires to be used with --move or --delete """ env = ap_setup_fixture expected_message = "Command --leaveEmptyFolders must be used with command --move or --delete" @@ -162,6 +179,11 @@ class TestsAssetRelocator_WindowsAndMac(object): Asset with UUID/AssetId reference in non-standard format is successfully scanned and relocated to the MoveOutput folder. This test uses a pre-corrupted .slice file. + + Test Steps: + 1. Create temporary testing environment with a corrupted slice + 2. Attempt to move the corrupted slice + 3. Verify that corrupted slice was moved successfully """ env = ap_setup_fixture @@ -194,6 +216,11 @@ class TestsAssetRelocator_WindowsAndMac(object): def test_WindowsMacPlatforms_UpdateReferences_MoveCommandMessage(self, ap_setup_fixture, asset_processor): """ UpdateReferences without move or delete + + Test Steps: + 1. Create temporary testing environment + 2. Attempt to move with UpdateReferences but without move or delete flags + 3. Verify that message is returned to the user that additional flags are required """ env = ap_setup_fixture expected_message = "Command --updateReferences must be used with command --move" @@ -215,6 +242,11 @@ class TestsAssetRelocator_WindowsAndMac(object): """ When running the relocator command --AllowBrokenDependencies without the move or delete flags, the user should be warned that the flags are necessary for the functionality to be used + + Test Steps: + 1. Create temporary testing environment + 2. Attempt to move with AllowBrokenDependencies without the move or delete flag + 3. Verify that message is returned to the user that additional flags are required """ env = ap_setup_fixture @@ -302,10 +334,19 @@ class TestsAssetRelocator_WindowsAndMac(object): project ): """ + Dynamic data test for deleting a file with Asset Relocator: + C21968355 Delete a file with confirm C21968356 Delete a file without confirm C21968359 Delete a file that is marked as ReadOnly C21968360 Delete a file that is not marked as ReadOnly + + Test Steps: + 1. Create temporary testing environment + 2. Set the read-only status of the file based on the test case + 3. Run asset relocator with --delete and the confirm status based on the test case + 4. Assert file existence or nonexistence based on the test case + 5. Validate the relocation report based on expected and unexpected messages """ env = ap_setup_fixture test_file = "testFile.txt" @@ -430,6 +471,15 @@ class TestsAssetRelocator_WindowsAndMac(object): Test the LeaveEmptyFolders flag in various configurations :returns: None + + Test Steps: + 1. Create temporary testing environment + 2. Build the various move/delete commands here based on test data + 3. Run the move command with the various triggers based on test data + 4. Verify the original assets folder still exists based on test data + 5. Verify the files successfully moved to new location based on test data + 6. Verify that the files were removed from original location based on test data + 7. Verify the files have not been deleted or moved from original location based on test data """ # # Start test setup # # env = ap_setup_fixture @@ -517,6 +567,12 @@ class TestsAssetRelocator_WindowsAndMac(object): """ The test will attempt to move test assets that are not tracked under P4 source control using the EnableSCM flag Because the files are not tracked by source control, the relocation should fail + + Test Steps: + 1. Create temporary testing environment + 2. Set ReadOnly or Not-ReadOnly for the test files based on test data + 3. Generate and run the enableSCM command + 4. Verify the move failed and expected messages are present """ # Move the test assets into the project folder env = ap_setup_fixture @@ -1037,6 +1093,13 @@ class TestsAssetRelocator_WindowsAndMac(object): C21968370 AllowBrokenDependencies with move and confirm C21968371 AllowBrokenDependencies with move and without confirm C21968375 AllowBrokenDependencies with delete + + Test Steps: + 1. Create temporary testing environment + 2. Run Asset Processor to Process Assets + 3. Build primary AP Batch parameter value and destination paths + 4. Validate resulting file paths in source and output directories + 5. Validate the log based on expected and unexpected messages """ env = ap_setup_fixture all_test_asset_rel_paths = [ @@ -1254,6 +1317,18 @@ class TestsAssetRelocator_WindowsAndMac(object): @pytest.mark.parametrize("test", tests) def test_WindowsAndMac_MoveMetadataFiles_PathExistenceAndMessage(self, workspace, request, ap_setup_fixture, asset_processor, test): + """ + Tests whether moving metadata files can be moved + + Test Steps: + 1. Create temporary testing environment + 2. Determine if using wildcards on paths or not + 3. Determine if excludeMetaDataFiles is set or not + 4. Build primary AP Batch parameter value and destination paths + 5. Build and run the AP Batch command with parameters + 6. Validate resulting file paths in source and output directories + 7. Validate the log based on expected and unexpected messages + """ env = ap_setup_fixture def teardown(): @@ -1342,7 +1417,7 @@ class TestsAssetRelocator_WindowsAndMac(object): @dataclass class MoveTest: - description: str # test case title directly copied from Testrail + description: str # test case title asset_folder: str # which folder in ./assets will be used for this test encoded_command: str # the command to execute encoded_output_dir: str # the destination directory to validate @@ -1350,7 +1425,7 @@ class MoveTest: name_change_map: dict = None files_that_stay: List[str] = field(default_factory=lambda: []) output_messages: List[str] = field(default_factory=lambda: []) - step: str = None # the step of the test from Testrail + step: str = None # the step of the test from test repository prefix_commands: List[str] = field(default_factory=lambda: ["AssetProcessorBatch", "--zeroAnalysisMode"]) suffix_commands: List[str] = field(default_factory=lambda: ["--confirm"]) env: dict = field(init=False, default=None) # inject the ap_setup_fixture at runtime @@ -3718,7 +3793,18 @@ class TestsAssetProcessorMove_WindowsAndMac: # -k C19462747 @pytest.mark.parametrize("test", move_a_file_tests + move_a_folder_tests) - def test_WindowsMacPlatforms_MoveCommand(self, asset_processor, ap_setup_fixture, test: MoveTest, project): + def test_WindowsMacPlatforms_MoveCommand_CommandResult(self, asset_processor, ap_setup_fixture, test: MoveTest, project): + """ + + Test Steps: + 1. Create temporary testing environment based on test data + 2. Validate that temporary testing environment was created successfully + 3. Execute the move command based upon the test data + 4. Validate that files are where they're expected according to the test data + 5. Validate unexpected files are not found according to the test data + 6. Validate output messages according to the test data + 7. Validate move status according to the test data + """ source_folder, _ = asset_processor.prepare_test_environment(ap_setup_fixture["tests_dir"], test.asset_folder) test.map_env(ap_setup_fixture, source_folder) diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/missing_dependency_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/missing_dependency_tests.py index 432b6cdfc8..74f6de1129 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/missing_dependency_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/missing_dependency_tests.py @@ -75,6 +75,15 @@ class TestsMissingDependencies_WindowsAndMac(object): def do_missing_dependency_test(self, source_product, expected_dependencies, dsp_param, platforms=None, max_iterations=0): + """ + Test Steps: + 1. Determine what platforms to run against + 2. Process assets for that platform + 3. Determine the missing dependency params to set + 4. Set the max iteration param + 5. Run missing dependency scanner against target platforms and search params based on test data + 6. Validate missing dependencies against test data + """ platforms = platforms or ASSET_PROCESSOR_PLATFORM_MAP[self._workspace.asset_processor_platform] if not isinstance(platforms, list): @@ -104,7 +113,14 @@ class TestsMissingDependencies_WindowsAndMac(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id("C17226567") def test_WindowsAndMac_ValidUUIDNotDependency_ReportsMissingDependency(self): - """Tests that a valid UUID referenced in a file will report any missing dependencies""" + """ + Tests that a valid UUID referenced in a file will report any missing dependencies + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test + """ # Relative path to the txt file with missing dependencies expected_product = f"testassets\\validuuidsnotdependency.txt" @@ -141,7 +157,14 @@ class TestsMissingDependencies_WindowsAndMac(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id("C17226567") def test_WindowsAndMac_InvalidUUIDsNotDependencies_NoReportedMessage(self): - """Tests that invalid UUIDs do not count as missing dependencies""" + """ + Tests that invalid UUIDs do not count as missing dependencies + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test + """ # Relative path to the txt file with invalid UUIDs expected_product = f"testassets\\invaliduuidnoreport.txt" expected_dependencies = [] # No expected missing dependencies @@ -153,7 +176,14 @@ class TestsMissingDependencies_WindowsAndMac(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id("C17226567") def test_WindowsAndMac_ValidAssetIdsNotDependencies_ReportsMissingDependency(self): - """Tests that valid asset IDs but not dependencies, show missing dependencies""" + """ + Tests that valid asset IDs but not dependencies, show missing dependencies + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test + """ # Relative path to the txt file with valid asset ids but not dependencies expected_product = f"testassets\\validassetidnotdependency.txt" @@ -173,7 +203,14 @@ class TestsMissingDependencies_WindowsAndMac(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id("C17226567") def test_WindowsAndMac_InvalidAssetsIDNotDependencies_NoReportedMessage(self): - """Tests that invalid asset IDs do not count as missing dependencies""" + """ + Tests that invalid asset IDs do not count as missing dependencies + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test + """ # Relative path to the txt file with invalid asset IDs expected_product = f"testassets\\invalidassetidnoreport.txt" @@ -188,7 +225,14 @@ class TestsMissingDependencies_WindowsAndMac(object): # fmt:off def test_WindowsAndMac_ValidSourcePathsNotDependencies_ReportsMissingDependencies(self): # fmt:on - """Tests that valid source paths can translate to missing dependencies""" + """ + Tests that valid source paths can translate to missing dependencies + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test + """ # Relative path to the txt file with missing dependencies as source paths expected_product = f"testassets\\relativesourcepathsnotdependencies.txt" @@ -212,7 +256,14 @@ class TestsMissingDependencies_WindowsAndMac(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id("C17226567") def test_WindowsAndMac_InvalidARelativePathsNotDependencies_NoReportedMessage(self): - """Tests that invalid relative paths do not resolve to missing dependencies""" + """ + Tests that invalid relative paths do not resolve to missing dependencies + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test + """ # Relative path to the txt file with invalid relative paths expected_product = f"testassets\\invalidrelativepathsnoreport.txt" @@ -227,7 +278,14 @@ class TestsMissingDependencies_WindowsAndMac(object): # fmt:off def test_WindowsAndMac_ValidProductPathsNotDependencies_ReportsMissingDependencies(self): # fmt:on - """Tests that valid product paths can resolve to missing dependencies""" + """ + Tests that valid product paths can resolve to missing dependencies + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test + """ self._asset_processor.add_source_folder_assets(f"Gems\\LyShineExamples\\Assets\\UI\\Fonts\\LyShineExamples") self._asset_processor.add_scan_folder(f"Gems\\LyShineExamples\\Assets") @@ -260,7 +318,14 @@ class TestsMissingDependencies_WindowsAndMac(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id("C17226567") def test_WindowsAndMac_WildcardScan_FindsAllExpectedFiles(self): - """Tests that the wildcard scanning will pick up multiple files""" + """ + Tests that the wildcard scanning will pick up multiple files + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test + """ helper = self._missing_dep_helper @@ -291,6 +356,11 @@ class TestsMissingDependencies_WindowsAndMac(object): For these references that are valid, all but one have available, matching dependencies. This test is primarily meant to verify that the missing dependency reporter checks the product dependency table before emitting missing dependencies. + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test """ # Relative path to target test file expected_product = f"testassets\\reportonemissingdependency.txt" @@ -305,7 +375,14 @@ class TestsMissingDependencies_WindowsAndMac(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id("C17226567") def test_WindowsAndMac_ReferencesSelfPath_NoReportedMessage(self): - """Tests that a file that references itself via relative path does not report itself as a missing dependency""" + """ + Tests that a file that references itself via relative path does not report itself as a missing dependency + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test + """ # Relative path to file that references itself via relative path expected_product = f"testassets\\selfreferencepath.txt" expected_dependencies = [] @@ -317,7 +394,14 @@ class TestsMissingDependencies_WindowsAndMac(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id("C17226567") def test_WindowsAndMac_ReferencesSelfUUID_NoReportedMessage(self): - """Tests that a file that references itself via its UUID does not report itself as a missing dependency""" + """ + Tests that a file that references itself via its UUID does not report itself as a missing dependency + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test + """ # Relative path to file that references itself via its UUID expected_product = f"testassets\\selfreferenceuuid.txt" @@ -330,7 +414,14 @@ class TestsMissingDependencies_WindowsAndMac(object): @pytest.mark.assetpipeline @pytest.mark.test_case_id("C17226567") def test_WindowsAndMac_ReferencesSelfAssetID_NoReportedMessage(self): - """Tests that a file that references itself via its Asset ID does not report itself as a missing dependency""" + """ + Tests that a file that references itself via its Asset ID does not report itself as a missing dependency + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test + """ # Relative path to file that references itself via its Asset ID expected_product = f"testassets\\selfreferenceassetid.txt" @@ -347,6 +438,11 @@ class TestsMissingDependencies_WindowsAndMac(object): Tests that the scan limit fails to find a missing dependency that is out of reach. The max iteration count is set to just under where a valid missing dependency is on a line in the file, so this will not report any missing dependencies. + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test """ # Relative path to file that has a missing dependency at 31 iterations deep @@ -364,7 +460,13 @@ class TestsMissingDependencies_WindowsAndMac(object): Tests that the scan limit succeeds in finding a missing dependency that is barely in reach. In the previous test, the scanner was set to stop recursion just before a missing dependency was found. This test runs with the recursion limit set deep enough to actually find the missing dependency. + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test """ + # Relative path to file that has a missing dependency at 31 iterations deep expected_product = f"testassets\\maxiteration31deep.txt" @@ -383,7 +485,14 @@ class TestsMissingDependencies_WindowsAndMac(object): # fmt:off def test_WindowsAndMac_PotentialMatchesLongerThanUUIDString_OnlyReportsCorrectLengthUUIDs(self): # fmt:on - """Tests that dependency references that are longer than expected are ignored""" + """ + Tests that dependency references that are longer than expected are ignored + + Test Steps: + 1. Set the expected product + 2. Set the expected missing dependencies + 3. Execute test + """ # Relative path to text file with varying length UUID references expected_product = f"testassets\\onlymatchescorrectlengthuuids.txt" @@ -408,7 +517,14 @@ class TestsMissingDependencies_WindowsAndMac(object): def test_WindowsAndMac_MissingDependencyScanner_GradImageSuccess( self, ap_setup_fixture ): - """Tests the Missing Dependency Scanner can scan gradimage files""" + """ + Tests the Missing Dependency Scanner can scan gradimage files + + Test Steps: + 1. Create temporary testing environment + 2. Run the move dependency scanner against the gradimage + 2. Validate that the expected product files and and expected depdencies match + """ env = ap_setup_fixture helper = self._missing_dep_helper diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/auxiliary_content_tests/auxiliary_content_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/auxiliary_content_tests/auxiliary_content_tests.py index 7e9f65de60..452dc66352 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/auxiliary_content_tests/auxiliary_content_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/auxiliary_content_tests/auxiliary_content_tests.py @@ -51,6 +51,11 @@ class TestAuxiliaryContent: def test_CreateAuxiliaryContent_DontSkipLevelPaks(self, workspace, level): """ This test ensure that Auxiliary Content contain level.pak files + + Test Steps: + 1. Run auxiliary content against project under test + 2. Validate auxiliary content exists + 3. Verifies that level.pak exists """ path_to_dev = workspace.paths.engine_root() @@ -70,6 +75,11 @@ class TestAuxiliaryContent: def test_CreateAuxiliaryContent_SkipLevelPaks(self, workspace, level): """ This test ensure that Auxiliary Content contain no level.pak file + + Test Steps: + 1. Run auxiliary content against project under test with skiplevelPaks flag + 2. Validate auxiliary content exists + 3. Validate level.pak was added to auxiliary content """ path_to_dev = workspace.paths.engine_root() diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py index b66984666b..afd7190c1b 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py @@ -533,6 +533,14 @@ class TestsFBX_AllPlatforms(object): def test_FBXBlackboxTest_SourceFiles_Processed_ResultInExpectedProducts(self, workspace, ap_setup_fixture, asset_processor, project, blackbox_param): + """ + Please see run_fbx_test(...) for details + + Test Steps: + 1. Determine if blackbox is set to none + 2. Run FBX Test + """ + if blackbox_param == None: return self.run_fbx_test(workspace, ap_setup_fixture, @@ -544,6 +552,15 @@ class TestsFBX_AllPlatforms(object): workspace, ap_setup_fixture, asset_processor, project, blackbox_param): + """ + Please see run_fbx_test(...) for details + + Test Steps: + 1. Determine if blackbox is set to none + 2. Run FBX Test + 2. Re-run FBX test and validate the information in override assets + """ + if blackbox_param == None: return self.run_fbx_test(workspace, ap_setup_fixture, @@ -567,6 +584,19 @@ class TestsFBX_AllPlatforms(object): def run_fbx_test(self, workspace, ap_setup_fixture, asset_processor, project, blackbox_params: BlackboxAssetTest, overrideAsset = False): + """ + These tests work by having the test case ingest the test data and determine the run pattern. + Tests will process scene settings files and will additionally do a verification against a provided debug file + Additionally, if an override is passed, the output is checked against the override. + + Test Steps: + 1. Create temporary test environment + 2. Process Assets + 3. Determine what assets to validate based upon test data + 4. Validate assets were created in cache + 5. If debug file provided, verify scene files were generated correctly + 6. Verify that each given source asset resulted in the expected jobs and products + """ test_assets_folder = blackbox_params.override_asset_folder if overrideAsset else blackbox_params.asset_folder logger.info(f"{blackbox_params.test_name}: Processing assets in folder '" diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/wwise_bank_dependency_tests/bank_info_parser_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/wwise_bank_dependency_tests/bank_info_parser_tests.py index 0db13bf53c..764b7723bf 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/wwise_bank_dependency_tests/bank_info_parser_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/wwise_bank_dependency_tests/bank_info_parser_tests.py @@ -26,6 +26,18 @@ def soundbank_metadata_generator_setup_fixture(workspace): def success_case_test(test_folder, expected_dependencies_dict, bank_info, expected_result_code=0): + """ + Test Steps: + 1. Make sure the return code is what was expected, and that the expected number of banks were returned. + 2. Validate bank is in the expected dependencies dictionary. + 3. Validate the path to output the metadata file to was assembled correctly. + 4. Validate metadata object for this bank is set, and that it has an object assigned to its dependencies field + and its includedEvents field + 5. Validate metadata object has the correct number of dependencies, and validated that every expected dependency + exists in the dependencies list of the metadata object. + 6. Validate metadata object has the correct number of events, and validate that every expected event exists in the + events of the metadata object. + """ expected_bank_count = len(expected_dependencies_dict) banks, result_code = bank_info.generate_metadata( @@ -80,8 +92,17 @@ class TestSoundBankMetadataGenerator: def test_NoMetadataTooFewBanks_ReturnCodeIsError(self, workspace, soundbank_metadata_generator_setup_fixture): - # Trying to generate metadata for banks in a folder with one or fewer banks and no metadata is not possible - # and should fail. + """ + Trying to generate metadata for banks in a folder with one or fewer banks and no metadata is not possible + and should fail. + + Test Steps: + 1. Setup testing environment with only 1 bank file + 2. Get Sound Bank Info + 3. Attempt to generate sound bank metadata + 4. Verify that proper error code is returned + """ + # test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_NoMetadataTooFewBanks_ReturnCodeIsError') if not os.path.isdir(test_assets_folder): @@ -97,15 +118,30 @@ class TestSoundBankMetadataGenerator: assert error_code is 2, 'Metadata was generated when there were fewer than two banks in the target directory.' def test_NoMetadataNoContentBank_NoMetadataGenerated(self, workspace, soundbank_metadata_generator_setup_fixture): + """ + Test Steps: + 1. Setup testing environment + 2. No expected dependencies + 3. Call success case test + """ test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_NoMetadataNoContentBank_NoMetadataGenerated') expected_dependencies = dict() success_case_test(test_assets_folder, expected_dependencies, get_bank_info(workspace)) def test_NoMetadataOneContentBank_NoStreamedFiles_OneDependency(self, workspace, soundbank_metadata_generator_setup_fixture): - # When no Wwise metadata is present, and there is only one content bank in the target directory with no wem - # files, then only the content bank should have metadata associated with it. The generated metadata should - # only describe a dependency on the init bank. + """ + When no Wwise metadata is present, and there is only one content bank in the target directory with no wem + files, then only the content bank should have metadata associated with it. The generated metadata should + only describe a dependency on the init bank. + + Test Steps: + 1. Setup testing environment + 2. Get current bank info + 3. Build expected dependencies + 4. Call success case test + """ + test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_NoMetadataOneContentBank_NoStreamedFiles_OneDependency') @@ -116,9 +152,18 @@ class TestSoundBankMetadataGenerator: def test_NoMetadataOneContentBank_StreamedFiles_MultipleDependencies(self, workspace, soundbank_metadata_generator_setup_fixture): - # When no Wwise metadata is present, and there is only one content bank in the target directory with wem files - # present, then only the content bank should have metadata associated with it. The generated metadata should - # describe a dependency on the init bank and all wem files in the folder. + """ + When no Wwise metadata is present, and there is only one content bank in the target directory with wem files + present, then only the content bank should have metadata associated with it. The generated metadata should + describe a dependency on the init bank and all wem files in the folder. + + Test Steps: + 1. Setup testing environment + 2. Get current bank info + 3. Build expected dependencies + 4. Call success case test + """ + test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_NoMetadataOneContentBank_StreamedFiles_MultipleDependencies') @@ -136,10 +181,19 @@ class TestSoundBankMetadataGenerator: success_case_test(test_assets_folder, expected_dependencies, get_bank_info(workspace)) def test_NoMetadataMultipleBanks_OneDependency_ReturnCodeIsWarning(self, workspace, soundbank_metadata_generator_setup_fixture): - # When no Wwise metadata is present, and there are multiple content banks in the target directory with wem files - # present, there is no way to tell which bank requires which wem files. A warning should be emitted, - # stating that the full dependency graph could not be created, and only dependencies on the init bank are - # described in the generated metadata files. + """ + When no Wwise metadata is present, and there are multiple content banks in the target directory with wem files + present, there is no way to tell which bank requires which wem files. A warning should be emitted, + stating that the full dependency graph could not be created, and only dependencies on the init bank are + described in the generated metadata files. + + Test Steps: + 1. Setup testing environment + 2. Get current bank info + 3. Build expected dependencies + 4. Call success case test + """ + test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_NoMetadataMultipleBanks_OneDependency_ReturnCodeIsWarning') bank_info = get_bank_info(workspace) @@ -150,8 +204,17 @@ class TestSoundBankMetadataGenerator: success_case_test(test_assets_folder, expected_dependencies, get_bank_info(workspace), expected_result_code=1) def test_OneContentBank_NoStreamedFiles_OneDependency(self, workspace, soundbank_metadata_generator_setup_fixture): - # Wwise metadata describes one content bank that contains all media needed by its events. Generated metadata - # describes a dependency only on the init bank. + """ + Wwise metadata describes one content bank that contains all media needed by its events. Generated metadata + describes a dependency only on the init bank. + + Test Steps: + 1. Setup testing environment + 2. Get current bank info + 3. Build expected dependencies + 4. Call success case test + """ + test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_OneContentBank_NoStreamedFiles_OneDependency') @@ -165,8 +228,17 @@ class TestSoundBankMetadataGenerator: success_case_test(test_assets_folder, expected_dependencies, get_bank_info(workspace)) def test_OneContentBank_StreamedFiles_MultipleDependencies(self, workspace, soundbank_metadata_generator_setup_fixture): - # Wwise metadata describes one content bank that references streamed media files needed by its events. Generated - # metadata describes dependencies on the init bank and wems named by the IDs of referenced streamed media. + """ + Wwise metadata describes one content bank that references streamed media files needed by its events. Generated + metadata describes dependencies on the init bank and wems named by the IDs of referenced streamed media. + + Test Steps: + 1. Setup testing environment + 2. Get current bank info + 3. Build expected dependencies + 4. Call success case test + """ + test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_OneContentBank_StreamedFiles_MultipleDependencies') @@ -187,8 +259,17 @@ class TestSoundBankMetadataGenerator: success_case_test(test_assets_folder, expected_dependencies, get_bank_info(workspace)) def test_MultipleContentBanks_NoStreamedFiles_OneDependency(self, workspace, soundbank_metadata_generator_setup_fixture): - # Wwise metadata describes multiple content banks. Each bank contains all media needed by its events. Generated - # metadata describes each bank having a dependency only on the init bank. + """ + Wwise metadata describes multiple content banks. Each bank contains all media needed by its events. Generated + metadata describes each bank having a dependency only on the init bank. + + Test Steps: + 1. Setup testing environment + 2. Get current bank info + 3. Build expected dependencies + 4. Call success case test + """ + test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_MultipleContentBanks_NoStreamedFiles_OneDependency') @@ -206,8 +287,17 @@ class TestSoundBankMetadataGenerator: success_case_test(test_assets_folder, expected_dependencies, get_bank_info(workspace)) def test_MultipleContentBanks_Bank1StreamedFiles(self, workspace, soundbank_metadata_generator_setup_fixture): - # Wwise metadata describes multiple content banks. Bank 1 references streamed media files needed by its events, - # while bank 2 contains all media need by its events. + """ + Wwise metadata describes multiple content banks. Bank 1 references streamed media files needed by its events, + while bank 2 contains all media need by its events. + + Test Steps: + 1. Setup testing environment + 2. Get current bank info + 3. Build expected dependencies + 4. Call success case test + """ + test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_MultipleContentBanks_Bank1StreamedFiles') @@ -228,9 +318,18 @@ class TestSoundBankMetadataGenerator: success_case_test(test_assets_folder, expected_dependencies, get_bank_info(workspace)) def test_MultipleContentBanks_SplitBanks_OnlyBankDependenices(self, workspace, soundbank_metadata_generator_setup_fixture): - # Wwise metadata describes multiple content banks. Bank 3 events require media that is contained in bank 4. - # Generated metadata describes each bank having a dependency on the init bank, while bank 3 has an additional - # dependency on bank 4. + """ + Wwise metadata describes multiple content banks. Bank 3 events require media that is contained in bank 4. + Generated metadata describes each bank having a dependency on the init bank, while bank 3 has an additional + dependency on bank 4. + + Test Steps: + 1. Setup testing environment + 2. Get current bank info + 3. Build expected dependencies + 4. Call success case test + """ + test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_MultipleContentBanks_SplitBanks_OnlyBankDependenices') @@ -248,9 +347,18 @@ class TestSoundBankMetadataGenerator: success_case_test(test_assets_folder, expected_dependencies, get_bank_info(workspace)) def test_MultipleContentBanks_ReferencedEvent_MediaEmbeddedInBank(self, workspace, soundbank_metadata_generator_setup_fixture): - # Wwise metadata describes multiple content banks. Bank 1 contains all media required by its events, while bank - # 5 contains a reference to an event in bank 1, but no media for that event. Generated metadata describes both - # banks having a dependency on the init bank, while bank 5 has an additional dependency on bank 1. + """ + Wwise metadata describes multiple content banks. Bank 1 contains all media required by its events, while bank + 5 contains a reference to an event in bank 1, but no media for that event. Generated metadata describes both + banks having a dependency on the init bank, while bank 5 has an additional dependency on bank 1. + + Test Steps: + 1. Setup testing environment + 2. Get current bank info + 3. Build expected dependencies + 4. Call success case test + """ + test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_MultipleContentBanks_ReferencedEvent_MediaEmbeddedInBank') @@ -271,10 +379,19 @@ class TestSoundBankMetadataGenerator: success_case_test(test_assets_folder, expected_dependencies, get_bank_info(workspace)) def test_MultipleContentBanks_ReferencedEvent_MediaStreamed(self, workspace, soundbank_metadata_generator_setup_fixture): - # Wwise metadata describes multiple content banks. Bank 1 references streamed media files needed by its events, - # while bank 5 contains a reference to an event in bank 1. This causes bank 5 to also describe a reference to - # the streamed media file referenced by the event from bank 1. Generated metadata describes both banks having - # dependencies on the init bank, as well as the wem named by the ID of referenced streamed media. + """ + Wwise metadata describes multiple content banks. Bank 1 references streamed media files needed by its events, + while bank 5 contains a reference to an event in bank 1. This causes bank 5 to also describe a reference to + the streamed media file referenced by the event from bank 1. Generated metadata describes both banks having + dependencies on the init bank, as well as the wem named by the ID of referenced streamed media. + + Test Steps: + 1. Setup testing environment + 2. Get current bank info + 3. Build expected dependencies + 4. Call success case test + """ + test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_MultipleContentBanks_ReferencedEvent_MediaStreamed') @@ -298,11 +415,20 @@ class TestSoundBankMetadataGenerator: success_case_test(test_assets_folder, expected_dependencies, get_bank_info(workspace)) def test_MultipleContentBanks_ReferencedEvent_MixedSources(self, workspace, soundbank_metadata_generator_setup_fixture): - # Wwise metadata describes multiple content banks. Bank 1 references a streamed media files needed by one of its - # events, and contains all media needed for its other events, while bank 5 contains a reference to two events - # in bank 1: one that requires streamed media, and one that requires media embedded in bank 1. Generated - # metadata describes both banks having dependencies on the init bank and the wem named by the ID of referenced - # streamed media, while bank 5 has an additional dependency on bank 1. + """ + Wwise metadata describes multiple content banks. Bank 1 references a streamed media files needed by one of its + events, and contains all media needed for its other events, while bank 5 contains a reference to two events + in bank 1: one that requires streamed media, and one that requires media embedded in bank 1. Generated + metadata describes both banks having dependencies on the init bank and the wem named by the ID of referenced + streamed media, while bank 5 has an additional dependency on bank 1. + + Test Steps: + 1. Setup testing environment + 2. Get current bank info + 3. Build expected dependencies + 4. Call success case test + """ + test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_MultipleContentBanks_ReferencedEvent_MixedSources') @@ -332,8 +458,17 @@ class TestSoundBankMetadataGenerator: success_case_test(test_assets_folder, expected_dependencies, get_bank_info(workspace)) def test_MultipleContentBanks_VaryingDependencies_MixedSources(self, workspace, soundbank_metadata_generator_setup_fixture): - # Wwise metadata describes multiple content banks that have varying dependencies on each other, and dependencies - # on streamed media files. + """ + Wwise metadata describes multiple content banks that have varying dependencies on each other, and dependencies + on streamed media files. + + Test Steps: + 1. Setup testing environment + 2. Get current bank info + 3. Build expected dependencies + 4. Call success case test + """ + test_assets_folder = os.path.join(soundbank_metadata_generator_setup_fixture['tests_dir'], 'assets', 'test_MultipleContentBanks_VaryingDependencies_MixedSources') diff --git a/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt index e8f3349df4..834254134e 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt @@ -39,4 +39,19 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ COMPONENT Editor ) + + ly_add_pytest( + NAME AutomatedTesting::EditorTests_Sandbox + TEST_SUITE sandbox + TEST_SERIAL + PATH ${CMAKE_CURRENT_LIST_DIR} + PYTEST_MARKS "SUITE_sandbox" + TIMEOUT 1500 + RUNTIME_DEPENDENCIES + Legacy::Editor + AZ::AssetProcessor + AutomatedTesting.Assets + COMPONENT + Editor + ) endif() diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_Docking.py b/AutomatedTesting/Gem/PythonTests/editor/test_Docking.py index c2d515e250..f887560a19 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_Docking.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_Docking.py @@ -39,7 +39,7 @@ class TestDocking(object): file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) @pytest.mark.test_case_id("C6376081") - @pytest.mark.SUITE_periodic + @pytest.mark.SUITE_sandbox def test_Docking_BasicDockedTools(self, request, editor, level, launcher_platform): expected_lines = [ "The tools are all docked together in a tabbed widget", diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py index 70a22f9e2a..c2da1343de 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py @@ -39,7 +39,7 @@ class TestMenus(object): file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) @pytest.mark.test_case_id("C16780783", "C2174438") - @pytest.mark.SUITE_periodic + @pytest.mark.SUITE_sandbox def test_Menus_EditMenuOptions_Work(self, request, editor, level, launcher_platform): expected_lines = [ "Undo Action triggered", @@ -113,7 +113,7 @@ class TestMenus(object): ) @pytest.mark.test_case_id("C16780778") - @pytest.mark.SUITE_periodic + @pytest.mark.SUITE_sandbox def test_Menus_FileMenuOptions_Work(self, request, editor, level, launcher_platform): expected_lines = [ "New Level Action triggered", 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 4f58a23a19..730a557a9e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_BasicSurfaceTagCreation.py @@ -23,6 +23,25 @@ class TestSurfaceMaskFilter_BasicSurfaceTagCreation(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="TestSurfaceMaskFilter_BasicSurfaceTagCreation", args=["level"]) def run_test(self): + """ + Summary: + Verifies basic surface tag value equality + + 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. + + 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 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 c25761d655..46c5483988 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py @@ -33,6 +33,25 @@ class TestVegetationInstances_DespawnWhenOutOfRange(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix='VegetationInstances_DespawnWhenOutOfRange', args=['level']) 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. + + 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. + + :return: None + """ # Create a new level self.test_success = self.create_level( diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py index cc9a15bba0..c37bc9780f 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientGenerators_Incompatibilities.py @@ -28,8 +28,21 @@ class TestGradientGeneratorIncompatibilities(EditorTestHelper): def run_test(self): """ Summary: - Verify that Entities are not active when a Gradient Generator and incompatible component are both present - on the same Entity. + This test verifies that components are disabled when conflicting components are present on the same entity. + + Expected Behavior: + Gradient Generator components are incompatible with Vegetation area components. + + Test Steps: + 1) Create a new level + 2) Create a new entity in the level + 3) Add each Gradient Generator component to an entity, and add a Vegetation Area component to the same entity + 4) Verify that components are only enabled when entity is free of a conflicting component + + 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 """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py index b7d12d074a..f2edc2924e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientModifiers_Incompatibilities.py @@ -28,8 +28,21 @@ class TestGradientModifiersIncompatibilities(EditorTestHelper): def run_test(self): """ Summary: - Verify that Entities are not active when a Gradient Modifier and incompatible component are both present - on the same Entity. + This test verifies that components are disabled when conflicting components are present on the same entity. + + Expected Behavior: + Gradient Modifier components are incompatible with Vegetation area components. + + Test Steps: + 1) Create a new level + 2) Create a new entity in the level + 3) Add each Gradient Modifier component to an entity, and add a Vegetation Area component to the same entity + 4) Verify that components are only enabled when entity is free of a conflicting component + + 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 """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py index c37ee36265..45da74d6cd 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py @@ -9,19 +9,6 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ -""" -The below cases are combined in this script -C2676829 -C3961326 -C3980659 -C3980664 -C3980669 -C3416548 -C2676823 -C3961321 -C2676826 -""" - import os import sys diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py index 5a758b9d89..b8f4114d30 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientPreviewSettings_DefaultPinnedEntityIsSelf.py @@ -44,7 +44,21 @@ class TestGradientPreviewSettings(EditorTestHelper): def run_test(self): """ Summary: - Verify if the current entity is set to the pin preview to shape entity by default for several components. + This test verifies default values for the pinned entity for Gradient Preview settings. + + Expected Behavior: + Pinned entity is self for all gradient generator/modifiers. + + Test Steps: + 1) Create a new level + 2) Create a new entity in the level + 3) Add each Gradient Generator component to an entity, and verify the Pin Preview to Shape property is set to + self + + 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 """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py index a16e37e0fc..8e2d0611af 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientSurfaceTagEmitter_ComponentDependencies.py @@ -31,11 +31,21 @@ class TestGradientSurfaceTagEmitterDependencies(EditorTestHelper): def run_test(self): """ Summary: - Component has a dependency on a Gradient component + This test verifies that the Gradient Surface Tag Emitter component is dependent on a gradient component. Expected Result: - Component is disabled until a Gradient Generator, Modifier or Gradient Reference component - (and any sub-dependencies) is added to the entity. + Gradient Surface Tag Emitter component is disabled until a Gradient Generator, Modifier or Gradient Reference + component (and any sub-dependencies) is added to the entity. + + Test Steps: + 1) Open level + 2) Create a new entity with a Gradient Surface Tag Emitter component + 3) Verify the component is disabled until a dependent component is also added to the entity + + 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 """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py index e1e901f2f7..2311363db9 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/GradientTransform_RequiresShape.py @@ -28,8 +28,20 @@ class TestGradientTransformRequiresShape(EditorTestHelper): def run_test(self): """ Summary: - Verify that Gradient Transform Modifier component requires a - Shape component before the Entity can become active. + This test verifies that the Gradient Transform Modifier component is dependent on a shape component. + + Expected Result: + Gradient Transform Modifier component is disabled until a shape component is added to the entity. + + Test Steps: + 1) Open level + 2) Create a new entity with a Gradient Transform Modifier component + 3) Verify the component is disabled until a shape component is also added to the entity + + 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 """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py index dab8e6928a..a5d9632fd6 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_RequiresShape.py @@ -28,8 +28,20 @@ class TestImageGradientRequiresShape(EditorTestHelper): def run_test(self): """ Summary: - Verify that Image Gradient component requires a - Shape component before the Entity can become active. + This test verifies that the Image Gradient component is dependent on a shape component. + + Expected Result: + Gradient Transform Modifier component is disabled until a shape component is added to the entity. + + Test Steps: + 1) Open level + 2) Create a new entity with a Image Gradient component + 3) Verify the component is disabled until a shape component is also added to the entity + + 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 """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py index d1e0b68ef4..c41d153cfa 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_DependentComponentsAdded.py @@ -33,6 +33,26 @@ class TestAreaNodeComponentDependency(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="AreaNodeComponentDependency", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities with + proper dependent components. + + Expected Behavior: + All expected component dependencies are met when adding an area node to a graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the area nodes to the graph area, and ensure the proper dependent components are added + + 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 + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityCreatedOnNodeAdd.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityCreatedOnNodeAdd.py index 4e429a192b..fb977b4987 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityCreatedOnNodeAdd.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityCreatedOnNodeAdd.py @@ -33,7 +33,25 @@ class TestGradientNodeEntityCreate(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="AreaNodeEntityCreate", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities. + Expected Behavior: + New entities are created when dragging area nodes to graph area. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the area nodes to the graph area, and ensure a new entity is created + + 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 + """ def onEntityCreated(parameters): global newEntityId newEntityId = parameters[0] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityRemovedOnNodeDelete.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityRemovedOnNodeDelete.py index 38f8641b4c..57ba8fc006 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityRemovedOnNodeDelete.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/AreaNodes_EntityRemovedOnNodeDelete.py @@ -34,7 +34,26 @@ class TestAreaNodeEntityDelete(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="AreaNodeEntityDelete", args=["level"]) def run_test(self): - + """ + Summary: + This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor. + + Expected Behavior: + Entities are removed when area nodes are deleted from a graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the area nodes to the graph area, and ensure a new entity is created + 4) Delete the nodes, and ensure the newly created entities are removed + + 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 + """ def onEntityCreated(parameters): global createdEntityId createdEntityId = parameters[0] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ComponentUpdates_UpdateGraph.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ComponentUpdates_UpdateGraph.py index 26062c01f8..60527b64d2 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ComponentUpdates_UpdateGraph.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ComponentUpdates_UpdateGraph.py @@ -9,24 +9,6 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ - -""" -C22602072 - Graph is updated when underlying components are added/removed - -1. Open Level. -2. Find LandscapeCanvas named entity. -3. Ensure Vegetation Distribution Component is present on the BushSpawner entity. -4. Open graph and ensure Distribution Filter wrapped node is present. -5. Delete the Vegetation Distribution Filter component from the BushSpawner entity via Entity Inspector. -6. Ensure the Vegetation Distribution Filter component was deleted from the BushSpawner entity and node is no longer -present in the graph. -7. Add Vegetation Altitude Filter to the BushSpawner entity through Entity Inspector. -8. Ensure Altitude Filter was added to the BushSpawner node in the open graph. -9. Add a new entity with unique name as a child of the Landscape Canvas entity. -10. Add a Box Shape component to the new child entity. -11. Ensure Box Shape node is present on the open graph. -""" - import os import sys @@ -50,6 +32,36 @@ class TestComponentUpdatesUpdateGraph(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="ComponentUpdatesUpdateGraph", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas graphs update properly when components are added/removed outside of + Landscape Canvas. + + Expected Behavior: + Graphs properly reflect component changes made to entities outside of Landscape Canvas. + + Test Steps: + 1. Open Level + 2. Find LandscapeCanvas named entity + 3. Ensure Vegetation Distribution Component is present on the BushSpawner entity + 4. Open graph and ensure Distribution Filter wrapped node is present + 5. Delete the Vegetation Distribution Filter component from the BushSpawner entity via Entity Inspector + 6. Ensure the Vegetation Distribution Filter component was deleted from the BushSpawner entity and node is + no longer present in the graph + 7. Add Vegetation Altitude Filter to the BushSpawner entity through Entity Inspector + 8. Ensure Altitude Filter was added to the BushSpawner node in the open graph + 9. Add a new entity with unique name as a child of the Landscape Canvas entity + 10. Add a Box Shape component to the new child entity + 11. Ensure Box Shape node is present on the open graph + + 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 + """ + # Create a new empty level and instantiate LC_BushFlowerBlender.slice self.test_success = self.create_level( self.args["level"], diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/CreateNewGraph.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/CreateNewGraph.py index 5fed13985d..4b5e03abbc 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/CreateNewGraph.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/CreateNewGraph.py @@ -37,6 +37,25 @@ class TestCreateNewGraph(EditorTestHelper): print("New root entity created") def run_test(self): + """ + Summary: + This test verifies that new graphs can be created in Landscape Canvas. + + Expected Behavior: + New graphs can be created, and proper entity is created to hold graph data with a Landscape Canvas component. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Ensures the root entity created contains a Landscape Canvas component + + 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.test_success = self.create_level( self.args["level"], heightmap_resolution=128, diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_DisabledNodeDuplication.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_DisabledNodeDuplication.py index 7fd3f075e0..81e24b20e1 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_DisabledNodeDuplication.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_DisabledNodeDuplication.py @@ -33,7 +33,25 @@ class TestDisabledNodeDuplication(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="DisabledNodeDuplication", args=["level"]) def run_test(self): + """ + Summary: + This test verifies Editor stability after duplicating disabled Landscape Canvas nodes. + Expected Behavior: + Editor remains stable and free of crashes. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Create several new nodes, disable the nodes via disabling/deleting components, and duplicate the nodes + + 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 + """ def onEntityCreated(parameters): global newEntityId newEntityId = parameters[0] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_UndoNodeDelete_SliceEntity.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_UndoNodeDelete_SliceEntity.py index 27ab6fded3..61c4cf9ac2 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_UndoNodeDelete_SliceEntity.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/Edit_UndoNodeDelete_SliceEntity.py @@ -9,17 +9,6 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ - -""" -C30813586 - Editor remains stable after Undoing deletion of a node on a slice entity - -1. Open level with instantiated slice. -2. Open the graph. -3. Find the BushSpawner's Vegetation Layer Spawner node. -4. Delete the node. -5. Undo to restore the node. -""" - import os import sys @@ -44,7 +33,26 @@ class TestUndoNodeDeleteSlice(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="UndoNodeDeleteSlice", args=["level"]) def run_test(self): - + """ + Summary: + This test verifies Editor stability after undoing the deletion of nodes on a slice entity. + + Expected Behavior: + Editor remains stable and free of crashes. + + Test Steps: + 1) Create a new level + 2) Instantiate a slice with a Landscape Canvas setup + 3) Find a specific node on the graph, and delete it + 4) Restore the node with Undo + + 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 + """ # Create a new empty level and instantiate LC_BushFlowerBlender.slice self.test_success = self.create_level( self.args["level"], diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientMixer_NodeConstruction.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientMixer_NodeConstruction.py index ca3bc04f47..124baf9d2e 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientMixer_NodeConstruction.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientMixer_NodeConstruction.py @@ -34,6 +34,27 @@ class TestGradientMixerNodeConstruction(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GradientMixerNodeConstruction", args=["level"]) def run_test(self): + """ + Summary: + This test verifies a Gradient Mixer vegetation setup can be constructed through Landscape Canvas. + + Expected Behavior: + Entities contain all required components and component references after creating nodes and setting connections + on a Landscape Canvas graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Add all necessary nodes to the graph and set connections to form a Gradient Mixer setup + 4) Verify all components and component references were properly set during graph construction + + 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 + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityCreatedOnNodeAdd.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityCreatedOnNodeAdd.py index d40b19e7db..aa98eb3dc3 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityCreatedOnNodeAdd.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityCreatedOnNodeAdd.py @@ -33,6 +33,25 @@ class TestGradientModifierNodeEntityCreate(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GradientModifierNodeEntityCreate", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities. + + Expected Behavior: + New entities are created when dragging Gradient Modifier nodes to graph area. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the Gradient Modifier nodes to the graph area, and ensure a new entity is created + + 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 + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityRemovedOnNodeDelete.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityRemovedOnNodeDelete.py index dc263924d1..6a82b05039 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityRemovedOnNodeDelete.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientModifierNodes_EntityRemovedOnNodeDelete.py @@ -34,7 +34,26 @@ class TestGradientModifierNodeEntityDelete(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GradientModifierNodeEntityDelete", args=["level"]) def run_test(self): - + """ + Summary: + This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor. + + Expected Behavior: + Entities are removed when Gradient Modifier nodes are deleted from a graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the Gradient Modifier nodes to the graph area, and ensure a new entity is created + 4) Delete the nodes, and ensure the newly created entities are removed + + 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 + """ def onEntityCreated(parameters): global createdEntityId createdEntityId = parameters[0] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_DependentComponentsAdded.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_DependentComponentsAdded.py index 5e203e1892..f9360fe356 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_DependentComponentsAdded.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_DependentComponentsAdded.py @@ -33,6 +33,27 @@ class TestGradientNodeComponentDependency(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GradientNodeComponentDependency", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities with + proper dependent components. + + Expected Behavior: + All expected component dependencies are met when adding a Gradient Modifier node to a graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the Gradient Modifier nodes to the graph area, and ensure the proper dependent components are + added + + 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 + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityCreatedOnNodeAdd.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityCreatedOnNodeAdd.py index 6d4a2f58a7..8aaad9b81d 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityCreatedOnNodeAdd.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityCreatedOnNodeAdd.py @@ -32,6 +32,25 @@ class TestGradientNodeEntityCreate(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GradientNodeEntityCreate", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities. + + Expected Behavior: + New entities are created when dragging Gradient nodes to graph area. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the Gradient nodes to the graph area, and ensure a new entity is created + + 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 + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityRemovedOnNodeDelete.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityRemovedOnNodeDelete.py index 2b49e3a911..d74b86d0bf 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityRemovedOnNodeDelete.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GradientNodes_EntityRemovedOnNodeDelete.py @@ -34,6 +34,26 @@ class TestGradientNodeEntityDelete(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GradientNodeEntityDelete", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor. + + Expected Behavior: + Entities are removed when Gradient nodes are deleted from a graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the Gradient nodes to the graph area, and ensure a new entity is created + 4) Delete the nodes, and ensure the newly created entities are removed + + 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 + """ def onEntityCreated(parameters): global createdEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnEntityDelete.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnEntityDelete.py index d3ad5c1c1e..6aa539b554 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnEntityDelete.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnEntityDelete.py @@ -31,6 +31,26 @@ class TestGraphClosedOnEntityDelete(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GraphClosedOnEntityDelete", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that Landscape Canvas graphs are auto-closed when the corresponding entity is deleted. + + Expected Behavior: + When a Landscape Canvas root entity is deleted, the corresponding graph automatically closes. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Delete the automatically created entity + 4) Verify the open graph is closed + + 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 + """ def onEntityCreated(parameters): global newRootEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnLevelChange.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnLevelChange.py index b7b0008eb2..ebc75ab621 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnLevelChange.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_OnLevelChange.py @@ -29,7 +29,26 @@ class TestGraphClosedOnLevelChange(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GraphClosedOnLevelChange", args=["level"]) def run_test(self): - + """ + Summary: + This test verifies that Landscape Canvas graphs are auto-closed when the currently open level changes. + + Expected Behavior: + When a new level is loaded in the Editor, open Landscape Canvas graphs are automatically closed. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Open a different level + 4) Verify the open graph is closed + + 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 + """ # Create a new empty level self.test_success = self.create_level( self.args["level"], diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_TabbedGraph.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_TabbedGraph.py index efd1cc5a55..4b018aeb45 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_TabbedGraph.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphClosed_TabbedGraph.py @@ -29,6 +29,26 @@ class TestGraphClosedTabbedGraph(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GraphClosedTabbedGraph", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that Landscape Canvas tabbed graphs can be independently closed. + + Expected Behavior: + Closing a tabbed graph only closes the appropriate graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create several new graphs + 3) Close one of the open graphs + 4) Ensure the graph properly closed, and other open graphs remain open + + 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 + """ # Create a new empty level self.test_success = self.create_level( diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphUpdates_UpdateComponents.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphUpdates_UpdateComponents.py index f350d37178..f94a6c2e3a 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphUpdates_UpdateComponents.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/GraphUpdates_UpdateComponents.py @@ -9,21 +9,6 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ - -""" -C22715182 - Components are updated when nodes are added/removed/updated - -1. Open Level. -2. Open the graph on LC_BushFlowerBlender.slice -3. Find the Rotation Modifier node on the BushSpawner entity -4. Delete the Rotation Modifier node -5. Ensure the Vegetation Rotation Modifier component is removed from the BushSpawner entity -6. Delete the Vegetation Layer Spawner node from the graph -7. Ensure BushSpawner entity is deleted -8. Change connection from second Rotation Modifier node to a different Gradient -9. Ensure Gradient reference on component is updated -""" - import os import sys @@ -50,6 +35,31 @@ class TestGraphUpdatesUpdateComponents(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="GraphUpdatesUpdateComponents", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that components are properly updated as nodes are added/removed/updated. + + Expected Behavior: + Landscape Canvas node CRUD properly updates component entities. + + Test Steps: + 1. Open Level. + 2. Open the graph on LC_BushFlowerBlender.slice + 3. Find the Rotation Modifier node on the BushSpawner entity + 4. Delete the Rotation Modifier node + 5. Ensure the Vegetation Rotation Modifier component is removed from the BushSpawner entity + 6. Delete the Vegetation Layer Spawner node from the graph + 7. Ensure BushSpawner entity is deleted + 8. Change connection from second Rotation Modifier node to a different Gradient + 9. Ensure Gradient reference on component is updated + + 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 + """ # Create a new empty level and instantiate LC_BushFlowerBlender.slice self.test_success = self.create_level( self.args["level"], diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvasComponent_AddedRemoved.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvasComponent_AddedRemoved.py index 176429885f..c3857e1393 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvasComponent_AddedRemoved.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvasComponent_AddedRemoved.py @@ -30,6 +30,26 @@ class TestLandscapeCanvasComponentAddedRemoved(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="LandscapeCanvasComponentAddedRemoved", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas component can be added to/removed from an entity. + + Expected Behavior: + Closing a tabbed graph only closes the appropriate graph. + + Test Steps: + 1) Create a new level + 2) Create a new entity + 3) Add a Landscape Canvas component to the entity + 4) Remove the Landscape Canvas component from the entity + + 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 + """ # Create a new empty level self.test_success = self.create_level( diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvas_SliceCreateInstantiate.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvas_SliceCreateInstantiate.py index e0f13adaa9..f174a52610 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvas_SliceCreateInstantiate.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LandscapeCanvas_SliceCreateInstantiate.py @@ -30,12 +30,21 @@ class TestLandscapeCanvasSliceCreateInstantiate(EditorTestHelper): def run_test(self): """ Summary: - C22602016 A slice containing the LandscapeCanvas component can be created/instantiated. + A slice containing the LandscapeCanvas component can be created/instantiated. Expected Result: - Slice is created and processed successfully and free of errors/warnings. - Another copy of the slice is instantiated. - + Slice is created/processed/instantiated successfully and free of errors/warnings. + + Test Steps: + 1) Create a new level + 2) Create a new entity with a Landscape Canvas component + 3) Create a slice of the new entity + 4) Instantiate a new copy of the slice + + 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 """ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerBlender_NodeConstruction.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerBlender_NodeConstruction.py index ecc529b9b4..82a2abf5ea 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerBlender_NodeConstruction.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerBlender_NodeConstruction.py @@ -34,6 +34,27 @@ class TestLayerBlenderNodeConstruction(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="LayerBlenderNodeConstruction", args=["level"]) def run_test(self): + """ + Summary: + This test verifies a Layer Blender vegetation setup can be constructed through Landscape Canvas. + + Expected Behavior: + Entities contain all required components and component references after creating nodes and setting connections + on a Landscape Canvas graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Add all necessary nodes to the graph and set connections to form a Layer Blender setup + 4) Verify all components and component references were properly set during graph construction + + 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 + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerExtenderNodes_ComponentEntitySync.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerExtenderNodes_ComponentEntitySync.py index 00fcb5170c..df3c549fff 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerExtenderNodes_ComponentEntitySync.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/LayerExtenderNodes_ComponentEntitySync.py @@ -34,6 +34,25 @@ class TestLayerExtenderNodeComponentEntitySync(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="LayerExtenderNodeComponentEntitySync", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that all wrapped nodes can be successfully added to/removed from parent nodes. + + Expected Behavior: + All wrapped extender nodes can be added to/removed from appropriate parent nodes. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Add Area Blender and Layer Spawner nodes to the graph, and add/remove each extender node to/from each + + 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 + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityCreatedOnNodeAdd.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityCreatedOnNodeAdd.py index bd10e5f4c6..cd4915ea24 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityCreatedOnNodeAdd.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityCreatedOnNodeAdd.py @@ -33,6 +33,25 @@ class TestShapeNodeEntityCreate(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="ShapeNodeEntityCreate", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities. + + Expected Behavior: + New entities are created when dragging shape nodes to graph area. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the shape nodes to the graph area, and ensure a new entity is created + + 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 + """ def onEntityCreated(parameters): global newEntityId diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityRemovedOnNodeDelete.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityRemovedOnNodeDelete.py index f71f5ae906..fcfbe03576 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityRemovedOnNodeDelete.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/ShapeNodes_EntityRemovedOnNodeDelete.py @@ -34,7 +34,27 @@ class TestShapeNodeEntityDelete(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="ShapeNodeEntityDelete", args=["level"]) def run_test(self): - + """ + Summary: + This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor. + + Expected Behavior: + Entities are removed when shape nodes are deleted from a graph. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Drag each of the shape nodes to the graph area, and ensure a new entity is created + 4) Delete the nodes, and ensure the newly created entities are removed + + 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 + """ + def onEntityCreated(parameters): global createdEntityId createdEntityId = parameters[0] diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/SlotConnections_UpdateComponentReferences.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/SlotConnections_UpdateComponentReferences.py index 968f39c64d..183c3f7ccb 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/SlotConnections_UpdateComponentReferences.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/EditorScripts/SlotConnections_UpdateComponentReferences.py @@ -33,6 +33,27 @@ class TestSlotConnectionsUpdateComponents(EditorTestHelper): EditorTestHelper.__init__(self, log_prefix="SlotConnectionsUpdateComponents", args=["level"]) def run_test(self): + """ + Summary: + This test verifies that the Landscape Canvas slot connections properly update component references. + + Expected Behavior: + A reference created through slot connections in Landscape Canvas is reflected in the Entity Inspector. + + Test Steps: + 1) Create a new level + 2) Open Landscape Canvas and create a new graph + 3) Several nodes are added to a graph, and connections are set between the nodes + 4) Component references are verified via Entity Inspector + + 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 + """ + # Retrieve the proper component TypeIds per component name componentNames = [ 'Random Noise Gradient', diff --git a/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Main.py index 8f1f2f7481..2cf55c7a58 100644 --- a/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Main.py @@ -42,6 +42,7 @@ class TestAutomation(TestAutomationBase): self._run_test(request, workspace, editor, test_module) @revert_physics_config + @fm.file_override('physxsystemconfiguration.setreg','C4044459_Material_DynamicFriction.setreg_override', 'AutomatedTesting/Registry') def test_C4044459_Material_DynamicFriction(self, request, workspace, editor, launcher_platform): from . import C4044459_Material_DynamicFriction as test_module self._run_test(request, workspace, editor, test_module) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvent_AddRemoveParameter_ActionsSuccessful.py b/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvent_AddRemoveParameter_ActionsSuccessful.py new file mode 100644 index 0000000000..638c69e8f3 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvent_AddRemoveParameter_ActionsSuccessful.py @@ -0,0 +1,131 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + + +# fmt: off +class Tests(): + new_event_created = ("Successfully created a new event", "Failed to create a new event") + child_event_created = ("Successfully created Child Event", "Failed to create Child Event") + file_saved = ("Successfully saved event asset", "Failed to save event asset") + parameter_created = ("Successfully added parameter", "Failed to add parameter") + parameter_removed = ("Successfully removed parameter", "Failed to remove parameter") +# fmt: on + + +def ScriptEvent_AddRemoveParameter_ActionsSuccessful(): + """ + Summary: + Parameter can be removed from a Script Event method + + Expected Behavior: + Upon saving the updated .scriptevents asset the removed paramenter should no longer be present on the Script Event + + Test Steps: + 1) Open Asset Editor + 2) Get Asset Editor Qt object + 3) Create new Script Event Asset + 4) Add Parameter to Event + 5) Remove Parameter from Event + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ + import os + from PySide2 import QtWidgets + + from editor_python_test_tools.utils import Report + from editor_python_test_tools.utils import TestHelper as helper + import editor_python_test_tools.pyside_utils as pyside_utils + + import azlmbr.bus as bus + import azlmbr.editor as editor + import azlmbr.legacy.general as general + + GENERAL_WAIT = 1.0 # seconds + FILE_PATH = os.path.join("AutomatedTesting", "ScriptCanvas", "test_file.scriptevent") + QtObject = object + + def create_script_event(asset_editor: QtObject, file_path: str) -> None: + action = pyside_utils.find_child_by_pattern(menu_bar, {"type": QtWidgets.QAction, "text": "Script Events"}) + action.trigger() + result = helper.wait_for_condition( + lambda: container.findChild(QtWidgets.QFrame, "Events") is not None, 3 * GENERAL_WAIT + ) + Report.result(Tests.new_event_created, result) + + # Add new child event + add_event = container.findChild(QtWidgets.QFrame, "Events").findChild(QtWidgets.QToolButton, "") + add_event.click() + result = helper.wait_for_condition( + lambda: asset_editor.findChild(QtWidgets.QFrame, "EventName") is not None, GENERAL_WAIT + ) + Report.result(Tests.child_event_created, result) + # Save the Script Event file + editor.AssetEditorWidgetRequestsBus(bus.Broadcast, "SaveAssetAs", file_path) + + # Verify if file is created + result = helper.wait_for_condition(lambda: os.path.exists(file_path), 3 * GENERAL_WAIT) + Report.result(Tests.file_saved, result) + + def create_parameter(file_path: str) -> None: + add_param = container.findChild(QtWidgets.QFrame, "Parameters").findChild(QtWidgets.QToolButton, "") + add_param.click() + result = helper.wait_for_condition( + lambda: asset_editor_widget.findChild(QtWidgets.QFrame, "[0]") is not None, GENERAL_WAIT + ) + Report.result(Tests.parameter_created, result) + editor.AssetEditorWidgetRequestsBus(bus.Broadcast, "SaveAssetAs", file_path) + + def remove_parameter(file_path: str) -> None: + remove_param = container.findChild(QtWidgets.QFrame, "[0]").findChild(QtWidgets.QToolButton, "") + remove_param.click() + result = helper.wait_for_condition( + lambda: asset_editor_widget.findChild(QtWidgets.QFrame, "[0]") is None, GENERAL_WAIT + ) + Report.result(Tests.parameter_removed, result) + editor.AssetEditorWidgetRequestsBus(bus.Broadcast, "SaveAssetAs", file_path) + + # 1) Open Asset Editor + general.idle_enable(True) + # Initially close the Asset Editor and then reopen to ensure we don't have any existing assets open + general.close_pane("Asset Editor") + general.open_pane("Asset Editor") + helper.wait_for_condition(lambda: general.is_pane_visible("Asset Editor"), 5.0) + + # 2) Get Asset Editor Qt object + editor_window = pyside_utils.get_editor_main_window() + asset_editor_widget = editor_window.findChild(QtWidgets.QDockWidget, "Asset Editor").findChild( + QtWidgets.QWidget, "AssetEditorWindowClass" + ) + container = asset_editor_widget.findChild(QtWidgets.QWidget, "ContainerForRows") + menu_bar = asset_editor_widget.findChild(QtWidgets.QMenuBar) + + # 3) Create new Script Event Asset + create_script_event(asset_editor_widget, FILE_PATH) + + # 4) Add Parameter to Event + create_parameter(FILE_PATH) + + # 5) Remove Parameter from Event + remove_parameter(FILE_PATH) + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + from editor_python_test_tools.utils import Report + + Report.start_test(ScriptEvent_AddRemoveParameter_ActionsSuccessful) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvents_AllParamDatatypes_CreationSuccess.py b/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvents_AllParamDatatypes_CreationSuccess.py new file mode 100644 index 0000000000..4beedec7cf --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvents_AllParamDatatypes_CreationSuccess.py @@ -0,0 +1,210 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + + +# fmt: off +class Tests(): + new_event_created = ("New Script Event created", "New Script Event not created") + child_event_created = ("Child Event created", "Child Event not created") + params_added = ("New parameters added", "New parameters are not added") + file_saved = ("Script event file saved", "Script event file did not save") + node_found = ("Node found in Script Canvas", "Node not found in Script Canvas") +# fmt: on + + +def ScriptEvents_AllParamDatatypes_CreationSuccess(): + """ + Summary: + Parameters of all types can be created. + + Expected Behavior: + The Method handles the large number of Parameters gracefully. + Parameters of all data types can be successfully created. + Updated ScriptEvent toast appears in Script Canvas. + + Test Steps: + 1) Open Asset Editor + 2) Initially create new Script Event file with one method + 3) Add new method and set name to it + 4) Add new parameters of each type + 5) Verify if parameters are added + 6) Expand the parameter rows + 7) Set different names and datatypes for each parameter + 8) Save file and verify node in SC Node Palette + 9) Close Asset Editor + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ + import os + from utils import TestHelper as helper + import pyside_utils + + # Open 3D Engine imports + import azlmbr.legacy.general as general + import azlmbr.editor as editor + import azlmbr.bus as bus + + # Pyside imports + from PySide2 import QtWidgets, QtTest, QtCore + + GENERAL_WAIT = 1.0 # seconds + + FILE_PATH = os.path.join("AutomatedTesting", "TestAssets", "test_file.scriptevents") + N_VAR_TYPES = 10 # Top 10 variable types + TEST_METHOD_NAME = "test_method_name" + + editor_window = pyside_utils.get_editor_main_window() + asset_editor = asset_editor_widget = container = menu_bar = None + sc = node_palette = tree = search_frame = search_box = None + + def initialize_asset_editor_qt_objects(): + nonlocal asset_editor, asset_editor_widget, container, menu_bar + asset_editor = editor_window.findChild(QtWidgets.QDockWidget, "Asset Editor") + asset_editor_widget = asset_editor.findChild(QtWidgets.QWidget, "AssetEditorWindowClass") + container = asset_editor_widget.findChild(QtWidgets.QWidget, "ContainerForRows") + menu_bar = asset_editor_widget.findChild(QtWidgets.QMenuBar) + + def initialize_sc_qt_objects(): + nonlocal sc, node_palette, tree, search_frame, search_box + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + if sc.findChild(QtWidgets.QDockWidget, "NodePalette") is None: + action = pyside_utils.find_child_by_pattern(sc, {"text": "Node Palette", "type": QtWidgets.QAction}) + action.trigger() + node_palette = sc.findChild(QtWidgets.QDockWidget, "NodePalette") + tree = node_palette.findChild(QtWidgets.QTreeView, "treeView") + search_frame = node_palette.findChild(QtWidgets.QFrame, "searchFrame") + search_box = search_frame.findChild(QtWidgets.QLineEdit, "searchFilter") + + def save_file(): + editor.AssetEditorWidgetRequestsBus(bus.Broadcast, "SaveAssetAs", FILE_PATH) + action = pyside_utils.find_child_by_pattern(menu_bar, {"type": QtWidgets.QAction, "iconText": "Save"}) + action.trigger() + # wait till file is saved, to validate that check the text of QLabel at the bottom of the AssetEditor, + # if there are no unsaved changes we will not have any * in the text + label = asset_editor.findChild(QtWidgets.QLabel, "textEdit") + return helper.wait_for_condition(lambda: "*" not in label.text(), 3.0) + + def expand_container_rows(object_name): + children = container.findChildren(QtWidgets.QFrame, object_name) + for child in children: + check_box = child.findChild(QtWidgets.QCheckBox) + if check_box and not check_box.isChecked(): + QtTest.QTest.mouseClick(check_box, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier) + + def node_palette_search(node_name): + search_box.setText(node_name) + helper.wait_for_condition(lambda: search_box.text() == node_name, 1.0) + # Try clicking ENTER in search box multiple times + for _ in range(20): + QtTest.QTest.keyClick(search_box, QtCore.Qt.Key_Enter, QtCore.Qt.NoModifier) + if pyside_utils.find_child_by_pattern(tree, {"text": node_name}) is not None: + break + + def verify_added_params(): + for index in range(N_VAR_TYPES): + if container.findChild(QtWidgets.QFrame, f"[{index}]") is None: + return False + return True + + # 1) Open Asset Editor + general.idle_enable(True) + # Initially close the Asset Editor and then reopen to ensure we don't have any existing assets open + general.close_pane("Asset Editor") + general.open_pane("Asset Editor") + helper.wait_for_condition(lambda: general.is_pane_visible("Asset Editor"), 5.0) + + # 2) Initially create new Script Event file with one method + initialize_asset_editor_qt_objects() + action = pyside_utils.find_child_by_pattern(menu_bar, {"type": QtWidgets.QAction, "text": "Script Events"}) + action.trigger() + result = helper.wait_for_condition( + lambda: container.findChild(QtWidgets.QFrame, "Events") is not None + and container.findChild(QtWidgets.QFrame, "Events").findChild(QtWidgets.QToolButton, "") is not None, + 3 * GENERAL_WAIT, + ) + Report.result(Tests.new_event_created, result) + + # 3) Add new method and set name to it + add_event = container.findChild(QtWidgets.QFrame, "Events").findChild(QtWidgets.QToolButton, "") + add_event.click() + result = helper.wait_for_condition( + lambda: asset_editor_widget.findChild(QtWidgets.QFrame, "EventName") is not None, GENERAL_WAIT + ) + Report.result(Tests.child_event_created, result) + expand_container_rows("EventName") + expand_container_rows("Name") + initialize_asset_editor_qt_objects() + children = container.findChildren(QtWidgets.QFrame, "Name") + for child in children: + line_edit = child.findChild(QtWidgets.QLineEdit) + if line_edit is not None and line_edit.text() == "MethodName": + line_edit.setText(TEST_METHOD_NAME) + + # 4) Add new parameters of each type + helper.wait_for_condition(lambda: container.findChild(QtWidgets.QFrame, "Parameters") is not None, 2.0) + parameters = container.findChild(QtWidgets.QFrame, "Parameters") + add_param = parameters.findChild(QtWidgets.QToolButton, "") + for _ in range(N_VAR_TYPES): + add_param.click() + + # 5) Verify if parameters are added + result = helper.wait_for_condition(verify_added_params, 3.0) + Report.result(Tests.params_added, result) + + # 6) Expand the parameter rows (to render QFrame 'Type' for each param) + for index in range(N_VAR_TYPES): + expand_container_rows(f"[{index}]") + + # 7) Set different names and datatypes for each parameter + expand_container_rows("Name") + children = container.findChildren(QtWidgets.QFrame, "Name") + index = 0 + for child in children: + line_edit = child.findChild(QtWidgets.QLineEdit) + if line_edit is not None and line_edit.text() == "ParameterName": + line_edit.setText(f"param_{index}") + index += 1 + + children = container.findChildren(QtWidgets.QFrame, "Type") + index = 0 + for child in children: + combo_box = child.findChild(QtWidgets.QComboBox) + if combo_box is not None and index < N_VAR_TYPES: + combo_box.setCurrentIndex(index) + index += 1 + + # 8) Save file and verify node in SC Node Palette + Report.result(Tests.file_saved, save_file()) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + initialize_sc_qt_objects() + node_palette_search(TEST_METHOD_NAME) + get_node_index = lambda: pyside_utils.find_child_by_pattern(tree, {"text": TEST_METHOD_NAME}) is not None + result = helper.wait_for_condition(get_node_index, 2.0) + Report.result(Tests.node_found, result) + + # 9) Close Asset Editor + general.close_pane("Asset Editor") + general.close_pane("Script Canvas") + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + from utils import Report + + Report.start_test(ScriptEvents_AllParamDatatypes_CreationSuccess) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Periodic.py index 85d0b4523f..e3a8d874da 100755 --- a/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Periodic.py @@ -113,10 +113,6 @@ class TestAutomation(TestAutomationBase): from . import Debugger_HappyPath_TargetMultipleGraphs as test_module self._run_test(request, workspace, editor, test_module) - def test_Debugging_TargetMultipleGraphs(self, request, workspace, editor, launcher_platform, project): - from . import Debugging_TargetMultipleGraphs as test_module - self._run_test(request, workspace, editor, test_module) - @pytest.mark.parametrize("level", ["tmp_level"]) def test_Debugger_HappyPath_TargetMultipleEntities(self, request, workspace, editor, launcher_platform, project, level): def teardown(): @@ -190,6 +186,18 @@ class TestAutomation(TestAutomationBase): from . import Node_HappyPath_DuplicateNode as test_module self._run_test(request, workspace, editor, test_module) + def test_ScriptEvent_AddRemoveParameter_ActionsSuccessful(self, request, workspace, editor, launcher_platform): + def teardown(): + file_system.delete( + [os.path.join(workspace.paths.project(), "ScriptCanvas", "test_file.scriptevent")], True, True + ) + request.addfinalizer(teardown) + file_system.delete( + [os.path.join(workspace.paths.project(), "ScriptCanvas", "test_file.scriptevent")], True, True + ) + from . import ScriptEvent_AddRemoveParameter_ActionsSuccessful as test_module + self._run_test(request, workspace, editor, test_module) + # NOTE: We had to use hydra_test_utils.py, as TestAutomationBase run_test method # fails because of pyside_utils import @pytest.mark.SUITE_periodic @@ -317,4 +325,30 @@ class TestScriptCanvasTests(object): auto_test_mode=False, timeout=60, ) + + def test_ScriptEvents_AllParamDatatypes_CreationSuccess(self, request, workspace, editor, launcher_platform): + def teardown(): + file_system.delete( + [os.path.join(workspace.paths.project(), "TestAssets", "test_file.scriptevents")], True, True + ) + request.addfinalizer(teardown) + file_system.delete( + [os.path.join(workspace.paths.project(), "TestAssets", "test_file.scriptevents")], True, True + ) + expected_lines = [ + "Success: New Script Event created", + "Success: Child Event created", + "Success: New parameters added", + "Success: Script event file saved", + "Success: Node found in Script Canvas", + ] + hydra.launch_and_validate_results( + request, + TEST_DIRECTORY, + editor, + "ScriptEvents_AllParamDatatypes_CreationSuccess.py", + expected_lines, + auto_test_mode=False, + timeout=60, + ) \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonTests/smoke/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/smoke/CMakeLists.txt index d351ec0e6c..a3b6e36250 100644 --- a/AutomatedTesting/Gem/PythonTests/smoke/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/smoke/CMakeLists.txt @@ -14,6 +14,24 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) TEST_SUITE smoke TEST_SERIAL PATH ${CMAKE_CURRENT_LIST_DIR} + PYTEST_MARKS "SUITE_smoke" + TIMEOUT 1500 + RUNTIME_DEPENDENCIES + AZ::AssetProcessor + AZ::PythonBindingsExample + Legacy::Editor + AutomatedTesting.GameLauncher + AutomatedTesting.Assets + COMPONENT + Smoke + ) + + ly_add_pytest( + NAME AutomatedTesting::SandboxTest + TEST_SUITE sandbox + TEST_SERIAL + PATH ${CMAKE_CURRENT_LIST_DIR} + PYTEST_MARKS "SUITE_sandbox" TIMEOUT 1500 RUNTIME_DEPENDENCIES AZ::AssetProcessor diff --git a/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py b/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py index 985740307f..e6b072ba58 100644 --- a/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py +++ b/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py @@ -15,7 +15,7 @@ from automatedtesting_shared.base import TestAutomationBase import ly_test_tools.environment.file_system as file_system -@pytest.mark.SUITE_smoke +@pytest.mark.SUITE_sandbox @pytest.mark.parametrize("launcher_platform", ["windows_editor"]) @pytest.mark.parametrize("project", ["AutomatedTesting"]) @pytest.mark.parametrize("level", ["temp_level"]) diff --git a/AutomatedTesting/Levels/Simple/Simple.ly b/AutomatedTesting/Levels/Simple/Simple.ly index 0148ee6e34..0a063bf8f8 100644 --- a/AutomatedTesting/Levels/Simple/Simple.ly +++ b/AutomatedTesting/Levels/Simple/Simple.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:804193a2afd68cd1e6bec8155ea11400566f2941fbd6eb0c324839ebcd10192d -size 8492 +oid sha256:302d6172156e8ed665e44e206d81f54f1b0f1008d73327300ea92f8c1159780b +size 11820 diff --git a/AutomatedTesting/Levels/WaterSample/WaterSample.ly b/AutomatedTesting/Levels/WaterSample/WaterSample.ly deleted file mode 100644 index b1899f3710..0000000000 --- a/AutomatedTesting/Levels/WaterSample/WaterSample.ly +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d49aceca5ad4e0b9f46c8127afb5c53b68aa30272950b1abd66fba310977ff0c -size 15032 diff --git a/AutomatedTesting/Levels/WaterSample/filelist.xml b/AutomatedTesting/Levels/WaterSample/filelist.xml deleted file mode 100644 index d14b2fdaf2..0000000000 --- a/AutomatedTesting/Levels/WaterSample/filelist.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/AutomatedTesting/Levels/WaterSample/halfsphere.cgf b/AutomatedTesting/Levels/WaterSample/halfsphere.cgf deleted file mode 100644 index 4426d8a232..0000000000 --- a/AutomatedTesting/Levels/WaterSample/halfsphere.cgf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5f221acd847ec8a15e1333a5163d6d0fd886b8eda46fa7b133f76ddbf1d11216 -size 41472 diff --git a/AutomatedTesting/Levels/WaterSample/halfsphere2.cgf b/AutomatedTesting/Levels/WaterSample/halfsphere2.cgf deleted file mode 100644 index c776ff68b8..0000000000 --- a/AutomatedTesting/Levels/WaterSample/halfsphere2.cgf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c8e5dcfbe65fd2fd8ea29a38a96e703683c544fd42b9424857b1df3718c7775a -size 41472 diff --git a/AutomatedTesting/Levels/WaterSample/level.pak b/AutomatedTesting/Levels/WaterSample/level.pak deleted file mode 100644 index 1753ef4b93..0000000000 --- a/AutomatedTesting/Levels/WaterSample/level.pak +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0378911c27933302042550d5a031a5f9104296162edc2b21e44893f1b8cff969 -size 44124 diff --git a/AutomatedTesting/Levels/WaterSample/leveldata/Environment.xml b/AutomatedTesting/Levels/WaterSample/leveldata/Environment.xml deleted file mode 100644 index 6a95c631bb..0000000000 --- a/AutomatedTesting/Levels/WaterSample/leveldata/Environment.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Levels/WaterSample/leveldata/TerrainTexture.xml b/AutomatedTesting/Levels/WaterSample/leveldata/TerrainTexture.xml deleted file mode 100644 index 21741afe52..0000000000 --- a/AutomatedTesting/Levels/WaterSample/leveldata/TerrainTexture.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/AutomatedTesting/Levels/WaterSample/leveldata/TimeOfDay.xml b/AutomatedTesting/Levels/WaterSample/leveldata/TimeOfDay.xml deleted file mode 100644 index 60ad405904..0000000000 --- a/AutomatedTesting/Levels/WaterSample/leveldata/TimeOfDay.xml +++ /dev/null @@ -1,356 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Levels/WaterSample/leveldata/VegetationMap.dat b/AutomatedTesting/Levels/WaterSample/leveldata/VegetationMap.dat deleted file mode 100644 index dce5631cd0..0000000000 --- a/AutomatedTesting/Levels/WaterSample/leveldata/VegetationMap.dat +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0e6a5435c928079b27796f6b202bbc2623e7e454244ddc099a3cadf33b7cb9e9 -size 63 diff --git a/AutomatedTesting/Levels/WaterSample/pool.cgf b/AutomatedTesting/Levels/WaterSample/pool.cgf deleted file mode 100644 index 04bec52a62..0000000000 --- a/AutomatedTesting/Levels/WaterSample/pool.cgf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:12ca8f1942331abde4d58724aea22609c8d7951cc415afa6e5f1c550a14e67b0 -size 363624 diff --git a/AutomatedTesting/Levels/WaterSample/pool2.cgf b/AutomatedTesting/Levels/WaterSample/pool2.cgf deleted file mode 100644 index 204306f8a8..0000000000 --- a/AutomatedTesting/Levels/WaterSample/pool2.cgf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f5b525a410730d84c0b3e97396d392e1e72f4b894742ddef3de4ede5542b0f8e -size 86148 diff --git a/AutomatedTesting/Levels/WaterSample/tags.txt b/AutomatedTesting/Levels/WaterSample/tags.txt deleted file mode 100644 index 0d6c1880e7..0000000000 --- a/AutomatedTesting/Levels/WaterSample/tags.txt +++ /dev/null @@ -1,12 +0,0 @@ -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 -0,0,0,0,0,0 diff --git a/AutomatedTesting/Levels/WaterSample/terraintexture.pak b/AutomatedTesting/Levels/WaterSample/terraintexture.pak deleted file mode 100644 index fe3604a050..0000000000 --- a/AutomatedTesting/Levels/WaterSample/terraintexture.pak +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8739c76e681f900923b900c9df0ef75cf421d39cabb54650c4b9ad19b6a76d85 -size 22 diff --git a/AutomatedTesting/Levels/WaterSample/woodland_canyon_river.mtl b/AutomatedTesting/Levels/WaterSample/woodland_canyon_river.mtl deleted file mode 100644 index 4548bca421..0000000000 --- a/AutomatedTesting/Levels/WaterSample/woodland_canyon_river.mtl +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/AutomatedTesting/Objects/LumberTank/ProxyGray_ddna.tif.exportsettings b/AutomatedTesting/Objects/LumberTank/ProxyGray_ddna.tif.exportsettings index 013c774e9e..a4e1a9a3c5 100644 --- a/AutomatedTesting/Objects/LumberTank/ProxyGray_ddna.tif.exportsettings +++ b/AutomatedTesting/Objects/LumberTank/ProxyGray_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:0,pc:0,provo:0" \ No newline at end of file +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:0,pc:0,provo:0" \ No newline at end of file diff --git a/AutomatedTesting/Registry/C3510644_Collider_CollisionGroups.setreg_override b/AutomatedTesting/Registry/C3510644_Collider_CollisionGroups.setreg_override index 9fa5e26768..696a0a74da 100644 --- a/AutomatedTesting/Registry/C3510644_Collider_CollisionGroups.setreg_override +++ b/AutomatedTesting/Registry/C3510644_Collider_CollisionGroups.setreg_override @@ -119,6 +119,9 @@ ] } }, + "DefaultMaterial": { + "SurfaceType": "Default_1" + }, "MaterialLibrary": { "assetId": { "guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}" diff --git a/AutomatedTesting/Registry/C4044459_Material_DynamicFriction.setreg_override b/AutomatedTesting/Registry/C4044459_Material_DynamicFriction.setreg_override new file mode 100644 index 0000000000..c53b04e5c2 --- /dev/null +++ b/AutomatedTesting/Registry/C4044459_Material_DynamicFriction.setreg_override @@ -0,0 +1,118 @@ +{ + "Amazon": { + "Gems": { + "PhysX": { + "PhysXSystemConfiguration": { + "CollisionConfig": { + "Layers": { + "LayerNames": [ + "Default", + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + "TouchBend" + ] + }, + "Groups": { + "GroupPresets": [ + { + "Name": "All", + "ReadOnly": true + }, + { + "Id": { + "GroupId": "{CDB6B8D8-5CD0-40A8-874D-839B00A92EBB}" + }, + "Name": "None", + "Group": { + "Mask": 0 + }, + "ReadOnly": true + }, + { + "Id": { + "GroupId": "{22769429-5D46-429B-829A-0115239D9AAA}" + }, + "Name": "All_NoTouchBend", + "Group": { + "Mask": 9223372036854775807 + }, + "ReadOnly": true + } + ] + } + }, + "DefaultMaterial": { + "SurfaceType": "Default_1" + }, + "MaterialLibrary": { + "assetId": { + "guid": "{6AA79EE4-7EC3-5717-87AE-EDD7D886FD7F}" + }, + "loadBehavior": "QueueLoad", + "assetHint": "levels/physics/c4044459_material_dynamicfriction/dynamic_friction.physmaterial" + } + } + } + } + } +} \ No newline at end of file diff --git a/AutomatedTesting/Registry/C4976227_Collider_NewGroup.setreg_override b/AutomatedTesting/Registry/C4976227_Collider_NewGroup.setreg_override index afbe6a9d38..5e98e08ede 100644 --- a/AutomatedTesting/Registry/C4976227_Collider_NewGroup.setreg_override +++ b/AutomatedTesting/Registry/C4976227_Collider_NewGroup.setreg_override @@ -107,6 +107,9 @@ ] } }, + "DefaultMaterial": { + "SurfaceType": "Default_1" + }, "MaterialLibrary": { "assetId": { "guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}" diff --git a/AutomatedTesting/Registry/C4976244_Collider_SameGroupSameLayerCollision.setreg_override b/AutomatedTesting/Registry/C4976244_Collider_SameGroupSameLayerCollision.setreg_override index 9fa5e26768..696a0a74da 100644 --- a/AutomatedTesting/Registry/C4976244_Collider_SameGroupSameLayerCollision.setreg_override +++ b/AutomatedTesting/Registry/C4976244_Collider_SameGroupSameLayerCollision.setreg_override @@ -119,6 +119,9 @@ ] } }, + "DefaultMaterial": { + "SurfaceType": "Default_1" + }, "MaterialLibrary": { "assetId": { "guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}" diff --git a/AutomatedTesting/Registry/C4976245_PhysXCollider_CollisionLayerTest.setreg_override b/AutomatedTesting/Registry/C4976245_PhysXCollider_CollisionLayerTest.setreg_override index 9fa5e26768..696a0a74da 100644 --- a/AutomatedTesting/Registry/C4976245_PhysXCollider_CollisionLayerTest.setreg_override +++ b/AutomatedTesting/Registry/C4976245_PhysXCollider_CollisionLayerTest.setreg_override @@ -119,6 +119,9 @@ ] } }, + "DefaultMaterial": { + "SurfaceType": "Default_1" + }, "MaterialLibrary": { "assetId": { "guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}" diff --git a/AutomatedTesting/Registry/C4982593_PhysXCollider_CollisionLayer.setreg_override b/AutomatedTesting/Registry/C4982593_PhysXCollider_CollisionLayer.setreg_override index 9fa5e26768..696a0a74da 100644 --- a/AutomatedTesting/Registry/C4982593_PhysXCollider_CollisionLayer.setreg_override +++ b/AutomatedTesting/Registry/C4982593_PhysXCollider_CollisionLayer.setreg_override @@ -119,6 +119,9 @@ ] } }, + "DefaultMaterial": { + "SurfaceType": "Default_1" + }, "MaterialLibrary": { "assetId": { "guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}" diff --git a/AutomatedTesting/Registry/awscoreconfiguration.setreg b/AutomatedTesting/Registry/awscoreconfiguration.setreg index ca110eb103..b7c60b0fb9 100644 --- a/AutomatedTesting/Registry/awscoreconfiguration.setreg +++ b/AutomatedTesting/Registry/awscoreconfiguration.setreg @@ -3,7 +3,7 @@ { "AWSCore": { - "ProfileName": "default", + "ProfileName": "AWSAutomationTest", "ResourceMappingConfigFileName": "aws_resource_mappings.json" } } diff --git a/AutomatedTesting/Registry/physxsystemconfiguration.setreg b/AutomatedTesting/Registry/physxsystemconfiguration.setreg index 02f65b685b..30e9dced44 100644 --- a/AutomatedTesting/Registry/physxsystemconfiguration.setreg +++ b/AutomatedTesting/Registry/physxsystemconfiguration.setreg @@ -101,6 +101,9 @@ ] } }, + "DefaultMaterial": { + "SurfaceType": "Default_1" + }, "MaterialLibrary": { "assetId": { "guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}" diff --git a/AutomatedTesting/preview.png b/AutomatedTesting/preview.png index 2191a0ebc2..c6928d31fc 100644 --- a/AutomatedTesting/preview.png +++ b/AutomatedTesting/preview.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a18fae4040a22d2bb359a8ca642b97bb8f6468eeb52e2826b3b029bd8f1350b6 -size 5466 +oid sha256:b9cd9d6f67440c193a85969ec5c082c6343e6d1fff3b6f209a0a6931eb22dd47 +size 2949 diff --git a/AutomatedTesting/surfacetypemateriallibrary.physmaterial b/AutomatedTesting/surfacetypemateriallibrary.physmaterial index 3c39d5521e..481cd2fbfa 100644 --- a/AutomatedTesting/surfacetypemateriallibrary.physmaterial +++ b/AutomatedTesting/surfacetypemateriallibrary.physmaterial @@ -1,19 +1,155 @@ - + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - + + diff --git a/CMakeLists.txt b/CMakeLists.txt index 63177e9d60..387f536966 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,34 +25,13 @@ include(cmake/LySet.cmake) include(cmake/Version.cmake) include(cmake/OutputDirectory.cmake) -# Set the engine_path and engine_json -set(o3de_engine_path ${CMAKE_CURRENT_LIST_DIR}) -set(o3de_engine_json ${o3de_engine_path}/engine.json) - if(NOT PROJECT_NAME) project(O3DE LANGUAGES C CXX VERSION ${LY_VERSION_STRING} ) - - # o3de manifest - include(cmake/o3de_manifest.cmake) endif() -################################################################################ -# Resolve this engines name and restricted path -################################################################################ -o3de_engine_name(${o3de_engine_json} o3de_engine_name) -o3de_restricted_path(${o3de_engine_json} o3de_engine_restricted_path) -message(STATUS "O3DE Engine Name: ${o3de_engine_name}") -message(STATUS "O3DE Engine Path: ${o3de_engine_path}") -if(o3de_engine_restricted_path) - message(STATUS "O3DE Engine Restricted Path: ${o3de_engine_restricted_path}") -endif() - -# add the engines cmake folder to the CMAKE_MODULE_PATH -list(APPEND CMAKE_MODULE_PATH "${o3de_engine_path}/cmake") - ################################################################################ # Initialize ################################################################################ @@ -60,6 +39,7 @@ include(cmake/GeneralSettings.cmake) include(cmake/FileUtil.cmake) include(cmake/PAL.cmake) include(cmake/PALTools.cmake) +include(cmake/RuntimeDependencies.cmake) include(cmake/Install.cmake) include(cmake/Configurations.cmake) # Requires to be after PAL so we get platform variable definitions include(cmake/Dependencies.cmake) @@ -67,88 +47,106 @@ include(cmake/Deployment.cmake) include(cmake/3rdParty.cmake) include(cmake/LYPython.cmake) include(cmake/LYWrappers.cmake) +include(cmake/Gems.cmake) include(cmake/UnitTest.cmake) include(cmake/LYTestWrappers.cmake) include(cmake/Monolithic.cmake) include(cmake/SettingsRegistry.cmake) include(cmake/TestImpactFramework/LYTestImpactFramework.cmake) include(cmake/CMakeFiles.cmake) +include(cmake/O3DEJson.cmake) ################################################################################ # Subdirectory processing ################################################################################ +function(add_engine_json_external_subdirectories) + read_json_external_subdirs(external_subdis ${LY_ROOT_FOLDER}/engine.json) + foreach(external_subdir ${external_subdis}) + file(REAL_PATH ${external_subdir} real_external_subdir BASE_DIRECTORY ${LY_ROOT_FOLDER}) + list(APPEND engine_external_subdirs ${real_external_subdir}) + endforeach() + + set_property(GLOBAL APPEND PROPERTY LY_EXTERNAL_SUBDIRS ${engine_external_subdirs}) +endfunction() + # Add the projects first so the Launcher can find them include(cmake/Projects.cmake) if(NOT INSTALLED_ENGINE) + # Add the rest of the targets add_subdirectory(Code) -else() - ly_find_o3de_packages() -endif() - -# Add external subdirectories listed in the manifest -list(APPEND LY_EXTERNAL_SUBDIRS ${o3de_engine_external_subdirectories}) - -set(enabled_platforms - ${PAL_PLATFORM_NAME} - ${LY_PAL_TOOLS_ENABLED}) - -# Add any engine restricted platforms as external subdirs -o3de_add_engine_restricted_platform_external_subdirs() - -if(NOT INSTALLED_ENGINE) add_subdirectory(scripts) -endif() -# SPEC-1417 will investigate and fix this -if(NOT PAL_PLATFORM_NAME STREQUAL "Mac") - add_subdirectory(Tools/LyTestTools/tests/) - add_subdirectory(Tools/RemoteConsole/ly_remote_console/tests/) + # SPEC-1417 will investigate and fix this + if(NOT PAL_PLATFORM_NAME STREQUAL "Mac") + add_subdirectory(Tools/LyTestTools/tests/) + add_subdirectory(Tools/RemoteConsole/ly_remote_console/tests/) + endif() + + # Add external subdirectories listed in the engine.json. LY_EXTERNAL_SUBDIRS is a cache variable so the user can add extra + # external subdirectories + add_engine_json_external_subdirectories() + get_property(external_subdirs GLOBAL PROPERTY LY_EXTERNAL_SUBDIRS) + list(APPEND LY_EXTERNAL_SUBDIRS ${external_subdirs}) + + # Loop over the additional external subdirectories and invoke add_subdirectory on them + foreach(external_directory ${LY_EXTERNAL_SUBDIRS}) + # Hash the extenal_directory name and append it to the Binary Directory section of add_subdirectory + # This is to deal with potential situations where multiple external directories has the same last directory name + # For example if D:/Company1/RayTracingGem and F:/Company2/Path/RayTracingGem were both added as a subdirectory + file(REAL_PATH ${external_directory} full_directory_path) + string(SHA256 full_directory_hash ${full_directory_path}) + # Truncate the full_directory_hash down to 8 characters to avoid hitting the Windows 260 character path limit + # when the external subdirectory contains relative paths of significant length + string(SUBSTRING ${full_directory_hash} 0 8 full_directory_hash) + # Use the last directory as the suffix path to use for the Binary Directory + get_filename_component(directory_name ${external_directory} NAME) + add_subdirectory(${external_directory} ${CMAKE_BINARY_DIR}/External/${directory_name}-${full_directory_hash}) + endforeach() + +else() + ly_find_o3de_packages() endif() ################################################################################ # Post-processing ################################################################################ +# The following steps have to be done after all targets are registered: +# Defer generation of the StaticModules.inl file which is needed to create the AZ::Module derived class in monolithic +# builds until after all the targets are known +ly_delayed_generate_static_modules_inl() -# Loop over the additional external subdirectories and invoke add_subdirectory on them -foreach(external_directory ${LY_EXTERNAL_SUBDIRS}) - # Hash the extenal_directory name and append it to the Binary Directory section of add_subdirectory - # This is to deal with potential situations where multiple external directories has the same last directory name - # For example if D:/Company1/RayTracingGem and F:/Company2/Path/RayTracingGem were both added as a subdirectory - file(REAL_PATH ${external_directory} full_directory_path) - string(SHA256 full_directory_hash ${full_directory_path}) - # Truncate the full_directory_hash down to 8 characters to avoid hitting the Windows 260 character path limit - # when the external subdirectory contains relative paths of significant length - string(SUBSTRING ${full_directory_hash} 0 8 full_directory_hash) - # Use the last directory as the suffix path to use for the Binary Directory - get_filename_component(directory_name ${external_directory} NAME) - add_subdirectory(${external_directory} ${CMAKE_BINARY_DIR}/External/${directory_name}-${full_directory_hash}) -endforeach() +# 1. Add any dependencies registered via ly_enable_gems +ly_enable_gems_delayed() -# The following steps have to be done after all targets are registered: -# 1. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls +# 2. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls # to provide applications with the filenames of gem modules to load # This must be done before ly_delayed_target_link_libraries() as that inserts BUILD_DEPENDENCIES as MANUALLY_ADDED_DEPENDENCIES # if the build dependency is a MODULE_LIBRARY. That would cause a false load dependency to be generated ly_delayed_generate_settings_registry() -# 2. link targets where the dependency was yet not declared, we need to have the declaration so we do different + +# 3. link targets where the dependency was yet not declared, we need to have the declaration so we do different # linking logic depending on the type of target ly_delayed_target_link_libraries() -# 3. generate a registry file for unit testing for platforms that support unit testing + +# 4. generate a registry file for unit testing for platforms that support unit testing if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_delayed_generate_unit_test_module_registry() endif() -# 4. inject runtime dependencies to the targets. We need to do this after (1) since we are going to walk through + +# 5. inject runtime dependencies to the targets. We need to do this after (1) since we are going to walk through # the dependencies -include(cmake/RuntimeDependencies.cmake) -# 5. Perform test impact framework post steps once all of the targets have been enumerated +ly_delayed_generate_runtime_dependencies() + +# 6. Perform test impact framework post steps once all of the targets have been enumerated ly_test_impact_post_step() -# 6. Generate the O3DE find file and setup install locations for scripts, tools, assets etc., required by the engine + +# 7. Generate the O3DE find file and setup install locations for scripts, tools, assets etc., required by the engine if(NOT INSTALLED_ENGINE) + # 8. Generate the O3DE find file and setup install locations for scripts, tools, assets etc., required by the engine ly_setup_o3de_install() - - # IMPORTANT: must be included last + # 9. CPack information (to be included after install) include(cmake/Packaging.cmake) endif() diff --git a/Code/CryEngine/CryCommon/ISystem.h b/Code/CryEngine/CryCommon/ISystem.h index f863804f3d..653776f55b 100644 --- a/Code/CryEngine/CryCommon/ISystem.h +++ b/Code/CryEngine/CryCommon/ISystem.h @@ -125,7 +125,7 @@ enum ESystemConfigPlatform { CONFIG_INVALID_PLATFORM = 0, CONFIG_PC = 1, - CONFIG_OSX_GL = 2, + CONFIG_MAC = 2, CONFIG_OSX_METAL = 3, CONFIG_ANDROID = 4, CONFIG_IOS = 5, diff --git a/Code/CryEngine/CryCommon/LyShine/IDraw2d.h b/Code/CryEngine/CryCommon/LyShine/IDraw2d.h index 16fdfceca3..76a71c9e24 100644 --- a/Code/CryEngine/CryCommon/LyShine/IDraw2d.h +++ b/Code/CryEngine/CryCommon/LyShine/IDraw2d.h @@ -11,7 +11,6 @@ */ #pragma once -#include #include #include #include @@ -84,7 +83,7 @@ public: // types //! If this is not passed then the defaults below are used struct TextOptions { - IFFont* font; //!< default is "default" + AZStd::string fontName; //!< default is "default" unsigned int effectIndex; //!< default is 0 AZ::Vector3 color; //!< default is (1,1,1) HAlign horizontalAlignment; //!< default is HAlign::Left diff --git a/Code/CryEngine/CrySystem/LevelSystem/SpawnableLevelSystem.cpp b/Code/CryEngine/CrySystem/LevelSystem/SpawnableLevelSystem.cpp index ff6ebc0d17..31d1540d77 100644 --- a/Code/CryEngine/CrySystem/LevelSystem/SpawnableLevelSystem.cpp +++ b/Code/CryEngine/CrySystem/LevelSystem/SpawnableLevelSystem.cpp @@ -36,8 +36,8 @@ namespace LegacyLevelSystem //------------------------------------------------------------------------ static void LoadLevel(const AZ::ConsoleCommandContainer& arguments) { - AZ_Error("SpawnableLevelSystem", arguments.empty(), "LoadLevel requires a level file name to be provided."); - AZ_Error("SpawnableLevelSystem", arguments.size() > 1, "LoadLevel requires a single level file name to be provided."); + AZ_Error("SpawnableLevelSystem", !arguments.empty(), "LoadLevel requires a level file name to be provided."); + AZ_Error("SpawnableLevelSystem", arguments.size() == 1, "LoadLevel requires a single level file name to be provided."); if (!arguments.empty() && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor()) { diff --git a/Code/CryEngine/CrySystem/System.h b/Code/CryEngine/CrySystem/System.h index b91b1ba059..a258030f70 100644 --- a/Code/CryEngine/CrySystem/System.h +++ b/Code/CryEngine/CrySystem/System.h @@ -729,7 +729,7 @@ protected: // ------------------------------------------------------------- CCmdLine* m_pCmdLine; string m_currentLanguageAudio; - string m_systemConfigName; // computed from system_(hardwareplatform)_(assetsPlatform) - eg, system_android_es3.cfg or system_android_opengl.cfg or system_windows_pc.cfg + string m_systemConfigName; // computed from system_(hardwareplatform)_(assetsPlatform) - eg, system_android_android.cfg or system_windows_pc.cfg std::vector< std::pair > m_updateTimes; diff --git a/Code/CryEngine/CrySystem/SystemInit.cpp b/Code/CryEngine/CrySystem/SystemInit.cpp index 25d6b9c601..52744519bb 100644 --- a/Code/CryEngine/CrySystem/SystemInit.cpp +++ b/Code/CryEngine/CrySystem/SystemInit.cpp @@ -864,11 +864,6 @@ bool CSystem::InitShine([[maybe_unused]] const SSystemInitParams& initParams) EBUS_EVENT(UiSystemBus, InitializeSystem); - if (!m_env.pLyShine) - { - AZ_Error(AZ_TRACE_SYSTEM_WINDOW, false, "LYShine System did not initialize correctly. Please check that the LyShine gem is enabled for this project in *_dependencies.cmake."); - return false; - } return true; } @@ -2022,8 +2017,8 @@ void CSystem::CreateSystemVars() REGISTER_CVAR2("sys_streaming_in_blocks", &g_cvars.sys_streaming_in_blocks, 1, VF_NULL, "Streaming of large files happens in blocks"); -#if (defined(WIN32) || defined(WIN64)) && !defined(_RELEASE) - REGISTER_CVAR2("sys_float_exceptions", &g_cvars.sys_float_exceptions, 3, 0, "Use or not use floating point exceptions."); +#if (defined(WIN32) || defined(WIN64)) && defined(_DEBUG) + REGISTER_CVAR2("sys_float_exceptions", &g_cvars.sys_float_exceptions, 2, 0, "Use or not use floating point exceptions."); #else // Float exceptions by default disabled for console builds. REGISTER_CVAR2("sys_float_exceptions", &g_cvars.sys_float_exceptions, 0, 0, "Use or not use floating point exceptions."); #endif diff --git a/Code/Framework/AzAndroid/java/com/amazon/lumberyard/LumberyardActivity.java b/Code/Framework/AzAndroid/java/com/amazon/lumberyard/LumberyardActivity.java index b5d3de8164..5c1a120df6 100644 --- a/Code/Framework/AzAndroid/java/com/amazon/lumberyard/LumberyardActivity.java +++ b/Code/Framework/AzAndroid/java/com/amazon/lumberyard/LumberyardActivity.java @@ -244,7 +244,7 @@ public class LumberyardActivity extends NativeActivity boolean useMainObb = GetBooleanResource("use_main_obb"); boolean usePatchObb = GetBooleanResource("use_patch_obb"); - if (IsBootstrapInAPK() && (useMainObb || usePatchObb)) + if (AreAssetsInAPK() && (useMainObb || usePatchObb)) { Log.d(TAG, "Using OBB expansion files for game assets"); @@ -421,12 +421,12 @@ public class LumberyardActivity extends NativeActivity } //////////////////////////////////////////////////////////////// - private boolean IsBootstrapInAPK() + private boolean AreAssetsInAPK() { try { - InputStream bootstrap = getAssets().open("bootstrap.cfg", AssetManager.ACCESS_UNKNOWN); - bootstrap.close(); + InputStream engine = getAssets().open("engine.json", AssetManager.ACCESS_UNKNOWN); + engine.close(); return true; } catch (IOException exception) diff --git a/Code/Framework/AzCore/AzCore/Android/Utils.cpp b/Code/Framework/AzCore/AzCore/Android/Utils.cpp index efbbf50d1d..d6435c67be 100644 --- a/Code/Framework/AzCore/AzCore/Android/Utils.cpp +++ b/Code/Framework/AzCore/AzCore/Android/Utils.cpp @@ -148,7 +148,7 @@ namespace AZ } } - AZ_Assert(false, "Failed to locate the bootstrap.cfg path"); + AZ_Assert(false, "Failed to locate the engine.json path"); return nullptr; } diff --git a/Code/Framework/AzCore/AzCore/Android/Utils.h b/Code/Framework/AzCore/AzCore/Android/Utils.h index 222fac80ad..0862d53aa4 100644 --- a/Code/Framework/AzCore/AzCore/Android/Utils.h +++ b/Code/Framework/AzCore/AzCore/Android/Utils.h @@ -73,8 +73,8 @@ namespace AZ //! \return The pointer position of the relative asset path AZ::IO::FixedMaxPath StripApkPrefix(const char* filePath); - //! Searches application storage and the APK for bootstrap.cfg. Will return nullptr - //! if bootstrap.cfg is not found. + //! Searches application storage and the APK for engine.json. Will return nullptr + //! if engine.json is not found. const char* FindAssetsDirectory(); //! Calls into Java to show the splash screen on the main UI (Java) thread diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp index 8a170f5d89..7b33359023 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp @@ -77,6 +77,27 @@ #endif // defined(AZ_ENABLE_DEBUG_TOOLS) #include +#include + +static void PrintEntityName(const AZ::ConsoleCommandContainer& arguments) +{ + if (arguments.empty()) + { + return; + } + + const auto entityIdStr = AZStd::string(arguments.front()); + const auto entityIdValue = AZStd::stoull(entityIdStr); + + AZStd::string entityName; + AZ::ComponentApplicationBus::BroadcastResult( + entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, AZ::EntityId(entityIdValue)); + + AZ_Printf("Entity Debug", "EntityId: %" PRIu64 ", Entity Name: %s", entityIdValue, entityName.c_str()); +} + +AZ_CONSOLEFREEFUNC( + PrintEntityName, AZ::ConsoleFunctorFlags::Null, "Parameter: EntityId value, Prints the name of the entity to the console"); namespace AZ { @@ -462,8 +483,6 @@ namespace AZ // for the application root. CalculateAppRoot(); - // Merge the bootstrap.cfg file into the Settings Registry as soon as the OSAllocator has been created. - SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(*m_settingsRegistry); SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(*m_settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {}); SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands); SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry); @@ -1262,7 +1281,7 @@ namespace AZ // So auto load is turned off if option "AutoLoad" key is bool that is false if (valueName == "AutoLoad" && !value) { - // Strip off the AutoLoead entry from the path + // Strip off the AutoLoad entry from the path auto autoLoadKey = AZ::StringFunc::TokenizeLast(path, "/"); if (!autoLoadKey) { @@ -1332,7 +1351,7 @@ namespace AZ { auto CompareDynamicModuleDescriptor = [&dynamicLibraryPath](const DynamicModuleDescriptor& entry) { - return entry.m_dynamicLibraryPath.contains(dynamicLibraryPath); + return AZ::IO::PathView(entry.m_dynamicLibraryPath).Stem() == AZ::IO::PathView(dynamicLibraryPath).Stem(); }; if (auto moduleIter = AZStd::find_if(gemModules.begin(), gemModules.end(), CompareDynamicModuleDescriptor); moduleIter == gemModules.end()) diff --git a/Code/Framework/AzCore/AzCore/Component/TransformBus.h b/Code/Framework/AzCore/AzCore/Component/TransformBus.h index be18593d54..2a8d82c34c 100644 --- a/Code/Framework/AzCore/AzCore/Component/TransformBus.h +++ b/Code/Framework/AzCore/AzCore/Component/TransformBus.h @@ -172,78 +172,10 @@ namespace AZ //! Rotation modifiers //! @{ - //! @deprecated Use SetLocalRotation() - //! Sets the entity's rotation in the world. - //! The origin of the axes is the entity's position in world space. - //! @param eulerAnglesRadians A three-dimensional vector, containing Euler angles in radians, to rotate the entity by. - virtual void SetRotation([[maybe_unused]] const AZ::Vector3& eulerAnglesRadians) {} - - //! @deprecated Use SetLocalRotation() - //! Sets the entity's rotation around the world's X axis. - //! The origin of the axis is the entity's position in world space. - //! @param eulerAngleRadians The X coordinate Euler angle in radians to use for the entity's rotation. - virtual void SetRotationX([[maybe_unused]] float eulerAngleRadian) {} - - //! @deprecated Use SetLocalRotation() - //! Sets the entity's rotation around the world's Y axis. - //! The origin of the axis is the entity's position in world space. - //! @param eulerAngleRadians The Y coordinate Euler angle in radians to use for the entity's rotation. - virtual void SetRotationY([[maybe_unused]] float eulerAngleRadian) {} - - //! @deprecated Use SetLocalRotation() - //! Sets the entity's rotation around the world's Z axis. - //! The origin of the axis is the entity's position in world space. - //! @param eulerAngleRadians The Z coordinate Euler angle in radians to use for the entity's rotation. - virtual void SetRotationZ([[maybe_unused]] float eulerAngleRadian) {} - - //! @deprecated Use SetLocalRotationQuaternion() //! Sets the entity's rotation in the world in quaternion notation. //! The origin of the axes is the entity's position in world space. //! @param quaternion A quaternion that represents the rotation to use for the entity. - virtual void SetRotationQuaternion([[maybe_unused]] const AZ::Quaternion& quaternion) {} - - //! @deprecated Use RotateAroundLocalX() - //! Rotates the entity around the world's X axis. - //! The origin of the axis is the entity's position in world space. - //! @param eulerAngleRadians The Euler angle in radians by which to rotate the entity around the X axis. - virtual void RotateByX([[maybe_unused]] float eulerAngleRadian) {} - - //! @deprecated Use RotateAroundLocalY() - //! Rotates the entity around the world's Y axis. - //! The origin of the axis is the entity's position in world space. - //! @param eulerAngleRadians The Euler angle in radians by which to rotate the entity around the Y axis. - virtual void RotateByY([[maybe_unused]] float eulerAngleRadian) {} - - //! @deprecated Use RotateAroundLocalZ() - //! Rotates the entity around the world's Z axis. - //! The origin of the axis is the entity's position in world space. - //! @param eulerAngleRadians The Euler angle in radians by which to rotate the entity around the Z axis. - virtual void RotateByZ([[maybe_unused]] float eulerAngleRadian) {} - - //! @deprecated Use GetLocalRotation() - //! Gets the entity's rotation in the world in Euler angles rotation in radians. - //! @return A three-dimensional vector, containing Euler angles in radians, that represents the entity's rotation. - virtual AZ::Vector3 GetRotationEulerRadians() { return AZ::Vector3(FLT_MAX); } - - //! @deprecated Use GetLocalRotationQuaternion() - //! Gets the entity's rotation in the world in quaternion format. - //! @return A quaternion that represents the entity's rotation in world space. - virtual AZ::Quaternion GetRotationQuaternion() { return AZ::Quaternion::CreateZero(); } - - //! @deprecated Use GetLocalRotation() - //! Gets the entity's rotation around the world's X axis. - //! @return The Euler angle in radians by which the the entity is rotated around the X axis in world space. - virtual float GetRotationX() { return FLT_MAX; } - - //! @deprecated Use GetLocalRotation() - //! Gets the entity's rotation around the world's Y axis. - //! @return The Euler angle in radians by which the the entity is rotated around the Y axis in world space. - virtual float GetRotationY() { return FLT_MAX; } - - //! @deprecated Use GetLocalRotation() - //! Gets the entity's rotation around the world's Z axis. - //! @return The Euler angle in radians by which the the entity is rotated around the Z axis in world space. - virtual float GetRotationZ() { return FLT_MAX; } + virtual void SetWorldRotationQuaternion([[maybe_unused]] const AZ::Quaternion& quaternion) {} //! Get angles in radian for each principle axis around which the world transform is //! rotated in the order of z-axis and y-axis and then x-axis. @@ -287,18 +219,11 @@ namespace AZ //! Scale modifiers //! @{ - //! Set local scale of the transform. - //! @param scale The new scale to set. - virtual void SetLocalScale([[maybe_unused]] const AZ::Vector3& scale) {} - - //! Get the scale value in local space. + //! @deprecated GetLocalScale is deprecated, and is left only to allow migration of legacy vector scale. + //! Get the legacy vector scale value in local space. //! @return The scale value in local space. virtual AZ::Vector3 GetLocalScale() { return AZ::Vector3(FLT_MAX); } - //! Get the scale value in world space. - //! @return The scale value in world space. - virtual AZ::Vector3 GetWorldScale() { return AZ::Vector3(FLT_MAX); } - //! Set the uniform scale value in local space. virtual void SetLocalUniformScale([[maybe_unused]] float scale) {} diff --git a/Code/Framework/AzCore/AzCore/IO/Path/Path.h b/Code/Framework/AzCore/AzCore/IO/Path/Path.h index 61294cd637..6c1b519224 100644 --- a/Code/Framework/AzCore/AzCore/IO/Path/Path.h +++ b/Code/Framework/AzCore/AzCore/IO/Path/Path.h @@ -95,6 +95,12 @@ namespace AZ::IO constexpr int Compare(AZStd::string_view pathString) const noexcept; constexpr int Compare(const value_type* pathString) const noexcept; + // Extension for fixed strings + //! extension: fixed string types with MaxPathLength capacity + //! Returns a new instance of an AZStd::fixed_string with capacity of MaxPathLength + //! made from the internal string + constexpr AZStd::fixed_string FixedMaxPathString() const noexcept; + // decomposition //! Given a windows path of "C:\O3DE\foo\bar\name.txt" and a posix path of //! "/O3DE/foo/bar/name.txt" diff --git a/Code/Framework/AzCore/AzCore/IO/Path/Path.inl b/Code/Framework/AzCore/AzCore/IO/Path/Path.inl index 1e42fc9df7..05a92c5247 100644 --- a/Code/Framework/AzCore/AzCore/IO/Path/Path.inl +++ b/Code/Framework/AzCore/AzCore/IO/Path/Path.inl @@ -915,6 +915,11 @@ namespace AZ::IO return compare_string_view(path); } + constexpr AZStd::fixed_string PathView::FixedMaxPathString() const noexcept + { + return AZStd::fixed_string(m_path.begin(), m_path.end()); + } + // decomposition constexpr auto PathView::RootName() const -> PathView { diff --git a/Code/Framework/AzCore/AzCore/Math/Aabb.cpp b/Code/Framework/AzCore/AzCore/Math/Aabb.cpp index 3f7cb4ecf5..367594be63 100644 --- a/Code/Framework/AzCore/AzCore/Math/Aabb.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Aabb.cpp @@ -227,7 +227,7 @@ namespace AZ // the min and max of each part and sum them to get the min and max co-ordinate of the transformed box. For a given new axis, // the coefficients for what proportion of each original axis is rotated onto that new axis are the same as the components we // would get by performing the inverse rotation on the new axis, so we need to take the conjugate to get the inverse rotation. - axisCoeffs = transform.GetScale() * (transform.GetRotation().GetConjugate().TransformVector(axis)); + axisCoeffs = transform.GetUniformScale() * (transform.GetRotation().GetConjugate().TransformVector(axis)); a = axisCoeffs * m_min; b = axisCoeffs * m_max; diff --git a/Code/Framework/AzCore/AzCore/Math/Obb.cpp b/Code/Framework/AzCore/AzCore/Math/Obb.cpp index eb511669d0..9226ddd28f 100644 --- a/Code/Framework/AzCore/AzCore/Math/Obb.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Obb.cpp @@ -154,7 +154,7 @@ namespace AZ return Obb::CreateFromPositionRotationAndHalfLengths( transform.TransformPoint(obb.GetPosition()), transform.GetRotation() * obb.GetRotation(), - transform.GetScale() * obb.GetHalfLengths() + transform.GetUniformScale() * obb.GetHalfLengths() ); } } diff --git a/Code/Framework/AzCore/AzCore/Math/Random.h b/Code/Framework/AzCore/AzCore/Math/Random.h index 8b28f6aaad..8b2763df50 100644 --- a/Code/Framework/AzCore/AzCore/Math/Random.h +++ b/Code/Framework/AzCore/AzCore/Math/Random.h @@ -126,17 +126,16 @@ namespace AZ m_offsets.fill(1); // Halton sequences start at index 1. m_increments.fill(1); // By default increment by 1 between each number. } - - //! Returns a Halton sequence in an array of N length - template - AZStd::array, N> GetHaltonSequence() + + //! Fills a provided container from begin to end with a Halton sequence. + //! Entries are expected to be, or implicitly converted to, AZStd::array. + template + void FillHaltonSequence(Iterator begin, Iterator end) { - AZStd::array, N> result; - AZStd::array indices = m_offsets; // Generator that returns the Halton number for all bases for a single entry. - auto f = [&] () + auto f = [&]() { AZStd::array item; for (auto d = 0; d < Dimensions; ++d) @@ -147,12 +146,20 @@ namespace AZ return item; }; - AZStd::generate(result.begin(), result.end(), f); + AZStd::generate(begin, end, f); + } + + //! Returns a Halton sequence in an array of N length. + template + AZStd::array, N> GetHaltonSequence() + { + AZStd::array, N> result; + FillHaltonSequence(result.begin(), result.end()); return result; } //! Sets the offsets per dimension to start generating a sequence from. - //! By default, there is no offset (offset of 0 corresponds to starting at index 1) + //! By default, there is no offset (offset of 0 corresponds to starting at index 1). void SetOffsets(AZStd::array offsets) { m_offsets = offsets; diff --git a/Code/Framework/AzCore/AzCore/Math/Transform.cpp b/Code/Framework/AzCore/AzCore/Math/Transform.cpp index 9090a9e94e..62a390c138 100644 --- a/Code/Framework/AzCore/AzCore/Math/Transform.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Transform.cpp @@ -130,8 +130,8 @@ namespace AZ const Transform* transform = reinterpret_cast(classPtr); float data[NumFloats]; transform->GetRotation().StoreToFloat4(data); - transform->GetScale().StoreToFloat3(&data[4]); - transform->GetTranslation().StoreToFloat3(&data[7]); + data[4] = transform->GetUniformScale(); + transform->GetTranslation().StoreToFloat3(&data[5]); for (int i = 0; i < NumFloats; i++) { @@ -159,8 +159,8 @@ namespace AZ size_t TransformSerializer::TextToData(const char* text, unsigned int textVersion, IO::GenericStream& stream, bool isDataBigEndian) { - const size_t dataBufferSize = AZStd::max(NumFloatsVersion0, NumFloats); - const size_t numElements = textVersion < 1 ? NumFloatsVersion0 : NumFloats; + const size_t dataBufferSize = AZStd::max(AZStd::max(NumFloatsVersion1, NumFloatsVersion0), NumFloats); + const size_t numElements = textVersion < 1 ? NumFloatsVersion0 : (textVersion == 1 ? NumFloatsVersion1 : NumFloats); size_t nextNumberIndex = 0; AZStd::array data; @@ -201,7 +201,34 @@ namespace AZ return true; } - // otherwise load as a separate rotation, scale and translation + // version 1 had a quaternion rotation, vector3 scale and vector3 translation + else if (version == 1) + { + float data[NumFloatsVersion1]; + if (stream.GetLength() < sizeof(data)) + { + return false; + } + + stream.Read(sizeof(data), reinterpret_cast(data)); + + for (unsigned int i = 0; i < AZ_ARRAY_SIZE(data); ++i) + { + AZ_SERIALIZE_SWAP_ENDIAN(data[i], isDataBigEndian); + } + + Quaternion rotation = Quaternion::CreateFromFloat4(data); + Vector3 vectorScale = Vector3::CreateFromFloat3(&data[4]); + Vector3 translation = Vector3::CreateFromFloat3(&data[7]); + + float uniformScale = vectorScale.GetMaxElement(); + + *reinterpret_cast(classPtr) = + Transform::CreateFromQuaternionAndTranslation(rotation, translation) * Transform::CreateUniformScale(uniformScale); + return true; + } + + // otherwise load as a quaternion rotation, float scale and vector3 translation float data[NumFloats]; if (stream.GetLength() < sizeof(data)) { @@ -216,11 +243,11 @@ namespace AZ } Quaternion rotation = Quaternion::CreateFromFloat4(data); - Vector3 scale = Vector3::CreateFromFloat3(&data[4]); - Vector3 translation = Vector3::CreateFromFloat3(&data[7]); + float scale = data[4]; + Vector3 translation = Vector3::CreateFromFloat3(&data[5]); *reinterpret_cast(classPtr) = - Transform::CreateFromQuaternionAndTranslation(rotation, translation) * Transform::CreateScale(scale); + Transform::CreateFromQuaternionAndTranslation(rotation, translation) * Transform::CreateUniformScale(scale); return true; } @@ -237,7 +264,7 @@ namespace AZ if (serializeContext) { serializeContext->Class() - ->Version(1) + ->Version(2) ->Serializer(); } @@ -250,7 +277,7 @@ namespace AZ Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)-> Attribute(Script::Attributes::Storage, Script::Attributes::StorageType::Value)-> Attribute(Script::Attributes::GenericConstructorOverride, &Internal::TransformDefaultConstructor)-> - Constructor()-> + Constructor()-> Method("GetBasis", &Transform::GetBasis)-> Method("GetBasisX", &Transform::GetBasisX)-> Method("GetBasisY", &Transform::GetBasisY)-> @@ -283,15 +310,10 @@ namespace AZ Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)-> Method("GetRotation", &Transform::GetRotation)-> Method("SetRotation", &Transform::SetRotation)-> - Method("GetScale", &Transform::GetScale)-> Method("GetUniformScale", &Transform::GetUniformScale)-> - Method("SetScale", &Transform::SetScale)-> Method("SetUniformScale", &Transform::SetUniformScale)-> - Method("ExtractScale", &Transform::ExtractScale)-> - Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)-> Method("ExtractUniformScale", &Transform::ExtractUniformScale)-> Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)-> - Method("MultiplyByScale", &Transform::MultiplyByScale)-> Method("MultiplyByUniformScale", &Transform::MultiplyByUniformScale)-> Method("GetInverse", &Transform::GetInverse)-> Method("Invert", &Transform::Invert)-> @@ -310,7 +332,6 @@ namespace AZ Method("CreateFromQuaternionAndTranslation", &Transform::CreateFromQuaternionAndTranslation)-> Method("CreateFromMatrix3x3", &Transform::CreateFromMatrix3x3)-> Method("CreateFromMatrix3x3AndTranslation", &Transform::CreateFromMatrix3x3AndTranslation)-> - Method("CreateScale", &Transform::CreateScale)-> Method("CreateUniformScale", &Transform::CreateUniformScale)-> Method("CreateTranslation", &Transform::CreateTranslation)-> Method("ConstructFromValuesNumeric", &Internal::ConstructTransformFromValues); @@ -321,7 +342,7 @@ namespace AZ { Transform result; Matrix3x3 tmp = value; - result.m_scale = tmp.ExtractScale(); + result.m_scale = tmp.ExtractScale().GetMaxElement(); result.m_rotation = Quaternion::CreateFromMatrix3x3(tmp); result.m_translation = Vector3::CreateZero(); return result; @@ -331,7 +352,7 @@ namespace AZ { Transform result; Matrix3x3 tmp = value; - result.m_scale = tmp.ExtractScale(); + result.m_scale = tmp.ExtractScale().GetMaxElement(); result.m_rotation = Quaternion::CreateFromMatrix3x3(tmp); result.m_translation = p; return result; @@ -341,7 +362,7 @@ namespace AZ { Transform result; Matrix3x4 tmp = value; - result.m_scale = tmp.ExtractScale(); + result.m_scale = tmp.ExtractScale().GetMaxElement(); result.m_rotation = Quaternion::CreateFromMatrix3x4(tmp); result.m_translation = value.GetTranslation(); return result; diff --git a/Code/Framework/AzCore/AzCore/Math/Transform.h b/Code/Framework/AzCore/AzCore/Math/Transform.h index 7ae86edd89..3fe6ddc98a 100644 --- a/Code/Framework/AzCore/AzCore/Math/Transform.h +++ b/Code/Framework/AzCore/AzCore/Math/Transform.h @@ -25,10 +25,13 @@ namespace AZ : public SerializeContext::IDataSerializer { public: - // number of floats in the serialized representation, 4 for rotation, 3 for scale and 3 for translation - static constexpr int NumFloats = 10; + // number of floats in the serialized representation, 4 for rotation, 1 for scale and 3 for translation + static constexpr int NumFloats = 8; - // number of floats in the old format, which stored a 3x4 matrix + // number of floats in version 1, which used 4 for rotation, 3 for scale and 3 for translation + static constexpr int NumFloatsVersion1 = 10; + + // number of floats in version 0, which stored a 3x4 matrix static constexpr int NumFloatsVersion0 = 12; size_t Save(const void* classPtr, IO::GenericStream& stream, bool isDataBigEndian) override; @@ -45,7 +48,7 @@ namespace AZ static constexpr float MaxTransformScale = 1e9f; //! @} - //! The basic transformation class, represented using a quaternion rotation, vector scale and vector translation. + //! The basic transformation class, represented using a quaternion rotation, float scale and vector translation. //! By design, cannot represent skew transformations. class Transform { @@ -63,7 +66,7 @@ namespace AZ Transform() = default; //! Construct a transform from components. - Transform(const Vector3& translation, const Quaternion& rotation, const Vector3& scale); + Transform(const Vector3& translation, const Quaternion& rotation, float scale); //! Creates an identity transform. static Transform CreateIdentity(); @@ -82,16 +85,20 @@ namespace AZ static Transform CreateFromQuaternionAndTranslation(const class Quaternion& q, const Vector3& p); //! Constructs from a Matrix3x3, translation is set to zero. + //! Note that Transform only allows uniform scale, so if the matrix has different scale values along its axes, + //! the largest matrix scale value will be used to uniformly scale the Transform. static Transform CreateFromMatrix3x3(const class Matrix3x3& value); - //! Constructs from a Matrix3x3, translation is set to zero. + //! Constructs from a Matrix3x3 and translation Vector3. + //! Note that Transform only allows uniform scale, so if the matrix has different scale values along its axes, + //! the largest matrix scale value will be used to uniformly scale the Transform. static Transform CreateFromMatrix3x3AndTranslation(const class Matrix3x3& value, const Vector3& p); + //! Constructs from a Matrix3x4. + //! Note that Transform only allows uniform scale, so if the matrix has different scale values along its axes, + //! the largest matrix scale value will be used to uniformly scale the Transform. static Transform CreateFromMatrix3x4(const Matrix3x4& value); - //! Sets the transform to apply scale only, no rotation or translation. - static Transform CreateScale(const AZ::Vector3& scale); - //! Sets the transform to apply (uniform) scale only, no rotation or translation. static Transform CreateUniformScale(const float scale); @@ -122,18 +129,12 @@ namespace AZ const Quaternion& GetRotation() const; void SetRotation(const Quaternion& rotation); - Vector3 GetScale() const; float GetUniformScale() const; - void SetScale(const Vector3& v); void SetUniformScale(const float scale); - //! Sets the transform's scale to a unit value and returns the previous scale value. - Vector3 ExtractScale(); - //! Sets the transform's scale to a unit value and returns the previous scale value. float ExtractUniformScale(); - void MultiplyByScale(const AZ::Vector3& scale); void MultiplyByUniformScale(float scale); Transform operator*(const Transform& rhs) const; @@ -168,7 +169,7 @@ namespace AZ private: Quaternion m_rotation; - Vector3 m_scale; + float m_scale; Vector3 m_translation; }; diff --git a/Code/Framework/AzCore/AzCore/Math/Transform.inl b/Code/Framework/AzCore/AzCore/Math/Transform.inl index a7d5e72749..5f71316b52 100644 --- a/Code/Framework/AzCore/AzCore/Math/Transform.inl +++ b/Code/Framework/AzCore/AzCore/Math/Transform.inl @@ -12,7 +12,7 @@ namespace AZ { - AZ_MATH_INLINE Transform::Transform(const Vector3& translation, const Quaternion& rotation, const Vector3& scale) + AZ_MATH_INLINE Transform::Transform(const Vector3& translation, const Quaternion& rotation, float scale) : m_translation(translation) , m_rotation(rotation) , m_scale(scale) @@ -25,7 +25,7 @@ namespace AZ { Transform result; result.m_rotation = Quaternion::CreateIdentity(); - result.m_scale = Vector3::CreateOne(); + result.m_scale = 1.0f; result.m_translation = Vector3::CreateZero(); return result; } @@ -49,7 +49,7 @@ namespace AZ { Transform result; result.m_rotation = q; - result.m_scale = Vector3::CreateOne(); + result.m_scale = 1.0f; result.m_translation = Vector3::CreateZero(); return result; } @@ -58,26 +58,16 @@ namespace AZ { Transform result; result.m_rotation = q; - result.m_scale = Vector3::CreateOne(); + result.m_scale = 1.0f; result.m_translation = p; return result; } - AZ_MATH_INLINE Transform Transform::CreateScale(const Vector3& scale) - { - AZ_WarningOnce("Transform", false, "CreateScale is deprecated, please use CreateUniformScale instead."); - Transform result; - result.m_rotation = Quaternion::CreateIdentity(); - result.m_scale = scale; - result.m_translation = Vector3::CreateZero(); - return result; - } - AZ_MATH_INLINE Transform Transform::CreateUniformScale(float scale) { Transform result; result.m_rotation = Quaternion::CreateIdentity(); - result.m_scale = Vector3(scale); + result.m_scale = scale; result.m_translation = Vector3::CreateZero(); return result; } @@ -86,7 +76,7 @@ namespace AZ { Transform result; result.m_rotation = Quaternion::CreateIdentity(); - result.m_scale = Vector3::CreateOne(); + result.m_scale = 1.0f; result.m_translation = translation; return result; } @@ -114,17 +104,17 @@ namespace AZ AZ_MATH_INLINE Vector3 Transform::GetBasisX() const { - return m_rotation.TransformVector(Vector3::CreateAxisX(m_scale.GetX())); + return m_rotation.TransformVector(Vector3::CreateAxisX(m_scale)); } AZ_MATH_INLINE Vector3 Transform::GetBasisY() const { - return m_rotation.TransformVector(Vector3::CreateAxisY(m_scale.GetY())); + return m_rotation.TransformVector(Vector3::CreateAxisY(m_scale)); } AZ_MATH_INLINE Vector3 Transform::GetBasisZ() const { - return m_rotation.TransformVector(Vector3::CreateAxisZ(m_scale.GetZ())); + return m_rotation.TransformVector(Vector3::CreateAxisZ(m_scale)); } AZ_MATH_INLINE void Transform::GetBasisAndTranslation(Vector3* basisX, Vector3* basisY, Vector3* basisZ, Vector3* pos) const @@ -160,49 +150,23 @@ namespace AZ m_rotation = rotation; } - AZ_MATH_INLINE Vector3 Transform::GetScale() const - { - AZ_WarningOnce("Transform", false, "GetScale is deprecated, please use GetUniformScale instead."); - return m_scale; - } - AZ_MATH_INLINE float Transform::GetUniformScale() const { - return m_scale.GetMaxElement(); - } - - AZ_MATH_INLINE void Transform::SetScale(const Vector3& scale) - { - AZ_WarningOnce("Transform", false, "SetScale is deprecated, please use SetUniformScale instead."); - m_scale = scale; + return m_scale; } AZ_MATH_INLINE void Transform::SetUniformScale(const float scale) { - m_scale = Vector3(scale); - } - - AZ_MATH_INLINE Vector3 Transform::ExtractScale() - { - AZ_WarningOnce("Transform", false, "ExtractScale is deprecated, please use ExtractUniformScale instead."); - const Vector3 scale = m_scale; - m_scale = Vector3::CreateOne(); - return scale; + m_scale = scale; } AZ_MATH_INLINE float Transform::ExtractUniformScale() { - const float scale = m_scale.GetMaxElement(); - m_scale = Vector3::CreateOne(); + const float scale = m_scale; + m_scale = 1.0f; return scale; } - AZ_MATH_INLINE void Transform::MultiplyByScale(const Vector3& scale) - { - AZ_WarningOnce("Transform", false, "MultiplyByScale is deprecated, please use MultiplyByUniformScale instead."); - m_scale *= scale; - } - AZ_MATH_INLINE void Transform::MultiplyByUniformScale(float scale) { m_scale *= scale; @@ -240,10 +204,9 @@ namespace AZ AZ_MATH_INLINE Transform Transform::GetInverse() const { - // note - need to be careful about how to calculate inverse when there is non-uniform scale Transform out; out.m_rotation = m_rotation.GetConjugate(); - out.m_scale = m_scale.GetReciprocal(); + out.m_scale = 1.0f / m_scale; out.m_translation = -out.m_scale * (out.m_rotation.TransformVector(m_translation)); return out; } @@ -255,27 +218,27 @@ namespace AZ AZ_MATH_INLINE bool Transform::IsOrthogonal(float tolerance) const { - return m_scale.IsClose(Vector3::CreateOne(), tolerance); + return AZ::IsClose(m_scale, 1.0f, tolerance); } AZ_MATH_INLINE Transform Transform::GetOrthogonalized() const { Transform result; result.m_rotation = m_rotation; - result.m_scale = Vector3::CreateOne(); + result.m_scale = 1.0f; result.m_translation = m_translation; return result; } AZ_MATH_INLINE void Transform::Orthogonalize() { - m_scale = Vector3::CreateOne(); + m_scale = 1.0f; } AZ_MATH_INLINE bool Transform::IsClose(const Transform& rhs, float tolerance) const { return m_rotation.IsClose(rhs.m_rotation, tolerance) - && m_scale.IsClose(rhs.m_scale, tolerance) + && AZ::IsClose(m_scale, rhs.m_scale, tolerance) && m_translation.IsClose(rhs.m_translation, tolerance); } @@ -304,21 +267,21 @@ namespace AZ AZ_MATH_INLINE void Transform::SetFromEulerDegrees(const Vector3& eulerDegrees) { m_translation = Vector3::CreateZero(); - m_scale = Vector3::CreateOne(); + m_scale = 1.0f; m_rotation.SetFromEulerDegrees(eulerDegrees); } AZ_MATH_INLINE void Transform::SetFromEulerRadians(const Vector3& eulerRadians) { m_translation = Vector3::CreateZero(); - m_scale = Vector3::CreateOne(); + m_scale = 1.0f; m_rotation.SetFromEulerRadians(eulerRadians); } AZ_MATH_INLINE bool Transform::IsFinite() const { return m_rotation.IsFinite() - && m_scale.IsFinite() + && AZ::IsFiniteFloat(m_scale) && m_translation.IsFinite(); } diff --git a/Code/Framework/AzCore/AzCore/Math/TransformSerializer.cpp b/Code/Framework/AzCore/AzCore/Math/TransformSerializer.cpp index 86bc1c36ea..36c40265af 100644 --- a/Code/Framework/AzCore/AzCore/Math/TransformSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Math/TransformSerializer.cpp @@ -67,7 +67,7 @@ namespace AZ result.Combine(loadResult); - transformInstance->SetScale(AZ::Vector3(scale)); + transformInstance->SetUniformScale(scale); } return context.Report( diff --git a/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp b/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp index fe41050b00..0ce3ee5d8d 100644 --- a/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp +++ b/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp @@ -512,7 +512,7 @@ namespace AZ // Load DLLs specified in the application descriptor for (const auto& moduleDescriptor : modules) { - // For each module that is loaded, attempt to set the module's folder as a path for dependent module resolution + // For each module that is loaded, attempt to set the module's folder as a path for dependent module resolution moduleSearchPathHelper.SetModuleSearchPath(moduleDescriptor); LoadModuleOutcome result = LoadDynamicModule(moduleDescriptor.m_dynamicLibraryPath.c_str(), lastStepToPerform, maintainReferences); diff --git a/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.cpp b/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.cpp index 63aad1ecf4..e31c3b0a1e 100644 --- a/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.cpp +++ b/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.cpp @@ -19,7 +19,7 @@ namespace AZ { inline namespace PlatformDefaults { - static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformES3, PlatformIOS, PlatformOSX, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient }; + static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformAndroid, PlatformIOS, PlatformMac, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient }; const char* PlatformIdToPalFolder(AZ::PlatformId platform) { @@ -31,11 +31,11 @@ namespace AZ { case AZ::PC: return "PC"; - case AZ::ES3: + case AZ::ANDROID_ID: return "Android"; case AZ::IOS: return "iOS"; - case AZ::OSX: + case AZ::MAC_ID: return "Mac"; case AZ::PROVO: return "Provo"; @@ -66,11 +66,11 @@ namespace AZ } else if (osPlatform == PlatformCodeNameMac) { - return PlatformOSX; + return PlatformMac; } else if (osPlatform == PlatformCodeNameAndroid) { - return PlatformES3; + return PlatformAndroid; } else if (osPlatform == PlatformCodeNameiOS) { @@ -207,13 +207,13 @@ namespace AZ platformCodes.emplace_back(PlatformCodeNameWindows); platformCodes.emplace_back(PlatformCodeNameLinux); break; - case PlatformId::ES3: + case PlatformId::ANDROID_ID: platformCodes.emplace_back(PlatformCodeNameAndroid); break; case PlatformId::IOS: platformCodes.emplace_back(PlatformCodeNameiOS); break; - case PlatformId::OSX: + case PlatformId::MAC_ID: platformCodes.emplace_back(PlatformCodeNameMac); break; case PlatformId::PROVO: diff --git a/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.h b/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.h index 2d67c860cd..ba8c55f5f5 100644 --- a/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.h +++ b/Code/Framework/AzCore/AzCore/PlatformId/PlatformDefaults.h @@ -27,9 +27,9 @@ namespace AZ inline namespace PlatformDefaults { constexpr char PlatformPC[] = "pc"; - constexpr char PlatformES3[] = "es3"; + constexpr char PlatformAndroid[] = "android"; constexpr char PlatformIOS[] = "ios"; - constexpr char PlatformOSX[] = "osx_gl"; + constexpr char PlatformMac[] = "mac"; constexpr char PlatformProvo[] = "provo"; constexpr char PlatformSalem[] = "salem"; constexpr char PlatformJasper[] = "jasper"; @@ -54,9 +54,9 @@ namespace AZ AZ_ENUM_WITH_UNDERLYING_TYPE(PlatformId, int, (Invalid, -1), PC, - ES3, + ANDROID_ID, IOS, - OSX, + MAC_ID, PROVO, SALEM, JASPER, @@ -73,9 +73,9 @@ namespace AZ { Platform_NONE = 0x00, Platform_PC = 1 << PlatformId::PC, - Platform_ES3 = 1 << PlatformId::ES3, + Platform_ANDROID = 1 << PlatformId::ANDROID_ID, Platform_IOS = 1 << PlatformId::IOS, - Platform_OSX = 1 << PlatformId::OSX, + Platform_MAC = 1 << PlatformId::MAC_ID, Platform_PROVO = 1 << PlatformId::PROVO, Platform_SALEM = 1 << PlatformId::SALEM, Platform_JASPER = 1 << PlatformId::JASPER, @@ -87,7 +87,7 @@ namespace AZ // A special platform that will always correspond to all non-server platforms, even if new ones are added Platform_ALL_CLIENT = 1ULL << 31, - AllNamedPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER, + AllNamedPlatforms = Platform_PC | Platform_ANDROID | Platform_IOS | Platform_MAC | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER, }; AZ_DEFINE_ENUM_BITWISE_OPERATORS(PlatformFlags); diff --git a/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.cpp b/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.cpp index 0258869a0c..d56140be28 100644 --- a/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.cpp +++ b/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.cpp @@ -28,8 +28,8 @@ namespace AZ return "Android64"; case PlatformID::PLATFORM_APPLE_IOS: return "iOS"; - case PlatformID::PLATFORM_APPLE_OSX: - return "OSX"; + case PlatformID::PLATFORM_APPLE_MAC: + return "Mac"; #if defined(AZ_EXPAND_FOR_RESTRICTED_PLATFORM) || defined(AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS) #define AZ_RESTRICTED_PLATFORM_EXPANSION(CodeName, CODENAME, codename, PrivateName, PRIVATENAME, privatename, PublicName, PUBLICNAME, publicname, PublicAuxName1, PublicAuxName2, PublicAuxName3)\ case PlatformID::PLATFORM_##PUBLICNAME:\ diff --git a/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.h b/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.h index ce1a11d8ce..e8e7cef6dd 100644 --- a/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.h +++ b/Code/Framework/AzCore/AzCore/PlatformId/PlatformId.h @@ -23,7 +23,7 @@ namespace AZ PLATFORM_WINDOWS_64, PLATFORM_LINUX_64, PLATFORM_APPLE_IOS, - PLATFORM_APPLE_OSX, + PLATFORM_APPLE_MAC, PLATFORM_ANDROID_64, // ARMv8 / 64-bit #if defined(AZ_EXPAND_FOR_RESTRICTED_PLATFORM) || defined(AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS) #define AZ_RESTRICTED_PLATFORM_EXPANSION(CodeName, CODENAME, codename, PrivateName, PRIVATENAME, privatename, PublicName, PUBLICNAME, publicname, PublicAuxName1, PublicAuxName2, PublicAuxName3)\ diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp index 015554538f..11d4db2e07 100644 --- a/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp +++ b/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp @@ -937,7 +937,7 @@ void ScriptSystemComponent::Reflect(ReflectContext* reflection) ->Enum(PlatformID::PLATFORM_LINUX_64)>("Linux") ->Enum(PlatformID::PLATFORM_ANDROID_64)>("Android64") ->Enum(PlatformID::PLATFORM_APPLE_IOS)>("iOS") - ->Enum(PlatformID::PLATFORM_APPLE_OSX)>("OSX") + ->Enum(PlatformID::PLATFORM_APPLE_MAC)>("Mac") #if defined(AZ_EXPAND_FOR_RESTRICTED_PLATFORM) || defined(AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS) #define AZ_RESTRICTED_PLATFORM_EXPANSION(CodeName, CODENAME, codename, PrivateName, PRIVATENAME, privatename, PublicName, PUBLICNAME, publicname, PublicAuxName1, PublicAuxName2, PublicAuxName3)\ ->Enum(PlatformID::PLATFORM_##PUBLICNAME)>(#CodeName) diff --git a/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl b/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl index 1016027966..dfd0707ed2 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl +++ b/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl @@ -123,6 +123,7 @@ namespace AZ const static AZ::Crc32 NameLabelOverride = AZ_CRC("NameLabelOverride", 0x9ff79cab); const static AZ::Crc32 AssetPickerTitle = AZ_CRC_CE("AssetPickerTitle"); + const static AZ::Crc32 HideProductFilesInAssetPicker = AZ_CRC_CE("HideProductFilesInAssetPicker"); const static AZ::Crc32 ChildNameLabelOverride = AZ_CRC("ChildNameLabelOverride", 0x73dd2909); //! Container attribute that is used to override labels for its elements given the index of the element const static AZ::Crc32 IndexedChildNameLabelOverride = AZ_CRC("IndexedChildNameLabelOverride", 0x5f313ac2); diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index 82bf1db484..2abef3f808 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -88,6 +88,35 @@ namespace AZ::Internal m_enginePaths.emplace_back(EngineInfo{AZ::IO::FixedMaxPath{value}.LexicallyNormal(), {}}); } + AZ::SettingsRegistryInterface::VisitResponse Traverse( + [[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, + AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type type) override + { + auto response = AZ::SettingsRegistryInterface::VisitResponse::Continue; + if (action == AZ::SettingsRegistryInterface::VisitAction::Begin) + { + if (type == AZ::SettingsRegistryInterface::Type::Array) + { + if (valueName.compare("engines") != 0) + { + response = AZ::SettingsRegistryInterface::VisitResponse::Skip; + } + } + } + else if (action == AZ::SettingsRegistryInterface::VisitAction::Value) + { + if (type == AZ::SettingsRegistryInterface::Type::String) + { + if (valueName.compare("path") != 0) + { + response = AZ::SettingsRegistryInterface::VisitResponse::Skip; + } + } + } + + return response; + } + AZStd::vector m_enginePaths{}; }; @@ -494,13 +523,6 @@ namespace AZ::SettingsRegistryMergeUtils return configFileParsed; } - void MergeSettingsToRegistry_Bootstrap(SettingsRegistryInterface& registry) - { - ConfigParserSettings parserSettings; - parserSettings.m_registryRootPointerPath = BootstrapSettingsRootKey; - MergeSettingsToRegistry_ConfigFile(registry, "bootstrap.cfg", parserSettings); - } - void MergeSettingsToRegistry_AddRuntimeFilePaths(SettingsRegistryInterface& registry) { using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h index 576066c29f..b482530d24 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h @@ -172,9 +172,6 @@ namespace AZ::SettingsRegistryMergeUtils bool MergeSettingsToRegistry_ConfigFile(SettingsRegistryInterface& registry, AZStd::string_view filePath, const ConfigParserSettings& configParserSettings); - //! Loads bootstrap.cfg into the Settings Registry. This file does not support specializations. - void MergeSettingsToRegistry_Bootstrap(SettingsRegistryInterface& registry); - //! Extracts file path information from the environment and bootstrap to calculate the various file paths and adds those //! to the Settings Registry under the FilePathsRootKey. void MergeSettingsToRegistry_AddRuntimeFilePaths(SettingsRegistryInterface& registry); diff --git a/Code/Framework/AzCore/Platform/Mac/AzCore/PlatformId/PlatformId_Mac.h b/Code/Framework/AzCore/Platform/Mac/AzCore/PlatformId/PlatformId_Mac.h index d361e79f05..42dcd3e2f7 100644 --- a/Code/Framework/AzCore/Platform/Mac/AzCore/PlatformId/PlatformId_Mac.h +++ b/Code/Framework/AzCore/Platform/Mac/AzCore/PlatformId/PlatformId_Mac.h @@ -13,5 +13,5 @@ namespace AZ { - static const PlatformID g_currentPlatform = PlatformID::PLATFORM_APPLE_OSX; + static const PlatformID g_currentPlatform = PlatformID::PLATFORM_APPLE_MAC; } diff --git a/Code/Framework/AzCore/Tests/AZTestShared/Math/MathTestHelpers.cpp b/Code/Framework/AzCore/Tests/AZTestShared/Math/MathTestHelpers.cpp index 42b77f6976..f9616702f1 100644 --- a/Code/Framework/AzCore/Tests/AZTestShared/Math/MathTestHelpers.cpp +++ b/Code/Framework/AzCore/Tests/AZTestShared/Math/MathTestHelpers.cpp @@ -68,7 +68,7 @@ namespace AZ return os << "translation: " << transform.GetTranslation() << " rotation: " << transform.GetRotation() - << " scale: " << transform.GetScale(); + << " scale: " << transform.GetUniformScale(); } std::ostream& operator<<(std::ostream& os, const Color& color) diff --git a/Code/Framework/AzCore/Tests/Math/RandomTests.cpp b/Code/Framework/AzCore/Tests/Math/RandomTests.cpp index ace7d99704..7fe3acff54 100644 --- a/Code/Framework/AzCore/Tests/Math/RandomTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/RandomTests.cpp @@ -24,7 +24,7 @@ namespace UnitTest EXPECT_FLOAT_EQ(5981.0f / 15625.0f, GetHaltonNumber(4321, 5)); } - TEST(MATH_Random, HaltonSequence) + TEST(MATH_Random, HaltonSequenceStandard) { HaltonSequence<3> sequence({ 2, 3, 5 }); auto regularSequence = sequence.GetHaltonSequence<5>(); @@ -48,7 +48,11 @@ namespace UnitTest EXPECT_FLOAT_EQ(5.0f / 8.0f, regularSequence[4][0]); EXPECT_FLOAT_EQ(7.0f / 9.0f, regularSequence[4][1]); EXPECT_FLOAT_EQ(1.0f / 25.0f, regularSequence[4][2]); - + } + + TEST(MATH_Random, HaltonSequenceOffsets) + { + HaltonSequence<3> sequence({ 2, 3, 5 }); sequence.SetOffsets({ 1, 2, 3 }); auto offsetSequence = sequence.GetHaltonSequence<2>(); @@ -59,10 +63,15 @@ namespace UnitTest EXPECT_FLOAT_EQ(3.0f / 4.0f, offsetSequence[1][0]); EXPECT_FLOAT_EQ(4.0f / 9.0f, offsetSequence[1][1]); EXPECT_FLOAT_EQ(1.0f / 25.0f, offsetSequence[1][2]); - + } + + TEST(MATH_Random, HaltonSequenceIncrements) + { + HaltonSequence<3> sequence({ 2, 3, 5 }); + sequence.SetOffsets({ 1, 2, 3 }); sequence.SetIncrements({ 1, 2, 3 }); auto incrementedSequence = sequence.GetHaltonSequence<2>(); - + EXPECT_FLOAT_EQ(1.0f / 4.0f, incrementedSequence[0][0]); EXPECT_FLOAT_EQ(1.0f / 9.0f, incrementedSequence[0][1]); EXPECT_FLOAT_EQ(4.0f / 5.0f, incrementedSequence[0][2]); @@ -71,4 +80,35 @@ namespace UnitTest EXPECT_FLOAT_EQ(7.0f / 9.0f, incrementedSequence[1][1]); EXPECT_FLOAT_EQ(11.0f / 25.0f, incrementedSequence[1][2]); } + + TEST(MATH_Random, FillHaltonSequence) + { + HaltonSequence<3> sequence({ 2, 3, 5 }); + auto regularSequence = sequence.GetHaltonSequence<5>(); + + struct Point + { + Point() = default; + Point(AZStd::array arr) + :x(arr[0]) + ,y(arr[1]) + ,z(arr[2]) + {} + + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + }; + + AZStd::array ownedContainer; + sequence.FillHaltonSequence(ownedContainer.begin(), ownedContainer.end()); + + for (size_t i = 0; i < regularSequence.size(); ++i) + { + EXPECT_FLOAT_EQ(regularSequence[i][0], ownedContainer[i].x); + EXPECT_FLOAT_EQ(regularSequence[i][1], ownedContainer[i].y); + EXPECT_FLOAT_EQ(regularSequence[i][2], ownedContainer[i].z); + } + } + } diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/TransformSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/TransformSerializerTests.cpp index 7eabd6e5e0..750f2ebc9c 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/TransformSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/TransformSerializerTests.cpp @@ -44,7 +44,7 @@ namespace JsonSerializationTests AZStd::shared_ptr CreateFullySetInstance() override { return AZStd::make_shared( - AZ::Vector3(1.0f, 2.0f, 3.0f), AZ::Quaternion(0.25f, 0.5f, 0.75f, 1.0f), AZ::Vector3(9.0f)); + AZ::Vector3(1.0f, 2.0f, 3.0f), AZ::Quaternion(0.25f, 0.5f, 0.75f, 1.0f), 9.0f); } AZStd::string_view GetJsonForFullySetInstance() override @@ -95,7 +95,7 @@ namespace JsonSerializationTests AZ::Transform expectedTransform( AZ::Vector3(2.25f, 3.5f, 4.75f), AZ::Quaternion(0.25f, 0.5f, 0.75f, 1.0f), - AZ::Vector3(5.5f)); + 5.5f); rapidjson::Document json; json.Parse(R"({ "Translation": [ 2.25, 3.5, 4.75 ], "Rotation": [ 0.25, 0.5, 0.75, 1.0 ], "Scale": 5.5 })"); diff --git a/Code/Framework/AzCore/Tests/SettingsRegistryMergeUtilsTests.cpp b/Code/Framework/AzCore/Tests/SettingsRegistryMergeUtilsTests.cpp index 36b9757ce3..751d9ded6c 100644 --- a/Code/Framework/AzCore/Tests/SettingsRegistryMergeUtilsTests.cpp +++ b/Code/Framework/AzCore/Tests/SettingsRegistryMergeUtilsTests.cpp @@ -372,15 +372,15 @@ mac_remote_filesystem=0 -- We need to know this before we establish VFS because different platform assets -- are stored in different root folders in the cache. These correspond to the names -- In the asset processor config file. This value also controls what config file is read --- when you read system_xxxx_xxxx.cfg (for example, system_windows_pc.cfg or system_android_es3.cfg) +-- when you read system_xxxx_xxxx.cfg (for example, system_windows_pc.cfg or system_android_android.cfg) -- by default, pc assets (in the 'pc' folder) are used, with RC being fed 'pc' as the platform -- by default on console we use the default assets=pc for better iteration times -- we should turn on console specific assets only when in release and/or testing assets and/or loading performance -- that way most people will not need to have 3 different caches taking up disk space assets = pc -android_assets = es3 +android_assets = android ios_assets = ios -mac_assets = osx_gl +mac_assets = mac -- Add the IP address of your console to the white list that will connect to the asset processor here -- You can list addresses or CIDR's. CIDR's are helpful if you are using DHCP. A CIDR looks like an ip address with @@ -438,9 +438,9 @@ mac_wait_for_connect=0 ConfigFileParams::SettingsKeyValuePair{"/ios_remote_filesystem", AZ::s64{0}}, ConfigFileParams::SettingsKeyValuePair{"/mac_remote_filesystem", AZ::s64{0}}, ConfigFileParams::SettingsKeyValuePair{"/assets", AZStd::string_view{"pc"}}, - ConfigFileParams::SettingsKeyValuePair{"/android_assets", AZStd::string_view{"es3"}}, + ConfigFileParams::SettingsKeyValuePair{"/android_assets", AZStd::string_view{"android"}}, ConfigFileParams::SettingsKeyValuePair{"/ios_assets", AZStd::string_view{"ios"}}, - ConfigFileParams::SettingsKeyValuePair{"/mac_assets", AZStd::string_view{"osx_gl"}}, + ConfigFileParams::SettingsKeyValuePair{"/mac_assets", AZStd::string_view{"mac"}}, ConfigFileParams::SettingsKeyValuePair{"/connect_to_remote", AZ::s64{0}}, ConfigFileParams::SettingsKeyValuePair{"/windows_connect_to_remote", AZ::s64{1}}, ConfigFileParams::SettingsKeyValuePair{"/android_connect_to_remote", AZ::s64{0}}, @@ -478,20 +478,20 @@ test_asset_processor_tag = test_value [Platform pc] tags=tools,renderer,dx12,vulkan -[Platform es3] +[Platform android] tags=android,mobile,renderer,vulkan ; With Comments at the end [Platform ios] tags=mobile,renderer,metal -[Platform osx_gl] +[Platform mac] tags=tools,renderer,metal)" , AZStd::fixed_vector{ ConfigFileParams::SettingsKeyValuePair{"/test_asset_processor_tag", AZStd::string_view{"test_value"}}, ConfigFileParams::SettingsKeyValuePair{"/Platform pc/tags", AZStd::string_view{"tools,renderer,dx12,vulkan"}}, - ConfigFileParams::SettingsKeyValuePair{"/Platform es3/tags", AZStd::string_view{"android,mobile,renderer,vulkan"}}, + ConfigFileParams::SettingsKeyValuePair{"/Platform android/tags", AZStd::string_view{"android,mobile,renderer,vulkan"}}, ConfigFileParams::SettingsKeyValuePair{"/Platform ios/tags", AZStd::string_view{"mobile,renderer,metal"}}, - ConfigFileParams::SettingsKeyValuePair{"/Platform osx_gl/tags", AZStd::string_view{"tools,renderer,metal"}}, + ConfigFileParams::SettingsKeyValuePair{"/Platform mac/tags", AZStd::string_view{"tools,renderer,metal"}}, }} ) ); diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp index e02892de4e..c65ba373f8 100644 --- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp +++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp @@ -679,8 +679,6 @@ namespace AzFramework { auto fileIoBase = m_archiveFileIO.get(); // Set up the default file aliases based on the settings registry - fileIoBase->SetAlias("@assets@", ""); - fileIoBase->SetAlias("@root@", GetEngineRoot()); fileIoBase->SetAlias("@engroot@", GetEngineRoot()); fileIoBase->SetAlias("@projectroot@", GetEngineRoot()); fileIoBase->SetAlias("@exefolder@", GetExecutableFolder()); @@ -694,8 +692,8 @@ namespace AzFramework pathAliases.clear(); if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder)) { - fileIoBase->SetAlias("@projectplatformcache@", pathAliases.c_str()); fileIoBase->SetAlias("@assets@", pathAliases.c_str()); + fileIoBase->SetAlias("@projectplatformcache@", pathAliases.c_str()); fileIoBase->SetAlias("@root@", pathAliases.c_str()); // Deprecated Use @projectplatformcache@ } pathAliases.clear(); diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp index b0285616df..4a80db2b24 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp @@ -2008,13 +2008,12 @@ namespace AZ::IO // if no bind root is specified, compute one: strBindRoot = !bindRoot.empty() ? bindRoot : szFullPath->ParentPath().Native(); - // Check if archive file disk exist on disk or inside of pak. - bool bFileExists = IsFileExist(szFullPath->Native()); - - if (!bFileExists && (nFactoryFlags & ZipDir::CacheFactory::FLAGS_READ_ONLY)) + // Check if archive file disk exist on disk. + const bool pakOnDisk = FileIOBase::GetDirectInstance()->Exists(szFullPath->c_str()); + if (!pakOnDisk && (nFactoryFlags & ZipDir::CacheFactory::FLAGS_READ_ONLY)) { // Archive file not found. - AZ_TracePrintf("Archive", "Cannot open Archive file %s\n", szFullPath->c_str()); + AZ_TracePrintf("Archive", "Archive file %s does not exist\n", szFullPath->c_str()); return nullptr; } @@ -2492,8 +2491,6 @@ namespace AZ::IO void Archive::FindCompressionInfo(bool& found, AZ::IO::CompressionInfo& info, const AZStd::string_view filename) { - constexpr uint32_t s_compressionTag = static_cast('Z') << 24 | static_cast('C') << 16 | static_cast('R') << 8 | static_cast('Y'); - if (!found) { auto correctedFilename = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(filename); @@ -2519,7 +2516,6 @@ namespace AZ::IO found = true; info.m_archiveFilename.InitFromRelativePath(archive->GetFilePath()); - info.m_compressionTag.m_code = s_compressionTag; info.m_offset = pFileData->GetFileDataOffset(); info.m_compressedSize = entry->desc.lSizeCompressed; info.m_uncompressedSize = entry->desc.lSizeUncompressed; @@ -2539,9 +2535,8 @@ namespace AZ::IO break; } - info.m_decompressor = [&s_compressionTag]([[maybe_unused]] const AZ::IO::CompressionInfo& info, const void* compressed, size_t compressedSize, void* uncompressed, size_t uncompressedBufferSize)->bool + info.m_decompressor = []([[maybe_unused]] const AZ::IO::CompressionInfo& info, const void* compressed, size_t compressedSize, void* uncompressed, size_t uncompressedBufferSize)->bool { - AZ_Assert(info.m_compressionTag.m_code == s_compressionTag, "Provided compression info isn't supported by this decompressor."); size_t nSizeUncompressed = uncompressedBufferSize; return ZipDir::ZipRawUncompress(uncompressed, &nSizeUncompressed, compressed, compressedSize) == 0; }; diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp index 678f4e40bf..1794ae90e7 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp @@ -50,6 +50,7 @@ namespace AZ::IO , tWrite{ writeTime } { } + ArchiveFileIterator::ArchiveFileIterator(FindData* findData, AZStd::string_view filename, const FileDesc& fileDesc) : m_findData{ findData } , m_filename{ filename } @@ -108,13 +109,10 @@ namespace AZ::IO 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::FileDesc fileDesc; - - AZStd::string fullFilePath; - AZ::StringFunc::Path::GetFullFileName(filePath, fullFilePath); + AZStd::string filePathEntry{filePath}; if (AZ::IO::FileIOBase::GetDirectInstance()->IsDirectory(filePath)) { @@ -135,9 +133,8 @@ namespace AZ::IO fileDesc.tAccess = fileDesc.tWrite; fileDesc.tCreate = fileDesc.tWrite; } - [[maybe_unused]] auto result = m_mapFiles.emplace(AZStd::move(fullFilePath), fileDesc); - AZ_Assert(result.second, "Failed to insert FindData entry for %s", fullFilePath.c_str()); - + [[maybe_unused]] auto result = m_mapFiles.emplace(AZStd::move(filePathEntry), fileDesc); + AZ_Assert(result.second, "Failed to insert FindData entry for filePath %s", filePath); return true; }); } @@ -273,7 +270,9 @@ namespace AZ::IO } auto pakFileIter = m_mapFiles.begin(); - fileIterator.m_filename = pakFileIter->first; + AZStd::string fullFilePath; + AZ::StringFunc::Path::GetFullFileName(pakFileIter->first.c_str(), fullFilePath); + fileIterator.m_filename = AZStd::move(fullFilePath); fileIterator.m_fileDesc = pakFileIter->second; fileIterator.m_lastFetchValid = true; diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.cpp index 020feffc47..7280c4af5c 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.cpp @@ -308,6 +308,56 @@ namespace AzFramework } } + //--------------------------------------------------------------------- + GenerateRelativeSourcePathRequest::GenerateRelativeSourcePathRequest(const AZ::OSString& sourcePath) + { + AZ_Assert(!sourcePath.empty(), "GenerateRelativeSourcePathRequest: asset path is empty"); + m_sourcePath = sourcePath; + } + + unsigned int GenerateRelativeSourcePathRequest::GetMessageType() const + { + return MessageType; + } + + void GenerateRelativeSourcePathRequest::Reflect(AZ::ReflectContext* context) + { + auto serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("SourcePath", &GenerateRelativeSourcePathRequest::m_sourcePath); + } + } + + //--------------------------------------------------------------------- + GenerateRelativeSourcePathResponse::GenerateRelativeSourcePathResponse( + bool resolved, const AZ::OSString& relativeSourcePath, const AZ::OSString& rootFolder) + { + m_relativeSourcePath = relativeSourcePath; + m_resolved = resolved; + m_rootFolder = rootFolder; + } + + unsigned int GenerateRelativeSourcePathResponse::GetMessageType() const + { + return GenerateRelativeSourcePathRequest::MessageType; + } + + void GenerateRelativeSourcePathResponse::Reflect(AZ::ReflectContext* context) + { + auto serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("RelativeSourcePath", &GenerateRelativeSourcePathResponse::m_relativeSourcePath) + ->Field("RootFolder", &GenerateRelativeSourcePathResponse::m_rootFolder) + ->Field("Resolved", &GenerateRelativeSourcePathResponse::m_resolved); + } + } + //--------------------------------------------------------------------- GetFullSourcePathFromRelativeProductPathRequest::GetFullSourcePathFromRelativeProductPathRequest(const AZ::OSString& relativeProductPath) { diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h index 9661e61828..c15f75e3e7 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h @@ -288,6 +288,45 @@ namespace AzFramework bool m_resolved; }; + ////////////////////////////////////////////////////////////////////////// + class GenerateRelativeSourcePathRequest : public BaseAssetProcessorMessage + { + public: + AZ_CLASS_ALLOCATOR(GenerateRelativeSourcePathRequest, AZ::OSAllocator, 0); + AZ_RTTI(GenerateRelativeSourcePathRequest, "{B3865033-F5A3-4749-8147-7B1AB04D5F6D}", + BaseAssetProcessorMessage); + static void Reflect(AZ::ReflectContext* context); + + // For people that are debugging the network messages and just see MessageType as a value, + // the CRC value below is 739777771 (0x2C181CEB) + static constexpr unsigned int MessageType = + AZ_CRC_CE("AssetSystem::GenerateRelativeSourcePathRequest"); + + GenerateRelativeSourcePathRequest() = default; + GenerateRelativeSourcePathRequest(const AZ::OSString& sourcePath); + unsigned int GetMessageType() const override; + + AZ::OSString m_sourcePath; + }; + + class GenerateRelativeSourcePathResponse : public BaseAssetProcessorMessage + { + public: + AZ_CLASS_ALLOCATOR(GenerateRelativeSourcePathResponse, AZ::OSAllocator, 0); + AZ_RTTI(GenerateRelativeSourcePathResponse, "{938D33DB-C8F6-4FA4-BC81-2F139A9BE1D7}", + BaseAssetProcessorMessage); + static void Reflect(AZ::ReflectContext* context); + + GenerateRelativeSourcePathResponse() = default; + GenerateRelativeSourcePathResponse( + bool resolved, const AZ::OSString& relativeSourcePath, const AZ::OSString& rootFolder); + unsigned int GetMessageType() const override; + + AZ::OSString m_relativeSourcePath; + AZ::OSString m_rootFolder; ///< This is the folder it was found in (the watched/scanned folder, such as gems /assets/ folder) + bool m_resolved; + }; + ////////////////////////////////////////////////////////////////////////// class GetFullSourcePathFromRelativeProductPathRequest : public BaseAssetProcessorMessage diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp index 83c4907468..6b19084c2a 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetSystemComponent.cpp @@ -202,6 +202,7 @@ namespace AzFramework // Requests GetUnresolvedDependencyCountsRequest::Reflect(context); GetRelativeProductPathFromFullSourceOrProductPathRequest::Reflect(context); + GenerateRelativeSourcePathRequest::Reflect(context); GetFullSourcePathFromRelativeProductPathRequest::Reflect(context); SourceAssetInfoRequest::Reflect(context); AssetInfoRequest::Reflect(context); @@ -234,6 +235,7 @@ namespace AzFramework // Responses GetUnresolvedDependencyCountsResponse::Reflect(context); GetRelativeProductPathFromFullSourceOrProductPathResponse::Reflect(context); + GenerateRelativeSourcePathResponse::Reflect(context); GetFullSourcePathFromRelativeProductPathResponse::Reflect(context); SourceAssetInfoResponse::Reflect(context); AssetInfoResponse::Reflect(context); diff --git a/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.h b/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.h index 682e886061..ee8b79bf06 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.h +++ b/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.h @@ -1,22 +1,22 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once #include #include -#include #include #include +#include namespace AzFramework { @@ -64,15 +64,13 @@ namespace AzFramework the EditContext. TController can friend itself to the editor component to make this work if required. */ template - class ComponentAdapter - : public AZ::Component + class ComponentAdapter : public AZ::Component { public: - AZ_RTTI((ComponentAdapter, "{644A9187-4FDB-42C1-9D59-DD75304B551A}", TController, TConfiguration), AZ::Component); ComponentAdapter() = default; - ComponentAdapter(const TConfiguration& configuration); + explicit ComponentAdapter(const TConfiguration& configuration); static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); @@ -85,7 +83,6 @@ namespace AzFramework void Deactivate() override; protected: - static void Reflect(AZ::ReflectContext* context); // AZ::Component overrides ... diff --git a/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.inl b/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.inl index a1b0826193..5c36ff0bf7 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.inl +++ b/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapter.inl @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include @@ -32,10 +32,12 @@ namespace AzFramework if (auto serializeContext = azrtti_cast(context)) { + // clang-format off serializeContext->Class() ->Version(1) ->Field("Controller", &ComponentAdapter::m_controller) ; + // clang-format on } } @@ -66,9 +68,6 @@ namespace AzFramework GetDependentServicesHelper(services, typename AZ::HasComponentDependentServices::type()); } - ////////////////////////////////////////////////////////////////////////// - // AZ::Component interface implementation - template void ComponentAdapter::Init() { @@ -78,7 +77,7 @@ namespace AzFramework template void ComponentAdapter::Activate() { - m_controller.Activate(GetEntityId()); + ComponentActivateHelper::Activate(m_controller, AZ::EntityComponentIdPair(GetEntityId(), GetId())); } template diff --git a/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapterHelpers.h b/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapterHelpers.h index 158ee95f39..f0ef262a71 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapterHelpers.h +++ b/Code/Framework/AzFramework/AzFramework/Components/ComponentAdapterHelpers.h @@ -13,6 +13,7 @@ #pragma once #include +#include namespace AzFramework { @@ -27,18 +28,43 @@ namespace AzFramework template struct ComponentInitHelper { - static void Init(T& common) + static void Init([[maybe_unused]] T& controller) { - AZ_UNUSED(common); } }; template struct ComponentInitHelper().Init())>> { - static void Init(T& common) + static void Init(T& controller) { - common.Init(); + controller.Init(); + } + }; + + template + struct ComponentActivateHelper + { + static void Activate([[maybe_unused]] T& controller, [[maybe_unused]] const AZ::EntityComponentIdPair& entityComponentIdPair) + { + } + }; + + template + struct ComponentActivateHelper().Activate(AZ::EntityId()))>> + { + static void Activate(T& controller, const AZ::EntityComponentIdPair& entityComponentIdPair) + { + controller.Activate(entityComponentIdPair.GetEntityId()); + } + }; + + template + struct ComponentActivateHelper().Activate(AZ::EntityComponentIdPair()))>> + { + static void Activate(T& controller, const AZ::EntityComponentIdPair& entityComponentIdPair) + { + controller.Activate(entityComponentIdPair); } }; diff --git a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp index 3dafc7c717..b3c4f1b256 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp @@ -327,99 +327,13 @@ namespace AzFramework return localZ; } - void TransformComponent::SetRotation(const AZ::Vector3& eulerAnglesRadian) + void TransformComponent::SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) { - AZ_Warning("TransformComponent", false, "SetRotation is deprecated, please use SetLocalRotation"); - - AZ::Transform newWorldTransform = m_worldTM; - newWorldTransform.SetRotation(AZ::ConvertEulerRadiansToQuaternion(eulerAnglesRadian)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetRotationQuaternion(const AZ::Quaternion& quaternion) - { - AZ_Warning("TransformComponent", false, "SetRotationQuaternion is deprecated, please use SetLocalRotationQuaternion"); - AZ::Transform newWorldTransform = m_worldTM; newWorldTransform.SetRotation(quaternion); SetWorldTM(newWorldTransform); } - void TransformComponent::SetRotationX(float eulerAngleRadian) - { - AZ_Warning("TransformComponent", false, "SetRotationX is deprecated, please use SetLocalRotation"); - - AZ::Transform newWorldTransform = m_worldTM; - newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationX(eulerAngleRadian)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetRotationY(float eulerAngleRadian) - { - AZ_Warning("TransformComponent", false, "SetRotationY is deprecated, please use SetLocalRotation"); - - AZ::Transform newWorldTransform = m_worldTM; - newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationY(eulerAngleRadian)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetRotationZ(float eulerAngleRadian) - { - AZ_Warning("TransformComponent", false, "SetRotationZ is deprecated, please use SetLocalRotation"); - - AZ::Transform newWorldTransform = m_worldTM; - newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationZ(eulerAngleRadian)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::RotateByX(float eulerAngleRadian) - { - AZ_Warning("TransformComponent", false, "RotateByX is deprecated, please use RotateAroundLocalX"); - RotateAroundLocalX(eulerAngleRadian); - } - - void TransformComponent::RotateByY(float eulerAngleRadian) - { - AZ_Warning("TransformComponent", false, "RotateByY is deprecated, please use RotateAroundLocalY"); - RotateAroundLocalY(eulerAngleRadian); - } - - void TransformComponent::RotateByZ(float eulerAngleRadian) - { - AZ_Warning("TransformComponent", false, "RotateByZ is deprecated, please use RotateAroundLocalZ"); - RotateAroundLocalZ(eulerAngleRadian); - } - - AZ::Vector3 TransformComponent::GetRotationEulerRadians() - { - AZ_Warning("TransformComponent", false, "GetRotationEulerRadians is deprecated, please use GetWorldRotation"); - return m_worldTM.GetRotation().GetEulerRadians(); - } - - AZ::Quaternion TransformComponent::GetRotationQuaternion() - { - AZ_Warning("TransformComponent", false, "GetRotationQuaternion is deprecated, please use GetWorldRotationQuaternion"); - return m_worldTM.GetRotation(); - } - - float TransformComponent::GetRotationX() - { - AZ_Warning("TransformComponent", false, "GetRotationX is deprecated, please use GetWorldRotation"); - return GetRotationEulerRadians().GetX(); - } - - float TransformComponent::GetRotationY() - { - AZ_Warning("TransformComponent", false, "GetRotationY is deprecated, please use GetWorldRotation"); - return GetRotationEulerRadians().GetY(); - } - - float TransformComponent::GetRotationZ() - { - AZ_Warning("TransformComponent", false, "GetRotationZ is deprecated, please use GetWorldRotation"); - return GetRotationEulerRadians().GetZ(); - } - AZ::Vector3 TransformComponent::GetWorldRotation() { return m_worldTM.GetRotation().GetEulerRadians(); @@ -492,21 +406,10 @@ namespace AzFramework return m_localTM.GetRotation(); } - void TransformComponent::SetLocalScale(const AZ::Vector3& scale) - { - AZ::Transform newLocalTM = m_localTM; - newLocalTM.SetScale(scale); - SetLocalTM(newLocalTM); - } - AZ::Vector3 TransformComponent::GetLocalScale() { - return m_localTM.GetScale(); - } - - AZ::Vector3 TransformComponent::GetWorldScale() - { - return m_worldTM.GetScale(); + AZ_WarningOnce("TransformComponent", false, "GetLocalScale is deprecated, please use GetLocalUniformScale instead"); + return AZ::Vector3(m_localTM.GetUniformScale()); } void TransformComponent::SetLocalUniformScale(float scale) @@ -830,45 +733,7 @@ namespace AzFramework ->Event("GetLocalX", &AZ::TransformBus::Events::GetLocalX) ->Event("GetLocalY", &AZ::TransformBus::Events::GetLocalY) ->Event("GetLocalZ", &AZ::TransformBus::Events::GetLocalZ) - ->Event("RotateByX", &AZ::TransformBus::Events::RotateByX) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("RotateByY", &AZ::TransformBus::Events::RotateByY) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("RotateByZ", &AZ::TransformBus::Events::RotateByZ) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetEulerRotation", &AZ::TransformBus::Events::SetRotation) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetRotationQuaternion", &AZ::TransformBus::Events::SetRotationQuaternion) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetRotationX", &AZ::TransformBus::Events::SetRotationX) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetRotationY", &AZ::TransformBus::Events::SetRotationY) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("SetRotationZ", &AZ::TransformBus::Events::SetRotationZ) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetEulerRotation", &AZ::TransformBus::Events::GetRotationEulerRadians) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetRotationQuaternion", &AZ::TransformBus::Events::GetRotationQuaternion) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetRotationX", &AZ::TransformBus::Events::GetRotationX) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetRotationY", &AZ::TransformBus::Events::GetRotationY) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) - ->Event("GetRotationZ", &AZ::TransformBus::Events::GetRotationZ) - ->Attribute(AZ::Script::Attributes::Deprecated, true) - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Event("SetWorldRotationQuaternion", &AZ::TransformBus::Events::SetWorldRotationQuaternion) ->Event("GetWorldRotation", &AZ::TransformBus::Events::GetWorldRotation) ->Event("GetWorldRotationQuaternion", &AZ::TransformBus::Events::GetWorldRotationQuaternion) ->Event("SetLocalRotation", &AZ::TransformBus::Events::SetLocalRotation) @@ -880,11 +745,11 @@ namespace AzFramework ->Event("GetLocalRotationQuaternion", &AZ::TransformBus::Events::GetLocalRotationQuaternion) ->Attribute("Rotation", AZ::Edit::Attributes::PropertyRotation) ->VirtualProperty("Rotation", "GetLocalRotationQuaternion", "SetLocalRotationQuaternion") - ->Event("SetLocalScale", &AZ::TransformBus::Events::SetLocalScale) ->Event("GetLocalScale", &AZ::TransformBus::Events::GetLocalScale) ->Attribute("Scale", AZ::Edit::Attributes::PropertyScale) - ->VirtualProperty("Scale", "GetLocalScale", "SetLocalScale") - ->Event("GetWorldScale", &AZ::TransformBus::Events::GetWorldScale) + ->Event("SetLocalUniformScale", &AZ::TransformBus::Events::SetLocalUniformScale) + ->Event("GetLocalUniformScale", &AZ::TransformBus::Events::GetLocalUniformScale) + ->VirtualProperty("Uniform Scale", "GetLocalUniformScale", "SetLocalUniformScale") ->Event("GetChildren", &AZ::TransformBus::Events::GetChildren) ->Event("GetAllDescendants", &AZ::TransformBus::Events::GetAllDescendants) ->Event("GetEntityAndAllDescendants", &AZ::TransformBus::Events::GetEntityAndAllDescendants) diff --git a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h index e3a647d39f..0301334a0d 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h @@ -112,22 +112,7 @@ namespace AzFramework float GetLocalZ() override; // Rotation modifiers - void SetRotation(const AZ::Vector3& eulerAnglesRadian) override; - void SetRotationQuaternion(const AZ::Quaternion& quaternion) override; - void SetRotationX(float eulerAngleRadian) override; - void SetRotationY(float eulerAngleRadian) override; - void SetRotationZ(float eulerAngleRadian) override; - - void RotateByX(float eulerAngleRadian) override; - void RotateByY(float eulerAngleRadian) override; - void RotateByZ(float eulerAngleRadian) override; - - AZ::Vector3 GetRotationEulerRadians() override; - AZ::Quaternion GetRotationQuaternion() override; - - float GetRotationX() override; - float GetRotationY() override; - float GetRotationZ() override; + void SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) override; AZ::Vector3 GetWorldRotation() override; AZ::Quaternion GetWorldRotationQuaternion() override; @@ -143,9 +128,7 @@ namespace AzFramework AZ::Quaternion GetLocalRotationQuaternion() override; // Scale Modifiers - void SetLocalScale(const AZ::Vector3& scale) override; AZ::Vector3 GetLocalScale() override; - AZ::Vector3 GetWorldScale() override; void SetLocalUniformScale(float scale) override; float GetLocalUniformScale() override; diff --git a/Code/Framework/AzFramework/AzFramework/Font/FontInterface.h b/Code/Framework/AzFramework/AzFramework/Font/FontInterface.h index b64b61e22c..dae5fa9fe7 100644 --- a/Code/Framework/AzFramework/AzFramework/Font/FontInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Font/FontInterface.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -42,14 +43,18 @@ namespace AzFramework { ViewportId m_drawViewportId = InvalidViewportId; //!< Viewport to draw into AZ::Vector3 m_position; //!< world space position for 3d draws, screen space x,y,depth for 2d. - AZ::Color m_color = AZ::Colors::White; //!< Color to draw the text + AZ::Color m_color = AZ::Colors::White; //!< Color to draw the text + unsigned int m_effectIndex = 0; //!< effect index to apply AZ::Vector2 m_scale = AZ::Vector2(1.0f); //!< font scale - float m_lineSpacing; //!< Spacing between new lines, as a percentage of m_scale. + float m_textSizeFactor = 12.0f; //!< font size in pixels + float m_lineSpacing = 1.0f; //!< Spacing between new lines, as a percentage of m_scale. TextHorizontalAlignment m_hAlign = TextHorizontalAlignment::Left; //!< Horizontal text alignment TextVerticalAlignment m_vAlign = TextVerticalAlignment::Top; //!< Vertical text alignment + bool m_useTransform = false; //!< Use specified transform + AZ::Matrix3x4 m_transform = AZ::Matrix3x4::Identity(); //!< Transform to apply to text quads bool m_monospace = false; //!< disable character proportional spacing bool m_depthTest = false; //!< Test character against the depth buffer - bool m_virtual800x600ScreenSize = true; //!< Text placement and size are scaled relative to a virtual 800x600 resolution + bool m_virtual800x600ScreenSize = false; //!< Text placement and size are scaled relative to a virtual 800x600 resolution bool m_scaleWithWindow = false; //!< Font gets bigger as the window gets bigger bool m_multiline = true; //!< text respects ascii newline characters }; diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ClassConverters.cpp b/Code/Framework/AzFramework/AzFramework/Physics/ClassConverters.cpp index e43bda4c88..4f206858af 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ClassConverters.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/ClassConverters.cpp @@ -259,11 +259,18 @@ namespace Physics if (success) { - success = success && dataElement.RemoveElementByName(AZ_CRC("MaterialId", 0x9360e002)); + dataElement.RemoveElementByName(AZ_CRC("MaterialId", 0x9360e002)); + success = success && (dataElement.FindElement(AZ_CRC("MaterialId", 0x9360e002)) < 0); success = success && dataElement.AddElementWithData(context, "MaterialIds", AZStd::vector { materialId }); } } + if (success && dataElement.GetVersion() <= 2) + { + dataElement.RemoveElementByName(AZ_CRC_CE("Material")); + success = success && (dataElement.FindElement(AZ_CRC_CE("Material")) < 0); + } + return success; } } // namespace ClassConverters diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsEvents.h b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsEvents.h index d5a82c0367..a3a34dc1df 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsEvents.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsEvents.h @@ -58,9 +58,18 @@ namespace AzPhysics //! When triggered will send the handle to the old Scene (after this call, the Handle will be invalid). using OnSceneRemovedEvent = AZ::Event; - //! Event that triggers when the default material library changes. + //! Event that triggers when the material library changes. //! When triggered the event will send the Asset Id of the new material library. - using OnDefaultMaterialLibraryChangedEvent = AZ::Event; + using OnMaterialLibraryChangedEvent = AZ::Event; + + enum class MaterialLibraryLoadErrorType : uint8_t + { + InvalidId, + ErrorLoading + }; + + //! Event that triggers when the default material library has loaded with errors. + using OnMaterialLibraryLoadErrorEvent = AZ::Event; //! Event that triggers when the default scene configuration changes. //! When triggered the event will send the new default scene configuration. diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBody.h b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBody.h index ed8a68dc24..9d45a17edc 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBody.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSimulatedBody.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,22 @@ namespace AzPhysics return m_customUserData; } + //! Helper functions for setting frame ID. + //! @param frameId Optionally set frame ID for the systems moving the actors back in time. + void SetFrameId(uint32_t frameId) + { + m_frameId = frameId; + } + + //! Helper functions for getting the set frame ID. + //! @return Will return the frame ID. + uint32_t GetFrameId() const + { + return m_frameId; + } + + static constexpr uint32_t UndefinedFrameId = AZStd::numeric_limits::max(); + //! Perform a ray cast on this Simulated Body. //! @param request The request to make. //! @return Returns the closest hit, if any, against this simulated body. @@ -126,6 +143,7 @@ namespace AzPhysics SimulatedBodyEvents::OnTriggerExit m_triggerExitEvent; void* m_customUserData = nullptr; + uint32_t m_frameId = UndefinedFrameId; // helpers for reflecting to behavior context SimulatedBodyEvents::OnCollisionBegin* GetOnCollisionBeginEvent(); diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.cpp index cd250b71a9..d7532cbfea 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.cpp @@ -39,6 +39,8 @@ namespace AzPhysics ->Field("ShapecastBufferSize", &SystemConfiguration::m_shapecastBufferSize) ->Field("OverlapBufferSize", &SystemConfiguration::m_overlapBufferSize) ->Field("CollisionConfig", &SystemConfiguration::m_collisionConfig) + ->Field("DefaultMaterial", &SystemConfiguration::m_defaultMaterialConfiguration) + ->Field("MaterialLibrary", &SystemConfiguration::m_materialLibraryAsset) ; if (AZ::EditContext* editContext = serializeContext->GetEditContext()) @@ -79,7 +81,9 @@ namespace AzPhysics m_overlapBufferSize == other.m_overlapBufferSize && AZ::IsClose(m_maxTimestep, other.m_maxTimestep) && AZ::IsClose(m_fixedTimestep, other.m_fixedTimestep) && - m_collisionConfig == other.m_collisionConfig + m_collisionConfig == other.m_collisionConfig && + m_defaultMaterialConfiguration == other.m_defaultMaterialConfiguration && + m_materialLibraryAsset == other.m_materialLibraryAsset ; } diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.h index 0a00d627a7..56fe9a68c4 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.h @@ -13,6 +13,7 @@ #include #include +#include namespace AZ { @@ -45,6 +46,9 @@ namespace AzPhysics //! Each Physics Scene uses this as a base and will override as needed. CollisionConfiguration m_collisionConfig; + Physics::MaterialConfiguration m_defaultMaterialConfiguration; //!< Default material parameters for the project. + AZ::Data::Asset m_materialLibraryAsset = AZ::Data::AssetLoadBehavior::NoLoad; //!< Material Library exposed by the system component SystemBus API. + //! Controls whether the Physics System will self register to the TickBus and call StartSimulation / FinishSimulation on each Scene. //! Disable this to manually control Physics Scene simulation logic. bool m_autoManageSimulationUpdate = true; diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp index 5552cef448..78e0431753 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp @@ -49,10 +49,7 @@ namespace Physics { materialSelection->SetMaterialSlots(Physics::MaterialSelection::SlotsArray()); } - if (materialSelection->IsDefaultMaterialLibraryAsset()) - { - materialSelection->SyncSelectionToMaterialLibrary(); - } + materialSelection->SyncSelectionToMaterialLibrary(); } }; @@ -122,6 +119,24 @@ namespace Physics } } + bool MaterialConfiguration::operator==(const MaterialConfiguration& other) const + { + return m_surfaceType == other.m_surfaceType && + AZ::IsClose(m_dynamicFriction, other.m_dynamicFriction) && + AZ::IsClose(m_staticFriction, other.m_staticFriction) && + AZ::IsClose(m_restitution, other.m_restitution) && + AZ::IsClose(m_density, other.m_density) && + m_restitutionCombine == other.m_restitutionCombine && + m_frictionCombine == other.m_frictionCombine && + m_debugColor == other.m_debugColor + ; + } + + bool MaterialConfiguration::operator!=(const MaterialConfiguration& other) const + { + return !(*this == other); + } + AZ::Color MaterialConfiguration::GenerateDebugColor(const char* materialName) { static const AZ::Color colors[] = @@ -191,51 +206,25 @@ namespace Physics ////////////////////////////////////////////////////////////////////////// - void MaterialLibraryAssetReflectionWrapper::Reflect(AZ::ReflectContext* context) - { - AZ::SerializeContext* serializeContext = azrtti_cast(context); - if (serializeContext) - { - serializeContext->Class() - ->Version(1) - ->Field("Asset", &MaterialLibraryAssetReflectionWrapper::m_asset) - ; - - AZ::EditContext* editContext = serializeContext->GetEditContext(); - if (editContext) - { - editContext->Class("", "") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->DataElement(AZ::Edit::UIHandlers::Default, &MaterialLibraryAssetReflectionWrapper::m_asset, "Physics Material Library", "Physics Material Library") - ->Attribute("EditButton", "") - ; - } - } - } - - ////////////////////////////////////////////////////////////////////////// - - - void DefaultMaterialLibraryAssetReflectionWrapper::Reflect(AZ::ReflectContext* context) + void MaterialInfoReflectionWrapper::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class() + serializeContext->Class() ->Version(1) - ->Field("Asset", &DefaultMaterialLibraryAssetReflectionWrapper::m_asset) + ->Field("DefaultMaterial", &MaterialInfoReflectionWrapper::m_defaultMaterialConfiguration) + ->Field("Asset", &MaterialInfoReflectionWrapper::m_materialLibraryAsset) ; AZ::EditContext* editContext = serializeContext->GetEditContext(); if (editContext) { - editContext->Class("", "") + editContext->Class("Physics Materials", "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->DataElement(AZ::Edit::UIHandlers::Default, &DefaultMaterialLibraryAssetReflectionWrapper::m_asset, "Default Physics Material Library", "Library to use by default") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &MaterialInfoReflectionWrapper::m_defaultMaterialConfiguration, "Default Physics Material", "Material used by default") + ->DataElement(AZ::Edit::UIHandlers::Default, &MaterialInfoReflectionWrapper::m_materialLibraryAsset, "Physics Material Library", "Library to use for the project") ->Attribute(AZ::Edit::Attributes::AllowClearAsset, false) ->Attribute("EditButton", "") ; @@ -269,6 +258,17 @@ namespace Physics } } + bool MaterialFromAssetConfiguration::operator==(const MaterialFromAssetConfiguration& other) const + { + return m_configuration == other.m_configuration && + m_id == other.m_id; + } + + bool MaterialFromAssetConfiguration::operator!=(const MaterialFromAssetConfiguration& other) const + { + return !(*this == other); + } + ////////////////////////////////////////////////////////////////////////// bool MaterialLibraryAsset::GetDataForMaterialId(const MaterialId& materialId, MaterialFromAssetConfiguration& configuration) const @@ -370,9 +370,8 @@ namespace Physics if (auto serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(2, &ClassConverters::MaterialSelectionConverter) + ->Version(3, &ClassConverters::MaterialSelectionConverter) ->EventHandler() - ->Field("Material", &MaterialSelection::m_materialLibrary) ->Field("MaterialIds", &MaterialSelection::m_materialIdsAssignedToSlots) ; @@ -381,14 +380,8 @@ namespace Physics editContext->Class("Physics Material", "Select physics material library and which materials to use for the object") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialLibrary, "Library", "Physics material library to use for this object") - ->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, true) - ->Attribute("EditButton", "") - ->Attribute("EditDescription", "Open in Asset Editor") - ->Attribute(AZ::Edit::Attributes::DefaultAsset, &MaterialSelection::GetDefaultMaterialLibraryId) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &MaterialSelection::OnMaterialLibraryChanged) ->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialIdsAssignedToSlots, "Mesh Surfaces", "Specify which Physics Material to use for each element of this object") - ->ElementAttribute(Attributes::MaterialLibraryAssetId, &MaterialSelection::GetMaterialLibraryAssetId) + ->ElementAttribute(Attributes::MaterialLibraryAssetId, &MaterialSelection::GetMaterialLibraryId) ->Attribute(AZ::Edit::Attributes::IndexedChildNameLabelOverride, &MaterialSelection::GetMaterialSlotLabel) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->ElementAttribute(AZ::Edit::Attributes::ReadOnly, &MaterialSelection::AreMaterialSlotsReadOnly) @@ -398,12 +391,6 @@ namespace Physics } } - AZ::u32 MaterialSelection::OnMaterialLibraryChanged() - { - SyncSelectionToMaterialLibrary(); - return AZ::Edit::PropertyRefreshLevels::EntireTree; - } - AZStd::string MaterialSelection::GetMaterialSlotLabel(int index) { if (index < m_materialSlots.size()) @@ -425,28 +412,9 @@ namespace Physics } } - AZ::Data::AssetId MaterialSelection::GetMaterialLibraryAssetId() const - { - return GetMaterialLibraryAsset().GetId(); - } - - const Physics::MaterialLibraryAsset* MaterialSelection::GetMaterialLibraryAssetData() const - { - return GetMaterialLibraryAsset().Get(); - } - - const AZStd::string& MaterialSelection::GetMaterialLibraryAssetHint() const + void MaterialSelection::OnMaterialLibraryChanged([[maybe_unused]] const AZ::Data::AssetId& defaultMaterialLibraryId) { - return m_materialLibrary.GetHint(); - } - - void MaterialSelection::OnDefaultMaterialLibraryChanged(const AZ::Data::AssetId& defaultMaterialLibraryId) - { - AZ_UNUSED(defaultMaterialLibraryId); - if (IsDefaultMaterialLibraryAsset()) - { - OnMaterialLibraryChanged(); - } + SyncSelectionToMaterialLibrary(); } void MaterialSelection::SetSlotsReadOnly(bool readOnly) @@ -454,45 +422,6 @@ namespace Physics m_slotsReadOnly = readOnly; } - bool MaterialSelection::IsMaterialLibraryValid() const - { - if (GetMaterialLibraryAssetId().IsValid()) - { - auto materialAsset = LoadAsset(); - const auto& materialsData = materialAsset.Get()->GetMaterialsData(); - - if (materialsData.size() != 0) - { - return true; - } - } - return false; - } - - bool MaterialSelection::GetMaterialConfiguration(Physics::MaterialFromAssetConfiguration& configuration, const Physics::MaterialId& materialId) const - { - if (IsMaterialLibraryValid()) - { - auto materialAsset = LoadAsset(); - if (materialAsset.Get()) - { - return materialAsset.Get()->GetDataForMaterialId(materialId, configuration); - } - } - return false; - } - - void MaterialSelection::SetMaterialLibrary(const AZ::Data::AssetId& assetId) - { - m_materialLibrary = AZ::Data::AssetManager::Instance().GetAsset(assetId, m_materialLibrary.GetAutoLoadBehavior()); - m_materialLibrary.BlockUntilLoadComplete(); - } - - void MaterialSelection::ResetToDefaultMaterialLibrary() - { - m_materialLibrary = {}; - } - void MaterialSelection::SetMaterialSlots(const SlotsArray& slots) { if (slots.empty()) @@ -533,74 +462,45 @@ namespace Physics m_materialIdsAssignedToSlots[slotIndex] = materialId; } - AZ::Data::Asset MaterialSelection::LoadAsset() const - { - AZ::Data::Asset asset = AZ::Data::AssetManager::Instance() - .GetAsset(GetMaterialLibraryAssetId(), AZ::Data::AssetLoadBehavior::Default); - - asset.BlockUntilLoadComplete(); - - return asset; - } - void MaterialSelection::SyncSelectionToMaterialLibrary() { - if (GetMaterialLibraryAssetId().IsValid()) + auto* materialLibrary = GetMaterialLibrary().Get(); + if (!materialLibrary) { - auto materialLibraryAsset = AZ::Data::AssetManager::Instance().GetAsset(GetMaterialLibraryAssetId(), AZ::Data::AssetLoadBehavior::Default); - - materialLibraryAsset.BlockUntilLoadComplete(); - - // We try to check whether existing selection matches any materials in the newly assigned library and do one of the following: - // 1. If previous MaterialId is invalid for this material library, and it is not the Default material, we set it to the Default material from the library. - // 2. If it's valid, or it is the Default material, we don't change it (useful when user accidentally re-assigns the same library: previous selection won't go away). + return; + } - if (materialLibraryAsset.Get()) + for (Physics::MaterialId& materialId : m_materialIdsAssignedToSlots) + { + // Leave nulls (default) unchanged. + if (materialId.IsNull()) { - for (Physics::MaterialId& materialId : m_materialIdsAssignedToSlots) - { - if (!materialLibraryAsset.Get()->HasDataForMaterialId(materialId) - && !materialId.IsNull()) // Null materialId is the Default material. - { - materialId = MaterialId(); - } - } + continue; } - else + + // If the material id is not present in the library anymore, set it to default + if (!materialLibrary->HasDataForMaterialId(materialId)) { - AZ_Warning("PhysX", false, "MaterialSelection: invalid material library"); + materialId = MaterialId(); } } } - const AZ::Data::Asset& MaterialSelection::GetMaterialLibraryAsset() const - { - if (IsDefaultMaterialLibraryAsset()) - { - const AZ::Data::Asset& defaultMaterialLibrary = GetDefaultMaterialLibrary(); - return defaultMaterialLibrary; - } - - return m_materialLibrary; - } - - bool MaterialSelection::IsDefaultMaterialLibraryAsset() const - { - return !m_materialLibrary.GetId().IsValid(); - } - - const AZ::Data::Asset& MaterialSelection::GetDefaultMaterialLibrary() + const AZ::Data::Asset& MaterialSelection::GetMaterialLibrary() { if (auto* physicsSystem = AZ::Interface::Get()) { - return physicsSystem->GetDefaultMaterialLibrary(); + if (const auto* physicsConfiguration = physicsSystem->GetConfiguration()) + { + return physicsConfiguration->m_materialLibraryAsset; + } } return s_invalidMaterialLibrary; } - const AZ::Data::AssetId& MaterialSelection::GetDefaultMaterialLibraryId() + const AZ::Data::AssetId& MaterialSelection::GetMaterialLibraryId() { - return GetDefaultMaterialLibrary().GetId(); + return GetMaterialLibrary().GetId(); } bool MaterialSelection::AreMaterialSlotsReadOnly() const diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Material.h b/Code/Framework/AzFramework/AzFramework/Physics/Material.h index e9eaae929f..69edf3ed25 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Material.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Material.h @@ -29,7 +29,6 @@ namespace Physics /// ========================= /// This is the interface to the wrapper around native material type (such as PxMaterial in PhysX gem) /// that stores extra metadata, like Surface Type name. - /// To see more details about PhysX implementation please refer to PhysX::Material class /// /// Usage example /// ------------------------- @@ -37,14 +36,7 @@ namespace Physics /// /// Physics::MaterialConfiguration materialProperties; /// AZStd::shared_ptr newMaterial = AZ::Interface::Get()->CreateMaterial(materialProperties); - /// - /// To get PxMaterial use GetNativePointer function - /// - /// physx::PxMaterial* material = static_cast(newMaterial->GetNativePointer()); - /// - /// You can use retrieved PxMaterial pointer on its own, provided you increment its reference count. - /// If this class goes out of scope, the PxMaterial pointer will be valid, but its userData - /// will be cleaned up to point to nullptr. + /// class Material { public: @@ -63,9 +55,9 @@ namespace Physics /// Returns AZ::Crc32 of the surface name. virtual AZ::Crc32 GetSurfaceType() const = 0; - virtual void SetSurfaceType(AZ::Crc32 surfaceType) = 0; virtual const AZStd::string& GetSurfaceTypeName() const = 0; + virtual void SetSurfaceTypeName(const AZStd::string& surfaceTypeName) = 0; virtual float GetDynamicFriction() const = 0; virtual void SetDynamicFriction(float dynamicFriction) = 0; @@ -85,6 +77,9 @@ namespace Physics virtual float GetDensity() const = 0; virtual void SetDensity(float density) = 0; + virtual AZ::Color GetDebugColor() const = 0; + virtual void SetDebugColor(const AZ::Color& debugColor) = 0; + /// If the name of this material matches the name of one of the CrySurface types, it will return its CrySurface Id.\n /// If there's no match it will return default CrySurface Id.\n /// CrySurface types are defined in libs/materialeffects/surfacetypes.xml @@ -122,6 +117,10 @@ namespace Physics Material::CombineMode m_frictionCombine = Material::CombineMode::Average; AZ::Color m_debugColor = AZ::Colors::White; + + bool operator==(const MaterialConfiguration& other) const; + bool operator!=(const MaterialConfiguration& other) const; + private: static bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); static AZ::Color GenerateDebugColor(const char* materialName); @@ -147,6 +146,7 @@ namespace Physics static MaterialId FromUUID(const AZ::Uuid& uuid); bool IsNull() const { return m_id.IsNull(); } bool operator==(const MaterialId& other) const { return m_id == other.m_id; } + bool operator!=(const MaterialId& other) const { return !(*this == other); } const AZ::Uuid& GetUuid() const { return m_id; } private: @@ -166,6 +166,9 @@ namespace Physics MaterialConfiguration m_configuration; MaterialId m_id; + + bool operator==(const MaterialFromAssetConfiguration& other) const; + bool operator!=(const MaterialFromAssetConfiguration& other) const; }; /// An asset that holds a list of materials to be edited and assigned in Open 3D Engine Editor @@ -222,40 +225,27 @@ namespace Physics AZStd::vector m_materialLibrary; }; - /// The class is used to expose a MaterialLibraryAsset to Edit Context + /// The class is used to expose a default material and material library asset to Edit Context /// ======================================================================= /// /// Since AZ::Data::Asset doesn't reflect the data to EditContext /// we have to have a wrapper doing it. - class MaterialLibraryAssetReflectionWrapper + class MaterialInfoReflectionWrapper { public: - AZ_CLASS_ALLOCATOR(MaterialLibraryAssetReflectionWrapper, AZ::SystemAllocator, 0); - AZ_TYPE_INFO(Physics::MaterialLibraryAssetReflectionWrapper, "{3D2EF5DF-EFD0-47EB-B88F-3E6FE1FEE5B0}"); + AZ_CLASS_ALLOCATOR(MaterialInfoReflectionWrapper, AZ::SystemAllocator, 0); + AZ_TYPE_INFO(Physics::MaterialInfoReflectionWrapper, "{02AB8CBC-D35B-4E0F-89BA-A96D94DAD4F9}"); static void Reflect(AZ::ReflectContext* context); - AZ::Data::Asset m_asset = + Physics::MaterialConfiguration m_defaultMaterialConfiguration; + AZ::Data::Asset m_materialLibraryAsset = AZ::Data::AssetLoadBehavior::NoLoad; }; - /// Customized material library for use as default material library - class DefaultMaterialLibraryAssetReflectionWrapper : public Physics::MaterialLibraryAssetReflectionWrapper - { - public: - AZ_CLASS_ALLOCATOR(MaterialLibraryAssetReflectionWrapper, AZ::SystemAllocator, 0); - AZ_TYPE_INFO(Physics::DefaultMaterialLibraryAssetReflectionWrapper, "{02AB8CBC-D35B-4E0F-89BA-A96D94DAD4F9}"); - static void Reflect(AZ::ReflectContext* context); - - AZ::Data::Asset m_asset = - AZ::Data::AssetLoadBehavior::NoLoad; - }; - - /// The class is used to store a MaterialLibraryAsset and a vector of MaterialIds selected from the library + /// The class is used to store a vector of MaterialIds selected from the library /// ======================================================================= /// - /// This class is used to store a reference to the library asset and user's - /// selection of the materials from this library.\n - /// It also reflects UI controls for assigning MaterialLibraryAsset and selecting a material from it. + /// This class is used to store the user's selection of the materials from this library. /// You can reflect this class in EditorContext to provide UI for selecting materials /// on any custom component or QWidget. class MaterialSelection @@ -269,27 +259,6 @@ namespace Physics static void Reflect(AZ::ReflectContext* context); - /// Returns whether MaterialLibraryAsset assigned to this selection exists and valid. Attempts to load - /// the library if it's not loaded yet. - /// @return true if MaterialLibraryAsset has a valid AssetId, loaded and isn't empty - bool IsMaterialLibraryValid() const; - - /// Looks up MaterialLibraryAsset for MaterialFromAssetConfiguration with MaterialId that is stored intrenally. - /// @param configuration contains material data if there is a material selected by user - /// and if it exists in the MaterialLibraryAsset - /// @param materialId MaterialId to retrieve MaterialFromAssetConfiguration for - /// @return true if lookup was successful. - bool GetMaterialConfiguration(Physics::MaterialFromAssetConfiguration& configuration, const Physics::MaterialId& materialId) const; - - /// Sets and loads MaterialLibraryAsset with specified AssetId. - /// It is used to construct MaterialSelection at runtime. - /// It is not a typical use case and mostly needed to convert legacy entities and auto-generate material libraries - /// @param assetId AssetId to create MaterialLibraryAsset with - void SetMaterialLibrary(const AZ::Data::AssetId& assetId); - - /// Sets the material library to none, this will cause to use the project-wide default material library - void ResetToDefaultMaterialLibrary(); - /// Sets an array of material slots to pick MaterialIds for. Having multiple slots is required for assigning multiple materials on a mesh /// or heightfield object. SlotsArray can be empty and in this case Default slot will be created. /// @param slots Array of names for slots. Can be empty, in this case Default slot will be created @@ -298,48 +267,34 @@ namespace Physics /// Returns a list of MaterialId that were assigned for each corresponding slot. const AZStd::vector& GetMaterialIdsAssignedToSlots() const; - /// Sets the MaterialId from MaterialLibraryAsset as the selected material at a specific slotIndex. - /// @param materialId MaterialId that user selected from the MaterialLibraryAsset - /// @param slotIndex index of the slot to set MaterialId for + /// Sets the MaterialId as the selected material at a specific slotIndex. + /// @param materialId MaterialId that user selected + /// @param slotIndex Index of the slot to set the MaterialId void SetMaterialId(const Physics::MaterialId& materialId, int slotIndex = 0); - /// Returns the material library asset id. - AZ::Data::AssetId GetMaterialLibraryAssetId() const; - /// Returns the material id assigned to this selection at a specific slotIndex. - /// @param slotIndex index of the slot to retrieve MaterialId for + /// @param slotIndex Index of the slot to retrieve the MaterialId Physics::MaterialId GetMaterialId(int slotIndex = 0) const; - /// Returns the material library asset. - const Physics::MaterialLibraryAsset* GetMaterialLibraryAssetData() const; - - /// Returns the material library asset hint(UI display string) - const AZStd::string& GetMaterialLibraryAssetHint() const; - /// Called when the material library has changed - void OnDefaultMaterialLibraryChanged(const AZ::Data::AssetId& defaultMaterialLibraryId); + void OnMaterialLibraryChanged(const AZ::Data::AssetId& defaultMaterialLibraryId); /// Set if the material slots are editable in the edit context void SetSlotsReadOnly(bool readOnly); private: - AZ::Data::Asset m_materialLibrary { AZ::Data::AssetLoadBehavior::NoLoad }; AZStd::vector m_materialIdsAssignedToSlots; SlotsArray m_materialSlots; bool m_slotsReadOnly = false; - const AZ::Data::Asset& GetMaterialLibraryAsset() const; - AZ::Data::Asset LoadAsset() const; - bool IsDefaultMaterialLibraryAsset() const; void SyncSelectionToMaterialLibrary(); - static const AZ::Data::Asset& GetDefaultMaterialLibrary(); - static const AZ::Data::AssetId& GetDefaultMaterialLibraryId(); + static const AZ::Data::Asset& GetMaterialLibrary(); + static const AZ::Data::AssetId& GetMaterialLibraryId(); bool AreMaterialSlotsReadOnly() const; // EditorContext callbacks - AZ::u32 OnMaterialLibraryChanged(); AZStd::string GetMaterialSlotLabel(int index); }; diff --git a/Code/Framework/AzFramework/AzFramework/Physics/MaterialBus.h b/Code/Framework/AzFramework/AzFramework/Physics/MaterialBus.h index edfa3096d3..a7e4869df1 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/MaterialBus.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/MaterialBus.h @@ -25,21 +25,26 @@ namespace Physics static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; // Implemented by sole owner of materials, e.g. class MaterialManager in PhysX gem. - /// Get default material + /// Get default material. virtual AZStd::shared_ptr GetGenericDefaultMaterial() = 0; /// Returns weak pointers to physics materials. /// Connect to PhysicsMaterialNotifications::MaterialsReleased to be informed when material pointers are deleted by owner. virtual void GetMaterials(const MaterialSelection& materialSelection - , AZStd::vector>& outMaterials) = 0; + , AZStd::vector>& outMaterials) = 0; + + /// Returns a weak pointer to physics material with the given id. + virtual AZStd::shared_ptr GetMaterialById(Physics::MaterialId id) = 0; /// Returns a weak pointer to physics material with the given name. - virtual AZStd::weak_ptr GetMaterialByName(const AZStd::string& name) = 0; + virtual AZStd::shared_ptr GetMaterialByName(const AZStd::string& name) = 0; - /// Returns index of the first selected material in MaterialSelection's material library. - /// A MaterialSelection can contain multiple material selections. - /// Returned index is 0-based where 0 is the Default material, and materials from the material library are 1 and onwards. - virtual AZ::u32 GetFirstSelectedMaterialIndex(const MaterialSelection& materialSelection) = 0; + /// Updates the material selection from the physics asset or sets it to default if there's no asset provided. + /// @param shapeConfiguration The shape information that contains the physics asset. + /// @param materialSelection The material selection to update. + virtual void UpdateMaterialSelectionFromPhysicsAsset( + const ShapeConfiguration& shapeConfiguration, + MaterialSelection& materialSelection) = 0; }; using PhysicsMaterialRequestBus = AZ::EBus; diff --git a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.h b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.h index e3ed449046..36ae4dbecb 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/PhysicsSystem.h @@ -130,13 +130,6 @@ namespace AzPhysics //! @param forceReinitialization Flag to force a reinitialization of the physics system. Default false. virtual void UpdateConfiguration(const SystemConfiguration* newConfig, bool forceReinitialization = false) = 0; - //! Update the default material library. - //! @param materialLibrary The new material library asset to use. - virtual void UpdateDefaultMaterialLibrary(const AZ::Data::Asset& materialLibrary) = 0; - - //! Accessor to get the current Material Library. This is also available in the PhysXSystemConfiguration. - virtual const AZ::Data::Asset& GetDefaultMaterialLibrary() const = 0; - //! Update the current default scene configuration. //! This is the configuration used to to create scenes without a custom configuration. //! @param sceneConfiguration The new configuration to apply. @@ -169,9 +162,12 @@ namespace AzPhysics //! Register to receive notifications when the SystemConfiguration changes. //! @param handler The handler to receive the event. void RegisterSystemConfigurationChangedEvent(SystemEvents::OnConfigurationChangedEvent::Handler& handler) { handler.Connect(m_configChangeEvent); } - //! Register a handler to receive an event when the default material library changes. + //! Register a handler to receive an event when the material library changes. + //! @param handler The handler to receive the event. + void RegisterOnMaterialLibraryChangedEventHandler(SystemEvents::OnMaterialLibraryChangedEvent::Handler& handler) { handler.Connect(m_onMaterialLibraryChangedEvent); } + //! Register a handler to receive an event when the material library fails to load on startup. //! @param handler The handler to receive the event. - void RegisterOnDefaultMaterialLibraryChangedEventHandler(SystemEvents::OnDefaultMaterialLibraryChangedEvent::Handler& handler) { handler.Connect(m_onDefaultMaterialLibraryChangedEvent); } + void RegisterOnMaterialLibraryLoadErrorEventHandler(SystemEvents::OnMaterialLibraryLoadErrorEvent::Handler& handler) { handler.Connect(m_onMaterialLibraryLoadErrorEvent); } //! Register a handler to receive an event when the default SceneConfiguration changes. //! @param handler The handler to receive the event. void RegisterOnDefaultSceneConfigurationChangedEventHandler(SystemEvents::OnDefaultSceneConfigurationChangedEvent::Handler& handler) { handler.Connect(m_onDefaultSceneConfigurationChangedEvent); } @@ -185,7 +181,8 @@ namespace AzPhysics SystemEvents::OnSceneAddedEvent m_sceneAddedEvent; SystemEvents::OnSceneRemovedEvent m_sceneRemovedEvent; SystemEvents::OnConfigurationChangedEvent m_configChangeEvent; - SystemEvents::OnDefaultMaterialLibraryChangedEvent m_onDefaultMaterialLibraryChangedEvent; + SystemEvents::OnMaterialLibraryChangedEvent m_onMaterialLibraryChangedEvent; + SystemEvents::OnMaterialLibraryLoadErrorEvent m_onMaterialLibraryLoadErrorEvent; SystemEvents::OnDefaultSceneConfigurationChangedEvent m_onDefaultSceneConfigurationChangedEvent; }; } // namespace AzPhysics diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.h b/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.h index 239d93cf32..97c841e8f8 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Ragdoll.h @@ -102,7 +102,7 @@ namespace Physics /// Is the ragdoll currently simulated? /// @result True in case the ragdoll is simulated, false if not. - virtual bool IsSimulated() = 0; + virtual bool IsSimulated() const = 0; /// Writes the state for all of the bodies in the ragdoll to the provided output. /// The caller owns the output state and can safely manipulate it without affecting the physics simulation. diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp index a535f5f65d..275103bc28 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp @@ -17,6 +17,21 @@ namespace Physics { + namespace Internal + { + bool ShapeConfigurationVersionConverter( + [[maybe_unused]] AZ::SerializeContext& context, + AZ::SerializeContext::DataElementNode& classElement) + { + if (classElement.GetVersion() <= 1) + { + classElement.RemoveElementByName(AZ_CRC_CE("UseMaterialsFromAsset")); + } + + return true; + } + } + void ShapeConfiguration::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context)) @@ -166,10 +181,9 @@ namespace Physics ->RegisterGenericType>(); serializeContext->Class() - ->Version(1) + ->Version(2, &Internal::ShapeConfigurationVersionConverter) ->Field("PhysicsAsset", &PhysicsAssetShapeConfiguration::m_asset) ->Field("AssetScale", &PhysicsAssetShapeConfiguration::m_assetScale) - ->Field("UseMaterialsFromAsset", &PhysicsAssetShapeConfiguration::m_useMaterialsFromAsset) ->Field("SubdivisionLevel", &PhysicsAssetShapeConfiguration::m_subdivisionLevel) ; @@ -182,7 +196,6 @@ namespace Physics ->DataElement(AZ::Edit::UIHandlers::Default, &PhysicsAssetShapeConfiguration::m_assetScale, "Asset Scale", "The scale of the asset shape") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Step, 0.01f) - ->DataElement(AZ::Edit::UIHandlers::Default, &PhysicsAssetShapeConfiguration::m_useMaterialsFromAsset, "Physics Materials from Mesh", "Auto-set physics materials using Mesh's material surfaces names") ; } } diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h index b3d04a10c9..8234ef9173 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h @@ -140,7 +140,7 @@ namespace Physics AZ::Data::Asset m_asset{ AZ::Data::AssetLoadBehavior::PreLoad }; AZ::Vector3 m_assetScale = AZ::Vector3::CreateOne(); - bool m_useMaterialsFromAsset = true; + bool m_useMaterialsFromAsset = false; // Not reflected or exposed to the user until there is a way to auto-match mesh's materials with physics materials AZ::u8 m_subdivisionLevel = 4; ///< The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling. }; diff --git a/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h b/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h index f198551148..8cdd0e0cf0 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/SystemBus.h @@ -142,24 +142,12 @@ namespace Physics virtual AZStd::shared_ptr CreateShape(const ColliderConfiguration& colliderConfiguration, const ShapeConfiguration& configuration) = 0; + virtual AZStd::shared_ptr CreateMaterial(const Physics::MaterialConfiguration& materialConfiguration) = 0; + /// Releases the mesh object created by the physics backend. /// @param nativeMeshObject Pointer to the mesh object. virtual void ReleaseNativeMeshObject(void* nativeMeshObject) = 0; - ////////////////////////////////////////////////////////////////////////// - //// Physics Materials - - virtual AZStd::shared_ptr CreateMaterial(const Physics::MaterialConfiguration& materialConfiguration) = 0; - virtual AZStd::shared_ptr GetDefaultMaterial() = 0; - virtual AZStd::vector> CreateMaterialsFromLibrary(const Physics::MaterialSelection& materialSelection) = 0; - - - /// Updates the collider material selection from the physics asset or sets it to default if there's no asset provided. - /// @param shapeConfiguration The shape information - /// @param colliderConfiguration The collider information - virtual bool UpdateMaterialSelection(const Physics::ShapeConfiguration& shapeConfiguration, - Physics::ColliderConfiguration& colliderConfiguration) = 0; - ////////////////////////////////////////////////////////////////////////// //// Joints diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp index b5f113582b..2c3b62bb88 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp @@ -119,8 +119,7 @@ namespace Physics AzPhysics::SceneConfiguration::Reflect(context); MaterialConfiguration::Reflect(context); MaterialLibraryAsset::Reflect(context); - MaterialLibraryAssetReflectionWrapper::Reflect(context); - DefaultMaterialLibraryAssetReflectionWrapper::Reflect(context); + MaterialInfoReflectionWrapper::Reflect(context); JointLimitConfiguration::Reflect(context); AzPhysics::SimulatedBodyConfiguration::Reflect(context); AzPhysics::RigidBodyConfiguration::Reflect(context); diff --git a/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp b/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp index bdbfe6197f..22598595fc 100644 --- a/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp @@ -46,7 +46,6 @@ namespace AzFramework::ProjectManager // Store the Command line to the Setting Registry AZ::SettingsRegistryImpl settingsRegistry; AZ::SettingsRegistryMergeUtils::StoreCommandLineToRegistry(settingsRegistry, commandLine); - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(settingsRegistry); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {}); // Retrieve Command Line from Settings Registry, it may have been updated by the call to FindEngineRoot() // in MergeSettingstoRegistry_ConfigFile @@ -79,7 +78,7 @@ namespace AzFramework::ProjectManager projectJsonPath.c_str()); } - if (LaunchProjectManager(engineRootPath)) + if (LaunchProjectManager()) { AZ_TracePrintf("ProjectManager", "Project Manager launched successfully, requesting exit."); return ProjectPathCheckResult::ProjectManagerLaunched; @@ -88,7 +87,7 @@ namespace AzFramework::ProjectManager return ProjectPathCheckResult::ProjectManagerLaunchFailed; } - bool LaunchProjectManager([[maybe_unused]] const AZ::IO::FixedMaxPath& engineRootPath) + bool LaunchProjectManager(const AZStd::string& commandLineArgs) { bool launchSuccess = false; #if (AZ_TRAIT_AZFRAMEWORK_USE_PROJECT_MANAGER) @@ -110,7 +109,7 @@ namespace AzFramework::ProjectManager } AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - processLaunchInfo.m_commandlineParameters = executablePath.String(); + processLaunchInfo.m_commandlineParameters = executablePath.String() + commandLineArgs; launchSuccess = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); } if (ownsSystemAllocator) diff --git a/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.h b/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.h index cc79bd4184..d0ef7172b0 100644 --- a/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.h +++ b/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.h @@ -12,6 +12,7 @@ #pragma once #include +#include namespace AzFramework::ProjectManager { @@ -21,8 +22,16 @@ namespace AzFramework::ProjectManager ProjectManagerLaunched = 0, ProjectPathFound = 1 }; - // Check for a project name, if not found, attempts to launch project manager and returns false + + //! Check for a project name, if not found, attempts to launch project manager and returns false + //! @param argc the number of arguments in argv + //! @param argv arguments provided to this executable + //! @return a ProjectPathCheckResult ProjectPathCheckResult CheckProjectPathProvided(const int argc, char* argv[]); - // Attempt to Launch the project manager. Requires locating the engine root, project manager script, and python. - bool LaunchProjectManager(const AZ::IO::FixedMaxPath& engineRootPath); + + //! Attempt to Launch the project manager, assuming the o3de executable exists in same folder as + //! current executable. Requires the o3de cli and python. + //! @param commandLineArgs additional command line arguments to provide to the project manager + //! @return true on success, false if failed to find or launch the executable + bool LaunchProjectManager(const AZStd::string& commandLineArgs = ""); } // AzFramework::ProjectManager diff --git a/Code/Framework/AzFramework/AzFramework/Render/GeometryIntersectionBus.h b/Code/Framework/AzFramework/AzFramework/Render/GeometryIntersectionBus.h index ba1d2d1e06..749f457286 100644 --- a/Code/Framework/AzFramework/AzFramework/Render/GeometryIntersectionBus.h +++ b/Code/Framework/AzFramework/AzFramework/Render/GeometryIntersectionBus.h @@ -12,6 +12,7 @@ #pragma once #include +#include #include namespace AzFramework @@ -35,12 +36,12 @@ namespace AzFramework AzFramework::EntityContextId m_contextId; }; - //! Interface for intersection requests, implement this interface for making your component - //! render geometry intersectable. + //! Interface for intersection requests. + //! Implement this interface to make your component 'intersectable'. class IntersectionRequests : public AZ::EBusTraits { - //! Policy for notifying the Intersector bus of entities connected/disconnected to this ebus + //! Policy for notifying the Intersector bus of entities connected/disconnected to this EBus //! so it updates the internal data of the entities template struct IntersectionRequestsConnectionPolicy diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h index 47388c56c3..a0731626ef 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h +++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h @@ -12,6 +12,7 @@ #pragma once +#include #include namespace AzFramework @@ -49,13 +50,17 @@ namespace AzFramework class ISessionHandlingClientRequests { public: - // Handle the player join session process + AZ_RTTI(ISessionHandlingClientRequests, "{41DE6BD3-72BC-4443-BFF9-5B1B9396657A}"); + ISessionHandlingClientRequests() = default; + virtual ~ISessionHandlingClientRequests() = default; + + // Request the player join session // @param sessionConnectionConfig The required properties to handle the player join session process // @return The result of player join session process - virtual bool HandlePlayerJoinSession(const SessionConnectionConfig& sessionConnectionConfig) = 0; + virtual bool RequestPlayerJoinSession(const SessionConnectionConfig& sessionConnectionConfig) = 0; - // Handle the player leave session process - virtual void HandlePlayerLeaveSession() = 0; + // Request the connected player leave session + virtual void RequestPlayerLeaveSession() = 0; }; //! ISessionHandlingServerRequests @@ -63,6 +68,10 @@ namespace AzFramework class ISessionHandlingServerRequests { public: + AZ_RTTI(ISessionHandlingServerRequests, "{4F0C17BA-F470-4242-A8CB-EC7EA805257C}"); + ISessionHandlingServerRequests() = default; + virtual ~ISessionHandlingServerRequests() = default; + // Handle the destroy session process virtual void HandleDestroySession() = 0; @@ -74,5 +83,10 @@ namespace AzFramework // Handle the player leave session process // @param playerConnectionConfig The required properties to handle the player leave session process virtual void HandlePlayerLeaveSession(const PlayerConnectionConfig& playerConnectionConfig) = 0; + + // Retrieves the file location of a pem-encoded TLS certificate + // @return If successful, returns the file location of TLS certificate file; if not successful, returns + // empty string. + virtual AZStd::string GetSessionCertificate() = 0; }; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h index 9d21a7f282..da65eb47f0 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h +++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h @@ -167,6 +167,9 @@ namespace AzFramework : public AZ::EBusTraits { public: + // Safeguard handler for multi-threaded use case + using MutexType = AZStd::recursive_mutex; + ////////////////////////////////////////////////////////////////////////// // EBusTraits overrides static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h b/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h index a61c995db7..c472fcb228 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h @@ -24,6 +24,9 @@ namespace AzFramework : public AZ::EBusTraits { public: + // Safeguard handler for multi-threaded use case + using MutexType = AZStd::recursive_mutex; + ////////////////////////////////////////////////////////////////////////// // EBusTraits overrides static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp index 7ab2d48814..46b3dfe87c 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp @@ -21,22 +21,6 @@ namespace AzFramework { } - Spawnable::Spawnable(Spawnable&& other) - : m_entities(AZStd::move(other.m_entities)) - { - } - - - Spawnable& Spawnable::operator=(Spawnable&& other) - { - if (this != &other) - { - m_entities = AZStd::move(other.m_entities); - } - - return *this; - } - const Spawnable::EntityList& Spawnable::GetEntities() const { return m_entities; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h index 5e507b3498..677f0326cf 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h @@ -19,10 +19,13 @@ #include #include -namespace AzFramework +namespace AZ { class ReflectContext; +} +namespace AzFramework +{ class Spawnable final : public AZ::Data::AssetData { @@ -38,11 +41,11 @@ namespace AzFramework Spawnable() = default; explicit Spawnable(const AZ::Data::AssetId& id, AssetStatus status = AssetStatus::NotLoaded); Spawnable(const Spawnable& rhs) = delete; - Spawnable(Spawnable&& other); + Spawnable(Spawnable&& other) = delete; ~Spawnable() override = default; Spawnable& operator=(const Spawnable& rhs) = delete; - Spawnable& operator=(Spawnable&& other); + Spawnable& operator=(Spawnable&& other) = delete; const EntityList& GetEntities() const; EntityList& GetEntities(); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp index b3ba1568bd..da046ff172 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp @@ -10,6 +10,7 @@ * */ +#include #include #include #include @@ -88,4 +89,10 @@ namespace AzFramework { extensions.push_back(Spawnable::FileExtension); } + + uint32_t SpawnableAssetHandler::BuildSubId(AZStd::string_view id) + { + AZ::Uuid subIdHash = AZ::Uuid::CreateData(id.data(), id.size()); + return azlossy_caster(subIdHash.GetHash()); + } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.h index deef314955..78268bf71a 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.h @@ -47,6 +47,7 @@ namespace AzFramework const char* GetGroup() const override; const char* GetBrowserIcon() const override; void GetAssetTypeExtensions(AZStd::vector& extensions) override; + static uint32_t BuildSubId(AZStd::string_view id); protected: LoadResult LoadAssetData( diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp index 673701cac4..808de74e71 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp @@ -38,19 +38,20 @@ namespace AzFramework void SpawnableEntitiesContainer::SpawnAllEntities() { AZ_Assert(m_threadData, "Calling SpawnAllEntities on a Spawnable container that's not set."); - SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_threadData->m_spawnedEntitiesTicket); + SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default); } void SpawnableEntitiesContainer::SpawnEntities(AZStd::vector entityIndices) { AZ_Assert(m_threadData, "Calling SpawnEntities on a Spawnable container that's not set."); - SpawnableEntitiesInterface::Get()->SpawnEntities(m_threadData->m_spawnedEntitiesTicket, AZStd::move(entityIndices)); + SpawnableEntitiesInterface::Get()->SpawnEntities( + m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default, AZStd::move(entityIndices)); } void SpawnableEntitiesContainer::DespawnAllEntities() { AZ_Assert(m_threadData, "Calling DespawnEntities on a Spawnable container that's not set."); - SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_threadData->m_spawnedEntitiesTicket); + SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default); } void SpawnableEntitiesContainer::Reset(AZ::Data::Asset spawnable) @@ -66,8 +67,10 @@ namespace AzFramework m_monitor.Disconnect(); m_monitor.m_threadData.reset(); - SpawnableEntitiesInterface::Get()->Barrier(m_threadData->m_spawnedEntitiesTicket, - [threadData = m_threadData](EntitySpawnTicket&) mutable + SpawnableEntitiesInterface::Get()->Barrier( + m_threadData->m_spawnedEntitiesTicket, + SpawnablePriority_Default, + [threadData = m_threadData](EntitySpawnTicket::Id) mutable { threadData.reset(); }); @@ -83,8 +86,10 @@ namespace AzFramework void SpawnableEntitiesContainer::Alert(AlertCallback callback) { AZ_Assert(m_threadData, "Calling DespawnEntities on a Spawnable container that's not set."); - SpawnableEntitiesInterface::Get()->Barrier(m_threadData->m_spawnedEntitiesTicket, - [generation = m_threadData->m_generation, callback = AZStd::move(callback)](EntitySpawnTicket&) + SpawnableEntitiesInterface::Get()->Barrier( + m_threadData->m_spawnedEntitiesTicket, + SpawnablePriority_Default, + [generation = m_threadData->m_generation, callback = AZStd::move(callback)](EntitySpawnTicket::Id) { callback(generation); }); @@ -110,6 +115,7 @@ namespace AzFramework AZ_Assert(m_threadData, "SpawnableEntitiesContainer is monitoring a spawnable, but doesn't have the associated data."); AZ_TracePrintf("Spawnables", "Reloading spawnable '%s'.\n", replacementAsset.GetHint().c_str()); - SpawnableEntitiesInterface::Get()->ReloadSpawnable(m_threadData->m_spawnedEntitiesTicket, AZStd::move(replacementAsset)); + SpawnableEntitiesInterface::Get()->ReloadSpawnable( + m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default, AZStd::move(replacementAsset)); } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp index a528797f63..ad1bf032c8 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp @@ -14,6 +14,10 @@ namespace AzFramework { + // + // SpawnableEntityContainerView + // + SpawnableEntityContainerView::SpawnableEntityContainerView(AZ::Entity** begin, size_t length) : m_begin(begin) , m_end(begin + length) @@ -52,6 +56,9 @@ namespace AzFramework } + // + // SpawnableConstEntityContainerView + // SpawnableConstEntityContainerView::SpawnableConstEntityContainerView(AZ::Entity** begin, size_t length) : m_begin(begin) @@ -91,6 +98,136 @@ namespace AzFramework } + // + // SpawnableIndexEntityPair + // + + SpawnableIndexEntityPair::SpawnableIndexEntityPair(AZ::Entity** entityIterator, size_t* indexIterator) + : m_entity(entityIterator) + , m_index(indexIterator) + { + } + + AZ::Entity* SpawnableIndexEntityPair::GetEntity() + { + return *m_entity; + } + + const AZ::Entity* SpawnableIndexEntityPair::GetEntity() const + { + return *m_entity; + } + + size_t SpawnableIndexEntityPair::GetIndex() const + { + return *m_index; + } + + // + // SpawnableIndexEntityIterator + // + + SpawnableIndexEntityIterator::SpawnableIndexEntityIterator(AZ::Entity** entityIterator, size_t* indexIterator) + : m_value(entityIterator, indexIterator) + { + } + + SpawnableIndexEntityIterator& SpawnableIndexEntityIterator::operator++() + { + ++m_value.m_entity; + ++m_value.m_index; + return *this; + } + + SpawnableIndexEntityIterator SpawnableIndexEntityIterator::operator++(int) + { + SpawnableIndexEntityIterator result = *this; + ++m_value.m_entity; + ++m_value.m_index; + return result; + } + + SpawnableIndexEntityIterator& SpawnableIndexEntityIterator::operator--() + { + --m_value.m_entity; + --m_value.m_index; + return *this; + } + + SpawnableIndexEntityIterator SpawnableIndexEntityIterator::operator--(int) + { + SpawnableIndexEntityIterator result = *this; + --m_value.m_entity; + --m_value.m_index; + return result; + } + + bool SpawnableIndexEntityIterator::operator==(const SpawnableIndexEntityIterator& rhs) + { + return m_value.m_entity == rhs.m_value.m_entity && m_value.m_index == rhs.m_value.m_index; + } + + bool SpawnableIndexEntityIterator::operator!=(const SpawnableIndexEntityIterator& rhs) + { + return m_value.m_entity != rhs.m_value.m_entity || m_value.m_index != rhs.m_value.m_index; + } + + SpawnableIndexEntityPair& SpawnableIndexEntityIterator::operator*() + { + return m_value; + } + + const SpawnableIndexEntityPair& SpawnableIndexEntityIterator::operator*() const + { + return m_value; + } + + SpawnableIndexEntityPair* SpawnableIndexEntityIterator::operator->() + { + return &m_value; + } + + const SpawnableIndexEntityPair* SpawnableIndexEntityIterator::operator->() const + { + return &m_value; + } + + + // + // SpawnableConstIndexEntityContainerView + // + + SpawnableConstIndexEntityContainerView::SpawnableConstIndexEntityContainerView( + AZ::Entity** beginEntity, size_t* beginIndices, size_t length) + : m_begin(beginEntity, beginIndices) + , m_end(beginEntity + length, beginIndices + length) + { + } + + const SpawnableIndexEntityIterator& SpawnableConstIndexEntityContainerView::begin() + { + return m_begin; + } + + const SpawnableIndexEntityIterator& SpawnableConstIndexEntityContainerView::end() + { + return m_end; + } + + const SpawnableIndexEntityIterator& SpawnableConstIndexEntityContainerView::cbegin() + { + return m_begin; + } + + const SpawnableIndexEntityIterator& SpawnableConstIndexEntityContainerView::cend() + { + return m_end; + } + + + // + // EntitySpawnTicket + // EntitySpawnTicket::EntitySpawnTicket(EntitySpawnTicket&& rhs) : m_payload(rhs.m_payload) @@ -102,7 +239,9 @@ namespace AzFramework { auto manager = SpawnableEntitiesInterface::Get(); AZ_Assert(manager, "Attempting to create an entity spawn ticket while the SpawnableEntitiesInterface has no implementation."); - m_payload = manager->CreateTicket(AZStd::move(spawnable)); + AZStd::pair result = manager->CreateTicket(AZStd::move(spawnable)); + m_id = result.first; + m_payload = result.second; } EntitySpawnTicket::~EntitySpawnTicket() @@ -113,6 +252,7 @@ namespace AzFramework AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation."); manager->DestroyTicket(m_payload); m_payload = nullptr; + m_id = 0; } } @@ -126,12 +266,20 @@ namespace AzFramework AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation."); manager->DestroyTicket(m_payload); } + m_id = rhs.m_id; + rhs.m_id = 0; + m_payload = rhs.m_payload; rhs.m_payload = nullptr; } return *this; } + auto EntitySpawnTicket::GetId() const -> Id + { + return m_id; + } + bool EntitySpawnTicket::IsValid() const { return m_payload != nullptr; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index ac66288ff2..27f45064b6 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -24,6 +25,14 @@ namespace AZ namespace AzFramework { + AZ_TYPE_SAFE_INTEGRAL(SpawnablePriority, uint8_t); + + inline static constexpr SpawnablePriority SpawnablePriority_Highest { 0 }; + inline static constexpr SpawnablePriority SpawnablePriority_High { 32 }; + inline static constexpr SpawnablePriority SpawnablePriority_Default { 128 }; + inline static constexpr SpawnablePriority SpawnablePriority_Low { 192 }; + inline static constexpr SpawnablePriority SpawnablePriority_Lowest { 255 }; + class SpawnableEntityContainerView { public: @@ -58,16 +67,84 @@ namespace AzFramework AZ::Entity** m_end; }; - //! Requests to the SpawnableEntitiesInterface require a ticket with a valid spawnable that be used as a template. A ticket can - //! be reused for multiple calls on the same spawnable and is safe to use by multiple threads at the same time. Entities created + class SpawnableIndexEntityPair + { + public: + friend class SpawnableIndexEntityIterator; + + AZ::Entity* GetEntity(); + const AZ::Entity* GetEntity() const; + size_t GetIndex() const; + + private: + SpawnableIndexEntityPair() = default; + SpawnableIndexEntityPair(const SpawnableIndexEntityPair&) = default; + SpawnableIndexEntityPair(SpawnableIndexEntityPair&&) = default; + SpawnableIndexEntityPair(AZ::Entity** entityIterator, size_t* indexIterator); + + SpawnableIndexEntityPair& operator=(const SpawnableIndexEntityPair&) = default; + SpawnableIndexEntityPair& operator=(SpawnableIndexEntityPair&&) = default; + + AZ::Entity** m_entity { nullptr }; + size_t* m_index { nullptr }; + }; + + class SpawnableIndexEntityIterator + { + public: + // Limited to bidirectional iterator as there's no use case for extending it further, but can be extended if a use case is found. + using iterator_category = AZStd::bidirectional_iterator_tag; + using value_type = SpawnableIndexEntityPair; + using difference_type = size_t; + using pointer = SpawnableIndexEntityPair*; + using reference = SpawnableIndexEntityPair&; + + SpawnableIndexEntityIterator(AZ::Entity** entityIterator, size_t* indexIterator); + + SpawnableIndexEntityIterator& operator++(); + SpawnableIndexEntityIterator operator++(int); + SpawnableIndexEntityIterator& operator--(); + SpawnableIndexEntityIterator operator--(int); + + bool operator==(const SpawnableIndexEntityIterator& rhs); + bool operator!=(const SpawnableIndexEntityIterator& rhs); + + SpawnableIndexEntityPair& operator*(); + const SpawnableIndexEntityPair& operator*() const; + SpawnableIndexEntityPair* operator->(); + const SpawnableIndexEntityPair* operator->() const; + + private: + SpawnableIndexEntityPair m_value; + }; + + class SpawnableConstIndexEntityContainerView + { + public: + SpawnableConstIndexEntityContainerView(AZ::Entity** beginEntity, size_t* beginIndices, size_t length); + + const SpawnableIndexEntityIterator& begin(); + const SpawnableIndexEntityIterator& end(); + const SpawnableIndexEntityIterator& cbegin(); + const SpawnableIndexEntityIterator& cend(); + + private: + SpawnableIndexEntityIterator m_begin; + SpawnableIndexEntityIterator m_end; + }; + + //! Requests to the SpawnableEntitiesInterface require a ticket with a valid spawnable that is used as a template. A ticket can + //! be reused for multiple calls on the same spawnable and is safe to be used by multiple threads at the same time. Entities created //! from the spawnable may be tracked by the ticket and so using the same ticket is needed to despawn the exact entities created - //! by a call so spawn entities. The life cycle of the spawned entities is tied to the ticket and all entities spawned using a + //! by a call to spawn entities. The life cycle of the spawned entities is tied to the ticket and all entities spawned using a //! ticket will be despawned when it's deleted. class EntitySpawnTicket { public: friend class SpawnableEntitiesDefinition; + using Id = uint64_t; + EntitySpawnTicket() = default; EntitySpawnTicket(const EntitySpawnTicket&) = delete; EntitySpawnTicket(EntitySpawnTicket&& rhs); @@ -77,25 +154,37 @@ namespace AzFramework EntitySpawnTicket& operator=(const EntitySpawnTicket&) = delete; EntitySpawnTicket& operator=(EntitySpawnTicket&& rhs); + Id GetId() const; bool IsValid() const; private: void* m_payload{ nullptr }; + Id m_id { 0 }; //!< An id that uniquely identifies a ticket. }; - using EntitySpawnCallback = AZStd::function; - using EntityPreInsertionCallback = AZStd::function; - using EntityDespawnCallback = AZStd::function; - using ReloadSpawnableCallback = AZStd::function; - using ListEntitiesCallback = AZStd::function; - using ClaimEntitiesCallback = AZStd::function; - using BarrierCallback = AZStd::function; + using EntitySpawnCallback = AZStd::function; + using EntityPreInsertionCallback = AZStd::function; + using EntityDespawnCallback = AZStd::function; + using ReloadSpawnableCallback = AZStd::function; + using ListEntitiesCallback = AZStd::function; + using ListIndicesEntitiesCallback = AZStd::function; + using ClaimEntitiesCallback = AZStd::function; + using BarrierCallback = AZStd::function; //! Interface definition to (de)spawn entities from a spawnable into the game world. + //! //! While the callbacks of the individual calls are being processed they will block processing any other request. Callbacks can be //! issued from threads other than the one that issued the call, including the main thread. + //! //! Calls on the same ticket are guaranteed to be executed in the order they are issued. Note that when issuing requests from //! multiple threads on the same ticket the order in which the requests are assigned to the ticket is not guaranteed. + //! + //! Most calls have a priority with values that range from 0 (highest priority) to 255 (lowest priority). The implementation of this + //! interface may choose to use priority lanes which doesn't guarantee that higher priority requests happen before lower priority + //! requests if they don't pass the priority lane threshold. Priority lanes and their thresholds are implementation specific and may + //! differ between platforms. Note that if a call happened on a ticket with lower priority followed by a one with a higher priority + //! the first lower priority call will still need to complete before the second higher priority call can be executed and the priority + //! of the first call will not be updated. class SpawnableEntitiesDefinition { public: @@ -106,49 +195,72 @@ namespace AzFramework virtual ~SpawnableEntitiesDefinition() = default; //! Spawn instances of all entities in the spawnable. - //! @param spawnable The Spawnable asset that will be used to create entity instances from. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. + //! @param priority The priority at which this call will be executed. //! @param completionCallback Optional callback that's called when spawning entities has completed. This can be called from //! a different thread than the one that made the function call. The returned list of entities contains all the newly //! created entities. - virtual void SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback = {}, + virtual void SpawnAllEntities( + EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) = 0; //! Spawn instances of some entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. + //! @param priority The priority at which this call will be executed. //! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from. //! @param completionCallback Optional callback that's called when spawning entities has completed. This can be called from //! a different thread than the one that made this function call. The returned list of entities contains all the newly //! created entities. - virtual void SpawnEntities(EntitySpawnTicket& ticket, AZStd::vector entityIndices, + virtual void SpawnEntities( + EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) = 0; //! Removes all entities in the provided list from the environment. //! @param ticket The ticket previously used to spawn entities with. + //! @param priority The priority at which this call will be executed. //! @param completionCallback Optional callback that's called when despawning entities has completed. This can be called from //! a different thread than the one that made this function call. - virtual void DespawnAllEntities(EntitySpawnTicket& ticket, EntityDespawnCallback completionCallback = {}) = 0; + virtual void DespawnAllEntities( + EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback = {}) = 0; //! Removes all entities in the provided list from the environment and reconstructs the entities from the provided spawnable. - //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. + //! @param ticket Holds the information on the entities to reload. + //! @param priority The priority at which this call will be executed. //! @param spawnable The spawnable that will replace the existing spawnable. Both need to have the same asset id. //! @param completionCallback Optional callback that's called when the entities have been reloaded. This can be called from //! a different thread than the one that made this function call. The returned list of entities contains all the replacement //! entities. - virtual void ReloadSpawnable(EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, + virtual void ReloadSpawnable( + EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, ReloadSpawnableCallback completionCallback = {}) = 0; //! List all entities that are spawned using this ticket. //! @param ticket Only the entities associated with this ticket will be listed. + //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to list the entities on. - virtual void ListEntities(EntitySpawnTicket& ticket, ListEntitiesCallback listCallback) = 0; + virtual void ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) = 0; + //! List all entities that are spawned using this ticket with their spawnable index. + //! Spawnables contain a flat list of entities, which are used as templates to spawn entities from. For every spawned entity + //! the index of the entity in the spawnable that was used as a template is stored. This version of ListEntities will return + //! both the entities and this index. The index can be used with SpawnEntities to create the same entities again. Note that + //! the same index may appear multiple times as there are no restriction on how many instance of a specific entity can be + //! created. + //! @param ticket Only the entities associated with this ticket will be listed. + //! @param priority The priority at which this call will be executed. + //! @param listCallback Required callback that will be called to list the entities and indices on. + virtual void ListIndicesAndEntities( + EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) = 0; //! Claim all entities that are spawned using this ticket. Ownership of the entities is transferred from the ticket to the //! caller through the callback. After this call the ticket will have no entities associated with it. The caller of //! this function will need to manage the entities after this call. //! @param ticket Only the entities associated with this ticket will be released. + //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to transfer the entities through. - virtual void ClaimEntities(EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback) = 0; + virtual void ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) = 0; //! Blocks until all operations made on the provided ticket before the barrier call have completed. - virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback) = 0; + //! @param ticket The ticket to monitor. + //! @param priority The priority at which this call will be executed. + //! @param completionCallback Required callback that will be called as soon as the barrier has been reached. + virtual void Barrier(EntitySpawnTicket& ticket, SpawnablePriority priority, BarrierCallback completionCallback) = 0; //! Register a handler for OnSpawned events. virtual void AddOnSpawnedHandler(AZ::Event>::Handler& handler) = 0; @@ -157,7 +269,7 @@ namespace AzFramework virtual void AddOnDespawnedHandler(AZ::Event>::Handler& handler) = 0; protected: - [[nodiscard]] virtual void* CreateTicket(AZ::Data::Asset&& spawnable) = 0; + [[nodiscard]] virtual AZStd::pair CreateTicket(AZ::Data::Asset&& spawnable) = 0; virtual void DestroyTicket(void* ticket) = 0; template diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 8045766686..7b767d2a72 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -10,9 +10,11 @@ * */ +#include #include #include #include +#include #include #include #include @@ -22,102 +24,122 @@ namespace AzFramework { - void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback, + template + void SpawnableEntitiesManager::QueueRequest(EntitySpawnTicket& ticket, SpawnablePriority priority, T&& request) + { + request.m_ticket = &GetTicketPayload(ticket); + Queue& queue = priority <= m_highPriorityThreshold ? m_highPriorityQueue : m_regularPriorityQueue; + { + AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex); + request.m_requestId = GetTicketPayload(ticket).m_nextRequestId++; + queue.m_pendingRequest.push(AZStd::move(request)); + } + } + + SpawnableEntitiesManager::SpawnableEntitiesManager() + { + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + AZ::u64 value = aznumeric_caster(m_highPriorityThreshold); + settingsRegistry->Get(value, "/O3DE/AzFramework/Spawnables/HighPriorityThreshold"); + m_highPriorityThreshold = aznumeric_cast(AZStd::clamp(value, 0llu, 255llu)); + } + } + + void SpawnableEntitiesManager::SpawnAllEntities( + EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback, EntitySpawnCallback completionCallback) { + AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnAllEntities hasn't been initialized."); + SpawnAllEntitiesCommand queueEntry; - queueEntry.m_ticket = &ticket; + queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_completionCallback = AZStd::move(completionCallback); queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback); - { - AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex); - queueEntry.m_ticketId = GetTicketPayload(ticket).m_nextTicketId++; - m_pendingRequestQueue.push(AZStd::move(queueEntry)); - } + QueueRequest(ticket, priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::SpawnEntities( - EntitySpawnTicket& ticket, AZStd::vector entityIndices, + EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, EntityPreInsertionCallback preInsertionCallback, EntitySpawnCallback completionCallback) { + AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnEntities hasn't been initialized."); + SpawnEntitiesCommand queueEntry; - queueEntry.m_ticket = &ticket; + queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_entityIndices = AZStd::move(entityIndices); queueEntry.m_completionCallback = AZStd::move(completionCallback); queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback); - { - AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex); - queueEntry.m_ticketId = GetTicketPayload(ticket).m_nextTicketId++; - m_pendingRequestQueue.push(AZStd::move(queueEntry)); - } + QueueRequest(ticket, priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::DespawnAllEntities(EntitySpawnTicket& ticket, EntityDespawnCallback completionCallback) + void SpawnableEntitiesManager::DespawnAllEntities( + EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback) { + AZ_Assert(ticket.IsValid(), "Ticket provided to DespawnAllEntities hasn't been initialized."); + DespawnAllEntitiesCommand queueEntry; - queueEntry.m_ticket = &ticket; + queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_completionCallback = AZStd::move(completionCallback); - { - AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex); - queueEntry.m_ticketId = GetTicketPayload(ticket).m_nextTicketId++; - m_pendingRequestQueue.push(AZStd::move(queueEntry)); - } + QueueRequest(ticket, priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::ReloadSpawnable(EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, + void SpawnableEntitiesManager::ReloadSpawnable( + EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, ReloadSpawnableCallback completionCallback) { + AZ_Assert(ticket.IsValid(), "Ticket provided to ReloadSpawnable hasn't been initialized."); + ReloadSpawnableCommand queueEntry; - queueEntry.m_ticket = &ticket; + queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_spawnable = AZStd::move(spawnable); queueEntry.m_completionCallback = AZStd::move(completionCallback); - { - AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex); - queueEntry.m_ticketId = GetTicketPayload(ticket).m_nextTicketId++; - m_pendingRequestQueue.push(AZStd::move(queueEntry)); - } + QueueRequest(ticket, priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::ListEntities(EntitySpawnTicket& ticket, ListEntitiesCallback listCallback) + void SpawnableEntitiesManager::ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) { AZ_Assert(listCallback, "ListEntities called on spawnable entities without a valid callback to use."); + AZ_Assert(ticket.IsValid(), "Ticket provided to ListEntities hasn't been initialized."); ListEntitiesCommand queueEntry; - queueEntry.m_ticket = &ticket; + queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_listCallback = AZStd::move(listCallback); - { - AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex); - queueEntry.m_ticketId = GetTicketPayload(ticket).m_nextTicketId++; - m_pendingRequestQueue.push(AZStd::move(queueEntry)); - } + QueueRequest(ticket, priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::ClaimEntities(EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback) + void SpawnableEntitiesManager::ListIndicesAndEntities( + EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) + { + AZ_Assert(listCallback, "ListEntities called on spawnable entities without a valid callback to use."); + AZ_Assert(ticket.IsValid(), "Ticket provided to ListEntities hasn't been initialized."); + + ListIndicesEntitiesCommand queueEntry; + queueEntry.m_ticketId = ticket.GetId(); + queueEntry.m_listCallback = AZStd::move(listCallback); + QueueRequest(ticket, priority, AZStd::move(queueEntry)); + } + + void SpawnableEntitiesManager::ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) { AZ_Assert(listCallback, "ClaimEntities called on spawnable entities without a valid callback to use."); + AZ_Assert(ticket.IsValid(), "Ticket provided to ClaimEntities hasn't been initialized."); ClaimEntitiesCommand queueEntry; - queueEntry.m_ticket = &ticket; + queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_listCallback = AZStd::move(listCallback); - { - AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex); - queueEntry.m_ticketId = GetTicketPayload(ticket).m_nextTicketId++; - m_pendingRequestQueue.push(AZStd::move(queueEntry)); - } + QueueRequest(ticket, priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback) + void SpawnableEntitiesManager::Barrier(EntitySpawnTicket& ticket, SpawnablePriority priority, BarrierCallback completionCallback) { AZ_Assert(completionCallback, "Barrier on spawnable entities called without a valid callback to use."); + AZ_Assert(ticket.IsValid(), "Ticket provided to Barrier hasn't been initialized."); BarrierCommand queueEntry; - queueEntry.m_ticket = &ticket; + queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_completionCallback = AZStd::move(completionCallback); - { - AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex); - queueEntry.m_ticketId = GetTicketPayload(ticket).m_nextTicketId++; - m_pendingRequestQueue.push(AZStd::move(queueEntry)); - } + QueueRequest(ticket, priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::AddOnSpawnedHandler(AZ::Event>::Handler& handler) @@ -130,34 +152,54 @@ namespace AzFramework handler.Connect(m_onDespawnedEvent); } - auto SpawnableEntitiesManager::ProcessQueue() -> CommandQueueStatus + auto SpawnableEntitiesManager::ProcessQueue(CommandQueuePriority priority) -> CommandQueueStatus + { + CommandQueueStatus result = CommandQueueStatus::NoCommandsLeft; + if ((priority & CommandQueuePriority::High) == CommandQueuePriority::High) + { + if (ProcessQueue(m_highPriorityQueue) == CommandQueueStatus::HasCommandsLeft) + { + result = CommandQueueStatus::HasCommandsLeft; + } + } + if ((priority & CommandQueuePriority::Regular) == CommandQueuePriority::Regular) + { + if (ProcessQueue(m_regularPriorityQueue) == CommandQueueStatus::HasCommandsLeft) + { + result = CommandQueueStatus::HasCommandsLeft; + } + } + return result; + } + + auto SpawnableEntitiesManager::ProcessQueue(Queue& queue) -> CommandQueueStatus { AZStd::queue pendingRequestQueue; { - AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex); - m_pendingRequestQueue.swap(pendingRequestQueue); + AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex); + queue.m_pendingRequest.swap(pendingRequestQueue); } - if (!pendingRequestQueue.empty() || !m_delayedQueue.empty()) + if (!pendingRequestQueue.empty() || !queue.m_delayed.empty()) { AZ::SerializeContext* serializeContext = nullptr; AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); AZ_Assert(serializeContext, "Failed to retrieve serialization context."); // Only process the requests that are currently in this queue, not the ones that could be re-added if they still can't complete. - size_t delayedSize = m_delayedQueue.size(); + size_t delayedSize = queue.m_delayed.size(); for (size_t i = 0; i < delayedSize; ++i) { - Requests& request = m_delayedQueue.front(); + Requests& request = queue.m_delayed.front(); bool result = AZStd::visit([this, serializeContext](auto&& args) -> bool { return ProcessRequest(args, *serializeContext); }, request); if (!result) { - m_delayedQueue.emplace_back(AZStd::move(request)); + queue.m_delayed.emplace_back(AZStd::move(request)); } - m_delayedQueue.pop_front(); + queue.m_delayed.pop_front(); } do @@ -171,7 +213,7 @@ namespace AzFramework }, request); if (!result) { - m_delayedQueue.emplace_back(AZStd::move(request)); + queue.m_delayed.emplace_back(AZStd::move(request)); } pendingRequestQueue.pop(); } @@ -179,20 +221,22 @@ namespace AzFramework // Spawning entities can result in more entities being queued to spawn. Repeat spawning until the queue is // empty to avoid a chain of entity spawning getting dragged out over multiple frames. { - AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex); - m_pendingRequestQueue.swap(pendingRequestQueue); + AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex); + queue.m_pendingRequest.swap(pendingRequestQueue); } } while (!pendingRequestQueue.empty()); } - return m_delayedQueue.empty() ? CommandQueueStatus::NoCommandLeft : CommandQueueStatus::HasCommandsLeft; + return queue.m_delayed.empty() ? CommandQueueStatus::NoCommandsLeft : CommandQueueStatus::HasCommandsLeft; } - void* SpawnableEntitiesManager::CreateTicket(AZ::Data::Asset&& spawnable) + AZStd::pair SpawnableEntitiesManager::CreateTicket(AZ::Data::Asset&& spawnable) { + static AZStd::atomic_uint64_t idCounter { 1 }; + auto result = aznew Ticket(); result->m_spawnable = AZStd::move(spawnable); - return result; + return AZStd::make_pair(idCounter++, result); } void SpawnableEntitiesManager::DestroyTicket(void* ticket) @@ -200,9 +244,9 @@ namespace AzFramework DestroyTicketCommand queueEntry; queueEntry.m_ticket = reinterpret_cast(ticket); { - AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex); - queueEntry.m_ticketId = reinterpret_cast(ticket)->m_nextTicketId++; - m_pendingRequestQueue.push(AZStd::move(queueEntry)); + AZStd::scoped_lock queueLock(m_regularPriorityQueue.m_pendingRequestMutex); + queueEntry.m_requestId = reinterpret_cast(ticket)->m_nextRequestId++; + m_regularPriorityQueue.m_pendingRequest.push(AZStd::move(queueEntry)); } } @@ -225,8 +269,8 @@ namespace AzFramework bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext) { - Ticket& ticket = GetTicketPayload(*request.m_ticket); - if (ticket.m_spawnable.IsReady() && request.m_ticketId == ticket.m_currentTicketId) + Ticket& ticket = *request.m_ticket; + if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) { AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; @@ -274,7 +318,7 @@ namespace AzFramework // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. if (request.m_preInsertionCallback) { - request.m_preInsertionCallback(*request.m_ticket, SpawnableEntityContainerView( + request.m_preInsertionCallback(request.m_ticketId, SpawnableEntityContainerView( ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); } @@ -288,13 +332,13 @@ namespace AzFramework // Let other systems know about newly spawned entities for any post-processing after adding to the scene/game context. if (request.m_completionCallback) { - request.m_completionCallback(*request.m_ticket, SpawnableConstEntityContainerView( + request.m_completionCallback(request.m_ticketId, SpawnableConstEntityContainerView( ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); } m_onSpawnedEvent.Signal(ticket.m_spawnable); - ticket.m_currentTicketId++; + ticket.m_currentRequestId++; return true; } else @@ -305,8 +349,8 @@ namespace AzFramework bool SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext) { - Ticket& ticket = GetTicketPayload(*request.m_ticket); - if (ticket.m_spawnable.IsReady() && request.m_ticketId == ticket.m_currentTicketId) + Ticket& ticket = *request.m_ticket; + if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) { AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; @@ -341,9 +385,7 @@ namespace AzFramework // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. if (request.m_preInsertionCallback) { - request.m_preInsertionCallback( - *request.m_ticket, - SpawnableEntityContainerView( + request.m_preInsertionCallback(request.m_ticketId, SpawnableEntityContainerView( ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); } @@ -356,13 +398,13 @@ namespace AzFramework if (request.m_completionCallback) { - request.m_completionCallback(*request.m_ticket, SpawnableConstEntityContainerView( + request.m_completionCallback(request.m_ticketId, SpawnableConstEntityContainerView( ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); } m_onSpawnedEvent.Signal(ticket.m_spawnable); - ticket.m_currentTicketId++; + ticket.m_currentRequestId++; return true; } else @@ -374,8 +416,8 @@ namespace AzFramework bool SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) { - Ticket& ticket = GetTicketPayload(*request.m_ticket); - if (request.m_ticketId == ticket.m_currentTicketId) + Ticket& ticket = *request.m_ticket; + if (request.m_requestId == ticket.m_currentRequestId) { for (AZ::Entity* entity : ticket.m_spawnedEntities) { @@ -391,12 +433,12 @@ namespace AzFramework if (request.m_completionCallback) { - request.m_completionCallback(*request.m_ticket); + request.m_completionCallback(request.m_ticketId); } m_onDespawnedEvent.Signal(ticket.m_spawnable); - ticket.m_currentTicketId++; + ticket.m_currentRequestId++; return true; } else @@ -407,11 +449,11 @@ namespace AzFramework bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request, AZ::SerializeContext& serializeContext) { - Ticket& ticket = GetTicketPayload(*request.m_ticket); + Ticket& ticket = *request.m_ticket; AZ_Assert(ticket.m_spawnable.GetId() == request.m_spawnable.GetId(), "Spawnable is being reloaded, but the provided spawnable has a different asset id. " "This will likely result in unexpected entities being created."); - if (ticket.m_spawnable.IsReady() && request.m_ticketId == ticket.m_currentTicketId) + if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) { // Delete the original entities. for (AZ::Entity* entity : ticket.m_spawnedEntities) @@ -467,11 +509,11 @@ namespace AzFramework if (request.m_completionCallback) { - request.m_completionCallback(*request.m_ticket, SpawnableConstEntityContainerView( + request.m_completionCallback(request.m_ticketId, SpawnableConstEntityContainerView( ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntities.end())); } - ticket.m_currentTicketId++; + ticket.m_currentRequestId++; m_onSpawnedEvent.Signal(ticket.m_spawnable); @@ -485,12 +527,31 @@ namespace AzFramework bool SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) { - Ticket& ticket = GetTicketPayload(*request.m_ticket); - if (request.m_ticketId == ticket.m_currentTicketId) + Ticket& ticket = *request.m_ticket; + if (request.m_requestId == ticket.m_currentRequestId) { - request.m_listCallback(*request.m_ticket, SpawnableConstEntityContainerView( + request.m_listCallback(request.m_ticketId, SpawnableConstEntityContainerView( ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntities.end())); - ticket.m_currentTicketId++; + ticket.m_currentRequestId++; + return true; + } + else + { + return false; + } + } + + bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + { + Ticket& ticket = *request.m_ticket; + if (request.m_requestId == ticket.m_currentRequestId) + { + AZ_Assert( + ticket.m_spawnedEntities.size() == ticket.m_spawnedEntityIndices.size(), + "Entities and indices on spawnable ticket have gone out of sync."); + request.m_listCallback(request.m_ticketId, SpawnableConstIndexEntityContainerView( + ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntityIndices.begin(), ticket.m_spawnedEntities.size())); + ticket.m_currentRequestId++; return true; } else @@ -501,16 +562,16 @@ namespace AzFramework bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) { - Ticket& ticket = GetTicketPayload(*request.m_ticket); - if (request.m_ticketId == ticket.m_currentTicketId) + Ticket& ticket = *request.m_ticket; + if (request.m_requestId == ticket.m_currentRequestId) { - request.m_listCallback(*request.m_ticket, SpawnableEntityContainerView( + request.m_listCallback(request.m_ticketId, SpawnableEntityContainerView( ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntities.end())); ticket.m_spawnedEntities.clear(); ticket.m_spawnedEntityIndices.clear(); - ticket.m_currentTicketId++; + ticket.m_currentRequestId++; return true; } else @@ -521,15 +582,15 @@ namespace AzFramework bool SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) { - Ticket& ticket = GetTicketPayload(*request.m_ticket); - if (request.m_ticketId == ticket.m_currentTicketId) + Ticket& ticket = *request.m_ticket; + if (request.m_requestId == ticket.m_currentRequestId) { if (request.m_completionCallback) { - request.m_completionCallback(*request.m_ticket); + request.m_completionCallback(request.m_ticketId); } - ticket.m_currentTicketId++; + ticket.m_currentRequestId++; return true; } else @@ -540,7 +601,7 @@ namespace AzFramework bool SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) { - if (request.m_ticketId == request.m_ticket->m_currentTicketId) + if (request.m_requestId == request.m_ticket->m_currentRequestId) { for (AZ::Entity* entity : request.m_ticket->m_spawnedEntities) { @@ -559,24 +620,4 @@ namespace AzFramework return false; } } - - bool SpawnableEntitiesManager::IsEqualTicket(const EntitySpawnTicket* lhs, const EntitySpawnTicket* rhs) - { - return GetTicketPayload(lhs) == GetTicketPayload(rhs); - } - - bool SpawnableEntitiesManager::IsEqualTicket(const Ticket* lhs, const EntitySpawnTicket* rhs) - { - return lhs == GetTicketPayload(rhs); - } - - bool SpawnableEntitiesManager::IsEqualTicket(const EntitySpawnTicket* lhs, const Ticket* rhs) - { - return GetTicketPayload(lhs) == rhs; - } - - bool SpawnableEntitiesManager::IsEqualTicket(const Ticket* lhs, const Ticket* rhs) - { - return lhs = rhs; - } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index e20f58ac76..afffdab8b5 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -29,38 +29,54 @@ namespace AZ namespace AzFramework { - using EntityIdMap = AZStd::unordered_map; - class SpawnableEntitiesManager : public SpawnableEntitiesInterface::Registrar { public: AZ_RTTI(AzFramework::SpawnableEntitiesManager, "{6E14333F-128C-464C-94CA-A63B05A5E51C}"); + AZ_CLASS_ALLOCATOR(SpawnableEntitiesManager, AZ::SystemAllocator, 0); + + using EntityIdMap = AZStd::unordered_map; enum class CommandQueueStatus : bool { HasCommandsLeft, - NoCommandLeft + NoCommandsLeft }; + enum class CommandQueuePriority + { + High = 1 << 0, + Regular = 1 << 1 + }; + + SpawnableEntitiesManager(); ~SpawnableEntitiesManager() override = default; // // The following functions are thread safe // - void SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) override; - void SpawnEntities(EntitySpawnTicket& ticket, AZStd::vector entityIndices, EntityPreInsertionCallback preInsertionCallback = {}, + void SpawnAllEntities( + EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback = {}, + EntitySpawnCallback completionCallback = {}) override; + void SpawnEntities( + EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, + EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) override; - void DespawnAllEntities(EntitySpawnTicket& ticket, EntityDespawnCallback completionCallback = {}) override; + void DespawnAllEntities( + EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback = {}) override; - void ReloadSpawnable(EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, + void ReloadSpawnable( + EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, ReloadSpawnableCallback completionCallback = {}) override; - void ListEntities(EntitySpawnTicket& ticket, ListEntitiesCallback listCallback) override; - void ClaimEntities(EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback) override; + void ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) override; + void ListIndicesAndEntities( + EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) override; + void ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) override; - void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback) override; + void Barrier(EntitySpawnTicket& spawnInfo, SpawnablePriority priority, BarrierCallback completionCallback) override; void AddOnSpawnedHandler(AZ::Event>::Handler& handler) override; void AddOnDespawnedHandler(AZ::Event>::Handler& handler) override; @@ -69,13 +85,9 @@ namespace AzFramework // The following function is thread safe but intended to be run from the main thread. // - CommandQueueStatus ProcessQueue(); + CommandQueueStatus ProcessQueue(CommandQueuePriority priority); protected: - void* CreateTicket(AZ::Data::Asset&& spawnable) override; - void DestroyTicket(void* ticket) override; - - private: struct Ticket { AZ_CLASS_ALLOCATOR(Ticket, AZ::ThreadPoolAllocator, 0); @@ -84,8 +96,8 @@ namespace AzFramework AZStd::vector m_spawnedEntities; AZStd::vector m_spawnedEntityIndices; AZ::Data::Asset m_spawnable; - uint32_t m_nextTicketId{ 0 }; //!< Next id for this ticket. - uint32_t m_currentTicketId{ 0 }; //!< The id for the command that should be executed. + uint32_t m_nextRequestId{ 0 }; //!< Next id for this ticket. + uint32_t m_currentRequestId { 0 }; //!< The id for the command that should be executed. bool m_loadAll{ true }; }; @@ -93,56 +105,85 @@ namespace AzFramework { EntitySpawnCallback m_completionCallback; EntityPreInsertionCallback m_preInsertionCallback; - EntitySpawnTicket* m_ticket; - uint32_t m_ticketId; + Ticket* m_ticket; + EntitySpawnTicket::Id m_ticketId; + uint32_t m_requestId; }; struct SpawnEntitiesCommand { AZStd::vector m_entityIndices; EntitySpawnCallback m_completionCallback; EntityPreInsertionCallback m_preInsertionCallback; - EntitySpawnTicket* m_ticket; - uint32_t m_ticketId; + Ticket* m_ticket; + EntitySpawnTicket::Id m_ticketId; + uint32_t m_requestId; }; struct DespawnAllEntitiesCommand { EntityDespawnCallback m_completionCallback; - EntitySpawnTicket* m_ticket; - uint32_t m_ticketId; + Ticket* m_ticket; + EntitySpawnTicket::Id m_ticketId; + uint32_t m_requestId; }; struct ReloadSpawnableCommand { AZ::Data::Asset m_spawnable; ReloadSpawnableCallback m_completionCallback; - EntitySpawnTicket* m_ticket; - uint32_t m_ticketId; + Ticket* m_ticket; + EntitySpawnTicket::Id m_ticketId; + uint32_t m_requestId; }; struct ListEntitiesCommand { ListEntitiesCallback m_listCallback; - EntitySpawnTicket* m_ticket; - uint32_t m_ticketId; + Ticket* m_ticket; + EntitySpawnTicket::Id m_ticketId; + uint32_t m_requestId; + }; + struct ListIndicesEntitiesCommand + { + ListIndicesEntitiesCallback m_listCallback; + Ticket* m_ticket; + EntitySpawnTicket::Id m_ticketId; + uint32_t m_requestId; }; struct ClaimEntitiesCommand { ClaimEntitiesCallback m_listCallback; - EntitySpawnTicket* m_ticket; - uint32_t m_ticketId; + Ticket* m_ticket; + EntitySpawnTicket::Id m_ticketId; + uint32_t m_requestId; }; struct BarrierCommand { BarrierCallback m_completionCallback; - EntitySpawnTicket* m_ticket; - uint32_t m_ticketId; + Ticket* m_ticket; + EntitySpawnTicket::Id m_ticketId; + uint32_t m_requestId; }; struct DestroyTicketCommand { Ticket* m_ticket; - uint32_t m_ticketId; + uint32_t m_requestId; }; - using Requests = AZStd::variant; + using Requests = AZStd::variant< + SpawnAllEntitiesCommand, SpawnEntitiesCommand, DespawnAllEntitiesCommand, ReloadSpawnableCommand, ListEntitiesCommand, + ListIndicesEntitiesCommand, ClaimEntitiesCommand, BarrierCommand, DestroyTicketCommand>; + + struct Queue + { + AZStd::deque m_delayed; //!< Requests that were processed before, but couldn't be completed. + AZStd::queue m_pendingRequest; //!< Requests waiting to be processed for the first time. + AZStd::mutex m_pendingRequestMutex; + }; + + template + void QueueRequest(EntitySpawnTicket& ticket, SpawnablePriority priority, T&& request); + AZStd::pair CreateTicket(AZ::Data::Asset&& spawnable) override; + void DestroyTicket(void* ticket) override; + + CommandQueueStatus ProcessQueue(Queue& queue); AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, AZ::SerializeContext& serializeContext); @@ -155,20 +196,23 @@ namespace AzFramework bool ProcessRequest(DespawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(ReloadSpawnableCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(ListEntitiesCommand& request, AZ::SerializeContext& serializeContext); + bool ProcessRequest(ListIndicesEntitiesCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(ClaimEntitiesCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(BarrierCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(DestroyTicketCommand& request, AZ::SerializeContext& serializeContext); - [[nodiscard]] static bool IsEqualTicket(const EntitySpawnTicket* lhs, const EntitySpawnTicket* rhs); - [[nodiscard]] static bool IsEqualTicket(const Ticket* lhs, const EntitySpawnTicket* rhs); - [[nodiscard]] static bool IsEqualTicket(const EntitySpawnTicket* lhs, const Ticket* rhs); - [[nodiscard]] static bool IsEqualTicket(const Ticket* lhs, const Ticket* rhs); - - AZStd::deque m_delayedQueue; //!< Requests that were processed before, but couldn't be completed. - AZStd::queue m_pendingRequestQueue; - AZStd::mutex m_pendingRequestQueueMutex; + Queue m_highPriorityQueue; + Queue m_regularPriorityQueue; AZ::Event> m_onSpawnedEvent; AZ::Event> m_onDespawnedEvent; + + //! The threshold used to determine if a request goes in the regular (if bigger than the value) or high priority queue (if smaller + //! or equal to this value). The starting value of 64 is chosen as it's between default values SpawnablePriority_High and + //! SpawnablePriority_Default which gives users a bit of room to fine tune the priorities as this value can be configured + //! through the Settings Registry under the key "/O3DE/AzFramework/Spawnables/HighPriorityThreshold". + SpawnablePriority m_highPriorityThreshold { 64 }; }; + + AZ_DEFINE_ENUM_BITWISE_OPERATORS(AzFramework::SpawnableEntitiesManager::CommandQueuePriority); } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp index 262f006f15..300ff1441e 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp @@ -48,10 +48,23 @@ namespace AzFramework void SpawnableSystemComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) { - m_entitiesManager.ProcessQueue(); + m_entitiesManager.ProcessQueue( + SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular); RootSpawnableNotificationBus::ExecuteQueuedEvents(); } + int SpawnableSystemComponent::GetTickOrder() + { + return AZ::ComponentTickBus::TICK_GAME; + } + + void SpawnableSystemComponent::OnSystemTick() + { + // Handle only high priority spawning events such as those created from network. These need to happen even if the client + // doesn't have focus to avoid time-out issues for instance. + m_entitiesManager.ProcessQueue(SpawnableEntitiesManager::CommandQueuePriority::High); + } + void SpawnableSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile) { if (!m_catalogAvailable) @@ -168,7 +181,8 @@ namespace AzFramework SpawnableEntitiesManager::CommandQueueStatus queueStatus; do { - queueStatus = m_entitiesManager.ProcessQueue(); + queueStatus = m_entitiesManager.ProcessQueue( + SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular); } while (queueStatus == SpawnableEntitiesManager::CommandQueueStatus::HasCommandsLeft); } diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h index b29fbea5e4..1549b86385 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h @@ -28,6 +28,7 @@ namespace AzFramework class SpawnableSystemComponent : public AZ::Component , public AZ::TickBus::Handler + , public AZ::SystemTickBus::Handler , public AssetCatalogEventBus::Handler , public RootSpawnableInterface::Registrar , public RootSpawnableNotificationBus::Handler @@ -58,6 +59,13 @@ namespace AzFramework // void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + int GetTickOrder() override; + + // + // SystemTickBus + // + + void OnSystemTick() override; // // AssetCatalogEventBus diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index e4833ccb3c..559f7ce460 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -29,7 +29,7 @@ namespace AzFramework AZ_CVAR(float, ed_cameraSystemOrbitDollyScrollSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(float, ed_cameraSystemOrbitDollyCursorSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(float, ed_cameraSystemScrollTranslateSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); - AZ_CVAR(float, ed_cameraSystemMinOrbitDistance, 6.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); + AZ_CVAR(float, ed_cameraSystemMinOrbitDistance, 10.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(float, ed_cameraSystemMaxOrbitDistance, 50.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(float, ed_cameraSystemLookSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(float, ed_cameraSystemTranslateSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); @@ -37,7 +37,6 @@ namespace AzFramework AZ_CVAR(float, ed_cameraSystemPanSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(bool, ed_cameraSystemPanInvertX, true, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(bool, ed_cameraSystemPanInvertY, true, nullptr, AZ::ConsoleFunctorFlags::Null, ""); - AZ_CVAR(float, ed_cameraSystemLookDeadzone, 2.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR( AZ::CVarFixedString, ed_cameraSystemTranslateForwardKey, "keyboard_key_alphanumeric_W", nullptr, AZ::ConsoleFunctorFlags::Null, ""); @@ -144,7 +143,7 @@ namespace AzFramework z = AZStd::atan2(-orientation.GetElement(1, 2), orientation.GetElement(1, 1)); } - return {x, y, z}; + return { x, y, z }; } void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform) @@ -179,7 +178,7 @@ namespace AzFramework { const auto nextCamera = m_cameras.StepCamera(targetCamera, m_motionDelta, m_scrollDelta, deltaTime); - m_motionDelta = ScreenVector{0, 0}; + m_motionDelta = ScreenVector{ 0, 0 }; m_scrollDelta = 0.0f; return nextCamera; @@ -213,7 +212,10 @@ namespace AzFramework auto& cameraInput = m_idleCameraInputs[i]; const bool canBegin = cameraInput->Beginning() && AZStd::all_of(m_activeCameraInputs.cbegin(), m_activeCameraInputs.cend(), - [](const auto& input) { return !input->Exclusive(); }) && + [](const auto& input) + { + return !input->Exclusive(); + }) && (!cameraInput->Exclusive() || (cameraInput->Exclusive() && m_activeCameraInputs.empty())); if (canBegin) @@ -231,7 +233,8 @@ namespace AzFramework const Camera nextCamera = AZStd::accumulate( AZStd::begin(m_activeCameraInputs), AZStd::end(m_activeCameraInputs), targetCamera, - [cursorDelta, scrollDelta, deltaTime](Camera acc, auto& camera) { + [cursorDelta, scrollDelta, deltaTime](Camera acc, auto& camera) + { acc = camera->StepCamera(acc, cursorDelta, scrollDelta, deltaTime); return acc; }); @@ -284,7 +287,8 @@ namespace AzFramework bool RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta) { - const ClickDetector::ClickEvent clickEvent = [&event, this] { + const ClickDetector::ClickEvent clickEvent = [&event, this] + { if (const auto& input = AZStd::get_if(&event)) { if (input->m_channelId == m_rotateChannelId) @@ -330,7 +334,10 @@ namespace AzFramework nextCamera.m_pitch -= float(cursorDelta.m_y) * ed_cameraSystemRotateSpeed; nextCamera.m_yaw -= float(cursorDelta.m_x) * ed_cameraSystemRotateSpeed; - const auto clampRotation = [](const float angle) { return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); }; + const auto clampRotation = [](const float angle) + { + return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); + }; nextCamera.m_yaw = clampRotation(nextCamera.m_yaw); // clamp pitch to be +-90 degrees @@ -377,9 +384,10 @@ namespace AzFramework const auto deltaPanX = float(cursorDelta.m_x) * panAxes.m_horizontalAxis * ed_cameraSystemPanSpeed; const auto deltaPanY = float(cursorDelta.m_y) * panAxes.m_verticalAxis * ed_cameraSystemPanSpeed; - const auto inv = [](const bool invert) { - constexpr float Dir[] = {1.0f, -1.0f}; - return Dir[static_cast(invert)]; + const auto inv = [](const bool invert) + { + constexpr float Dir[] = { 1.0f, -1.0f }; + return Dir[aznumeric_cast(invert)]; }; nextCamera.m_lookAt += deltaPanX * inv(ed_cameraSystemPanInvertX); @@ -475,7 +483,8 @@ namespace AzFramework const auto axisY = translationBasis.GetBasisY(); const auto axisZ = translationBasis.GetBasisZ(); - const float speed = [boost = m_boost]() { + const float speed = [boost = m_boost]() + { return ed_cameraSystemTranslateSpeed * (boost ? ed_cameraSystemBoostMultiplier : 1.0f); }(); @@ -555,10 +564,12 @@ namespace AzFramework if (Beginning()) { - const auto hasLookAt = [&nextCamera, &targetCamera, &lookAtFn = m_lookAtFn] { + const auto hasLookAt = [&nextCamera, &targetCamera, &lookAtFn = m_lookAtFn] + { if (lookAtFn) { - if (const auto lookAt = lookAtFn()) + // pass through the camera's position and look vector for use in the lookAt function + if (const auto lookAt = lookAtFn(targetCamera.Translation(), targetCamera.Rotation().GetBasisY())) { auto transform = AZ::Transform::CreateLookAt(targetCamera.m_lookAt, *lookAt); nextCamera.m_lookDist = -lookAt->GetDistance(targetCamera.m_lookAt); @@ -692,14 +703,20 @@ namespace AzFramework Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const float deltaTime) { - const auto clamp_rotation = [](const float angle) { return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); }; + const auto clamp_rotation = [](const float angle) + { + return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); + }; // keep yaw in 0 - 360 range float targetYaw = clamp_rotation(targetCamera.m_yaw); const float currentYaw = clamp_rotation(currentCamera.m_yaw); // return the sign of the float input (-1, 0, 1) - const auto sign = [](const float value) { return aznumeric_cast((0.0f < value) - (value < 0.0f)); }; + const auto sign = [](const float value) + { + return aznumeric_cast((0.0f < value) - (value < 0.0f)); + }; // ensure smooth transition when moving across 0 - 360 boundary const float yawDelta = targetYaw - currentYaw; @@ -727,26 +744,28 @@ namespace AzFramework const auto& inputChannelId = inputChannel.GetInputChannelId(); const auto& inputDeviceId = inputChannel.GetInputDevice().GetInputDeviceId(); - const bool wasMouseButton = - AZStd::any_of(InputDeviceMouse::Button::All.begin(), InputDeviceMouse::Button::All.end(), [inputChannelId](const auto& button) { + const bool wasMouseButton = AZStd::any_of( + InputDeviceMouse::Button::All.begin(), InputDeviceMouse::Button::All.end(), + [inputChannelId](const auto& button) + { return button == inputChannelId; }); if (inputChannelId == InputDeviceMouse::Movement::X) { - return HorizontalMotionEvent{(int)inputChannel.GetValue()}; + return HorizontalMotionEvent{ aznumeric_cast(inputChannel.GetValue()) }; } else if (inputChannelId == InputDeviceMouse::Movement::Y) { - return VerticalMotionEvent{(int)inputChannel.GetValue()}; + return VerticalMotionEvent{ aznumeric_cast(inputChannel.GetValue()) }; } else if (inputChannelId == InputDeviceMouse::Movement::Z) { - return ScrollEvent{inputChannel.GetValue()}; + return ScrollEvent{ inputChannel.GetValue() }; } else if (wasMouseButton || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId)) { - return DiscreteInputEvent{inputChannelId, inputChannel.GetState()}; + return DiscreteInputEvent{ inputChannelId, inputChannel.GetState() }; } return AZStd::monostate{}; diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h index ec70fc00de..a02f796899 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h @@ -34,9 +34,9 @@ namespace AzFramework AZ::Vector3 m_lookAt = AZ::Vector3::CreateZero(); //!< Position of camera when m_lookDist is zero, //!< or position of m_lookAt when m_lookDist is greater //!< than zero. - float m_yaw{0.0}; - float m_pitch{0.0}; - float m_lookDist{0.0}; //!< Zero gives first person free look, otherwise orbit about m_lookAt + float m_yaw{ 0.0 }; + float m_pitch{ 0.0 }; + float m_lookDist{ 0.0 }; //!< Zero gives first person free look, otherwise orbit about m_lookAt //! View camera transform (v in MVP). AZ::Transform View() const; @@ -195,7 +195,11 @@ namespace AzFramework inline bool Cameras::Exclusive() const { return AZStd::any_of( - m_activeCameraInputs.begin(), m_activeCameraInputs.end(), [](const auto& cameraInput) { return cameraInput->Exclusive(); }); + m_activeCameraInputs.begin(), m_activeCameraInputs.end(), + [](const auto& cameraInput) + { + return cameraInput->Exclusive(); + }); } //! Responsible for updating a series of cameras given various inputs. @@ -209,7 +213,7 @@ namespace AzFramework private: ScreenVector m_motionDelta; //!< The delta used for look/orbit/pan (rotation + translation) - two dimensional. - float m_scrollDelta = 0.0f; //!< The delta used for dolly/movement (translation) - one dimensional. + float m_scrollDelta = 0.0f; //!< The delta used for dolly/movement (translation) - one dimensional. }; class RotateCameraInput : public CameraInput @@ -237,7 +241,7 @@ namespace AzFramework inline PanAxes LookPan(const Camera& camera) { const AZ::Matrix3x3 orientation = camera.Rotation(); - return {orientation.GetBasisX(), orientation.GetBasisZ()}; + return { orientation.GetBasisX(), orientation.GetBasisZ() }; } inline PanAxes OrbitPan(const Camera& camera) @@ -245,12 +249,13 @@ namespace AzFramework const AZ::Matrix3x3 orientation = camera.Rotation(); const auto basisX = orientation.GetBasisX(); - const auto basisY = [&orientation] { + const auto basisY = [&orientation] + { const auto forward = orientation.GetBasisY(); return AZ::Vector3(forward.GetX(), forward.GetY(), 0.0f).GetNormalized(); }(); - return {basisX, basisY}; + return { basisX, basisY }; } class PanCameraInput : public CameraInput @@ -285,7 +290,8 @@ namespace AzFramework const AZ::Matrix3x3 orientation = camera.Rotation(); const auto basisX = orientation.GetBasisX(); - const auto basisY = [&orientation] { + const auto basisY = [&orientation] + { const auto forward = orientation.GetBasisY(); return AZ::Vector3(forward.GetX(), forward.GetY(), 0.0f).GetNormalized(); }(); @@ -398,7 +404,7 @@ namespace AzFramework class OrbitCameraInput : public CameraInput { public: - using LookAtFn = AZStd::function()>; + using LookAtFn = AZStd::function(const AZ::Vector3& position, const AZ::Vector3& direction)>; // CameraInput overrides ... bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp index 1ae3945bd6..d501271f59 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp @@ -35,7 +35,8 @@ namespace AzFramework::AssetSystem::Platform if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str())) { // Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure. - assetProcessorPath = AZ::IO::FixedMaxPath{engineRoot} / "bin" / AZ_BUILD_CONFIGURATION_TYPE / "AssetProcessor"; + assetProcessorPath = + AZ::IO::FixedMaxPath{engineRoot} / "bin" / AZ_TRAIT_OS_PLATFORM_NAME / AZ_BUILD_CONFIGURATION_TYPE / "AssetProcessor"; if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str())) { diff --git a/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp b/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp index 6f1f860932..890b6b32c3 100644 --- a/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp +++ b/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp @@ -34,7 +34,8 @@ namespace AzFramework::AssetSystem::Platform if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str())) { // Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure. - assetProcessorPath = AZ::IO::FixedMaxPath{engineRoot} / "bin" / AZ_BUILD_CONFIGURATION_TYPE / "AssetProcessor.app"; + assetProcessorPath = + AZ::IO::FixedMaxPath{engineRoot} / "bin" / AZ_TRAIT_OS_PLATFORM_NAME / AZ_BUILD_CONFIGURATION_TYPE / "AssetProcessor.app"; if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str())) { diff --git a/Code/Framework/AzFramework/Platform/Windows/AzFramework/Asset/AssetSystemComponentHelper_Windows.cpp b/Code/Framework/AzFramework/Platform/Windows/AzFramework/Asset/AssetSystemComponentHelper_Windows.cpp index b716778cf4..b0debfd3b0 100644 --- a/Code/Framework/AzFramework/Platform/Windows/AzFramework/Asset/AssetSystemComponentHelper_Windows.cpp +++ b/Code/Framework/AzFramework/Platform/Windows/AzFramework/Asset/AssetSystemComponentHelper_Windows.cpp @@ -71,7 +71,8 @@ namespace AzFramework::AssetSystem::Platform if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str())) { // Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure. - assetProcessorPath = AZ::IO::FixedMaxPath{engineRoot} / "bin" / AZ_BUILD_CONFIGURATION_TYPE / "AssetProcessor.exe"; + assetProcessorPath = + AZ::IO::FixedMaxPath{engineRoot} / "bin" / AZ_TRAIT_OS_PLATFORM_NAME / AZ_BUILD_CONFIGURATION_TYPE / "AssetProcessor.exe"; if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str())) { diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ViewportInteraction.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ViewportInteraction.h index 884562d7e8..28971dc779 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ViewportInteraction.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ViewportInteraction.h @@ -41,6 +41,7 @@ namespace AzManipulatorTestFramework AZStd::optional ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) override; AZStd::optional ViewportScreenToWorldRay( const AzFramework::ScreenPoint& screenPosition) override; + float DeviceScalingFactor() override; private: // ViewportInteractionRequestBus ... bool GridSnappingEnabled(); diff --git a/Code/Framework/AzManipulatorTestFramework/Source/ViewportInteraction.cpp b/Code/Framework/AzManipulatorTestFramework/Source/ViewportInteraction.cpp index ebef9dea30..7d32187a74 100644 --- a/Code/Framework/AzManipulatorTestFramework/Source/ViewportInteraction.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Source/ViewportInteraction.cpp @@ -127,4 +127,9 @@ namespace AzManipulatorTestFramework { return {}; } -} // namespace AzManipulatorTestFramework + + float ViewportInteraction::DeviceScalingFactor() + { + return 1.0f; + } +}// namespace AzManipulatorTestFramework diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/Menu.qss b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/Menu.qss index af9c675f23..7f48637cc8 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/Menu.qss +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/Menu.qss @@ -49,7 +49,7 @@ QMenu::right-arrow QMenu::icon { - right: 8px; + right: 20px; } QMenu::indicator:checked diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Local.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Local.svg new file mode 100644 index 0000000000..2017cabe21 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Local.svg @@ -0,0 +1,8 @@ + + + Icon / Local + + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Parent.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Parent.svg new file mode 100644 index 0000000000..c0b9580985 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/Parent.svg @@ -0,0 +1,8 @@ + + + Icon / Parent + + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/World.svg b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/World.svg new file mode 100644 index 0000000000..4d77775e3d --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/img/UI20/toolbar/World.svg @@ -0,0 +1,8 @@ + + + Icon / World + + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc b/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc index 8ea4755a24..00fa95d094 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc @@ -354,6 +354,7 @@ img/UI20/toolbar/Grid.svg img/UI20/toolbar/Lighting.svg img/UI20/toolbar/Load.svg + img/UI20/toolbar/Local.svg img/UI20/toolbar/Locked.svg img/UI20/toolbar/LUA.svg img/UI20/toolbar/Material.svg @@ -362,6 +363,7 @@ img/UI20/toolbar/Object_follow_terrain.svg img/UI20/toolbar/Object_height.svg img/UI20/toolbar/Object_list.svg + img/UI20/toolbar/Parent.svg img/UI20/toolbar/particle.svg img/UI20/toolbar/Play.svg img/UI20/toolbar/Redo.svg @@ -380,6 +382,7 @@ img/UI20/toolbar/undo.svg img/UI20/toolbar/Unlocked.svg img/UI20/toolbar/Vertex_snapping.svg + img/UI20/toolbar/World.svg img/UI20/toolbar/X_axis.svg img/UI20/toolbar/Y_axis.svg img/UI20/toolbar/Z_axis.svg diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Notifications/link.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Notifications/link.svg new file mode 100644 index 0000000000..dfd21d157f --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Notifications/link.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc b/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc index dbbf0e78e2..7b0c6530ab 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc @@ -13,5 +13,6 @@ Notifications/checkmark.svg Notifications/download.svg + Notifications/link.svg diff --git a/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h b/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h index 0090ce066b..855b4fe416 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h +++ b/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h @@ -39,4 +39,3 @@ #define AZ_TRAIT_DISABLE_FAILED_EMOTION_FX_EDITOR_TESTS true #define AZ_TRAIT_DISABLE_FAILED_METRICS_TESTS true -#define AZ_TRAIT_DISABLE_ASSET_JOB_PARALLEL_TESTS true diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h index 98e4c6b5eb..715de30bfa 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h @@ -60,10 +60,20 @@ namespace AzToolsFramework //! and is generally checked into source control. virtual const char* GetAbsoluteDevRootFolderPath() = 0; - /// Convert a full source path like "c:\\dev\gamename\\blah\\test.tga" into a relative product path. + /// Convert a full source path like "c:\\dev\\gamename\\blah\\test.tga" into a relative product path. /// asset paths never mention their alias and are relative to the asset cache root virtual bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& relativeProductPath) = 0; + /** Convert a source path like "c:\\dev\\gamename\\blah\\test.tga" into a relative source path, like "blah/test.tga". + * If no valid relative path could be created, the input source path will be returned in relativePath. + * @param sourcePath partial or full path to a source file. (The file doesn't need to exist) + * @param relativePath the output relative path for the source file, if a valid one could be created + * @param rootFilePath the root path that relativePath is relative to + * @return true if a valid relative path was created, false if it wasn't + */ + virtual bool GenerateRelativeSourcePath( + const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& rootFilePath) = 0; + /// Convert a relative asset path like "blah/test.tga" to a full source path path. /// Once the asset processor has finished building, this function is capable of handling even when the extension changes /// or when the source is in a different folder or in a different location (such as inside gems) @@ -110,14 +120,14 @@ namespace AzToolsFramework /** * Query to see if a specific asset platform is enabled - * @param platform the asset platform to check e.g. es3, ios, etc. + * @param platform the asset platform to check e.g. android, ios, etc. * @return true if enabled, false otherwise */ virtual bool IsAssetPlatformEnabled(const char* platform) = 0; /** * Get the total number of pending assets left to process for a specific asset platform - * @param platform the asset platform to check e.g. es3, ios, etc. + * @param platform the asset platform to check e.g. android, ios, etc. * @return -1 if the process fails, a positive number otherwise */ virtual int GetPendingAssetsForPlatform(const char* platform) = 0; @@ -302,7 +312,7 @@ namespace AzToolsFramework inline const char* GetHostAssetPlatform() { #if defined(AZ_PLATFORM_MAC) - return "osx_gl"; + return "mac"; #elif defined(AZ_PLATFORM_WINDOWS) return "pc"; #elif defined(AZ_PLATFORM_LINUX) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorViewportIconDisplayInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorViewportIconDisplayInterface.h new file mode 100644 index 0000000000..a2264b5058 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorViewportIconDisplayInterface.h @@ -0,0 +1,80 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include + +#include + +namespace AzToolsFramework +{ + //! An interface for loading simple icon assets and rendering them to screen on a per-viewport basis. + class EditorViewportIconDisplayInterface + { + public: + AZ_RTTI(EditorViewportIconDisplayInterface, "{D5190B58-2561-4F3F-B793-F1E7D454CDF2}"); + + using IconId = AZ::s32; + static constexpr IconId InvalidIconId = -1; + + enum class CoordinateSpace : AZ::u8 + { + ScreenSpace, + WorldSpace + }; + + //! These draw parameters control rendering for a single icon to a single viewport. + struct DrawParameters + { + //! The ViewportId to render to. + AzFramework::ViewportId m_viewport = AzFramework::InvalidViewportId; + //! The icon ID, retrieved from GetOrLoadIconForPath, to render to screen. + IconId m_icon = InvalidIconId; + //! The color, including opacity, to render the icon with. White will render the icon as opaque in its original color. + AZ::Color m_color = AZ::Colors::White; + //! The position to render the icon to, in world or screen space depending on m_positionSpace. + AZ::Vector3 m_position; + //! The coordinate system to use for m_position. + //! ScreenSpace will accept m_position in the form of [X, Y, Depth], where X & Y are screen coordinates in + //! pixels and Depth is a z-ordering depth value from 0.0f to 1.0f. + //! WorldSpace will accept a 3D vector in world space coordinates that will be translated back into screen + //! space when the icon is rendered. + CoordinateSpace m_positionSpace = CoordinateSpace::ScreenSpace; + //! The size to render the icon as, in pixels. + AZ::Vector2 m_size; + }; + + //! The current load status of an icon retrieved by GetOrLoadIconForPath. + enum class IconLoadStatus : AZ::u8 + { + Unloaded, + Loading, + Loaded, + Error + }; + + //! Draws an icon to a viewport given a set of draw parameters. + //! Requires an IconId retrieved from GetOrLoadIconForPath. + virtual void DrawIcon(const DrawParameters& drawParameters) = 0; + //! Retrieves a reusable IconId for an icon at a given path. + //! This will load the icon, if it has not already been loaded. + //! @param path should be a relative asset path to an icon image asset. + //! png and svg icons are currently supported. + virtual IconId GetOrLoadIconForPath(AZStd::string_view path) = 0; + //! Gets the current load status of an icon retrieved via GetOrLoadIconForPath. + virtual IconLoadStatus GetIconLoadStatus(IconId icon) = 0; + }; + + using EditorViewportIconDisplay = AZ::Interface; +} //namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h index 82fa3f94f5..72150b1b57 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h @@ -239,6 +239,11 @@ namespace AzToolsFramework */ virtual int RemoveDirtyEntity(AZ::EntityId target) = 0; + /*! + * Clears the dirty entity set. + */ + virtual void ClearDirtyEntities() = 0; + /*! * \return true if an undo/redo operation is in progress. */ diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index e77704c920..88057787bb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -1354,6 +1354,11 @@ namespace AzToolsFramework return static_cast(m_dirtyEntities.erase(entityId)); } + void ToolsApplication::ClearDirtyEntities() + { + m_dirtyEntities.clear(); + } + void ToolsApplication::UndoPressed() { if (m_undoStack) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h index 6c836ac888..bafced67bd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h @@ -85,6 +85,7 @@ namespace AzToolsFramework void AddDirtyEntity(AZ::EntityId entityId) override; int RemoveDirtyEntity(AZ::EntityId entityId) override; + void ClearDirtyEntities() override; bool IsDuringUndoRedo() override { return m_isDuringUndoRedo; } void UndoPressed() override; void RedoPressed() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp index 5529829913..4966d9cce9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp @@ -265,6 +265,30 @@ namespace AzToolsFramework return response.m_resolved; } + bool AssetSystemComponent::GenerateRelativeSourcePath( + const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& rootFilePath) + { + AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance(); + if (!engineConnection || !engineConnection->IsConnected()) + { + relativePath = sourcePath; + return false; + } + + AzFramework::AssetSystem::GenerateRelativeSourcePathRequest request(sourcePath); + AzFramework::AssetSystem::GenerateRelativeSourcePathResponse response; + if (!SendRequest(request, response)) + { + AZ_Error("Editor", false, "Failed to send GenerateRelativeSourcePath request for %s", sourcePath.c_str()); + relativePath = sourcePath; + return false; + } + + relativePath = response.m_relativeSourcePath; + rootFilePath = response.m_rootFolder; + return response.m_resolved; + } + bool AssetSystemComponent::GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullPath) { auto foundIt = m_assetSourceRelativePathToFullPathCache.find(relPath); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h index 399ee1ac9d..9d839c60f5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h @@ -63,6 +63,8 @@ namespace AzToolsFramework const char* GetAbsoluteDevGameFolderPath() override; const char* GetAbsoluteDevRootFolderPath() override; bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& outputPath) override; + bool GenerateRelativeSourcePath( + const AZStd::string& sourcePath, AZStd::string& outputPath, AZStd::string& watchFolder) override; bool GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullPath) override; bool GetAssetInfoById(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath) override; bool GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp index 706d8243e2..a59b29ddf8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp @@ -483,6 +483,8 @@ namespace AzToolsFramework } } + m_dirty = false; + AddRecentPath(targetFilePath); SetStatusText(Status::assetCreated); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h index 8412361657..32ea9db3da 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h @@ -56,5 +56,7 @@ namespace AzToolsFramework virtual void StartPlayInEditor() = 0; virtual void StopPlayInEditor() = 0; + + virtual void CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) = 0; }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp index 97c3041de6..7fd11ff9bf 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp @@ -57,7 +57,6 @@ namespace AzToolsFramework "Couldn't get prefab loader interface, it's a requirement for PrefabEntityOwnership system to work"); m_rootInstance = AZStd::unique_ptr(m_prefabSystemComponent->CreatePrefab({}, {}, "NewLevel.prefab")); - m_sliceOwnershipService.BusConnect(m_entityContextId); m_sliceOwnershipService.m_shouldAssertForLegacySlicesUsage = m_shouldAssertForLegacySlicesUsage; m_editorSliceOwnershipService.BusConnect(); @@ -91,14 +90,17 @@ namespace AzToolsFramework void PrefabEditorEntityOwnershipService::Reset() { - Prefab::TemplateId templateId = m_rootInstance->GetTemplateId(); - if (templateId != Prefab::InvalidTemplateId) + if (m_rootInstance) { - m_rootInstance->SetTemplateId(Prefab::InvalidTemplateId); - m_prefabSystemComponent->RemoveTemplate(templateId); + Prefab::TemplateId templateId = m_rootInstance->GetTemplateId(); + if (templateId != Prefab::InvalidTemplateId) + { + m_rootInstance->SetTemplateId(Prefab::InvalidTemplateId); + m_prefabSystemComponent->RemoveTemplate(templateId); + } + m_rootInstance->Reset(); + m_rootInstance->SetContainerEntityName("Level"); } - m_rootInstance->Reset(); - m_rootInstance->SetContainerEntityName("Level"); AzFramework::EntityOwnershipServiceNotificationBus::Event( m_entityContextId, &AzFramework::EntityOwnershipServiceNotificationBus::Events::OnEntityOwnershipServiceReset); @@ -202,7 +204,7 @@ namespace AzToolsFramework } m_rootInstance->SetTemplateId(templateId); - m_rootInstance->SetTemplateSourcePath(m_loaderInterface->GetRelativePathToProject(filename)); + m_rootInstance->SetTemplateSourcePath(m_loaderInterface->GenerateRelativePath(filename)); m_rootInstance->SetContainerEntityName("Level"); m_prefabSystemComponent->PropagateTemplateChanges(templateId); @@ -220,55 +222,24 @@ namespace AzToolsFramework bool PrefabEditorEntityOwnershipService::SaveToStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) { - AZ::IO::Path relativePath = m_loaderInterface->GetRelativePathToProject(filename); + AZ::IO::Path relativePath = m_loaderInterface->GenerateRelativePath(filename); AzToolsFramework::Prefab::TemplateId templateId = m_prefabSystemComponent->GetTemplateIdFromFilePath(relativePath); m_rootInstance->SetTemplateSourcePath(relativePath); - bool newLevelFromTemplate = false; - if (templateId == AzToolsFramework::Prefab::InvalidTemplateId) { - AZStd::string watchFolder; - AZ::Data::AssetInfo assetInfo; - bool sourceInfoFound = false; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, DefaultLevelTemplateName, - assetInfo, watchFolder); - - if (sourceInfoFound) - { - AZStd::string fullPath; - AZ::StringFunc::Path::Join(watchFolder.c_str(), assetInfo.m_relativePath.c_str(), fullPath); - - // Get the default prefab and copy the Dom over to the new template being saved - Prefab::TemplateId defaultId = m_loaderInterface->LoadTemplateFromFile(fullPath.c_str()); - Prefab::PrefabDom& dom = m_prefabSystemComponent->FindTemplateDom(defaultId); - - Prefab::PrefabDom levelDefaultDom; - levelDefaultDom.CopyFrom(dom, levelDefaultDom.GetAllocator()); - - Prefab::PrefabDomPath sourcePath("/Source"); - sourcePath.Set(levelDefaultDom, relativePath.c_str()); + m_rootInstance->m_containerEntity->AddComponent(aznew Prefab::EditorPrefabComponent()); + HandleEntitiesAdded({ m_rootInstance->m_containerEntity.get() }); - templateId = m_prefabSystemComponent->AddTemplate(relativePath, std::move(levelDefaultDom)); - newLevelFromTemplate = true; - } - else + AzToolsFramework::Prefab::PrefabDom dom; + bool success = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*m_rootInstance, dom); + if (!success) { - // Create an empty level since we couldn't find the default template - m_rootInstance->m_containerEntity->AddComponent(aznew Prefab::EditorPrefabComponent()); - HandleEntitiesAdded({ m_rootInstance->m_containerEntity.get() }); - - AzToolsFramework::Prefab::PrefabDom dom; - bool success = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*m_rootInstance, dom); - if (!success) - { - AZ_Error("Prefab", false, "Failed to convert current root instance into a DOM when saving file '%.*s'", AZ_STRING_ARG(filename)); - return false; - } - templateId = m_prefabSystemComponent->AddTemplate(relativePath, std::move(dom)); + AZ_Error("Prefab", false, "Failed to convert current root instance into a DOM when saving file '%.*s'", AZ_STRING_ARG(filename)); + return false; } + templateId = m_prefabSystemComponent->AddTemplate(relativePath, AZStd::move(dom)); if (templateId == AzToolsFramework::Prefab::InvalidTemplateId) { @@ -286,13 +257,6 @@ namespace AzToolsFramework m_prefabSystemComponent->RemoveTemplate(prevTemplateId); } - // If we have a new level from a template, we need to make sure to propagate the changes here otherwise - // the entities from the new template won't show up - if (newLevelFromTemplate) - { - m_prefabSystemComponent->PropagateTemplateChanges(templateId); - } - AZStd::string out; if (m_loaderInterface->SaveTemplateToString(m_rootInstance->GetTemplateId(), out)) { @@ -303,6 +267,71 @@ namespace AzToolsFramework return false; } + void PrefabEditorEntityOwnershipService::CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) + { + AZ::IO::Path relativePath = m_loaderInterface->GenerateRelativePath(filename); + AzToolsFramework::Prefab::TemplateId templateId = m_prefabSystemComponent->GetTemplateIdFromFilePath(relativePath); + + m_rootInstance->SetTemplateSourcePath(relativePath); + + AZStd::string watchFolder; + AZ::Data::AssetInfo assetInfo; + bool sourceInfoFound = false; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, templateFilename.c_str(), + assetInfo, watchFolder); + + if (sourceInfoFound) + { + AZStd::string fullPath; + AZ::StringFunc::Path::Join(watchFolder.c_str(), assetInfo.m_relativePath.c_str(), fullPath); + + // Get the default prefab and copy the Dom over to the new template being saved + Prefab::TemplateId defaultId = m_loaderInterface->LoadTemplateFromFile(fullPath.c_str()); + Prefab::PrefabDom& dom = m_prefabSystemComponent->FindTemplateDom(defaultId); + + Prefab::PrefabDom levelDefaultDom; + levelDefaultDom.CopyFrom(dom, levelDefaultDom.GetAllocator()); + + Prefab::PrefabDomPath sourcePath("/Source"); + sourcePath.Set(levelDefaultDom, relativePath.c_str()); + + templateId = m_prefabSystemComponent->AddTemplate(relativePath, AZStd::move(levelDefaultDom)); + } + else + { + m_rootInstance->m_containerEntity->AddComponent(aznew Prefab::EditorPrefabComponent()); + HandleEntitiesAdded({ m_rootInstance->m_containerEntity.get() }); + + AzToolsFramework::Prefab::PrefabDom dom; + bool success = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*m_rootInstance, dom); + if (!success) + { + AZ_Error( + "Prefab", false, "Failed to convert current root instance into a DOM when saving file '%.*s'", AZ_STRING_ARG(filename)); + return; + } + templateId = m_prefabSystemComponent->AddTemplate(relativePath, std::move(dom)); + } + + if (templateId == AzToolsFramework::Prefab::InvalidTemplateId) + { + AZ_Error("Prefab", false, "Couldn't create new template id '%i' when creating new level '%.*s'", templateId, AZ_STRING_ARG(filename)); + return; + } + + Prefab::TemplateId prevTemplateId = m_rootInstance->GetTemplateId(); + m_rootInstance->SetTemplateId(templateId); + + if (prevTemplateId != Prefab::InvalidTemplateId && templateId != prevTemplateId) + { + // Make sure we only have one level template loaded at a time + m_prefabSystemComponent->RemoveTemplate(prevTemplateId); + } + + m_prefabSystemComponent->PropagateTemplateChanges(templateId); + } + Prefab::InstanceOptionalReference PrefabEditorEntityOwnershipService::CreatePrefab( const AZStd::vector& entities, AZStd::vector>&& nestedPrefabInstances, AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) @@ -351,7 +380,12 @@ namespace AzToolsFramework Prefab::InstanceOptionalReference PrefabEditorEntityOwnershipService::GetRootPrefabInstance() { AZ_Assert(m_rootInstance, "A valid root prefab instance couldn't be found in PrefabEditorEntityOwnershipService."); - return *m_rootInstance; + if (m_rootInstance) + { + return *m_rootInstance; + } + + return AZStd::nullopt; } const AZStd::vector>& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h index 606d5f495f..d8eb81dd40 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h @@ -170,6 +170,8 @@ namespace AzToolsFramework void StartPlayInEditor() override; void StopPlayInEditor() override; + void CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) override; + protected: AZ::SliceComponent::SliceInstanceAddress GetOwningSlice() override; @@ -216,7 +218,5 @@ namespace AzToolsFramework Prefab::PrefabLoaderInterface* m_loaderInterface; AzFramework::EntityContextId m_entityContextId; AZ::SerializeContext m_serializeContext; - - static inline constexpr const char* DefaultLevelTemplateName = "Prefabs/Default_Level.prefab"; }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipService.cpp index 906ea98357..14dcf5e55d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipService.cpp @@ -614,7 +614,7 @@ namespace AzToolsFramework AZ::Quaternion oldEntityRotation; AZ::TransformBus::EventResult(oldEntityRotation, id, &AZ::TransformBus::Events::GetWorldRotationQuaternion); - transformComponent->SetRotationQuaternion(oldEntityRotation); + transformComponent->SetWorldRotationQuaternion(oldEntityRotation); // Ensure the existing hierarchy is maintained AZ::EntityId oldParentEntityId; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.cpp index a8fe0e55bd..8874e0dcd9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LineSegmentSelectionManipulator.cpp @@ -31,7 +31,7 @@ namespace AzToolsFramework rayProportion, lineSegmentProportion, worldClosestPositionRay, worldClosestPositionLineSegment); AZ::Transform worldFromLocalNormalized = worldFromLocal; - const AZ::Vector3 scale = worldFromLocalNormalized.ExtractScale() * nonUniformScale; + const AZ::Vector3 scale = worldFromLocalNormalized.ExtractUniformScale() * nonUniformScale; const AZ::Transform localFromWorldNormalized = worldFromLocalNormalized.GetInverse(); return { (localFromWorldNormalized.TransformPoint(worldClosestPositionLineSegment)) / scale }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp index 87d966fe84..aa84fc5752 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp @@ -59,7 +59,7 @@ namespace AzToolsFramework ? CalculateSnappedOffset(localTransform.GetTranslation(), axis, gridSize * scaleRecip) : AZ::Vector3::CreateZero(); - const AZ::Vector3 localScale = localTransform.GetScale(); + const AZ::Vector3 localScale = AZ::Vector3(localTransform.GetUniformScale()); const AZ::Quaternion localRotation = QuaternionFromTransformNoScaling(localTransform); // calculate scale amount to snap, to align to round scale value const AZ::Vector3 scaleSnapOffset = snapping && !gridSnapAction.m_localSnapping diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h index db0baa1479..e6c70079df 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h @@ -113,7 +113,7 @@ namespace AzToolsFramework /// noise in the value returned when dealing with values far from the origin. inline float ScaleReciprocal(const AZ::Transform& transform) { - return Round3(transform.GetScale().GetReciprocal().GetMinElement()); + return Round3(1.0f / transform.GetUniformScale()); } /// Find the reciprocal of the non-uniform scale. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSpace.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSpace.cpp index fba7e35078..b3f691a62f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSpace.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSpace.cpp @@ -39,7 +39,7 @@ namespace AzToolsFramework AZ::Transform result; result.SetRotation(m_space.GetRotation() * localTransform.GetRotation()); result.SetTranslation(m_space.TransformPoint(m_nonUniformScale * localTransform.GetTranslation())); - result.SetScale(m_space.GetScale() * localTransform.GetScale()); + result.SetUniformScale(m_space.GetUniformScale() * localTransform.GetUniformScale()); return result; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceSerializer.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceSerializer.cpp index 836140eb74..39351df486 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceSerializer.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceSerializer.cpp @@ -124,7 +124,7 @@ namespace AzToolsFramework "PrefabLoaderInterface could not be found. It is required to load Prefab Instances"); // Make sure we have a relative path - instance->m_templateSourcePath = loaderInterface->GetRelativePathToProject(instance->m_templateSourcePath); + instance->m_templateSourcePath = loaderInterface->GenerateRelativePath(instance->m_templateSourcePath); TemplateId templateId = prefabSystemComponentInterface->GetTemplateIdFromFilePath(instance->GetTemplateSourcePath()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp index a21c5301aa..6d3ddedd51 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp @@ -276,18 +276,14 @@ namespace AzToolsFramework PrefabDomValueReference linkPatchesReference = PrefabDomUtils::FindPrefabDomValue(linkDom, PrefabDomUtils::PatchesName); - // This logic only covers addition of patches. If patches already exists, the given list of patches must be appended to them. - if (!linkPatchesReference.has_value()) - { - /* - If the original allocator the patches were created with gets destroyed, then the patches would become garbage in the - linkDom. Since we cannot guarantee the lifecycle of the patch allocators, we are doing a copy of the patches here to - associate them with the linkDom's allocator. - */ - PrefabDom patchesCopy; - patchesCopy.CopyFrom(patches, linkDom.GetAllocator()); - linkDom.AddMember(rapidjson::StringRef(PrefabDomUtils::PatchesName), patchesCopy, linkDom.GetAllocator()); - } + /* + If the original allocator the patches were created with gets destroyed, then the patches would become garbage in the + linkDom. Since we cannot guarantee the lifecycle of the patch allocators, we are doing a copy of the patches here to + associate them with the linkDom's allocator. + */ + PrefabDom patchesCopy; + patchesCopy.CopyFrom(patches, linkDom.GetAllocator()); + linkDom.AddMember(rapidjson::StringRef(PrefabDomUtils::PatchesName), patchesCopy, linkDom.GetAllocator()); } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp index 308749ab28..2fb22ea8e8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp @@ -234,5 +234,10 @@ namespace AzToolsFramework } } + PrefabDomValueReference Link::GetLinkPatches() + { + return PrefabDomUtils::FindPrefabDomValue(m_linkDom, PrefabDomUtils::PatchesName); + } + } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h index 073e619f20..c8f43b291e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h @@ -79,6 +79,8 @@ namespace AzToolsFramework */ void AddLinkIdToInstanceDom(PrefabDomValue& instanceDomValue); + PrefabDomValueReference GetLinkPatches(); + private: /** diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp index 7b4761c39a..d7de634c11 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp @@ -18,7 +18,9 @@ #include #include +#include #include +#include #include #include #include @@ -41,7 +43,7 @@ namespace AzToolsFramework [[maybe_unused]] bool result = settingsRegistry->Get(m_projectPathWithOsSeparator.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath); - AZ_Assert(result, "Couldn't retrieve project root path"); + AZ_Warning("Prefab", result, "Couldn't retrieve project root path"); m_projectPathWithSlashSeparator = AZ::IO::Path(m_projectPathWithOsSeparator.Native(), '/').MakePreferred(); AZ::Interface::Register(this); @@ -112,7 +114,7 @@ namespace AzToolsFramework return InvalidTemplateId; } - AZ::IO::Path relativePath = GetRelativePathToProject(originPath); + AZ::IO::Path relativePath = GenerateRelativePath(originPath); // Cyclical dependency detected if the prefab file is already part of the progressed // file path set. @@ -301,6 +303,45 @@ namespace AzToolsFramework return true; } + bool PrefabLoader::SaveTemplateToFile(TemplateId templateId, AZ::IO::PathView absolutePath) + { + AZ_Assert(absolutePath.IsAbsolute(), "SaveTemplateToFile requires an absolute path for saving the initial prefab file."); + + const auto& domAndFilepath = StoreTemplateIntoFileFormat(templateId); + if (!domAndFilepath) + { + return false; + } + + // Verify that the absolute path provided to this matches the relative path saved in the template. + // Otherwise, the saved prefab won't be able to be loaded. + auto relativePath = GenerateRelativePath(absolutePath); + if (relativePath != domAndFilepath->second) + { + AZ_Error( + "Prefab", false, + "PrefabLoader::SaveTemplateToFile - " + "Failed to save template '%s' to location '%.*s'." + "Error: Relative path '%.*s' for location didn't match template name.", + domAndFilepath->second.c_str(), AZ_STRING_ARG(absolutePath.Native()), AZ_STRING_ARG(relativePath.Native())); + return false; + } + + auto outcome = AzFramework::FileFunc::WriteJsonFile(domAndFilepath->first, absolutePath); + if (!outcome.IsSuccess()) + { + AZ_Error( + "Prefab", false, + "PrefabLoader::SaveTemplateToFile - " + "Failed to save template '%s' to location '%.*s'." + "Error: %s", + domAndFilepath->second.c_str(), AZ_STRING_ARG(absolutePath.Native()), outcome.GetError().c_str()); + return false; + } + m_prefabSystemComponentInterface->SetTemplateDirtyFlag(templateId, false); + return true; + } + bool PrefabLoader::SaveTemplateToString(TemplateId templateId, AZStd::string& output) { const auto& domAndFilepath = StoreTemplateIntoFileFormat(templateId); @@ -385,21 +426,100 @@ namespace AzToolsFramework AZ::IO::Path pathWithOSSeparator = AZ::IO::Path(path).MakePreferred(); if (pathWithOSSeparator.IsAbsolute()) { + // If an absolute path was passed in, just return it as-is. return path; } - return AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator); + // A relative path was passed in, so try to turn it back into an absolute path. + + AZ::IO::Path fullPath; + + bool pathFound = false; + AZ::Data::AssetInfo assetInfo; + AZStd::string rootFolder; + AZStd::string inputPath(path.Native()); + + // Given an input path that's expected to exist, try to look it up. + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + pathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, + inputPath.c_str(), assetInfo, rootFolder); + + if (pathFound) + { + // The asset system provided us with a valid root folder and relative path, so return it. + fullPath = AZ::IO::Path(rootFolder) / assetInfo.m_relativePath; + } + else + { + // If for some reason the Asset system couldn't provide a relative path, provide some fallback logic. + + // Check to see if the AssetProcessor is ready. If it *is* and we didn't get a path, print an error then follow + // the fallback logic. If it's *not* ready, we're probably either extremely early in a tool startup flow or inside + // a unit test, so just execute the fallback logic without an error. + [[maybe_unused]] bool assetProcessorReady = false; + AzFramework::AssetSystemRequestBus::BroadcastResult( + assetProcessorReady, &AzFramework::AssetSystemRequestBus::Events::AssetProcessorIsReady); + + AZ_Error( + "Prefab", !assetProcessorReady, "Full source path for '%.*s' could not be determined. Using fallback logic.", + AZ_STRING_ARG(path.Native())); + + // If a relative path was passed in, make it relative to the project root. + fullPath = AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator); + } + + return fullPath; } - AZ::IO::Path PrefabLoader::GetRelativePathToProject(AZ::IO::PathView path) + AZ::IO::Path PrefabLoader::GenerateRelativePath(AZ::IO::PathView path) { - AZ::IO::Path pathWithOSSeparator = AZ::IO::Path(path.Native()).MakePreferred(); - if (!pathWithOSSeparator.IsAbsolute()) + bool pathFound = false; + + AZStd::string relativePath; + AZStd::string rootFolder; + AZ::IO::Path finalPath; + + // The asset system allows for paths to be relative to multiple root folders, using a priority system. + // This request will make the input path relative to the most appropriate, highest-priority root folder. + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + pathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GenerateRelativeSourcePath, path.Native(), + relativePath, rootFolder); + + if (pathFound && !relativePath.empty()) { - return path; + // A relative path was generated successfully, so return it. + finalPath = relativePath; + } + else + { + // If for some reason the Asset system couldn't provide a relative path, provide some fallback logic. + + // Check to see if the AssetProcessor is ready. If it *is* and we didn't get a path, print an error then follow + // the fallback logic. If it's *not* ready, we're probably either extremely early in a tool startup flow or inside + // a unit test, so just execute the fallback logic without an error. + [[maybe_unused]] bool assetProcessorReady = false; + AzFramework::AssetSystemRequestBus::BroadcastResult( + assetProcessorReady, &AzFramework::AssetSystemRequestBus::Events::AssetProcessorIsReady); + + AZ_Error("Prefab", !assetProcessorReady, + "Relative source path for '%.*s' could not be determined. Using project path as relative root.", + AZ_STRING_ARG(path.Native())); + + AZ::IO::Path pathWithOSSeparator = AZ::IO::Path(path.Native()).MakePreferred(); + + if (pathWithOSSeparator.IsAbsolute()) + { + // If an absolute path was passed in, make it relative to the project path. + finalPath = AZ::IO::Path(path.Native(), '/').MakePreferred().LexicallyRelative(m_projectPathWithSlashSeparator); + } + else + { + // If a relative path was passed in, just return it. + finalPath = path; + } } - return AZ::IO::Path(path.Native(), '/').MakePreferred().LexicallyRelative(m_projectPathWithSlashSeparator); + return finalPath; } AZ::IO::Path PrefabLoaderInterface::GeneratePath() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h index d11cb62ca3..3722e14a97 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h @@ -72,6 +72,16 @@ namespace AzToolsFramework */ bool SaveTemplate(TemplateId templateId) override; + /** + * Saves a Prefab Template to the provided absolute source path, which needs to match the relative path in the template. + * Converts Prefab Template form into .prefab form by collapsing nested Template info + * into a source path and patches. + * @param templateId Id of the template to be saved + * @param absolutePath Absolute path to save the file to + * @return bool on whether the operation succeeded or not + */ + bool SaveTemplateToFile(TemplateId templateId, AZ::IO::PathView absolutePath) override; + /** * Saves a Prefab Template into the provided output string. * Converts Prefab Template form into .prefab form by collapsing nested Template info @@ -91,9 +101,11 @@ namespace AzToolsFramework //! The path will always have the correct separator for the current OS AZ::IO::Path GetFullPath(AZ::IO::PathView path) override; - //! Converts path into a relative path to the project, this will be the paths in .prefab file. - //! The path will always have '/' separator. - AZ::IO::Path GetRelativePathToProject(AZ::IO::PathView path) override; + //! Converts path into a path that's relative to the highest-priority containing folder of all the folders registered + //! with the engine. + //! This path will be the path that appears in the .prefab file. + //! The path will always use the '/' separator. + AZ::IO::Path GenerateRelativePath(AZ::IO::PathView path) override; //! Returns if the path is a valid path for a prefab static bool IsValidPrefabPath(AZ::IO::PathView path); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h index a4055fb15a..0e551cee6b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h @@ -60,6 +60,16 @@ namespace AzToolsFramework */ virtual bool SaveTemplate(TemplateId templateId) = 0; + /** + * Saves a Prefab Template to the provided absolute source path, which needs to match the relative path in the template. + * Converts Prefab Template form into .prefab form by collapsing nested Template info + * into a source path and patches. + * @param templateId Id of the template to be saved + * @param absolutePath Absolute path to save the file to + * @return bool on whether the operation succeeded or not + */ + virtual bool SaveTemplateToFile(TemplateId templateId, AZ::IO::PathView absolutePath) = 0; + /** * Saves a Prefab Template into the provided output string. * Converts Prefab Template form into .prefab form by collapsing nested Template info @@ -74,9 +84,11 @@ namespace AzToolsFramework //! The path will always have the correct separator for the current OS virtual AZ::IO::Path GetFullPath(AZ::IO::PathView path) = 0; - //! Converts path into a relative path to the current project, this will be the paths in .prefab file. - //! The path will always have '/' separator. - virtual AZ::IO::Path GetRelativePathToProject(AZ::IO::PathView path) = 0; + //! Converts path into a path that's relative to the highest-priority containing folder of all the folders registered + //! with the engine. + //! This path will be the path that appears in the .prefab file. + //! The path will always use the '/' separator. + virtual AZ::IO::Path GenerateRelativePath(AZ::IO::PathView path) = 0; protected: diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 579f465eb2..fadcc1b81f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -10,8 +10,6 @@ * */ -#include - #include #include #include @@ -28,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -65,7 +64,7 @@ namespace AzToolsFramework m_prefabUndoCache.Destroy(); } - PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView filePath) + PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView absolutePath) { EntityList inputEntityList, topLevelEntities; AZ::EntityId commonRootEntityId; @@ -77,6 +76,8 @@ namespace AzToolsFramework return findCommonRootOutcome; } + AZ_Assert(absolutePath.IsAbsolute(), "CreatePrefab requires an absolute path for saving the initial prefab file."); + InstanceOptionalReference instanceToCreate; { // Initialize Undo Batch object @@ -98,9 +99,13 @@ namespace AzToolsFramework AZStd::string("Could not create a new prefab out of the entities provided - invalid selection.")); } + AZStd::unordered_map oldEntityAliases; + // Detach the retrieved entities for (AZ::Entity* entity : entities) { + AZ::EntityId entityId = entity->GetId(); + oldEntityAliases.emplace(entityId, commonRootEntityOwningInstance->get().GetEntityAlias(entityId)->get()); commonRootEntityOwningInstance->get().DetachEntity(entity->GetId()).release(); } @@ -110,15 +115,18 @@ namespace AzToolsFramework { AZStd::unique_ptr outInstance = commonRootEntityOwningInstance->get().DetachNestedInstance(nestedInstance->GetInstanceAlias()); - auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstance->GetLinkId()); + LinkId detachingInstanceLinkId = nestedInstance->GetLinkId(); + auto linkRef = m_prefabSystemComponentInterface->FindLink(detachingInstanceLinkId); + AZ_Assert(linkRef.has_value(), "Unable to find link with id '%llu' during prefab creation.", detachingInstanceLinkId); - if (linkRef.has_value()) - { - PrefabDom oldLinkPatches; - oldLinkPatches.CopyFrom(linkRef->get().GetLinkDom(), oldLinkPatches.GetAllocator()); + PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches(); + AZ_Assert( + linkPatches.has_value(), "Unable to get patches on link with id '%llu' during prefab creation.", + detachingInstanceLinkId); - nestedInstanceLinkPatchesMap.emplace(nestedInstance, AZStd::move(oldLinkPatches)); - } + PrefabDom linkPatchesCopy; + linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator()); + nestedInstanceLinkPatchesMap.emplace(nestedInstance, AZStd::move(linkPatchesCopy)); RemoveLink(outInstance, commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch()); @@ -138,7 +146,8 @@ namespace AzToolsFramework // Create the Prefab instanceToCreate = prefabEditorEntityOwnershipInterface->CreatePrefab( - entities, AZStd::move(instancePtrs), filePath, commonRootEntityOwningInstance); + entities, AZStd::move(instancePtrs), m_prefabLoaderInterface->GenerateRelativePath(absolutePath), + commonRootEntityOwningInstance); if (!instanceToCreate) { @@ -182,6 +191,24 @@ namespace AzToolsFramework if (nestedInstanceLinkPatchesMap.contains(nestedInstance.get())) { previousPatch = AZStd::move(nestedInstanceLinkPatchesMap[nestedInstance.get()]); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + previousPatch.Accept(writer); + QString previousPatchString(buffer.GetString()); + + for (AZ::Entity* entity : entities) + { + AZ::EntityId entityId = entity->GetId(); + AZStd::string oldEntityAlias = oldEntityAliases[entityId]; + EntityAliasOptionalReference newEntityAlias = instanceToCreate->get().GetEntityAlias(entityId); + AZ_Assert( + newEntityAlias.has_value(), + "Could not fetch entity alias for entity with id '%llu' during prefab creation.", + static_cast(entityId)); + ReplaceOldAliases(previousPatchString, oldEntityAlias, newEntityAlias->get()); + } + + previousPatch.Parse(previousPatchString.toUtf8().constData()); } // These link creations shouldn't be undone because that would put the template in a non-usable state if a user @@ -203,36 +230,23 @@ namespace AzToolsFramework m_instanceToTemplateInterface->GeneratePatch(reparentPatch, containerEntityDomBefore, containerEntityDomAfter); m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(reparentPatch, nestedInstanceContainerEntityId); - // Update the cache - this prevents these changes from being stored in the regular undo/redo nodes as a separate step - m_prefabUndoCache.Store(nestedInstanceContainerEntityId, AZStd::move(containerEntityDomAfter)); - - // Save these changes as patches to the link - PrefabUndoLinkUpdate* linkUpdate = aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast(nestedInstanceContainerEntityId))); - linkUpdate->SetParent(undoBatch.GetUndoBatch()); - linkUpdate->Capture(reparentPatch, nestedInstance->GetLinkId()); - - linkUpdate->Redo(); + // We won't parent this undo node to the undo batch so that the newly created template and link will remain + // unaffected by undo actions. This is needed so that any future instantiations of the template will work. + PrefabUndoLinkUpdate linkUpdate = PrefabUndoLinkUpdate(AZStd::to_string(static_cast(nestedInstanceContainerEntityId))); + linkUpdate.Capture(reparentPatch, nestedInstance->GetLinkId()); + linkUpdate.Redo(); } }); - + // Create a link between the templates of the newly created instance and the instance it's being parented under. CreateLink( instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(patch)); - for (AZ::Entity* topLevelEntity : topLevelEntities) - { - AZ::EntityId topLevelEntityId = topLevelEntity->GetId(); - if (topLevelEntityId.IsValid()) - { - m_prefabUndoCache.UpdateCache(topLevelEntity->GetId()); - - // Parenting entities would mark entities as dirty. But we want to unmark the top level entities as dirty because - // if we don't, the template created would be updated and cause issues with undo operation followed by instantiation. - ToolsApplicationRequests::Bus::Broadcast( - &ToolsApplicationRequests::Bus::Events::RemoveDirtyEntity, topLevelEntity->GetId()); - } - } + // This clears any entities marked as dirty due to reparenting of entities during the process of creating a prefab. + // We are doing this so that the changes in those enities are not queued up twice for propagation. + AzToolsFramework::ToolsApplicationRequestBus::Broadcast( + &AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities); // Select Container Entity { @@ -243,7 +257,7 @@ namespace AzToolsFramework } // Save Template to file - m_prefabLoaderInterface->SaveTemplate(instanceToCreate->get().GetTemplateId()); + m_prefabLoaderInterface->SaveTemplateToFile(instanceToCreate->get().GetTemplateId(), absolutePath); return AZ::Success(); } @@ -307,7 +321,7 @@ namespace AzToolsFramework } //Detect whether this instantiation would produce a cyclical dependency - auto relativePath = m_prefabLoaderInterface->GetRelativePathToProject(filePath); + auto relativePath = m_prefabLoaderInterface->GenerateRelativePath(filePath); Prefab::TemplateId templateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(relativePath); if (templateId == InvalidTemplateId) @@ -824,15 +838,7 @@ namespace AzToolsFramework // This will cover both cases where an alias could be used in a normal entity vs. an instance for (auto aliasMapIter : oldAliasToNewAliasMap) { - QString oldAliasQuotes = QString("\"%1\"").arg(aliasMapIter.first.c_str()); - QString newAliasQuotes = QString("\"%1\"").arg(aliasMapIter.second.c_str()); - - newEntityDomString.replace(oldAliasQuotes, newAliasQuotes); - - QString oldAliasPathRef = QString("/%1").arg(aliasMapIter.first.c_str()); - QString newAliasPathRef = QString("/%1").arg(aliasMapIter.second.c_str()); - - newEntityDomString.replace(oldAliasPathRef, newAliasPathRef); + ReplaceOldAliases(newEntityDomString, aliasMapIter.first, aliasMapIter.second); } // Create the new Entity DOM from parsing the JSON string @@ -1233,5 +1239,18 @@ namespace AzToolsFramework return true; } + + void PrefabPublicHandler::ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias) + { + QString oldAliasQuotes = QString("\"%1\"").arg(oldAlias.data()); + QString newAliasQuotes = QString("\"%1\"").arg(newAlias.data()); + + stringToReplace.replace(oldAliasQuotes, newAliasQuotes); + + QString oldAliasPathRef = QString("/%1").arg(oldAlias.data()); + QString newAliasPathRef = QString("/%1").arg(newAlias.data()); + + stringToReplace.replace(oldAliasPathRef, newAliasPathRef); + } } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 223a725c6c..e68a3e0b1e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -14,12 +14,15 @@ #include #include +#include #include #include #include #include +class QString; + namespace AzToolsFramework { using EntityList = AZStd::vector; @@ -27,7 +30,6 @@ namespace AzToolsFramework namespace Prefab { class Instance; - class InstanceEntityMapperInterface; class InstanceToTemplateInterface; class PrefabLoaderInterface; @@ -44,7 +46,7 @@ namespace AzToolsFramework void UnregisterPrefabPublicHandlerInterface(); // PrefabPublicInterface... - PrefabOperationResult CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView filePath) override; + PrefabOperationResult CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView absolutePath) override; PrefabOperationResult InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) override; PrefabOperationResult SavePrefab(AZ::IO::Path filePath) override; PrefabEntityResult CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) override; @@ -130,6 +132,8 @@ namespace AzToolsFramework bool IsCyclicalDependencyFound( InstanceOptionalConstReference instance, const AZStd::unordered_set& templateSourcePaths); + void ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias); + static Instance* GetParentInstance(Instance* instance); static Instance* GetAncestorOfInstanceThatIsChildOfRoot(const Instance* ancestor, Instance* descendant); static void GenerateContainerEntityTransform(const EntityList& topLevelEntities, AZ::Vector3& translation, AZ::Quaternion& rotation); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h index 0750c4d264..2e9152fd1b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h @@ -46,10 +46,10 @@ namespace AzToolsFramework * Create a prefab out of the entities provided, at the path provided. * Automatically detects descendants of entities, and discerns between entities and child instances. * @param entityIds The entities that should form the new prefab (along with their descendants). - * @param filePath The path for the new prefab file. + * @param filePath The absolute path for the new prefab file. * @return An outcome object; on failure, it comes with an error message detailing the cause of the error. */ - virtual PrefabOperationResult CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView filePath) = 0; + virtual PrefabOperationResult CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView absolutePath) = 0; /** * Instantiate a prefab from a prefab file. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index c4e6415b02..0136f791b3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -95,7 +95,7 @@ namespace AzToolsFramework const AZStd::vector& entities, AZStd::vector>&& instancesToConsume, AZ::IO::PathView filePath, AZStd::unique_ptr containerEntity, bool shouldCreateLinks) { - AZ::IO::Path relativeFilePath = m_prefabLoader.GetRelativePathToProject(filePath); + AZ::IO::Path relativeFilePath = m_prefabLoader.GenerateRelativePath(filePath); if (GetTemplateIdFromFilePath(relativeFilePath) != InvalidTemplateId) { AZ_Error("Prefab", false, diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp index 78d1332a71..050afd813d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp @@ -10,7 +10,7 @@ * */ -#include +#include #include namespace AzToolsFramework::Prefab::PrefabConversionUtils @@ -73,8 +73,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils uint32_t ProcessedObjectStore::BuildSubId(AZStd::string_view id) { - AZ::Uuid subIdHash = AZ::Uuid::CreateData(id.data(), id.size()); - return azlossy_caster(subIdHash.GetHash()); + return AzFramework::SpawnableAssetHandler::BuildSubId(id); } const AZStd::string& ProcessedObjectStore::GetId() const diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp index 716c3098d9..3c91f99b05 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp @@ -24,17 +24,6 @@ namespace AzToolsFramework::Prefab::SpawnableUtils { - - AzFramework::Spawnable CreateSpawnable(const PrefabDom& prefabDom) - { - AzFramework::Spawnable spawnable; - AZStd::vector> referencedAssets; - [[maybe_unused]] bool result = CreateSpawnable(spawnable, prefabDom, referencedAssets); - AZ_Assert(result, - "Failed to Load Prefab Instance from given Prefab DOM while Spawnable creation."); - return spawnable; - } - bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom) { AZStd::vector> referencedAssets; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h index 3b5ea488cb..cdf07346d0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h @@ -17,7 +17,6 @@ namespace AzToolsFramework::Prefab::SpawnableUtils { - AzFramework::Spawnable CreateSpawnable(const PrefabDom& prefabDom); bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom); bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom, AZStd::vector>& referencedAssets); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.h index 6950717499..0ca5853adc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.h @@ -1,22 +1,22 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once -#include -#include -#include #include #include +#include +#include +#include namespace AzToolsFramework { @@ -31,7 +31,7 @@ namespace AzToolsFramework To use the EditorComponentAdapter, 3 classes are required: - a class that implements the functions required for TController (see below) - a configuration struct/class which extends AZ::ComponentConfig - - A runtime component that will be generated by the editor comoinent on export + - A runtime component that will be generated by the editor component on export The concrete component extends the adapter and implements behavior which is unique to the component. @@ -64,15 +64,15 @@ namespace AzToolsFramework the EditContext. TController can friend itself to the editor component to make this work if required. */ template - class EditorComponentAdapter - : public EditorComponentBase + class EditorComponentAdapter : public EditorComponentBase { public: - - AZ_RTTI((EditorComponentAdapter, "{2F5A3669-FFE9-4CD7-B9E2-7FC8100CF1A2}", TController, TRuntimeComponent, TConfiguration), EditorComponentBase); + AZ_RTTI( + (EditorComponentAdapter, "{2F5A3669-FFE9-4CD7-B9E2-7FC8100CF1A2}", TController, TRuntimeComponent, TConfiguration), + EditorComponentBase); EditorComponentAdapter() = default; - EditorComponentAdapter(const TConfiguration& configuration); + explicit EditorComponentAdapter(const TConfiguration& configuration); static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); @@ -86,7 +86,6 @@ namespace AzToolsFramework void BuildGameEntity(AZ::Entity* gameEntity) override; protected: - static void Reflect(AZ::ReflectContext* context); // AZ::Component overrides ... diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.inl b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.inl index 04619b079d..b8bd24589a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.inl +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentAdapter.inl @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include @@ -28,23 +28,21 @@ namespace AzToolsFramework template void EditorComponentAdapter::Reflect(AZ::ReflectContext* context) { - if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + if (auto serializeContext = azrtti_cast(context)) { - serializeContext->Class() - ->Version(1) - ->Field("Controller", &EditorComponentAdapter::m_controller) - ; + serializeContext->Class()->Version(1)->Field( + "Controller", &EditorComponentAdapter::m_controller); if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { - editContext->Class( - "EditorComponentAdapter", "") + // clang-format off + editContext->Class("EditorComponentAdapter", "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorComponentAdapter::m_controller, "Controller", "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorComponentAdapter::OnConfigurationChanged) - ; + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorComponentAdapter::OnConfigurationChanged); + // clang-format on } } } @@ -53,27 +51,35 @@ namespace AzToolsFramework // Get*Services functions template - void EditorComponentAdapter::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + void EditorComponentAdapter::GetProvidedServices( + AZ::ComponentDescriptor::DependencyArrayType& services) { - AzFramework::Components::GetProvidedServicesHelper(services, typename AZ::HasComponentProvidedServices::type()); + AzFramework::Components::GetProvidedServicesHelper( + services, typename AZ::HasComponentProvidedServices::type()); } template - void EditorComponentAdapter::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) + void EditorComponentAdapter::GetRequiredServices( + AZ::ComponentDescriptor::DependencyArrayType& services) { - AzFramework::Components::GetRequiredServicesHelper(services, typename AZ::HasComponentRequiredServices::type()); + AzFramework::Components::GetRequiredServicesHelper( + services, typename AZ::HasComponentRequiredServices::type()); } template - void EditorComponentAdapter::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + void EditorComponentAdapter::GetIncompatibleServices( + AZ::ComponentDescriptor::DependencyArrayType& services) { - AzFramework::Components::GetIncompatibleServicesHelper(services, typename AZ::HasComponentIncompatibleServices::type()); + AzFramework::Components::GetIncompatibleServicesHelper( + services, typename AZ::HasComponentIncompatibleServices::type()); } template - void EditorComponentAdapter::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services) + void EditorComponentAdapter::GetDependentServices( + AZ::ComponentDescriptor::DependencyArrayType& services) { - AzFramework::Components::GetDependentServicesHelper(services, typename AZ::HasComponentDependentServices::type()); + AzFramework::Components::GetDependentServicesHelper( + services, typename AZ::HasComponentDependentServices::type()); } ////////////////////////////////////////////////////////////////////////// @@ -99,7 +105,8 @@ namespace AzToolsFramework if (ShouldActivateController()) { - m_controller.Activate(GetEntityId()); + AzFramework::Components::ComponentActivateHelper::Activate( + m_controller, AZ::EntityComponentIdPair(GetEntityId(), GetId())); } } @@ -122,7 +129,8 @@ namespace AzToolsFramework } template - bool EditorComponentAdapter::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + bool EditorComponentAdapter::WriteOutConfig( + AZ::ComponentConfig* outBaseConfig) const { if (auto config = azrtti_cast(outBaseConfig)) { @@ -139,7 +147,8 @@ namespace AzToolsFramework if (ShouldActivateController()) { - m_controller.Activate(GetEntityId()); + AzFramework::Components::ComponentActivateHelper::Activate( + m_controller, AZ::EntityComponentIdPair(GetEntityId(), GetId())); } return AZ::Edit::PropertyRefreshLevels::None; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorEntityIconComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorEntityIconComponent.cpp index 48a7a37487..c46bb158c6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorEntityIconComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorEntityIconComponent.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -313,8 +314,7 @@ namespace AzToolsFramework // if we do not yet have a valid texture id, request it using the entity icon path if (m_entityIconTextureId == 0) { - EditorRequestBus::BroadcastResult( - m_entityIconTextureId, &EditorRequests::GetIconTextureIdFromEntityIconPath, m_entityIconPath); + m_entityIconTextureId = EditorViewportIconDisplay::Get()->GetOrLoadIconForPath(m_entityIconPath); } return m_entityIconTextureId; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.cpp index 97e27ac748..497bcf15d7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.cpp @@ -28,7 +28,7 @@ namespace AzToolsFramework AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(worldFromLocal, m_entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); - worldFromLocal.ExtractScale(); + worldFromLocal.ExtractUniformScale(); m_manipulators = AZStd::make_unique(worldFromLocal); m_manipulators->Register(g_mainManipulatorManagerId); m_manipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp index b73978c792..3e13e6226b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -50,10 +49,10 @@ namespace AzToolsFramework { const AZ::u32 ParentEntityCRC = AZ_CRC("Parent Entity", 0x5b1b276c); - // Decompose a transform into euler angles in degrees, scale (along basis, any shear will be dropped), and translation. - void DecomposeTransform(const AZ::Transform& transform, AZ::Vector3& translation, AZ::Vector3& rotation, AZ::Vector3& scale) + // Decompose a transform into euler angles in degrees, uniform scale, and translation. + void DecomposeTransform(const AZ::Transform& transform, AZ::Vector3& translation, AZ::Vector3& rotation, float& scale) { - scale = transform.GetScale(); + scale = transform.GetUniformScale(); translation = transform.GetTranslation(); rotation = transform.GetRotation().GetEulerDegrees(); } @@ -120,7 +119,7 @@ namespace AzToolsFramework // Decompose the old slice-relative transform and set it as a our editor transform, // since the entity is now our parent. EditorTransform editorTransform; - DecomposeTransform(sliceRelTransform, editorTransform.m_translate, editorTransform.m_rotate, editorTransform.m_scale); + DecomposeTransform(sliceRelTransform, editorTransform.m_translate, editorTransform.m_rotate, editorTransform.m_uniformScale); editorTransformElement.Convert(context); editorTransformElement.SetData(context, editorTransform); } @@ -170,6 +169,23 @@ namespace AzToolsFramework return true; } + + bool EditorTransformDataConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) + { + if (classElement.GetVersion() < 3) + { + // version 3 replaces vector scale with uniform scale but does not yet delete the legacy scale data + // in order to allow for migration + AZ::Vector3 vectorScale; + if (classElement.FindSubElementAndGetData(AZ_CRC_CE("Scale"), vectorScale)) + { + const float uniformScale = vectorScale.GetMaxElement(); + classElement.AddElementWithData(context, "UniformScale", uniformScale); + } + } + + return true; + } } // namespace Internal TransformComponent::TransformComponent() @@ -357,7 +373,7 @@ namespace AzToolsFramework AZ::Transform TransformComponent::GetLocalScaleTM() const { - return AZ::Transform::CreateUniformScale(m_editorTransform.m_scale.GetMaxElement()); + return AZ::Transform::CreateUniformScale(m_editorTransform.m_uniformScale); } const AZ::Transform& TransformComponent::GetLocalTM() @@ -374,12 +390,13 @@ namespace AzToolsFramework // given a local transform, update local transform. void TransformComponent::SetLocalTM(const AZ::Transform& finalTx) { - AZ::Vector3 tx, rot, scale; - Internal::DecomposeTransform(finalTx, tx, rot, scale); + AZ::Vector3 tx, rot; + float uniformScale; + Internal::DecomposeTransform(finalTx, tx, rot, uniformScale); m_editorTransform.m_translate = tx; m_editorTransform.m_rotate = rot; - m_editorTransform.m_scale = scale; + m_editorTransform.m_uniformScale = uniformScale; TransformChanged(); } @@ -520,91 +537,13 @@ namespace AzToolsFramework return m_editorTransform.m_translate.GetZ(); } - void TransformComponent::SetRotation(const AZ::Vector3& eulerAnglesRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotation is deprecated, please use SetLocalRotation"); - AZ::Transform newWorldTransform = GetWorldTM(); - newWorldTransform.SetRotation(AZ::ConvertEulerRadiansToQuaternion(eulerAnglesRadians)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetRotationQuaternion(const AZ::Quaternion& quaternion) + void TransformComponent::SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotationQuaternion is deprecated, please use SetLocalRotation"); AZ::Transform newWorldTransform = GetWorldTM(); newWorldTransform.SetRotation(quaternion); SetWorldTM(newWorldTransform); } - void TransformComponent::SetRotationX(float eulerAngleRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotationX is deprecated, please use SetLocalRotation"); - AZ::Transform newWorldTransform = GetWorldTM(); - newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationX(eulerAngleRadians)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetRotationY(float eulerAngleRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotationY is deprecated, please use SetLocalRotation"); - AZ::Transform newWorldTransform = GetWorldTM(); - newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationY(eulerAngleRadians)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::SetRotationZ(float eulerAngleRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotationZ is deprecated, please use SetLocalRotation"); - AZ::Transform newWorldTransform = GetWorldTM(); - newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationZ(eulerAngleRadians)); - SetWorldTM(newWorldTransform); - } - - void TransformComponent::RotateByX(float eulerAngleRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "RotateByX is deprecated, please use RotateAroundLocalX"); - SetWorldTM(GetWorldTM() * AZ::Transform::CreateRotationX(eulerAngleRadians)); - } - - void TransformComponent::RotateByY(float eulerAngleRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "RotateByY is deprecated, please use RotateAroundLocalY"); - SetWorldTM(GetWorldTM() * AZ::Transform::CreateRotationY(eulerAngleRadians)); - } - - void TransformComponent::RotateByZ(float eulerAngleRadians) - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "RotateByZ is deprecated, please use RotateAroundLocalZ"); - SetWorldTM(GetWorldTM() * AZ::Transform::CreateRotationZ(eulerAngleRadians)); - } - - AZ::Vector3 TransformComponent::GetRotationEulerRadians() - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "GetRotationEulerRadians is deprecated, please use GetWorldRotation"); - return GetWorldTM().GetRotation().GetEulerRadians(); - } - - AZ::Quaternion TransformComponent::GetRotationQuaternion() - { - AZ_Warning("AzToolsFramework::TransformComponent", false, "GetRotationQuaternion is deprecated, please use GetWorldRotationQuaternion"); - return GetWorldTM().GetRotation(); - } - - float TransformComponent::GetRotationX() - { - return GetRotationEulerRadians().GetX(); - } - - float TransformComponent::GetRotationY() - { - return GetRotationEulerRadians().GetY(); - } - - float TransformComponent::GetRotationZ() - { - return GetRotationEulerRadians().GetZ(); - } - AZ::Vector3 TransformComponent::GetWorldRotation() { return GetWorldTM().GetRotation().GetEulerRadians(); @@ -677,31 +616,21 @@ namespace AzToolsFramework return result; } - void TransformComponent::SetLocalScale(const AZ::Vector3& scale) - { - m_editorTransform.m_scale = scale; - TransformChanged(); - } - AZ::Vector3 TransformComponent::GetLocalScale() { - return m_editorTransform.m_scale; - } - - AZ::Vector3 TransformComponent::GetWorldScale() - { - return GetWorldTM().GetScale(); + AZ_WarningOnce("TransformComponent", false, "GetLocalScale is deprecated, please use GetLocalUniformScale instead"); + return m_editorTransform.m_legacyScale; } void TransformComponent::SetLocalUniformScale(float scale) { - m_editorTransform.m_scale = AZ::Vector3(scale); + m_editorTransform.m_uniformScale = scale; TransformChanged(); } float TransformComponent::GetLocalUniformScale() { - return m_editorTransform.m_scale.GetMaxElement(); + return m_editorTransform.m_uniformScale; } float TransformComponent::GetWorldUniformScale() @@ -1219,9 +1148,10 @@ namespace AzToolsFramework serializeContext->Class()-> Field("Translate", &EditorTransform::m_translate)-> Field("Rotate", &EditorTransform::m_rotate)-> - Field("Scale", &EditorTransform::m_scale)-> + Field("Scale", &EditorTransform::m_legacyScale)-> Field("Locked", &EditorTransform::m_locked)-> - Version(2); + Field("UniformScale", &EditorTransform::m_uniformScale)-> + Version(3, &Internal::EditorTransformDataConverter); serializeContext->Class()-> Field("Parent Entity", &TransformComponent::m_parentEntityId)-> @@ -1280,7 +1210,7 @@ namespace AzToolsFramework Attribute(AZ::Edit::Attributes::Suffix, " deg")-> Attribute(AZ::Edit::Attributes::ReadOnly, &EditorTransform::m_locked)-> Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushableOnSliceRoot)-> - DataElement(TransformScaleHandler, &EditorTransform::m_scale, "Scale", "Local Scale")-> + DataElement(AZ::Edit::UIHandlers::Default, &EditorTransform::m_uniformScale, "Uniform Scale", "Local Uniform Scale")-> Attribute(AZ::Edit::Attributes::Step, 0.1f)-> Attribute(AZ::Edit::Attributes::ReadOnly, &EditorTransform::m_locked) ; @@ -1308,7 +1238,8 @@ namespace AzToolsFramework { AzToolsFramework::ScopedUndoBatch undo("Reset transform values"); m_editorTransform.m_translate = AZ::Vector3::CreateZero(); - m_editorTransform.m_scale = AZ::Vector3::CreateOne(); + m_editorTransform.m_legacyScale = AZ::Vector3::CreateOne(); + m_editorTransform.m_uniformScale = 1.0f; m_editorTransform.m_rotate = AZ::Vector3::CreateZero(); OnTransformChanged(); SetDirty(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.h index 91d64b0533..80db5e10fb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.h @@ -99,22 +99,7 @@ namespace AzToolsFramework float GetLocalZ() override; // Rotation modifiers - void SetRotation(const AZ::Vector3& eulerAnglesRadians) override; - void SetRotationQuaternion(const AZ::Quaternion& quaternion) override; - void SetRotationX(float eulerAngleRadians) override; - void SetRotationY(float eulerAngleRadians) override; - void SetRotationZ(float eulerAngleRadians) override; - - void RotateByX(float eulerAngleRadians) override; - void RotateByY(float eulerAngleRadians) override; - void RotateByZ(float eulerAngleRadians) override; - - AZ::Vector3 GetRotationEulerRadians() override; - AZ::Quaternion GetRotationQuaternion() override; - - float GetRotationX() override; - float GetRotationY() override; - float GetRotationZ() override; + void SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) override; AZ::Vector3 GetWorldRotation() override; AZ::Quaternion GetWorldRotationQuaternion() override; @@ -130,9 +115,7 @@ namespace AzToolsFramework AZ::Quaternion GetLocalRotationQuaternion() override; // Scale Modifiers - void SetLocalScale(const AZ::Vector3& scale) override; AZ::Vector3 GetLocalScale() override; - AZ::Vector3 GetWorldScale() override; void SetLocalUniformScale(float scale) override; float GetLocalUniformScale() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponentBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponentBus.h index 437a39b1a0..26fa4d758e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponentBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponentBus.h @@ -30,7 +30,8 @@ namespace AzToolsFramework EditorTransform() { m_translate = AZ::Vector3::CreateZero(); - m_scale = AZ::Vector3::CreateOne(); + m_legacyScale = AZ::Vector3::CreateOne(); + m_uniformScale = 1.0f; m_rotate = AZ::Vector3::CreateZero(); m_locked = false; } @@ -40,9 +41,10 @@ namespace AzToolsFramework return EditorTransform(); } - AZ::Vector3 m_translate; //! Translation in engine units (meters) - AZ::Vector3 m_scale; - AZ::Vector3 m_rotate; //! Rotation in degrees + AZ::Vector3 m_translate; //!< Translation in engine units (meters) + AZ::Vector3 m_legacyScale; //!< Legacy vector scale value, retained only for migration. + float m_uniformScale; //!< Single scale value applied uniformly. + AZ::Vector3 m_rotate; //!< Rotation in degrees bool m_locked; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.cpp deleted file mode 100644 index 94d0113bcf..0000000000 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include "AzToolsFramework_precompiled.h" -#include -#include -#include - -namespace AzToolsFramework -{ - void RegisterTransformScaleHandler() - { - PropertyTypeRegistrationMessages::Bus::Broadcast(&PropertyTypeRegistrationMessages::RegisterPropertyType, aznew Components::TransformScalePropertyHandler()); - } - - namespace Components - { - AZ::u32 TransformScalePropertyHandler::GetHandlerName(void) const - { - return TransformScaleHandler; - } - - QWidget* TransformScalePropertyHandler::CreateGUI(QWidget* parent) - { - AzQtComponents::DoubleSpinBox* newCtrl = new AzQtComponents::DoubleSpinBox(parent); - connect(newCtrl, QOverload::of(&AzQtComponents::DoubleSpinBox::valueChanged), newCtrl, [newCtrl]() - { - AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestWrite, newCtrl); - }); - - newCtrl->setMinimum(AZ::MinTransformScale); - newCtrl->setMaximum(AZ::MaxTransformScale); - - return newCtrl; - } - - void TransformScalePropertyHandler::ConsumeAttribute(AzQtComponents::DoubleSpinBox* GUI, AZ::u32 attrib, - AzToolsFramework::PropertyAttributeReader* attrValue, [[maybe_unused]] const char* debugName) - { - if (attrib == AZ::Edit::Attributes::Suffix) - { - AZStd::string label; - if (attrValue->Read(label)) - { - GUI->setSuffix(label.c_str()); - } - } - } - - void TransformScalePropertyHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, AzQtComponents::DoubleSpinBox* GUI, - AZ::Vector3& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node) - { - const float value = aznumeric_cast(GUI->value()); - const float currentMaxElement = instance.GetMaxElement(); - if (currentMaxElement != 0.0f) - { - instance *= value / currentMaxElement; - } - else - { - instance = AZ::Vector3(value); - } - } - - bool TransformScalePropertyHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, AzQtComponents::DoubleSpinBox* GUI, - const AZ::Vector3& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node) - { - QSignalBlocker signalBlocker(GUI); - GUI->setValue(instance.GetMaxElement()); - return true; - } - } // namespace Components -} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.h deleted file mode 100644 index f13aa37904..0000000000 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#if !defined(Q_MOC_RUN) -#include -#include -#include -#endif - -namespace AzToolsFramework -{ - namespace Components - { - static const AZ::Crc32 TransformScaleHandler = AZ_CRC_CE("TransformScale"); - - //! Handler to allow the scale field inside the Transform Component to be represented as a single value in - //! the editor, but stored internally as a Vector3. - //! The purpose for this is to prevent any new entities being created with non-uniform scale on the Transform - //! Component, but preserve the data required for migrating any existing entities to use the Non-Uniform Scale - //! Component, until all migration work is completed. - //! The value shown in the editor will be the maximum value from the scale vector, and changing the value in - //! the editor will update the vector so that its maximum value matches the newly edited value, but its - //! components retain their existing proportion. - //! For example, if the current vector scale is (2, 3, 4), the value in the editor will appear as 4. If the value - //! in the editor is updated to 2, then the vector scale will update to (1, 1.5, 2), keeping the same proportion - //! between the x, y and z components. - class TransformScalePropertyHandler - : public QObject - , public AzToolsFramework::PropertyHandler - { - Q_OBJECT //AUTOMOC - public: - AZ_CLASS_ALLOCATOR(TransformScalePropertyHandler, AZ::SystemAllocator, 0); - - AZ::u32 GetHandlerName(void) const override; - QWidget* CreateGUI(QWidget* parent) override; - void ConsumeAttribute(AzQtComponents::DoubleSpinBox* GUI, AZ::u32 attrib, - AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override; - void WriteGUIValuesIntoProperty(size_t index, AzQtComponents::DoubleSpinBox* GUI, - AZ::Vector3& instance, AzToolsFramework::InstanceDataNode* node) override; - bool ReadValuesIntoGUI(size_t index, AzQtComponents::DoubleSpinBox* GUI, - const AZ::Vector3& instance, AzToolsFramework::InstanceDataNode* node) override; - }; - } // namespace Components -} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp index cc65b61908..8293614525 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp @@ -172,7 +172,7 @@ namespace AzToolsFramework const int autoExpandDelayMilliseconds = 2500; m_gui->m_objectTree->setSelectionMode(QAbstractItemView::ExtendedSelection); - m_gui->m_objectTree->setEditTriggers(QAbstractItemView::EditKeyPressed); + SetDefaultTreeViewEditTriggers(); m_gui->m_objectTree->setAutoExpandDelay(autoExpandDelayMilliseconds); m_gui->m_objectTree->setDragEnabled(true); m_gui->m_objectTree->setDropIndicatorShown(true); @@ -850,6 +850,11 @@ namespace AzToolsFramework addAction(m_actionGoToEntitiesInViewport); } + void EntityOutlinerWidget::SetDefaultTreeViewEditTriggers() + { + m_gui->m_objectTree->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::EditKeyPressed); + } + void EntityOutlinerWidget::OnEntityPickModeStarted() { m_gui->m_objectTree->setDragEnabled(false); @@ -862,7 +867,7 @@ namespace AzToolsFramework { m_gui->m_objectTree->setDragEnabled(true); m_gui->m_objectTree->setSelectionMode(QAbstractItemView::ExtendedSelection); - m_gui->m_objectTree->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed); + SetDefaultTreeViewEditTriggers(); m_inObjectPickMode = false; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx index 9a02febab2..6e3979a21e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx @@ -166,6 +166,8 @@ namespace AzToolsFramework // to a given entity void QueueScrollToNewContent(const AZ::EntityId& entityId) override; + void SetDefaultTreeViewEditTriggers(); + void ScrollToNewContent(); bool m_scrollToNewContentQueued; bool m_scrollToSelectedEntity; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 3edc190fb7..021b97a7dd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -333,7 +333,7 @@ namespace AzToolsFramework } } - auto createPrefabOutcome = s_prefabPublicInterface->CreatePrefab(selectedEntities, s_prefabLoaderInterface->GetRelativePathToProject(prefabFilePath.data())); + auto createPrefabOutcome = s_prefabPublicInterface->CreatePrefab(selectedEntities, prefabFilePath.data()); if (!createPrefabOutcome.IsSuccess()) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp index 23f8378df5..169a90497b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp @@ -777,6 +777,23 @@ namespace AzToolsFramework selection.SetDefaultDirectory(defaultDirectory); } + if (m_hideProductFilesInAssetPicker) + { + FilterConstType displayFilter = selection.GetDisplayFilter(); + + EntryTypeFilter* productsFilter = new EntryTypeFilter(); + productsFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Product); + + InverseFilter* noProductsFilter = new InverseFilter(); + noProductsFilter->SetFilter(FilterConstType(productsFilter)); + + CompositeFilter* compFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::AND); + compFilter->AddFilter(FilterConstType(displayFilter)); + compFilter->AddFilter(FilterConstType(noProductsFilter)); + + selection.SetDisplayFilter(FilterConstType(compFilter)); + } + AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, parentWidget()); if (selection.IsValid()) { @@ -936,11 +953,16 @@ namespace AzToolsFramework return; } - const AZ::Data::AssetId assetID = GetCurrentAssetID(); - m_currentAssetHint = ""; - - if (!m_unnamedType) + const AZStd::string& folderPath = GetFolderSelection(); + if (!folderPath.empty()) + { + m_currentAssetHint = folderPath; + } + else { + const AZ::Data::AssetId assetID = GetCurrentAssetID(); + m_currentAssetHint = ""; + AZ::Outcome jobOutcome = AZ::Failure(); AssetSystemJobRequestBus::BroadcastResult(jobOutcome, &AssetSystemJobRequestBus::Events::GetAssetJobsInfoByAssetID, assetID, false, false); @@ -954,7 +976,7 @@ namespace AzToolsFramework if (!jobs.empty()) { - // The default behavior is show to the source filename. + // The default behavior is to show the source filename. assetPath = jobs[0].m_sourceFile; AZStd::string errorLog; @@ -1172,6 +1194,16 @@ namespace AzToolsFramework return m_showProductAssetName; } + void PropertyAssetCtrl::SetHideProductFilesInAssetPicker(bool hide) + { + m_hideProductFilesInAssetPicker = hide; + } + + bool PropertyAssetCtrl::GetHideProductFilesInAssetPicker() const + { + return m_hideProductFilesInAssetPicker; + } + void PropertyAssetCtrl::SetShowThumbnail(bool enable) { m_showThumbnail = enable; @@ -1297,6 +1329,14 @@ namespace AzToolsFramework GUI->SetShowProductAssetName(showProductAssetName); } } + else if(attrib == AZ::Edit::Attributes::HideProductFilesInAssetPicker) + { + bool hideProductFilesInAssetPicker = false; + if (attrValue->Read(hideProductFilesInAssetPicker)) + { + GUI->SetHideProductFilesInAssetPicker(hideProductFilesInAssetPicker); + } + } else if (attrib == AZ::Edit::Attributes::ClearNotify) { PropertyAssetCtrl::ClearCallbackType* func = azdynamic_cast(attrValue->GetAttribute()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx index 37af3d0594..5a6310eb35 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx @@ -158,6 +158,10 @@ namespace AzToolsFramework //! Assets can be either source or product assets generated from source assets. By default, source assets are shown in the property asset. You can override that with this flag. bool m_showProductAssetName = true; + //! Assets can be either source or product assets generated from source assets. + //! By default the asset picker shows both on an AZ::Asset<> property. You can hide product assets with this flag. + bool m_hideProductFilesInAssetPicker = false; + bool m_showThumbnail = false; bool m_showThumbnailDropDownButton = false; EditCallbackType* m_thumbnailCallback = nullptr; @@ -211,6 +215,9 @@ namespace AzToolsFramework void SetShowProductAssetName(bool enable); bool GetShowProductAssetName() const; + void SetHideProductFilesInAssetPicker(bool hide); + bool GetHideProductFilesInAssetPicker() const; + void SetShowThumbnail(bool enable); bool GetShowThumbnail() const; void SetShowThumbnailDropDownButton(bool enable); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyManagerComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyManagerComponent.cpp index bd61e6ceed..6dc5bdd001 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyManagerComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyManagerComponent.cpp @@ -16,7 +16,6 @@ #include #include #include -#include namespace AzToolsFramework { @@ -38,7 +37,6 @@ namespace AzToolsFramework void RegisterButtonPropertyHandlers(); void RegisterMultiLineEditHandler(); void RegisterCrcHandler(); - void RegisterTransformScaleHandler(); void ReflectPropertyEditor(AZ::ReflectContext* context); namespace Components @@ -192,7 +190,6 @@ namespace AzToolsFramework RegisterVectorHandlers(); RegisterButtonPropertyHandlers(); RegisterMultiLineEditHandler(); - RegisterTransformScaleHandler(); // GenericComboBoxHandlers RegisterGenericComboBoxHandler(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h index 8e91dc945d..91eee18cb7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h @@ -165,15 +165,18 @@ namespace AzToolsFramework virtual bool AngleSnappingEnabled() = 0; /// Return the angle snapping/step size. virtual float AngleStep() = 0; - /// Transform a point in world space to screen space coordinates. + /// Transform a point in world space to screen space coordinates in Qt Widget space. + /// Multiply by DeviceScalingFactor to get the position in viewport pixel space. virtual AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) = 0; - /// Transform a point in screen space coordinates to a vector in world space based on clip space depth. + /// Transform a point from Qt widget screen space to world space based on the given clip space depth. /// Depth specifies a relative camera depth to project in the range of [0.f, 1.f]. /// Returns the world space position if successful. virtual AZStd::optional ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) = 0; /// Casts a point in screen space to a ray in world space originating from the viewport camera frustum's near plane. /// Returns a ray containing the ray's origin and a direction normal, if successful. virtual AZStd::optional ViewportScreenToWorldRay(const AzFramework::ScreenPoint& screenPosition) = 0; + /// Gets the DPI scaling factor that translates Qt widget space into viewport pixel space. + virtual float DeviceScalingFactor() = 0; protected: ~ViewportInteractionRequests() = default; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp index 5c62a2997b..31da01fadc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -21,6 +21,7 @@ #include #include #include +#include AZ_CVAR( bool, ed_visibility_showAggregateEntitySelectionBounds, false, nullptr, AZ::ConsoleFunctorFlags::Null, @@ -232,10 +233,14 @@ namespace AzToolsFramework return AZ::Color(1.0f, 1.0f, 1.0f, 1.0f); }(); - debugDisplay.SetColor(iconHighlight); - // debugDisplay.DrawTextureLabel( - // iconTextureId, entityPosition, iconSize, iconSize, - // /*DisplayContext::ETextureIconFlags::TEXICON_ON_TOP=*/ 0x0008); + EditorViewportIconDisplay::Get()->DrawIcon({ + viewportInfo.m_viewportId, + iconTextureId, + iconHighlight, + entityPosition, + EditorViewportIconDisplayInterface::CoordinateSpace::WorldSpace, + AZ::Vector2{iconSize, iconSize} + }); } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index a1949dd44a..3507f532b5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -435,7 +435,7 @@ namespace AzToolsFramework } } - static void DestroyTransformModeSelectionCluster(const ViewportUi::ClusterId clusterId) + static void DestroyCluster(const ViewportUi::ClusterId clusterId) { ViewportUi::ViewportUiRequestBus::Event( ViewportUi::DefaultViewportId, @@ -483,6 +483,26 @@ namespace AzToolsFramework return worldFromLocal.TransformPoint(CalculateCenterOffset(entityId, pivot)); } + void EditorTransformComponentSelection::UpdateSpaceCluster(const ReferenceFrame referenceFrame) + { + auto buttonIdFromFrameFn = [this](const ReferenceFrame referenceFrame) { + switch (referenceFrame) + { + case ReferenceFrame::Local: + return m_spaceCluster.m_localButtonId; + case ReferenceFrame::Parent: + return m_spaceCluster.m_parentButtonId; + case ReferenceFrame::World: + return m_spaceCluster.m_worldButtonId; + } + return m_spaceCluster.m_parentButtonId; + }; + + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, m_spaceCluster.m_spaceClusterId, + buttonIdFromFrameFn(referenceFrame)); + } + namespace ETCS { PivotOrientationResult CalculatePivotOrientation( @@ -789,14 +809,12 @@ namespace AzToolsFramework EntityIdManipulators& entityIdManipulators, OptionalFrame& pivotOverrideFrame, ViewportInteraction::KeyboardModifiers& prevModifiers, - bool& transformChangedInternally) + bool& transformChangedInternally, const AZStd::optional spaceLock) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); entityIdManipulators.m_manipulators->SetLocalPosition(action.LocalPosition()); - const ReferenceFrame referenceFrame = ReferenceFrameFromModifiers(action.m_modifiers); - if (action.m_modifiers.Ctrl()) { // moving with ctrl - setting override @@ -806,6 +824,8 @@ namespace AzToolsFramework } else { + const ReferenceFrame referenceFrame = spaceLock.value_or(ReferenceFrameFromModifiers(action.m_modifiers)); + // note: used for parent and world depending on the current reference frame const auto pivotOrientation = ETCS::CalculateSelectionPivotOrientation( @@ -1027,6 +1047,7 @@ namespace AzToolsFramework EditorManipulatorCommandUndoRedoRequestBus::Handler::BusConnect(entityContextId); CreateTransformModeSelectionCluster(); + CreateSpaceSelectionCluster(); RegisterActions(); SetupBoxSelect(); RefreshSelectedEntityIdsAndRegenerateManipulators(); @@ -1037,7 +1058,9 @@ namespace AzToolsFramework m_selectedEntityIds.clear(); DestroyManipulators(m_entityIdManipulators); - DestroyTransformModeSelectionCluster(m_transformModeClusterId); + DestroyCluster(m_transformModeClusterId); + DestroyCluster(m_spaceCluster.m_spaceClusterId); + UnregisterActions(); m_pivotOverrideFrame.Reset(); @@ -1274,8 +1297,8 @@ namespace AzToolsFramework [this, prevModifiers, manipulatorEntityIds](const LinearManipulator::Action& action) mutable -> void { UpdateTranslationManipulator( - action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, - m_pivotOverrideFrame, prevModifiers, m_transformChangedInternally); + action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, + m_transformChangedInternally, m_spaceCluster.m_spaceLock); }); translationManipulators->InstallLinearManipulatorMouseUpCallback( @@ -1305,8 +1328,8 @@ namespace AzToolsFramework [this, prevModifiers, manipulatorEntityIds](const PlanarManipulator::Action& action) mutable -> void { UpdateTranslationManipulator( - action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, - m_pivotOverrideFrame, prevModifiers, m_transformChangedInternally); + action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, + m_transformChangedInternally, m_spaceCluster.m_spaceLock); }); translationManipulators->InstallPlanarManipulatorMouseUpCallback( @@ -1335,8 +1358,8 @@ namespace AzToolsFramework [this, prevModifiers, manipulatorEntityIds](const SurfaceManipulator::Action& action) mutable -> void { UpdateTranslationManipulator( - action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, - m_pivotOverrideFrame, prevModifiers, m_transformChangedInternally); + action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers, + m_transformChangedInternally, m_spaceCluster.m_spaceLock); }); translationManipulators->InstallSurfaceManipulatorMouseUpCallback( @@ -1414,8 +1437,7 @@ namespace AzToolsFramework [this, prevModifiers, sharedRotationState] (const AngularManipulator::Action& action) mutable -> void { - const ReferenceFrame referenceFrame = ReferenceFrameFromModifiers(action.m_modifiers); - + const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(action.m_modifiers)); const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta; // store the pivot override frame when positioning the manipulator manually (ctrl) // so we don't lose the orientation when adding/removing entities from the selection @@ -2566,6 +2588,64 @@ namespace AzToolsFramework m_transformModeSelectionHandler); } + void EditorTransformComponentSelection::CreateSpaceSelectionCluster() + { + // create the cluster for switching spaces/reference frames + ViewportUi::ViewportUiRequestBus::EventResult( + m_spaceCluster.m_spaceClusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster, + ViewportUi::Alignment::TopRight); + + // create and register the buttons (strings correspond to icons even if the values appear different) + m_spaceCluster.m_worldButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "World"); + m_spaceCluster.m_parentButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Parent"); + m_spaceCluster.m_localButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Local"); + + auto onButtonClicked = [this](ViewportUi::ButtonId buttonId) { + if (buttonId == m_spaceCluster.m_localButtonId) + { + // Unlock + if (m_spaceCluster.m_spaceLock.has_value() && m_spaceCluster.m_spaceLock.value() == ReferenceFrame::Local) + { + m_spaceCluster.m_spaceLock = AZStd::nullopt; + } + else + { + m_spaceCluster.m_spaceLock = ReferenceFrame::Local; + } + } + else if (buttonId == m_spaceCluster.m_parentButtonId) + { + // Unlock + if (m_spaceCluster.m_spaceLock.has_value() && m_spaceCluster.m_spaceLock.value() == ReferenceFrame::Parent) + { + m_spaceCluster.m_spaceLock = AZStd::nullopt; + } + else + { + m_spaceCluster.m_spaceLock = ReferenceFrame::Parent; + } + } + else if (buttonId == m_spaceCluster.m_worldButtonId) + { + // Unlock + if (m_spaceCluster.m_spaceLock.has_value() && m_spaceCluster.m_spaceLock.value() == ReferenceFrame::World) + { + m_spaceCluster.m_spaceLock = AZStd::nullopt; + } + else + { + m_spaceCluster.m_spaceLock = ReferenceFrame::World; + } + } + }; + + m_spaceCluster.m_spaceSelectionHandler = AZ::Event::Handler(onButtonClicked); + + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler, + m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_spaceSelectionHandler); + } + EditorTransformComponentSelectionRequests::Mode EditorTransformComponentSelection::GetTransformMode() { return m_mode; @@ -3277,7 +3357,9 @@ namespace AzToolsFramework ViewportInteraction::BuildMouseButtons( QGuiApplication::mouseButtons()), m_boxSelect.Active()); - const ReferenceFrame referenceFrame = ReferenceFrameFromModifiers(modifiers); + const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(modifiers)); + + UpdateSpaceCluster(referenceFrame); bool refresh = false; if (referenceFrame != m_referenceFrame) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h index 500ae484f8..2bc4d7cbf6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h @@ -106,6 +106,22 @@ namespace AzToolsFramework World, //!< World space (space aligned to world axes - identity). }; + //! Grouping of viewport ui related state for controlling the current reference space of the Editor. + struct SpaceCluster + { + SpaceCluster() = default; + // disable copying and moving (implicit) + SpaceCluster(const SpaceCluster&) = delete; + SpaceCluster& operator=(const SpaceCluster&) = delete; + + ViewportUi::ClusterId m_spaceClusterId; //!< The id identifying the reference space cluster. + ViewportUi::ButtonId m_localButtonId; //!< Local reference space button id. + ViewportUi::ButtonId m_parentButtonId; //!< Parent reference space button id. + ViewportUi::ButtonId m_worldButtonId; //!< World reference space button id. + AZ::Event::Handler m_spaceSelectionHandler; //!< Callback for when a space cluster button is pressed. + AZStd::optional m_spaceLock; //!< Locked reference frame to use if set. + }; + //! Entity selection/interaction handling. //! Provide a suite of functionality for manipulating entities, primarily through their TransformComponent. class EditorTransformComponentSelection @@ -160,6 +176,7 @@ namespace AzToolsFramework void RegenerateManipulators(); void CreateTransformModeSelectionCluster(); + void CreateSpaceSelectionCluster(); void ClearManipulatorTranslationOverride(); void ClearManipulatorOrientationOverride(); @@ -253,6 +270,9 @@ namespace AzToolsFramework void SetEntityLocalScale(AZ::EntityId entityId, float localScale); void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation); + // Responsible for keeping the space cluster in sync with the current reference frame. + void UpdateSpaceCluster(ReferenceFrame referenceFrame); + AZ::EntityId m_hoveredEntityId; //!< What EntityId is the mouse currently hovering over (if any). AZ::EntityId m_cachedEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display. AZ::EntityId m_editorCameraComponentEntityId; //!< The EditorCameraComponent EntityId if it is set. @@ -285,6 +305,7 @@ namespace AzToolsFramework AZ::Event::Handler m_transformModeSelectionHandler; //!< Event handler for the Viewport UI cluster. AzFramework::ClickDetector m_clickDetector; //!< Detect different types of mouse click. AzFramework::CursorState m_cursorState; //!< Track the mouse position and delta movement each frame. + SpaceCluster m_spaceCluster; //!< Related viewport ui state for controlling the current reference space. }; //! The ETCS (EntityTransformComponentSelection) namespace contains functions and data used exclusively by diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 45f52704bf..8d0180f6ce 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -46,6 +46,7 @@ set(FILES API/EditorWindowRequestBus.h API/EntityCompositionRequestBus.h API/EntityCompositionNotificationBus.h + API/EditorViewportIconDisplayInterface.h API/ViewPaneOptions.h Application/Ticker.h Application/Ticker.cpp @@ -292,8 +293,6 @@ set(FILES ToolsComponents/TransformComponent.h ToolsComponents/TransformComponent.cpp ToolsComponents/TransformComponentBus.h - ToolsComponents/TransformScalePropertyHandler.cpp - ToolsComponents/TransformScalePropertyHandler.h ToolsComponents/ScriptEditorComponent.cpp ToolsComponents/ScriptEditorComponent.h ToolsComponents/ToolsAssetCatalogComponent.cpp diff --git a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp index 5ccbd95f09..33009bc0da 100644 --- a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp +++ b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,12 @@ namespace UnitTest m_assetSeedManager = new AzToolsFramework::AssetSeedManager(); m_assetRegistry = new AzFramework::AssetRegistry(); + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_application->Start(AzFramework::Application::Descriptor()); for (int idx = 0; idx < s_totalAssets; idx++) @@ -75,7 +82,7 @@ namespace UnitTest } m_testPlatforms[0] = AzFramework::PlatformId::PC; - m_testPlatforms[1] = AzFramework::PlatformId::ES3; + m_testPlatforms[1] = AzFramework::PlatformId::ANDROID_ID; int platformCount = 0; for(auto thisPlatform : m_testPlatforms) @@ -163,20 +170,20 @@ namespace UnitTest AzFramework::AssetCatalog assetCatalog(useRequestBus); AZStd::string pcCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::PC); - AZStd::string es3CatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ES3); + AZStd::string androidCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ANDROID_ID); if (!assetCatalog.SaveCatalog(pcCatalogFile.c_str(), m_assetRegistry)) { GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to save the asset catalog (PC) file.\n").c_str()); } - if (!assetCatalog.SaveCatalog(es3CatalogFile.c_str(), m_assetRegistry)) + if (!assetCatalog.SaveCatalog(androidCatalogFile.c_str(), m_assetRegistry)) { - GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to save the asset catalog (ES3) file.\n").c_str()); + GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to save the asset catalog (ANDROID) file.\n").c_str()); } m_pcCatalog = new AzToolsFramework::PlatformAddressedAssetCatalog(AzFramework::PlatformId::PC); - m_es3Catalog = new AzToolsFramework::PlatformAddressedAssetCatalog(AzFramework::PlatformId::ES3); + m_androidCatalog = new AzToolsFramework::PlatformAddressedAssetCatalog(AzFramework::PlatformId::ANDROID_ID); const AZStd::string engroot = AZ::Test::GetEngineRootPath(); AZ::IO::FileIOBase::GetInstance()->SetAlias("@engroot@", engroot.c_str()); @@ -220,21 +227,21 @@ namespace UnitTest } auto pcCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::PC); - auto es3CatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ES3); + auto androidCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ANDROID_ID); if (fileIO->Exists(pcCatalogFile.c_str())) { fileIO->Remove(pcCatalogFile.c_str()); } - if (fileIO->Exists(es3CatalogFile.c_str())) + if (fileIO->Exists(androidCatalogFile.c_str())) { - fileIO->Remove(es3CatalogFile.c_str()); + fileIO->Remove(androidCatalogFile.c_str()); } delete m_assetSeedManager; delete m_assetRegistry; delete m_pcCatalog; - delete m_es3Catalog; + delete m_androidCatalog; m_application->Stop(); delete m_application; } @@ -335,10 +342,10 @@ namespace UnitTest m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_PC); // Step we are testing - m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ES3); + m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ANDROID_ID); // Verification - AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3; + AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID; for (const auto& seedInfo : m_assetSeedManager->GetAssetSeedList()) { EXPECT_EQ(seedInfo.m_platformFlags, expectedPlatformFlags); @@ -351,14 +358,14 @@ namespace UnitTest m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC); m_assetSeedManager->AddSeedAsset(assets[1], AzFramework::PlatformFlags::Platform_PC); - m_es3Catalog->UnregisterAsset(assets[2]); + m_androidCatalog->UnregisterAsset(assets[2]); m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_PC); // Step we are testing - m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ES3); + m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ANDROID_ID); // Verification - AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3; + AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID; for (const auto& seedInfo : m_assetSeedManager->GetAssetSeedList()) { if (seedInfo.m_assetId == assets[2]) @@ -376,14 +383,14 @@ namespace UnitTest { // Setup m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC); - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_ANDROID); m_assetSeedManager->AddSeedAsset(assets[1], AzFramework::PlatformFlags::Platform_PC); - m_assetSeedManager->AddSeedAsset(assets[1], AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->AddSeedAsset(assets[1], AzFramework::PlatformFlags::Platform_ANDROID); m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_PC); - m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_ANDROID); // Step we are testing - m_assetSeedManager->RemovePlatformFromAllSeeds(AzFramework::PlatformId::ES3); + m_assetSeedManager->RemovePlatformFromAllSeeds(AzFramework::PlatformId::ANDROID_ID); // Verification for (const auto& seedInfo : m_assetSeedManager->GetAssetSeedList()) @@ -507,8 +514,8 @@ namespace UnitTest void DependencyValidation_MultipleAssetSeeds_MultiplePlatformFlags_ListValid() { - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3); - m_assetSeedManager->AddSeedAsset(assets[5], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID); + m_assetSeedManager->AddSeedAsset(assets[5], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID); AzToolsFramework::AssetFileInfoList assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC); @@ -524,7 +531,7 @@ namespace UnitTest assetList.m_fileInfoList.clear(); - m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID); assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC); @@ -540,7 +547,7 @@ namespace UnitTest EXPECT_TRUE(Search(assetList, assets[8])); assetList.m_fileInfoList.clear(); - m_assetSeedManager->RemoveSeedAsset(assets[5], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->RemoveSeedAsset(assets[5], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID); assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC); @@ -555,7 +562,7 @@ namespace UnitTest EXPECT_TRUE(Search(assetList, assets[8])); // Removing the android flag from the asset should still produce the same result - m_assetSeedManager->RemoveSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ES3); + m_assetSeedManager->RemoveSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID); assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC); @@ -569,7 +576,7 @@ namespace UnitTest EXPECT_TRUE(Search(assetList, assets[7])); EXPECT_TRUE(Search(assetList, assets[8])); - assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ES3); + assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ANDROID_ID); EXPECT_EQ(assetList.m_fileInfoList.size(), 5); EXPECT_TRUE(Search(assetList, assets[0])); @@ -579,8 +586,8 @@ namespace UnitTest EXPECT_TRUE(Search(assetList, assets[4])); // Adding the android flag again to the asset - m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ES3); - assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ES3); + m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID); + assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ANDROID_ID); EXPECT_EQ(assetList.m_fileInfoList.size(), 8); EXPECT_TRUE(Search(assetList, assets[0])); @@ -766,7 +773,7 @@ namespace UnitTest AzFramework::AssetRegistry* m_assetRegistry; ToolsTestApplication* m_application; AzToolsFramework::PlatformAddressedAssetCatalog* m_pcCatalog; - AzToolsFramework::PlatformAddressedAssetCatalog* m_es3Catalog; + AzToolsFramework::PlatformAddressedAssetCatalog* m_androidCatalog; AZ::IO::FileIOStream m_fileStreams[s_totalTestPlatforms][s_totalAssets]; AzFramework::PlatformId m_testPlatforms[s_totalTestPlatforms]; AZStd::string m_assetsPath[s_totalAssets]; @@ -929,7 +936,7 @@ namespace UnitTest TEST_F(AssetSeedManagerTest, AddSeedAssetForValidPlatforms_AllPlatformsValid_SeedAddedForEveryInputPlatform) { using namespace AzFramework; - PlatformFlags validPlatforms = PlatformFlags::Platform_PC | PlatformFlags::Platform_ES3; + PlatformFlags validPlatforms = PlatformFlags::Platform_PC | PlatformFlags::Platform_ANDROID; AZStd::pair result = m_assetSeedManager->AddSeedAssetForValidPlatforms(TestDynamicSliceAssetPath, validPlatforms); // Verify the function outputs @@ -946,8 +953,8 @@ namespace UnitTest TEST_F(AssetSeedManagerTest, AddSeedAssetForValidPlatforms_SomePlatformsValid_SeedAddedForEveryValidPlatform) { using namespace AzFramework; - PlatformFlags validPlatforms = PlatformFlags::Platform_PC | PlatformFlags::Platform_ES3; - PlatformFlags inputPlatforms = validPlatforms | PlatformFlags::Platform_OSX; + PlatformFlags validPlatforms = PlatformFlags::Platform_PC | PlatformFlags::Platform_ANDROID; + PlatformFlags inputPlatforms = validPlatforms | PlatformFlags::Platform_MAC; AZStd::pair result = m_assetSeedManager->AddSeedAssetForValidPlatforms(TestDynamicSliceAssetPath, inputPlatforms); // Verify the function outputs @@ -964,7 +971,7 @@ namespace UnitTest TEST_F(AssetSeedManagerTest, AddSeedAssetForValidPlatforms_NoPlatformsValid_NoSeedAdded) { using namespace AzFramework; - PlatformFlags inputPlatforms = PlatformFlags::Platform_OSX; + PlatformFlags inputPlatforms = PlatformFlags::Platform_MAC; AZStd::pair result = m_assetSeedManager->AddSeedAssetForValidPlatforms(TestDynamicSliceAssetPath, inputPlatforms); // Verify the function outputs @@ -978,30 +985,30 @@ namespace UnitTest TEST_F(AssetSeedManagerTest, Valid_Seed_Remove_ForAllPlatform_OK) { - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); - m_assetSeedManager->RemoveSeedAsset(assets[0].ToString(), AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->RemoveSeedAsset(assets[0].ToString(), AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); const AzFramework::AssetSeedList& seedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(seedList.size(), 0); - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); - m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(secondSeedList.size(), 0); } TEST_F(AssetSeedManagerTest, Valid_Seed_Remove_ForSpecificPlatform_OK) { - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); - m_assetSeedManager->RemoveSeedAsset(assets[0].ToString(), AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->RemoveSeedAsset(assets[0].ToString(), AzFramework::PlatformFlags::Platform_MAC); const AzFramework::AssetSeedList& seedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(seedList.size(), 1); - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC); const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList(); @@ -1010,14 +1017,14 @@ namespace UnitTest TEST_F(AssetSeedManagerTest, Invalid_NotRemove_SeedForAllPlatform_Ok) { - m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); - m_assetSeedManager->RemoveSeedAsset(assets[1].ToString(), AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->RemoveSeedAsset(assets[1].ToString(), AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); const AzFramework::AssetSeedList& seedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(seedList.size(), 1); - m_assetSeedManager->RemoveSeedAsset("asset1.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX); + m_assetSeedManager->RemoveSeedAsset("asset1.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC); const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList(); EXPECT_EQ(secondSeedList.size(), 1); } diff --git a/Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h b/Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h index 1e01229d73..8a394d3ab5 100644 --- a/Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h +++ b/Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h @@ -25,6 +25,8 @@ namespace UnitTests MOCK_METHOD0(GetAbsoluteDevGameFolderPath, const char* ()); MOCK_METHOD0(GetAbsoluteDevRootFolderPath, const char* ()); MOCK_METHOD2(GetRelativeProductPathFromFullSourceOrProductPath, bool(const AZStd::string& fullPath, AZStd::string& relativeProductPath)); + MOCK_METHOD3(GenerateRelativeSourcePath, + bool(const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder)); MOCK_METHOD2(GetFullSourcePathFromRelativeProductPath, bool(const AZStd::string& relPath, AZStd::string& fullSourcePath)); MOCK_METHOD5(GetAssetInfoById, bool(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath)); MOCK_METHOD3(GetSourceInfoBySourcePath, bool(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder)); diff --git a/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp b/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp index 328bf5dea5..0b705338d9 100644 --- a/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp @@ -10,6 +10,8 @@ * */ +#include +#include #include #include #include @@ -49,6 +51,14 @@ namespace UnitTest using namespace AZ::Data; m_application = new ToolsTestApplication("AddressedAssetCatalogManager"); // Shorter name because Setting Registry // specialization are 32 characters max. + + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_application->Start(AzFramework::Application::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash @@ -174,13 +184,13 @@ namespace UnitTest TEST_F(PlatformAddressedAssetCatalogManagerTest, PlatformAddressedAssetCatalogManager_CatalogExistsChecks_Success) { - EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ES3), true); - AZStd::string es3CatalogPath = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ES3); - if (AZ::IO::FileIOBase::GetInstance()->Exists(es3CatalogPath.c_str())) + EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ANDROID_ID), true); + AZStd::string androidCatalogPath = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ANDROID_ID); + if (AZ::IO::FileIOBase::GetInstance()->Exists(androidCatalogPath.c_str())) { - AZ::IO::FileIOBase::GetInstance()->Remove(es3CatalogPath.c_str()); + AZ::IO::FileIOBase::GetInstance()->Remove(androidCatalogPath.c_str()); } - EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ES3), false); + EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ANDROID_ID), false); } class PlatformAddressedAssetCatalogMessageTest : public AzToolsFramework::PlatformAddressedAssetCatalog @@ -241,7 +251,7 @@ namespace UnitTest AzFramework::AssetSystem::NetworkAssetUpdateInterface* notificationInterface = AZ::Interface::Get(); EXPECT_NE(notificationInterface, nullptr); - auto* mockCatalog = new ::testing::NiceMock(AzFramework::PlatformId::ES3); + auto* mockCatalog = new ::testing::NiceMock(AzFramework::PlatformId::ANDROID_ID); AZStd::unique_ptr< ::testing::NiceMock> catalogHolder; catalogHolder.reset(mockCatalog); @@ -249,7 +259,7 @@ namespace UnitTest EXPECT_CALL(*mockCatalog, AssetChanged(testing::_)).Times(0); notificationInterface->AssetChanged(testMessage); - testMessage.m_platform = "es3"; + testMessage.m_platform = "android"; EXPECT_CALL(*mockCatalog, AssetChanged(testing::_)).Times(1); notificationInterface->AssetChanged(testMessage); @@ -260,7 +270,7 @@ namespace UnitTest EXPECT_CALL(*mockCatalog, AssetRemoved(testing::_)).Times(0); notificationInterface->AssetRemoved(testMessage); - testMessage.m_platform = "es3"; + testMessage.m_platform = "android"; EXPECT_CALL(*mockCatalog, AssetRemoved(testing::_)).Times(1); notificationInterface->AssetRemoved(testMessage); } diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/SpawnableCreateBenchmarks.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/SpawnableCreateBenchmarks.cpp index 821daaba82..8e4c2eaad7 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/SpawnableCreateBenchmarks.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/SpawnableCreateBenchmarks.cpp @@ -34,7 +34,8 @@ namespace Benchmark { state.PauseTiming(); - auto spawnable = ::AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(prefabDom); + AzFramework::Spawnable spawnable; + AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(spawnable, prefabDom); state.ResumeTiming(); } diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/SpawnableCreateTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/SpawnableCreateTests.cpp index 2e1cd14b2a..af178ec7a4 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/SpawnableCreateTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/SpawnableCreateTests.cpp @@ -40,7 +40,8 @@ namespace UnitTest //Create Spawnable auto& prefabDom = m_prefabSystemComponent->FindTemplateDom(instance->GetTemplateId()); - auto spawnable = ::AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(prefabDom); + AzFramework::Spawnable spawnable; + AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(spawnable, prefabDom); EXPECT_EQ(spawnable.GetEntities().size() - 1, normalEntityCount); // 1 for container entity const auto& spawnableEntities = spawnable.GetEntities(); @@ -84,7 +85,8 @@ namespace UnitTest //Create Spawnable auto& prefabDom = m_prefabSystemComponent->FindTemplateDom(thirdInstance->GetTemplateId()); - auto spawnable = ::AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(prefabDom); + AzFramework::Spawnable spawnable; + AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(spawnable, prefabDom); EXPECT_EQ(spawnable.GetEntities().size() - 1, normalEntityCount); // 1 for container entity const auto& spawnableEntities = spawnable.GetEntities(); diff --git a/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.cpp b/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.cpp index 8455e6d669..5dcdaa045c 100644 --- a/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.cpp +++ b/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.cpp @@ -141,7 +141,7 @@ namespace UnitTest // Set the new entity's transform to non zero values // This helps validate in comparison tests that the transform values of created entities persist during slice operations - entityTransform->SetLocalScale(AZ::Vector3(5, 5, 5)); + entityTransform->SetLocalUniformScale(5); entityTransform->SetLocalRotation(AZ::Vector3RadToDeg(AZ::Vector3(90, 90, 90))); entityTransform->SetLocalTranslation(AZ::Vector3(100, 100, 100)); diff --git a/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h b/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h index 57ac16673d..7d3d312cdb 100644 --- a/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h +++ b/Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h @@ -149,6 +149,9 @@ namespace UnitTest const char* GetAbsoluteDevGameFolderPath() override { return ""; } const char* GetAbsoluteDevRootFolderPath() override { return ""; } bool GetRelativeProductPathFromFullSourceOrProductPath([[maybe_unused]] const AZStd::string& fullPath, [[maybe_unused]] AZStd::string& relativeProductPath) override { return false; } + bool GenerateRelativeSourcePath( + [[maybe_unused]] const AZStd::string& sourcePath, [[maybe_unused]] AZStd::string& relativePath, + [[maybe_unused]] AZStd::string& watchFolder) override { return false; } bool GetFullSourcePathFromRelativeProductPath([[maybe_unused]] const AZStd::string& relPath, [[maybe_unused]] AZStd::string& fullSourcePath) override { return false; } 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 false; } bool GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override; diff --git a/Code/Framework/GridMate/GridMate/Serialize/CompressionMarshal.cpp b/Code/Framework/GridMate/GridMate/Serialize/CompressionMarshal.cpp index 751e151ec6..1714ee5aa5 100644 --- a/Code/Framework/GridMate/GridMate/Serialize/CompressionMarshal.cpp +++ b/Code/Framework/GridMate/GridMate/Serialize/CompressionMarshal.cpp @@ -488,18 +488,17 @@ void TransformCompressor::Marshal(WriteBuffer& wb, const AZ::Transform& value) c { AZ::u8 flags = 0; auto flagsMarker = wb.InsertMarker(flags); - AZ::Matrix3x3 m33 = AZ::Matrix3x3::CreateFromTransform(value); - AZ::Vector3 scale = m33.ExtractScale(); - AZ::Quaternion rot = AZ::Quaternion::CreateFromMatrix3x3(m33.GetOrthogonalized()); + float scale = value.GetUniformScale(); + AZ::Quaternion rot = value.GetRotation(); if (!rot.IsIdentity()) { flags |= HAS_ROT; wb.Write(rot, QuatCompMarshaler()); } - if (!scale.IsClose(AZ::Vector3::CreateOne())) + if (!AZ::IsClose(scale, 1.0f, AZ::Constants::Tolerance)) { flags |= HAS_SCALE; - wb.Write(scale, Vec3CompMarshaler()); + wb.Write(scale, HalfMarshaler()); } AZ::Vector3 pos = value.GetTranslation(); if (!pos.IsZero()) @@ -527,9 +526,9 @@ void TransformCompressor::Unmarshal(AZ::Transform& value, ReadBuffer& rb) const } if (flags & HAS_SCALE) { - AZ::Vector3 scale; - rb.Read(scale, Vec3CompMarshaler()); - xform.MultiplyByScale(scale); + float scale; + rb.Read(scale, HalfMarshaler()); + xform.MultiplyByUniformScale(scale); } if (flags & HAS_POS) { diff --git a/Code/Framework/Tests/ArchiveCompressionTests.cpp b/Code/Framework/Tests/ArchiveCompressionTests.cpp index fb6beca0b5..c648262599 100644 --- a/Code/Framework/Tests/ArchiveCompressionTests.cpp +++ b/Code/Framework/Tests/ArchiveCompressionTests.cpp @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -40,6 +41,13 @@ namespace UnitTest void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_application->Start({}); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash diff --git a/Code/Framework/Tests/ArchiveTests.cpp b/Code/Framework/Tests/ArchiveTests.cpp index aaca043c4b..6dc081ee72 100644 --- a/Code/Framework/Tests/ArchiveTests.cpp +++ b/Code/Framework/Tests/ArchiveTests.cpp @@ -16,6 +16,7 @@ #include #include // for max path decl +#include #include #include #include // for function<> in the find files callback. @@ -42,6 +43,14 @@ namespace UnitTest { AZ::ComponentApplication::Descriptor descriptor; descriptor.m_stackRecordLevels = 30; + + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_application->Start(descriptor); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash diff --git a/Code/Framework/Tests/AssetCatalog.cpp b/Code/Framework/Tests/AssetCatalog.cpp index 8a89ba349c..1575b0a469 100644 --- a/Code/Framework/Tests/AssetCatalog.cpp +++ b/Code/Framework/Tests/AssetCatalog.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -304,6 +305,13 @@ namespace UnitTest m_app.reset(aznew AzFramework::Application()); AZ::ComponentApplication::Descriptor desc; desc.m_useExistingAllocator = true; + + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app->Start(desc); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is diff --git a/Code/Framework/Tests/ComponentAddRemove.cpp b/Code/Framework/Tests/ComponentAddRemove.cpp index 4fd6db7dde..f635a5ee3b 100644 --- a/Code/Framework/Tests/ComponentAddRemove.cpp +++ b/Code/Framework/Tests/ComponentAddRemove.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -572,6 +573,12 @@ namespace UnitTest { AllocatorsTestFixture::SetUp(); + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + AzFramework::Application::Descriptor descriptor; descriptor.m_enableDrilling = false; m_app.Start(descriptor); diff --git a/Code/Framework/Tests/FileFunc.cpp b/Code/Framework/Tests/FileFunc.cpp index d703164582..8db77fd79b 100644 --- a/Code/Framework/Tests/FileFunc.cpp +++ b/Code/Framework/Tests/FileFunc.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -278,6 +279,12 @@ namespace UnitTest { FrameworkApplicationFixture::SetUp(); + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_serializeContext = AZStd::make_unique(); m_jsonRegistrationContext = AZStd::make_unique(); m_jsonSystemComponent = AZStd::make_unique(); diff --git a/Code/Framework/Tests/FileTagTests.cpp b/Code/Framework/Tests/FileTagTests.cpp index 989c811111..b94131afee 100644 --- a/Code/Framework/Tests/FileTagTests.cpp +++ b/Code/Framework/Tests/FileTagTests.cpp @@ -83,6 +83,7 @@ namespace UnitTest void SetUp() override { AllocatorsFixture::SetUp(); + m_data = AZStd::make_unique(); using namespace AzFramework::FileTag; AZ::ComponentApplication::Descriptor desc; diff --git a/Code/Framework/Tests/FrameworkApplicationFixture.h b/Code/Framework/Tests/FrameworkApplicationFixture.h index f3a90864e7..c2fea389e0 100644 --- a/Code/Framework/Tests/FrameworkApplicationFixture.h +++ b/Code/Framework/Tests/FrameworkApplicationFixture.h @@ -54,7 +54,7 @@ namespace UnitTest }; void SetUp() override - { + { m_appDescriptor.m_allocationRecords = true; m_appDescriptor.m_allocationRecordsSaveNames = true; m_appDescriptor.m_recordingMode = AZ::Debug::AllocationRecords::Mode::RECORD_FULL; diff --git a/Code/Framework/Tests/GenericComponentWrapperTest.cpp b/Code/Framework/Tests/GenericComponentWrapperTest.cpp index b3dac90777..25af10b339 100644 --- a/Code/Framework/Tests/GenericComponentWrapperTest.cpp +++ b/Code/Framework/Tests/GenericComponentWrapperTest.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,12 @@ class WrappedEditorComponentTest protected: void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(AZ::ComponentApplication::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is @@ -178,6 +185,12 @@ class FindWrappedComponentsTest public: void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(AzFramework::Application::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is diff --git a/Code/Framework/Tests/PlatformHelper.cpp b/Code/Framework/Tests/PlatformHelper.cpp index 1ad7794c58..9a23fb8d25 100644 --- a/Code/Framework/Tests/PlatformHelper.cpp +++ b/Code/Framework/Tests/PlatformHelper.cpp @@ -30,11 +30,11 @@ TEST_F(PlatformHelperTest, SinglePlatformFlags_PlatformId_Valid) TEST_F(PlatformHelperTest, MultiplePlatformFlags_PlatformId_Valid) { - AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3; + AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID; auto platforms = AzFramework::PlatformHelper::GetPlatforms(platformFlags); EXPECT_EQ(platforms.size(), 2); EXPECT_EQ(platforms[0], "pc"); - EXPECT_EQ(platforms[1], "es3"); + EXPECT_EQ(platforms[1], "android"); } TEST_F(PlatformHelperTest, SpecialAllFlag_PlatformId_Valid) @@ -42,7 +42,7 @@ TEST_F(PlatformHelperTest, SpecialAllFlag_PlatformId_Valid) AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_ALL; auto platforms = AzFramework::PlatformHelper::GetPlatformsInterpreted(platformFlags); EXPECT_EQ(platforms.size(), AzFramework::NumPlatforms); - EXPECT_THAT(platforms, testing::UnorderedElementsAre("pc", "es3", "ios", "osx_gl", "provo", "salem", "jasper", "server")); + EXPECT_THAT(platforms, testing::UnorderedElementsAre("pc", "android", "ios", "mac", "provo", "salem", "jasper", "server")); } TEST_F(PlatformHelperTest, SpecialAllClientFlag_PlatformId_Valid) @@ -50,7 +50,7 @@ TEST_F(PlatformHelperTest, SpecialAllClientFlag_PlatformId_Valid) AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_ALL_CLIENT; auto platforms = AzFramework::PlatformHelper::GetPlatformsInterpreted(platformFlags); EXPECT_EQ(platforms.size(), AzFramework::NumClientPlatforms); - EXPECT_THAT(platforms, testing::UnorderedElementsAre("pc", "es3", "ios", "osx_gl", "provo", "salem", "jasper")); + EXPECT_THAT(platforms, testing::UnorderedElementsAre("pc", "android", "ios", "mac", "provo", "salem", "jasper")); } TEST_F(PlatformHelperTest, InvalidPlatformFlags_PlatformId_Empty) diff --git a/Code/Framework/Tests/Slices.cpp b/Code/Framework/Tests/Slices.cpp index 6a9ce858c0..8a20c43fb5 100644 --- a/Code/Framework/Tests/Slices.cpp +++ b/Code/Framework/Tests/Slices.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1059,6 +1060,12 @@ namespace UnitTest void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(AzFramework::Application::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is diff --git a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp new file mode 100644 index 0000000000..484b7f46d7 --- /dev/null +++ b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -0,0 +1,348 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class TestApplication : public AzFramework::Application + { + public: + // ComponentApplication + void SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations) override + { + Application::SetSettingsRegistrySpecializations(specializations); + specializations.Append("test"); + specializations.Append("spawnable"); + } + }; + + class SpawnableEntitiesManagerTest : public AllocatorsFixture + { + public: + void SetUp() override + { + AllocatorsFixture::SetUp(); + + m_application = new TestApplication(); + AZ::ComponentApplication::Descriptor descriptor; + m_application->Start(descriptor); + + m_spawnable = aznew AzFramework::Spawnable( + AZ::Data::AssetId::CreateString("{EB2E8A2B-F253-4A90-BBF4-55F2EED786B8}:0"), AZ::Data::AssetData::AssetStatus::Ready); + m_spawnableAsset = new AZ::Data::Asset(m_spawnable, AZ::Data::AssetLoadBehavior::Default); + m_ticket = new AzFramework::EntitySpawnTicket(*m_spawnableAsset); + + auto managerInterface = AzFramework::SpawnableEntitiesInterface::Get(); + m_manager = azrtti_cast(managerInterface); + } + + void TearDown() override + { + delete m_ticket; + m_ticket = nullptr; + // One more tick on the spawnable entities manager in order to delete the ticket fully. + while (m_manager->ProcessQueue( + AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | + AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular) != + AzFramework::SpawnableEntitiesManager::CommandQueueStatus::NoCommandsLeft) + ; + + delete m_spawnableAsset; + m_spawnableAsset = nullptr; + // This will also delete m_spawnable. + + delete m_application; + m_application = nullptr; + + AllocatorsFixture::TearDown(); + } + + void FillSpawnable(size_t numElements) + { + AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities(); + entities.reserve(numElements); + for (size_t i=0; i()); + } + } + + protected: + AZ::Data::Asset* m_spawnableAsset { nullptr }; + AzFramework::SpawnableEntitiesManager* m_manager { nullptr }; + AzFramework::EntitySpawnTicket* m_ticket { nullptr }; + AzFramework::Spawnable* m_spawnable { nullptr }; + TestApplication* m_application { nullptr }; + }; + + // + // SpawnAllEntitities + // + + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_Call_AllEntitiesSpawned) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + size_t spawnedEntitiesCount = 0; + auto callback = + [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(callback)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(NumEntities, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_DeleteTicketBeforeCall_NoCrash) + { + { + AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); + m_manager->SpawnAllEntities(ticket, AzFramework::SpawnablePriority_Default); + } + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + + + // + // SpawnEntities + // + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_DeleteTicketBeforeCall_NoCrash) + { + { + AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); + m_manager->SpawnEntities(ticket, AzFramework::SpawnablePriority_Default, {}); + } + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + + + // + // DespawnAllEntities + // + + TEST_F(SpawnableEntitiesManagerTest, DespawnAllEntities_DeleteTicketBeforeCall_NoCrash) + { + { + AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); + m_manager->DespawnAllEntities(ticket, AzFramework::SpawnablePriority_Default); + } + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + + + // + // ReloadSpawnable + // + + TEST_F(SpawnableEntitiesManagerTest, ReloadSpawnable_DeleteTicketBeforeCall_NoCrash) + { + { + AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); + m_manager->ReloadSpawnable(ticket, AzFramework::SpawnablePriority_Default, *m_spawnableAsset); + } + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + + + // + // ListEntitities + // + + TEST_F(SpawnableEntitiesManagerTest, ListEntities_Call_AllEntitiesAreReported) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + bool allValidEntityIds = true; + size_t spawnedEntitiesCount = 0; + auto callback = [&allValidEntityIds, &spawnedEntitiesCount] + (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + for (auto&& entity : entities) + { + allValidEntityIds = entity->GetId().IsValid() && allValidEntityIds; + } + spawnedEntitiesCount += entities.size(); + }; + + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default); + m_manager->ListEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_TRUE(allValidEntityIds); + EXPECT_EQ(NumEntities, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, ListEntities_DeleteTicketBeforeCall_NoCrash) + { + auto callback = [](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView) {}; + + { + AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); + m_manager->ListEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + } + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + + + // + // ListIndicesAndEntities + // + + TEST_F(SpawnableEntitiesManagerTest, ListIndicesAndEntities_Call_AllEntitiesAreReportedAndIncrementByOne) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + bool allValidEntityIds = true; + size_t spawnedEntitiesCount = 0; + auto callback = [&allValidEntityIds, &spawnedEntitiesCount] + (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstIndexEntityContainerView entities) + { + for (auto&& indexEntityPair : entities) + { + // Since all entities are spawned a single time, the indices should be 0..NumEntities. + if (indexEntityPair.GetIndex() == spawnedEntitiesCount) + { + spawnedEntitiesCount++; + } + allValidEntityIds = indexEntityPair.GetEntity()->GetId().IsValid() && allValidEntityIds; + } + }; + + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default); + m_manager->ListIndicesAndEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_TRUE(allValidEntityIds); + EXPECT_EQ(NumEntities, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, ListIndicesAndEntities_DeleteTicketBeforeCall_NoCrash) + { + auto callback = [](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstIndexEntityContainerView) {}; + + { + AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); + m_manager->ListIndicesAndEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + } + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + + + // + // ClaimEntities + // + + TEST_F(SpawnableEntitiesManagerTest, ClaimEntities_DeleteTicketBeforeCall_NoCrash) + { + auto callback = [](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView) {}; + + { + AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); + m_manager->ClaimEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + } + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + + + // + // Barrier + // + + TEST_F(SpawnableEntitiesManagerTest, Barrier_DeleteTicketBeforeCall_NoCrash) + { + auto callback = [](AzFramework::EntitySpawnTicket::Id) {}; + + { + AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); + m_manager->Barrier(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + } + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + + + // + // Misc. - Priority tests + // + + TEST_F(SpawnableEntitiesManagerTest, Priority_HighBeforeDefault_HigherPriorityCallHappensBeforeDefaultPriorityEvenWhenQueuedLater) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + AzFramework::EntitySpawnTicket highPriorityTicket(*m_spawnableAsset); + + size_t callCounter = 1; + size_t highPriorityCallId = 0; + size_t defaultPriorityCallId = 0; + auto highCallback = [&callCounter, &highPriorityCallId] + (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView) + { + highPriorityCallId = callCounter++; + }; + auto defaultCallback = [&callCounter, &defaultPriorityCallId] + (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView) + { + defaultPriorityCallId = callCounter++; + }; + + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(defaultCallback)); + m_manager->SpawnAllEntities(highPriorityTicket, AzFramework::SpawnablePriority_High, {}, AZStd::move(highCallback)); + m_manager->ProcessQueue( + AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | + AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_LT(highPriorityCallId, defaultPriorityCallId); + } + + TEST_F(SpawnableEntitiesManagerTest, Priority_SameTicket_DefaultPriorityCallHappensBeforeHighPriority) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + size_t callCounter = 1; + size_t highPriorityCallId = 0; + size_t defaultPriorityCallId = 0; + auto highCallback = + [&callCounter, &highPriorityCallId](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView) + { + highPriorityCallId = callCounter++; + }; + auto defaultCallback = + [&callCounter, &defaultPriorityCallId](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView) + { + defaultPriorityCallId = callCounter++; + }; + + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(defaultCallback)); + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_High, {}, AZStd::move(highCallback)); + m_manager->ProcessQueue( + AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | + AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + // Run a second time as the high priority task will be pending at this point. + m_manager->ProcessQueue( + AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | + AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_LT(defaultPriorityCallId, highPriorityCallId); + } +} // namespace UnitTest diff --git a/Code/Framework/Tests/frameworktests_files.cmake b/Code/Framework/Tests/frameworktests_files.cmake index 197bcc9fce..3fc84eb788 100644 --- a/Code/Framework/Tests/frameworktests_files.cmake +++ b/Code/Framework/Tests/frameworktests_files.cmake @@ -11,6 +11,7 @@ set(FILES ../AzCore/Tests/Main.cpp + Spawnable/SpawnableEntitiesManagerTests.cpp ArchiveCompressionTests.cpp ArchiveTests.cpp BehaviorEntityTests.cpp diff --git a/Code/LauncherUnified/Launcher.cpp b/Code/LauncherUnified/Launcher.cpp index 1f29478399..922397c325 100644 --- a/Code/LauncherUnified/Launcher.cpp +++ b/Code/LauncherUnified/Launcher.cpp @@ -488,8 +488,8 @@ namespace O3DELauncher const AZStd::string_view buildTargetName = GetBuildTargetName(); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(*settingsRegistry, buildTargetName); - AZ_TracePrintf("Launcher", R"(Running project "%.*s.)" "\n" - R"(The project name value has been successfully set in the Settings Registry at key "%s/project_name)" + AZ_TracePrintf("Launcher", R"(Running project "%.*s")" "\n" + R"(The project name has been successfully set in the Settings Registry at key "%s/project_name")" R"( for Launcher target "%.*s")" "\n", aznumeric_cast(launcherProjectName.size()), launcherProjectName.data(), AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey, @@ -643,7 +643,8 @@ namespace O3DELauncher if (gEnv && gEnv->pConsole) { // Execute autoexec.cfg to load the initial level - AZ::Interface::Get()->ExecuteConfigFile("autoexec.cfg"); + auto autoExecFile = AZ::IO::FixedMaxPath{pathToAssets} / "autoexec.cfg"; + AZ::Interface::Get()->ExecuteConfigFile(autoExecFile.Native()); // Find out if console command file was passed // via --console-command-file=%filename% and execute it diff --git a/Code/LauncherUnified/Platform/Windows/launcher_project_windows.cmake b/Code/LauncherUnified/Platform/Windows/launcher_project_windows.cmake index bcef59ec5a..35c89caf15 100644 --- a/Code/LauncherUnified/Platform/Windows/launcher_project_windows.cmake +++ b/Code/LauncherUnified/Platform/Windows/launcher_project_windows.cmake @@ -10,6 +10,11 @@ # set(ICON_FILE ${project_real_path}/Gem/Resources/GameSDK.ico) +if(NOT EXISTS ${ICON_FILE}) + # Try another project-relative path + set(ICON_FILE ${project_real_path}/Resources/GameSDK.ico) +endif() + if(NOT EXISTS ${ICON_FILE}) # Try the common LauncherUnified icon instead set(ICON_FILE Resources/GameSDK.ico) diff --git a/Code/LauncherUnified/launcher_generator.cmake b/Code/LauncherUnified/launcher_generator.cmake index 28429729e1..c5d60eb29e 100644 --- a/Code/LauncherUnified/launcher_generator.cmake +++ b/Code/LauncherUnified/launcher_generator.cmake @@ -9,6 +9,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # + +set_property(GLOBAL PROPERTY LAUNCHER_UNIFIED_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) # Launcher targets for a project need to be generated when configuring a project. # When building the engine source, this file will be included by LauncherUnified's CMakeLists.txt # When using an installed engine, this file will be included by the FindLauncherGenerator.cmake script @@ -40,28 +42,8 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC # In the monolithic case, we need to register the gem modules, to do so we will generate a StaticModules.inl # file from StaticModules.in - + set_property(GLOBAL APPEND PROPERTY LY_STATIC_MODULE_PROJECTS_NAME ${project_name}) get_property(game_gem_dependencies GLOBAL PROPERTY LY_DELAYED_DEPENDENCIES_${project_name}.GameLauncher) - - unset(extern_module_declarations) - unset(module_invocations) - - foreach(game_gem_dependency ${game_gem_dependencies}) - # To match the convention on how gems targets vs gem modules are named, we remove the "Gem::" from prefix - # and remove the ".Static" from the suffix - string(REGEX REPLACE "^Gem::" "Gem_" game_gem_dependency ${game_gem_dependency}) - string(REGEX REPLACE "^Project::" "Project_" game_gem_dependency ${game_gem_dependency}) - # Replace "." with "_" - string(REPLACE "." "_" game_gem_dependency ${game_gem_dependency}) - - string(APPEND extern_module_declarations "extern \"C\" AZ::Module* CreateModuleClass_${game_gem_dependency}();\n") - string(APPEND module_invocations " modulesOut.push_back(CreateModuleClass_${game_gem_dependency}());\n") - - endforeach() - - configure_file(StaticModules.in - ${CMAKE_CURRENT_BINARY_DIR}/${project_name}.GameLauncher/Includes/StaticModules.inl - ) set(game_build_dependencies ${game_gem_dependencies} @@ -70,29 +52,9 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC if(PAL_TRAIT_BUILD_SERVER_SUPPORTED) get_property(server_gem_dependencies GLOBAL PROPERTY LY_DELAYED_DEPENDENCIES_${project_name}.ServerLauncher) - - unset(extern_module_declarations) - unset(module_invocations) - - foreach(server_gem_dependency ${server_gem_dependencies}) - # To match the convention on how gems targets vs gem modules are named, we remove the "Gem::" from prefix - # and remove the ".Static" from the suffix - string(REGEX REPLACE "^Gem::" "Gem_" server_gem_dependency ${server_gem_dependency}) - string(REGEX REPLACE "^Project::" "Project_" server_gem_dependency ${server_gem_dependency}) - # Replace "." with "_" - string(REPLACE "." "_" server_gem_dependency ${server_gem_dependency}) - - string(APPEND extern_module_declarations "extern \"C\" AZ::Module* CreateModuleClass_${server_gem_dependency}();\n") - string(APPEND module_invocations " modulesOut.push_back(CreateModuleClass_${server_gem_dependency}());\n") - - endforeach() - - configure_file(StaticModules.in - ${CMAKE_CURRENT_BINARY_DIR}/${project_name}.ServerLauncher/Includes/StaticModules.inl - ) set(server_build_dependencies - ${game_gem_dependencies} + ${server_gem_dependencies} Legacy::CrySystem ) endif() @@ -186,3 +148,65 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC endif() endforeach() + +#! Defer generation of the StaticModules.inl file needed in monolithic builds until after all the CMake targets are known +# This is that the GEM_MODULE target runtime dependencies can be parsed to discover the list of dependent modules +# to load +function(ly_delayed_generate_static_modules_inl) + if(LY_MONOLITHIC_GAME) + get_property(launcher_unified_binary_dir GLOBAL PROPERTY LAUNCHER_UNIFIED_BINARY_DIR) + get_property(project_names GLOBAL PROPERTY LY_STATIC_MODULE_PROJECTS_NAME) + foreach(project_name ${project_names}) + + unset(extern_module_declarations) + unset(module_invocations) + + unset(all_game_gem_dependencies) + ly_get_gem_load_dependencies(all_game_gem_dependencies ${project_name}.GameLauncher) + + foreach(game_gem_dependency ${all_game_gem_dependencies}) + # To match the convention on how gems targets vs gem modules are named, + # we remove the ".Static" from the suffix + # Replace "." with "_" + string(REPLACE "." "_" game_gem_dependency ${game_gem_dependency}) + + string(APPEND extern_module_declarations "extern \"C\" AZ::Module* CreateModuleClass_Gem_${game_gem_dependency}();\n") + string(APPEND module_invocations " modulesOut.push_back(CreateModuleClass_Gem_${game_gem_dependency}());\n") + + endforeach() + + configure_file(${CMAKE_CURRENT_FUNCTION_LIST_DIR}/StaticModules.in + ${launcher_unified_binary_dir}/${project_name}.GameLauncher/Includes/StaticModules.inl + ) + + ly_target_link_libraries(${project_name}.GameLauncher PRIVATE ${all_game_gem_dependencies}) + if(PAL_TRAIT_BUILD_SERVER_SUPPORTED) + get_property(server_gem_dependencies GLOBAL PROPERTY LY_STATIC_MODULE_PROJECTS_DEPENDENCIES_${project_name}.ServerLauncher) + + unset(extern_module_declarations) + unset(module_invocations) + + unset(all_server_gem_dependencies) + ly_get_gem_load_dependencies(all_server_gem_dependencies ${project_name}.ServerLauncher) + foreach(server_gem_dependency ${server_gem_dependencies}) + ly_get_gem_load_dependencies(server_gem_load_dependencies ${server_gem_dependency}) + list(APPEND all_server_gem_dependencies ${server_gem_load_dependencies} ${server_gem_dependency}) + endforeach() + foreach(server_gem_dependency ${all_server_gem_dependencies}) + # Replace "." with "_" + string(REPLACE "." "_" server_gem_dependency ${server_gem_dependency}) + + string(APPEND extern_module_declarations "extern \"C\" AZ::Module* CreateModuleClass_Gem_${server_gem_dependency}();\n") + string(APPEND module_invocations " modulesOut.push_back(CreateModuleClass_Gem_${server_gem_dependency}());\n") + + endforeach() + + configure_file(${CMAKE_CURRENT_FUNCTION_LIST_DIR}/StaticModules.in + ${launcher_unified_binary_dir}/${project_name}.ServerLauncher/Includes/StaticModules.inl + ) + + ly_target_link_libraries(${project_name}.ServerLauncher PRIVATE ${all_server_gem_dependencies}) + endif() + endforeach() + endif() +endfunction() diff --git a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserRequestHandler.cpp b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserRequestHandler.cpp index c8bd157b65..e0ba106875 100644 --- a/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserRequestHandler.cpp +++ b/Code/Sandbox/Editor/AzAssetBrowser/AzAssetBrowserRequestHandler.cpp @@ -137,8 +137,20 @@ namespace AzAssetBrowserRequestHandlerPrivate entityName = AZStd::string::format("Entity%d", GetIEditor()->GetObjectManager()->GetObjectCount()); } - AZ::Entity* newEntity = aznew AZ::Entity(entityName.c_str()); - EditorEntityContextRequestBus::Broadcast(&EditorEntityContextRequests::AddRequiredComponents, *newEntity); + AZ::EntityId targetEntityId; + EditorRequests::Bus::BroadcastResult(targetEntityId, &EditorRequests::CreateNewEntityAtPosition, worldTransform.GetTranslation(), AZ::EntityId()); + + AZ::Entity* newEntity = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(newEntity, &AZ::ComponentApplicationRequests::FindEntity, targetEntityId); + + if (newEntity == nullptr) + { + return; + } + + newEntity->SetName(entityName); + + newEntity->Deactivate(); // Create component. AZ::Component* newComponent = newEntity->CreateComponent(componentTypeId); @@ -151,15 +163,7 @@ namespace AzAssetBrowserRequestHandlerPrivate newEntity->AddComponent(newComponent); } - // Set entity position. - auto* transformComponent = newEntity->FindComponent(); - if (transformComponent) - { - transformComponent->SetWorldTM(worldTransform); - } - - // Add the entity to the editor context, which activates it and creates the sandbox object. - EditorEntityContextRequestBus::Broadcast(&EditorEntityContextRequests::AddEditorEntity, newEntity); + newEntity->Activate(); // set asset after components have been activated in AddEditorEntity method if (newComponent) diff --git a/Code/Sandbox/Editor/CMakeLists.txt b/Code/Sandbox/Editor/CMakeLists.txt index c62e05f012..7be9947e99 100644 --- a/Code/Sandbox/Editor/CMakeLists.txt +++ b/Code/Sandbox/Editor/CMakeLists.txt @@ -129,6 +129,8 @@ ly_add_target( 3rdParty::AWSNativeSDK::Core 3rdParty::Qt::Network Legacy::EditorCore + RUNTIME_DEPENDENCIES + Gem::AtomViewportDisplayInfo ) ly_add_source_properties( SOURCES CryEdit.cpp @@ -173,6 +175,7 @@ ly_add_target( RUNTIME_DEPENDENCIES Legacy::CrySystem Legacy::EditorLib + ProjectManager ) ly_add_translations( TARGETS Editor @@ -243,7 +246,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) Legacy::CryCommon AZ::AzToolsFramework Legacy::EditorLib - Gem::LmbrCentral + RUNTIME_DEPENDENCIES + Gem::LmbrCentral ) ly_add_googletest( NAME Legacy::EditorLib.Tests diff --git a/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp b/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp index 0ce3fa55f5..8f6e927a84 100644 --- a/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp +++ b/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp @@ -421,17 +421,18 @@ QMenu* LevelEditorMenuHandler::CreateFileMenu() fileMenu.AddSeparator(); // Project Settings - auto projectSettingMenu = fileMenu.AddMenu(tr("Project Settings")); + fileMenu.AddAction(ID_FILE_PROJECT_MANAGER_SETTINGS); - // Project Settings Tool + // Platform Settings - Project Settings Tool // Shortcut must be set while adding the action otherwise it doesn't work - projectSettingMenu.Get()->addAction( + fileMenu.Get()->addAction( tr(LyViewPane::ProjectSettingsTool), []() { QtViewPaneManager::instance()->OpenPane(LyViewPane::ProjectSettingsTool); }, tr("Ctrl+Shift+P")); - projectSettingMenu.AddSeparator(); - + fileMenu.AddSeparator(); + fileMenu.AddAction(ID_FILE_PROJECT_MANAGER_NEW); + fileMenu.AddAction(ID_FILE_PROJECT_MANAGER_OPEN); fileMenu.AddSeparator(); // NEWMENUS: NEEDS IMPLEMENTATION diff --git a/Code/Sandbox/Editor/CryEdit.cpp b/Code/Sandbox/Editor/CryEdit.cpp index 0bbc48d5f9..c723e6049a 100644 --- a/Code/Sandbox/Editor/CryEdit.cpp +++ b/Code/Sandbox/Editor/CryEdit.cpp @@ -58,6 +58,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include // AzToolsFramework #include @@ -69,6 +70,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include #include // AzQtComponents @@ -279,6 +281,8 @@ BOOL CCryDocManager::DoPromptFileName(QString& fileName, [[maybe_unused]] UINT n [[maybe_unused]] DWORD lFlags, BOOL bOpenFileDialog, [[maybe_unused]] CDocTemplate* pTemplate) { CLevelFileDialog levelFileDialog(bOpenFileDialog); + levelFileDialog.show(); + levelFileDialog.adjustSize(); if (levelFileDialog.exec() == QDialog::Accepted) { @@ -476,6 +480,11 @@ void CCryEditApp::RegisterActionHandlers() ON_COMMAND(ID_FILE_SAVE_LEVEL, OnFileSave) ON_COMMAND(ID_FILE_EXPORTOCCLUSIONMESH, OnFileExportOcclusionMesh) + + // Project Manager + ON_COMMAND(ID_FILE_PROJECT_MANAGER_SETTINGS, OnOpenProjectManagerSettings) + ON_COMMAND(ID_FILE_PROJECT_MANAGER_NEW, OnOpenProjectManagerNew) + ON_COMMAND(ID_FILE_PROJECT_MANAGER_OPEN, OnOpenProjectManager) } CCryEditApp* CCryEditApp::s_currentInstance = nullptr; @@ -2072,6 +2081,8 @@ void CCryEditApp::OnDocumentationAWSSupport() void CCryEditApp::OnDocumentationFeedback() { FeedbackDialog dialog; + dialog.show(); + dialog.adjustSize(); dialog.exec(); } @@ -2853,6 +2864,34 @@ void CCryEditApp::OnPreferences() */ } +void CCryEditApp::OnOpenProjectManagerSettings() +{ + OpenProjectManager("UpdateProject"); +} + +void CCryEditApp::OnOpenProjectManagerNew() +{ + OpenProjectManager("CreateProject"); +} + +void CCryEditApp::OnOpenProjectManager() +{ + OpenProjectManager("Projects"); +} + +void CCryEditApp::OpenProjectManager(const AZStd::string& screen) +{ + // provide the current project path for in case we want to update the project + AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath(); + const AZStd::string commandLineOptions = AZStd::string::format(" --screen %s --project_path %s", screen.c_str(), projectPath.c_str()); + bool launchSuccess = AzFramework::ProjectManager::LaunchProjectManager(commandLineOptions); + if (!launchSuccess) + { + QMessageBox::critical(AzToolsFramework::GetActiveWindow(), QObject::tr("Failed to launch O3DE Project Manager"), QObject::tr("Failed to find or start the O3dE Project Manager")); + } +} + + ////////////////////////////////////////////////////////////////////////// void CCryEditApp::OnUndo() { @@ -3105,6 +3144,15 @@ CCryEditApp::ECreateLevelResult CCryEditApp::CreateLevel(const QString& levelNam GetIEditor()->GetDocument()->SetPathName(fullyQualifiedLevelName); GetIEditor()->GetGameEngine()->SetLevelPath(levelPath); + if (usePrefabSystemForLevels) + { + auto* service = AZ::Interface::Get(); + if (service) + { + service->CreateNewLevelPrefab(fullyQualifiedLevelName.toUtf8().constData(), DefaultLevelTemplateName); + } + } + if (GetIEditor()->GetDocument()->Save()) { if (!usePrefabSystemForLevels) @@ -3303,6 +3351,8 @@ void CCryEditApp::OnCreateSlice() void CCryEditApp::OnOpenLevel() { CLevelFileDialog levelFileDialog(true); + levelFileDialog.show(); + levelFileDialog.adjustSize(); if (levelFileDialog.exec() == QDialog::Accepted) { diff --git a/Code/Sandbox/Editor/CryEdit.h b/Code/Sandbox/Editor/CryEdit.h index 2e71ca6a58..dc4f015faf 100644 --- a/Code/Sandbox/Editor/CryEdit.h +++ b/Code/Sandbox/Editor/CryEdit.h @@ -229,6 +229,9 @@ public: void OnFileResaveSlices(); void OnFileEditEditorini(); void OnPreferences(); + void OnOpenProjectManagerSettings(); + void OnOpenProjectManagerNew(); + void OnOpenProjectManager(); void OnRedo(); void OnUpdateRedo(QAction* action); void OnUpdateUndo(QAction* action); @@ -358,12 +361,15 @@ AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING private: + static inline constexpr const char* DefaultLevelTemplateName = "Prefabs/Default_Level.prefab"; + struct PythonOutputHandler; AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING AZStd::shared_ptr m_pythonOutputHandler; AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING friend struct PythonTestOutputHandler; + void OpenProjectManager(const AZStd::string& screen); void OnWireframe(); void OnUpdateWireframe(QAction* action); void OnViewConfigureLayout(); diff --git a/Code/Sandbox/Editor/CryEditPy.cpp b/Code/Sandbox/Editor/CryEditPy.cpp index 135dd9878c..edbeccb04f 100644 --- a/Code/Sandbox/Editor/CryEditPy.cpp +++ b/Code/Sandbox/Editor/CryEditPy.cpp @@ -533,7 +533,7 @@ namespace AzToolsFramework ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); behaviorContext->EnumProperty("SystemConfigPlatform_Pc") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); - behaviorContext->EnumProperty("SystemConfigPlatform_OsxGl") + behaviorContext->EnumProperty("SystemConfigPlatform_Mac") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); behaviorContext->EnumProperty("SystemConfigPlatform_OsxMetal") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index ecd11da817..24d1590808 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -463,16 +463,20 @@ void EditorViewportWidget::Update() m_renderViewport->GetViewportContext()->SetCameraTransform(LYTransformToAZTransform(m_Camera.GetMatrix())); } - AZ::Matrix4x4 clipMatrix; - AZ::MakePerspectiveFovMatrixRH( - clipMatrix, - m_Camera.GetFov(), - aznumeric_cast(width()) / aznumeric_cast(height()), - m_Camera.GetNearPlane(), - m_Camera.GetFarPlane(), - true - ); - m_renderViewport->GetViewportContext()->SetCameraProjectionMatrix(clipMatrix); + // Don't override the game mode FOV + if (!GetIEditor()->IsInGameMode()) + { + AZ::Matrix4x4 clipMatrix; + AZ::MakePerspectiveFovMatrixRH( + clipMatrix, + GetFOV(), + aznumeric_cast(width()) / aznumeric_cast(height()), + m_Camera.GetNearPlane(), + m_Camera.GetFarPlane(), + true + ); + m_renderViewport->GetViewportContext()->SetCameraProjectionMatrix(clipMatrix); + } m_updatingCameraPosition = false; @@ -870,6 +874,13 @@ void EditorViewportWidget::OnBeginPrepareRender() int w = m_rcClient.width(); int h = m_rcClient.height(); + // Don't bother doing an FOV calculation if we don't have a valid viewport + // This prevents frustum calculation bugs with a null viewport + if (w <= 1 || h <= 1) + { + return; + } + float fov = gSettings.viewports.fDefaultFov; // match viewport fov to default / selected title menu fov @@ -1221,50 +1232,73 @@ void EditorViewportWidget::SetViewportId(int id) AzFramework::ReloadCameraKeyBindings(); auto controller = AZStd::make_shared(); - controller->SetCameraListBuilderCallback([](AzFramework::Cameras& cameras) - { - auto firstPersonRotateCamera = AZStd::make_shared(AzFramework::CameraFreeLookButton); - auto firstPersonPanCamera = - AZStd::make_shared(AzFramework::CameraFreePanButton, AzFramework::LookPan); - auto firstPersonTranslateCamera = AZStd::make_shared(AzFramework::LookTranslation); - auto firstPersonWheelCamera = AZStd::make_shared(); - - auto orbitCamera = AZStd::make_shared(); - orbitCamera->SetLookAtFn([]() -> AZStd::optional { - AZStd::optional manipulatorTransform; - AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult( - manipulatorTransform, AzToolsFramework::GetEntityContextId(), - &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); - - if (manipulatorTransform) - { - return manipulatorTransform->GetTranslation(); - } - - return {}; + controller->SetCameraListBuilderCallback( + [](AzFramework::Cameras& cameras) + { + auto firstPersonRotateCamera = AZStd::make_shared(AzFramework::CameraFreeLookButton); + auto firstPersonPanCamera = + AZStd::make_shared(AzFramework::CameraFreePanButton, AzFramework::LookPan); + auto firstPersonTranslateCamera = AZStd::make_shared(AzFramework::LookTranslation); + auto firstPersonWheelCamera = AZStd::make_shared(); + + auto orbitCamera = AZStd::make_shared(); + orbitCamera->SetLookAtFn( + [](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional + { + AZStd::optional manipulatorTransform; + AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult( + manipulatorTransform, AzToolsFramework::GetEntityContextId(), + &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); + + // initially attempt to use manipulator transform if one exists (there is a selection) + if (manipulatorTransform) + { + return manipulatorTransform->GetTranslation(); + } + + const float RayDistance = 1000.0f; + AzFramework::RenderGeometry::RayRequest ray; + ray.m_startWorldPosition = position; + ray.m_endWorldPosition = position + direction * RayDistance; + ray.m_onlyVisible = true; + + AzFramework::RenderGeometry::RayResult renderGeometryIntersectionResult; + AzFramework::RenderGeometry::IntersectorBus::EventResult( + renderGeometryIntersectionResult, AzToolsFramework::GetEntityContextId(), + &AzFramework::RenderGeometry::IntersectorInterface::RayIntersect, ray); + + // attempt a ray intersection with any visible mesh and return the intersection position if successful + if (renderGeometryIntersectionResult) + { + return renderGeometryIntersectionResult.m_worldPosition; + } + + // if there is no selection or no intersection, fallback to default camera orbit behavior (ground plane + // intersection) + return {}; + }); + + auto orbitRotateCamera = AZStd::make_shared(AzFramework::CameraOrbitLookButton); + auto orbitTranslateCamera = AZStd::make_shared(AzFramework::OrbitTranslation); + auto orbitDollyWheelCamera = AZStd::make_shared(); + auto orbitDollyMoveCamera = + AZStd::make_shared(AzFramework::CameraOrbitDollyButton); + auto orbitPanCamera = + AZStd::make_shared(AzFramework::CameraOrbitPanButton, AzFramework::OrbitPan); + + orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera); + orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera); + orbitCamera->m_orbitCameras.AddCamera(orbitDollyWheelCamera); + orbitCamera->m_orbitCameras.AddCamera(orbitDollyMoveCamera); + orbitCamera->m_orbitCameras.AddCamera(orbitPanCamera); + + cameras.AddCamera(firstPersonRotateCamera); + cameras.AddCamera(firstPersonPanCamera); + cameras.AddCamera(firstPersonTranslateCamera); + cameras.AddCamera(firstPersonWheelCamera); + cameras.AddCamera(orbitCamera); }); - auto orbitRotateCamera = AZStd::make_shared(AzFramework::CameraOrbitLookButton); - auto orbitTranslateCamera = AZStd::make_shared(AzFramework::OrbitTranslation); - auto orbitDollyWheelCamera = AZStd::make_shared(); - auto orbitDollyMoveCamera = - AZStd::make_shared(AzFramework::CameraOrbitDollyButton); - auto orbitPanCamera = - AZStd::make_shared(AzFramework::CameraOrbitPanButton, AzFramework::OrbitPan); - - orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera); - orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera); - orbitCamera->m_orbitCameras.AddCamera(orbitDollyWheelCamera); - orbitCamera->m_orbitCameras.AddCamera(orbitDollyMoveCamera); - orbitCamera->m_orbitCameras.AddCamera(orbitPanCamera); - - cameras.AddCamera(firstPersonRotateCamera); - cameras.AddCamera(firstPersonPanCamera); - cameras.AddCamera(firstPersonTranslateCamera); - cameras.AddCamera(firstPersonWheelCamera); - cameras.AddCamera(orbitCamera); - }); - m_renderViewport->GetControllerList()->Add(controller); } else @@ -1759,9 +1793,6 @@ void EditorViewportWidget::SetViewTM(const Matrix34& viewTM, bool bMoveOnly) cameraObject->SetWorldTM(camMatrix * AZMatrix3x3ToLYMatrix3x3(lookThroughEntityCorrection)); } } - - using namespace AzToolsFramework; - ComponentEntityObjectRequestBus::Event(cameraObject, &ComponentEntityObjectRequestBus::Events::UpdatePreemptiveUndoCache); } else if (m_viewEntityId.IsValid()) { diff --git a/Code/Sandbox/Editor/LyViewPaneNames.h b/Code/Sandbox/Editor/LyViewPaneNames.h index e95191ce06..b94cda3c52 100644 --- a/Code/Sandbox/Editor/LyViewPaneNames.h +++ b/Code/Sandbox/Editor/LyViewPaneNames.h @@ -30,7 +30,7 @@ namespace LyViewPane static const char* const EntityInspector = "Entity Inspector"; static const char* const EntityInspectorPinned = "Pinned Entity Inspector"; static const char* const LevelInspector = "Level Inspector"; - static const char* const ProjectSettingsTool = "Project Settings Tool"; + static const char* const ProjectSettingsTool = "Edit Platform Settings..."; static const char* const ErrorReport = "Error Report"; static const char* const Console = "Console"; static const char* const ConsoleMenuName = "&Console"; diff --git a/Code/Sandbox/Editor/MainWindow.cpp b/Code/Sandbox/Editor/MainWindow.cpp index 8086293207..9e983c3593 100644 --- a/Code/Sandbox/Editor/MainWindow.cpp +++ b/Code/Sandbox/Editor/MainWindow.cpp @@ -748,6 +748,9 @@ void MainWindow::InitActions() am->AddAction(ID_FILE_EXPORTOCCLUSIONMESH, tr("Export Occlusion Mesh")); am->AddAction(ID_FILE_EDITLOGFILE, tr("Show Log File")); am->AddAction(ID_FILE_RESAVESLICES, tr("Resave All Slices")); + am->AddAction(ID_FILE_PROJECT_MANAGER_SETTINGS, tr("Edit Project Settings...")); + am->AddAction(ID_FILE_PROJECT_MANAGER_NEW, tr("New Project...")); + am->AddAction(ID_FILE_PROJECT_MANAGER_OPEN, tr("Open Project...")); am->AddAction(ID_GAME_PC_ENABLEVERYHIGHSPEC, tr("Very High")).SetCheckable(true) .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateGameSpec); am->AddAction(ID_GAME_PC_ENABLEHIGHSPEC, tr("High")).SetCheckable(true) diff --git a/Code/Sandbox/Editor/RenderViewport.cpp b/Code/Sandbox/Editor/RenderViewport.cpp index f199590e4f..8cef9e802c 100644 --- a/Code/Sandbox/Editor/RenderViewport.cpp +++ b/Code/Sandbox/Editor/RenderViewport.cpp @@ -1259,9 +1259,6 @@ CBaseObject* CRenderViewport::GetCameraObject() const ////////////////////////////////////////////////////////////////////////// void CRenderViewport::OnEditorNotifyEvent(EEditorNotifyEvent event) { - static ICVar* outputToHMD = gEnv->pConsole->GetCVar("output_to_hmd"); - AZ_Assert(outputToHMD, "cvar output_to_hmd is undeclared"); - switch (event) { case eNotify_OnBeginGameMode: @@ -1282,7 +1279,6 @@ void CRenderViewport::OnEditorNotifyEvent(EEditorNotifyEvent event) if (deviceInfo) { - outputToHMD->Set(1); m_previousContext = SetCurrentContext(deviceInfo->renderWidth, deviceInfo->renderHeight); if (m_renderer->GetIStereoRenderer()) { @@ -1313,10 +1309,6 @@ void CRenderViewport::OnEditorNotifyEvent(EEditorNotifyEvent event) // failed to set the context back when done, or set it back to the wrong one. CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "RenderViewport render context was not correctly restored by someone else."); } - if (gSettings.bEnableGameModeVR) - { - outputToHMD->Set(0); - } RestorePreviousContext(m_previousContext); m_bInRotateMode = false; m_bInMoveMode = false; diff --git a/Code/Sandbox/Editor/RenderViewport.h b/Code/Sandbox/Editor/RenderViewport.h index d70dd59b98..b45c92b1c8 100644 --- a/Code/Sandbox/Editor/RenderViewport.h +++ b/Code/Sandbox/Editor/RenderViewport.h @@ -200,6 +200,7 @@ public: { return {}; } + float DeviceScalingFactor() override { return 1.0f; } // AzToolsFramework::ViewportFreezeRequestBus bool IsViewportInputFrozen() override; diff --git a/Code/Sandbox/Editor/Resource.h b/Code/Sandbox/Editor/Resource.h index 31fc9909f1..9c50045367 100644 --- a/Code/Sandbox/Editor/Resource.h +++ b/Code/Sandbox/Editor/Resource.h @@ -313,6 +313,9 @@ #define ID_CREATE_LEVEL_FG_MODULE_FROM_SELECTION 35077 #define ID_GRAPHVIEW_ADD_BLACK_BOX 35078 #define ID_GRAPHVIEW_UNGROUP 35079 +#define ID_FILE_PROJECT_MANAGER_NEW 35080 +#define ID_FILE_PROJECT_MANAGER_OPEN 35081 +#define ID_FILE_PROJECT_MANAGER_SETTINGS 35082 #define ID_TV_TRACKS_TOOLBAR_BASE 35083 // range between ID_TV_TRACKS_TOOLBAR_BASE to ID_TV_TRACKS_TOOLBAR_LAST reserved #define ID_TV_TRACKS_TOOLBAR_LAST 35183 // for up to 100 "Add Tracks..." dynamically added Track View Track buttons #define ID_OPEN_TERRAIN_EDITOR 36007 diff --git a/Code/Sandbox/Editor/TrackView/TrackViewAnimNode.cpp b/Code/Sandbox/Editor/TrackView/TrackViewAnimNode.cpp index ae7077b4fc..35306b9535 100644 --- a/Code/Sandbox/Editor/TrackView/TrackViewAnimNode.cpp +++ b/Code/Sandbox/Editor/TrackView/TrackViewAnimNode.cpp @@ -2012,9 +2012,9 @@ void CTrackViewAnimNode::SetPosRotScaleTracksDefaultValues(bool positionAllowed, } if (scaleAllowed) { - AZ::Vector3 scale = AZ::Vector3::CreateOne(); - AZ::TransformBus::EventResult(scale, entityId, &AZ::TransformBus::Events::GetWorldScale); - m_animNode->SetScale(time, AZVec3ToLYVec3(scale)); + float scale = 1.0f; + AZ::TransformBus::EventResult(scale, entityId, &AZ::TransformBus::Events::GetWorldUniformScale); + m_animNode->SetScale(time, Vec3(scale, scale, scale)); } } } diff --git a/Code/Sandbox/Editor/TrackView/TrackViewSequence.cpp b/Code/Sandbox/Editor/TrackView/TrackViewSequence.cpp index f915c804f8..d26c8fd973 100644 --- a/Code/Sandbox/Editor/TrackView/TrackViewSequence.cpp +++ b/Code/Sandbox/Editor/TrackView/TrackViewSequence.cpp @@ -828,7 +828,7 @@ void CTrackViewSequence::SyncSelectedTracksToBase() const Vec3 scale = pAnimNode->GetScale(); AZ::Transform transform = AZ::Transform::CreateIdentity(); - transform.SetScale(LYVec3ToAZVec3(scale)); + transform.SetUniformScale(LYVec3ToAZVec3(scale).GetMaxElement()); transform.SetRotation(LYQuaternionToAZQuaternion(rotation)); transform.SetTranslation(LYVec3ToAZVec3(position)); @@ -870,7 +870,7 @@ void CTrackViewSequence::SyncSelectedTracksFromBase() pAnimNode->SetPos(AZVec3ToLYVec3(transform.GetTranslation())); pAnimNode->SetRotation(AZQuaternionToLYQuaternion(transform.GetRotation())); - pAnimNode->SetScale(AZVec3ToLYVec3(transform.GetScale())); + pAnimNode->SetScale(AZVec3ToLYVec3(AZ::Vector3(transform.GetUniformScale()))); bNothingWasSynced = false; } diff --git a/Code/Sandbox/Editor/res/o3de_editor.ico b/Code/Sandbox/Editor/res/o3de_editor.ico index 0680ceea19..e7b77c35bf 100644 --- a/Code/Sandbox/Editor/res/o3de_editor.ico +++ b/Code/Sandbox/Editor/res/o3de_editor.ico @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c042fce57915fc749abc7b37de765fd697c3c4d7de045a3d44805aa0ce29901a -size 107016 +oid sha256:d717f77fe01f45df934a61bbc215e5322447d21e16f3cebcf2a02f148178f266 +size 106449 diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/CMakeLists.txt b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/CMakeLists.txt index d02c656b8f..53e04e1dee 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/CMakeLists.txt +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/CMakeLists.txt @@ -35,10 +35,12 @@ ly_add_target( AZ::AzToolsFramework Legacy::CryCommon Legacy::EditorLib - Gem::LmbrCentral AZ::AtomCore Gem::Atom_RPI.Public Gem::AtomToolsFramework.Static + Gem::LmbrCentral.Editor + RUNTIME_DEPENDENCIES + Gem::LmbrCentral.Editor ) ly_add_dependencies(Editor ComponentEntityEditorPlugin) @@ -68,7 +70,9 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzToolsFrameworkTestCommon Legacy::CryCommon Legacy::EditorLib - Gem::LmbrCentral + Gem::LmbrCentral.Editor + RUNTIME_DEPENDENCIES + Gem::LmbrCentral.Editor ) ly_add_googletest( NAME Legacy::ComponentEntityEditorPlugin.Tests diff --git a/Code/Sandbox/Plugins/EditorAssetImporter/AssetImporterPlugin.cpp b/Code/Sandbox/Plugins/EditorAssetImporter/AssetImporterPlugin.cpp index c04f81f50c..ba705295ae 100644 --- a/Code/Sandbox/Plugins/EditorAssetImporter/AssetImporterPlugin.cpp +++ b/Code/Sandbox/Plugins/EditorAssetImporter/AssetImporterPlugin.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,10 @@ AssetImporterPlugin::AssetImporterPlugin(IEditor* editor) opt.showInMenu = false; // this view pane is used to display scene settings, but the user never opens it directly through the Tools menu opt.saveKeyName = "Scene Settings (PREVIEW)"; // user settings for this pane were originally saved with PREVIEW, so ensure that's how they are loaded as well, even after the PREVIEW is removed from the name AzToolsFramework::RegisterViewPane(m_toolName.c_str(), LyViewPane::CategoryTools, opt); + + AzToolsFramework::ToolsApplicationRequestBus::Broadcast( + &AzToolsFramework::ToolsApplicationRequests::CreateAndAddEntityFromComponentTags, + AZStd::vector({ AZ::SceneAPI::Events::AssetImportRequest::GetAssetImportRequestComponentTag() }), "AssetImportersEntity"); } void AssetImporterPlugin::Release() diff --git a/Code/Tools/Android/ProjectBuilder/build.gradle.in b/Code/Tools/Android/ProjectBuilder/build.gradle.in index 66f58294ab..5980984516 100644 --- a/Code/Tools/Android/ProjectBuilder/build.gradle.in +++ b/Code/Tools/Android/ProjectBuilder/build.gradle.in @@ -15,14 +15,14 @@ android { ${SIGNING_CONFIGS} compileSdkVersion sdkVer buildToolsVersion buildToolsVer - + ndkVersion ndkPlatformVer lintOptions { abortOnError false checkReleaseBuilds false } defaultConfig { - minSdkVersion ndkPlatformVer + minSdkVersion minSdkVer targetSdkVersion sdkVer ${NATIVE_CMAKE_SECTION_DEFAULT_CONFIG} } diff --git a/Code/Tools/Android/ProjectBuilder/local.properties.in b/Code/Tools/Android/ProjectBuilder/local.properties.in index 559ea67bcb..4e82cb2940 100644 --- a/Code/Tools/Android/ProjectBuilder/local.properties.in +++ b/Code/Tools/Android/ProjectBuilder/local.properties.in @@ -16,6 +16,5 @@ # For customization when using a Version Control System, please read the # header note. # ${GENERATION_TIMESTAMP} -ndk.dir=${ANDROID_NDK_PATH} sdk.dir=${ANDROID_SDK_PATH} ${CMAKE_DIR_LINE} diff --git a/Code/Tools/Android/ProjectBuilder/root.build.gradle.in b/Code/Tools/Android/ProjectBuilder/root.build.gradle.in index 782a1f26b5..dfce99c3c7 100644 --- a/Code/Tools/Android/ProjectBuilder/root.build.gradle.in +++ b/Code/Tools/Android/ProjectBuilder/root.build.gradle.in @@ -12,10 +12,9 @@ buildscript { repositories { google() jcenter() - } dependencies { - classpath 'com.android.tools.build:gradle:3.6.4' + classpath 'com.android.tools.build:gradle:${ANDROID_GRADLE_PLUGIN_VERSION}' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -26,14 +25,14 @@ allprojects { repositories { google() jcenter() - } } subprojects { ext { + minSdkVer = ${MIN_SDK_VER} sdkVer = ${SDK_VER} - ndkPlatformVer = ${NDK_PLATFORM_VER} + ndkPlatformVer = '${NDK_VERSION}' buildToolsVer = '${SDK_BUILD_TOOL_VER}' lyEngineRoot = '${LY_ENGINE_ROOT}' } diff --git a/Code/Tools/AssetBundler/tests/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetBundler/tests/AssetProcessorPlatformConfig.setreg index 81fcdbcf12..ac546d4f39 100644 --- a/Code/Tools/AssetBundler/tests/AssetProcessorPlatformConfig.setreg +++ b/Code/Tools/AssetBundler/tests/AssetProcessorPlatformConfig.setreg @@ -3,7 +3,7 @@ "AssetProcessor": { "Settings": { "Platforms": { - "es3": "enabled" + "android": "enabled" } } } diff --git a/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp b/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp index 4156a6790d..be6cf61c29 100644 --- a/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp +++ b/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,22 @@ namespace AssetBundler UnitTest::ScopedAllocatorSetupFixture::SetUp(); m_data = AZStd::make_unique(); + AZ::SettingsRegistryInterface* registry = nullptr; + if (!AZ::SettingsRegistry::Get()) + { + AZ::SettingsRegistry::Register(&m_registry); + registry = &m_registry; + } + else + { + registry = AZ::SettingsRegistry::Get(); + } + auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + + m_data->m_applicationManager.reset(aznew MockApplicationManagerTest(0, 0)); m_data->m_applicationManager->Start(AzFramework::Application::Descriptor()); @@ -84,6 +101,12 @@ namespace AssetBundler delete m_data->m_localFileIO; AZ::IO::FileIOBase::SetInstance(m_data->m_priorFileIO); + auto settingsRegistry = AZ::SettingsRegistry::Get(); + if(settingsRegistry == &m_registry) + { + AZ::SettingsRegistry::Unregister(settingsRegistry); + } + m_data->m_applicationManager->Stop(); m_data->m_applicationManager.reset(); m_data.reset(); @@ -99,6 +122,7 @@ namespace AssetBundler }; AZStd::unique_ptr m_data; + AZ::SettingsRegistryImpl m_registry; }; TEST_F(ApplicationManagerTest, ValidatePlatformFlags_ReadConfigFiles_OK) @@ -126,7 +150,7 @@ namespace AssetBundler AzFramework::PlatformFlags platformFlags = GetEnabledPlatformFlags(m_data->m_testEngineRoot.c_str(), m_data->m_testEngineRoot.c_str(), DummyProjectName); AzFramework::PlatformFlags hostPlatformFlag = AzFramework::PlatformHelper::GetPlatformFlag(AzToolsFramework::AssetSystem::GetHostAssetPlatform()); - AzFramework::PlatformFlags expectedFlags = AzFramework::PlatformFlags::Platform_ES3 | AzFramework::PlatformFlags::Platform_IOS | AzFramework::PlatformFlags::Platform_PROVO | hostPlatformFlag; + AzFramework::PlatformFlags expectedFlags = AzFramework::PlatformFlags::Platform_ANDROID | AzFramework::PlatformFlags::Platform_IOS | AzFramework::PlatformFlags::Platform_PROVO | hostPlatformFlag; ASSERT_EQ(platformFlags, expectedFlags); } diff --git a/Code/Tools/AssetBundler/tests/tests_main.cpp b/Code/Tools/AssetBundler/tests/tests_main.cpp index 9e12623b0d..71046a576a 100644 --- a/Code/Tools/AssetBundler/tests/tests_main.cpp +++ b/Code/Tools/AssetBundler/tests/tests_main.cpp @@ -40,14 +40,14 @@ namespace AssetBundler TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_MacFile_OutputBaseNameAndPlatform) { - AZStd::string filePath = "assetInfoFile_osx_gl.xml"; + AZStd::string filePath = "assetInfoFile_mac.xml"; AZStd::string baseFilename; AZStd::string platformIdentifier; AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier); ASSERT_EQ(baseFilename, "assetInfoFile"); - ASSERT_EQ(platformIdentifier, "osx_gl"); + ASSERT_EQ(platformIdentifier, "mac"); } TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_PcFile_OutputBaseNameAndPlatform) @@ -64,14 +64,14 @@ namespace AssetBundler TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_MacFileWithUnderScoreInFileName_OutputBaseNameAndPlatform) { - AZStd::string filePath = "assetInfoFile_test_osx_gl.xml"; + AZStd::string filePath = "assetInfoFile_test_mac.xml"; AZStd::string baseFilename; AZStd::string platformIdentifier; AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier); ASSERT_EQ(baseFilename, "assetInfoFile_test"); - ASSERT_EQ(platformIdentifier, "osx_gl"); + ASSERT_EQ(platformIdentifier, "mac"); } TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_PcFileWithUnderScoreInFileName_OutputBaseNameAndPlatform) @@ -97,21 +97,21 @@ namespace AssetBundler { public: void SetUp() override - { - m_data = AZStd::make_unique(); - m_data->m_application.reset(aznew AzToolsFramework::ToolsApplication()); - m_data->m_application.get()->Start(AzFramework::Application::Descriptor()); - - // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash - // in the unit tests. - AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); - + { + AZ::SettingsRegistryInterface* registry = nullptr; if (!AZ::SettingsRegistry::Get()) { - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(m_registry); AZ::SettingsRegistry::Register(&m_registry); + registry = &m_registry; } + else + { + registry = AZ::SettingsRegistry::Get(); + } + auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath(); if (engineRoot.empty()) @@ -119,6 +119,14 @@ namespace AssetBundler GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to locate engine root.\n").c_str()); } + m_data = AZStd::make_unique(); + m_data->m_application.reset(aznew AzToolsFramework::ToolsApplication()); + m_data->m_application.get()->Start(AzFramework::Application::Descriptor()); + + // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // in the unit tests. + AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); m_data->m_testEngineRoot = (engineRoot / RelativeTestFolder).LexicallyNormal().String(); @@ -144,14 +152,24 @@ namespace AssetBundler } void TearDown() override { - AZ::IO::FileIOBase::SetInstance(nullptr); - delete m_data->m_localFileIO; - AZ::IO::FileIOBase::SetInstance(m_data->m_priorFileIO); + if (m_data) + { + AZ::IO::FileIOBase::SetInstance(nullptr); + delete m_data->m_localFileIO; + AZ::IO::FileIOBase::SetInstance(m_data->m_priorFileIO); + + m_data->m_gemInfoList.set_capacity(0); + m_data->m_gemSeedFilePairList.set_capacity(0); + m_data->m_application.get()->Stop(); + m_data->m_application.reset(); + } + + if(auto settingsRegistry = AZ::SettingsRegistry::Get(); + settingsRegistry == &m_registry) + { + AZ::SettingsRegistry::Unregister(settingsRegistry); + } - m_data->m_gemInfoList.set_capacity(0); - m_data->m_gemSeedFilePairList.set_capacity(0); - m_data->m_application.get()->Stop(); - m_data->m_application.reset(); } void AddGemData(const char* engineRoot, const char* gemName, bool seedFileExists = true) diff --git a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp index c477ec2c9e..4f8b4b9144 100644 --- a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp +++ b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp @@ -78,17 +78,17 @@ namespace AssetBuilderSDK { return AssetBuilderSDK::Platform_PC; } - if (azstricmp(newPlatformName, "es3") == 0) + if (azstricmp(newPlatformName, "android") == 0) { - return AssetBuilderSDK::Platform_ES3; + return AssetBuilderSDK::Platform_ANDROID; } if (azstricmp(newPlatformName, "ios") == 0) { return AssetBuilderSDK::Platform_IOS; } - if (azstricmp(newPlatformName, "osx_gl") == 0) + if (azstricmp(newPlatformName, "mac") == 0) { - return AssetBuilderSDK::Platform_OSX; + return AssetBuilderSDK::Platform_MAC; } if (azstricmp(newPlatformName, "provo") == 0) { @@ -115,12 +115,12 @@ namespace AssetBuilderSDK { case AssetBuilderSDK::Platform_PC: return "pc"; - case AssetBuilderSDK::Platform_ES3: - return "es3"; + case AssetBuilderSDK::Platform_ANDROID: + return "android"; case AssetBuilderSDK::Platform_IOS: return "ios"; - case AssetBuilderSDK::Platform_OSX: - return "osx_gl"; + case AssetBuilderSDK::Platform_MAC: + return "mac"; case AssetBuilderSDK::Platform_PROVO: return "provo"; case AssetBuilderSDK::Platform_SALEM: diff --git a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h index a11cc9a80d..126ecda2ba 100644 --- a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h +++ b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h @@ -148,15 +148,15 @@ namespace AssetBuilderSDK { Platform_NONE = 0x00, Platform_PC = 0x01, - Platform_ES3 = 0x02, + Platform_ANDROID = 0x02, Platform_IOS = 0x04, - Platform_OSX = 0x08, + Platform_MAC = 0x08, Platform_PROVO = 0x20, Platform_SALEM = 0x40, Platform_JASPER = 0x80, //! if you add a new platform entry to this enum, you must add it to allplatforms as well otherwise that platform would not be considered valid. - AllPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_PROVO | Platform_SALEM | Platform_JASPER + AllPlatforms = Platform_PC | Platform_ANDROID | Platform_IOS | Platform_MAC | Platform_PROVO | Platform_SALEM | Platform_JASPER }; #endif // defined(ENABLE_LEGACY_PLATFORMFLAGS_SUPPORT) //! Map data structure to holder parameters that are passed into a job for ProcessJob requests. @@ -503,7 +503,7 @@ namespace AssetBuilderSDK AZ_CLASS_ALLOCATOR(PlatformInfo, AZ::SystemAllocator, 0); AZ_TYPE_INFO(PlatformInfo, "{F7DA39A5-C319-4552-954B-3479E2454D3F}"); - AZStd::string m_identifier; ///< like "pc" or "es3" or "ios"... + AZStd::string m_identifier; ///< like "pc" or "android" or "ios"... AZStd::unordered_set m_tags; ///< The tags like "console" or "tools" on that platform PlatformInfo() = default; diff --git a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp index 196f6b0543..8bc6d0b6f7 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp @@ -655,6 +655,80 @@ namespace AssetProcessor return true; } + bool AssetCatalog::GenerateRelativeSourcePath( + const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& rootFolder) + { + QString normalizedSourcePath = AssetUtilities::NormalizeFilePath(sourcePath.c_str()); + QDir inputPath(normalizedSourcePath); + QString scanFolder; + QString relativeName; + + bool validResult = false; + + AZ_TracePrintf(AssetProcessor::DebugChannel, "ProcessGenerateRelativeSourcePathRequest: %s...\n", sourcePath.c_str()); + + if (sourcePath.empty()) + { + // For an empty input path, do nothing, we'll return an empty, invalid result. + // (We check fullPath instead of inputPath, because an empty fullPath actually produces "." for inputPath) + } + else if (inputPath.isAbsolute()) + { + // For an absolute path, try to convert it to a relative path, based on the existing scan folders. + // To get the inputPath, we use absolutePath() instead of path() so that any . or .. entries get collapsed. + validResult = m_platformConfig->ConvertToRelativePath(inputPath.absolutePath(), relativeName, scanFolder); + } + else if (inputPath.isRelative()) + { + // For a relative path, concatenate it with each scan folder, and see if a valid relative path emerges. + int scanFolders = m_platformConfig->GetScanFolderCount(); + for (int scanIdx = 0; scanIdx < scanFolders; scanIdx++) + { + auto& scanInfo = m_platformConfig->GetScanFolderAt(scanIdx); + QDir possibleRoot(scanInfo.ScanPath()); + QDir possibleAbsolutePath = possibleRoot.filePath(normalizedSourcePath); + // To get the inputPath, we use absolutePath() instead of path() so that any . or .. entries get collapsed. + if (m_platformConfig->ConvertToRelativePath(possibleAbsolutePath.absolutePath(), relativeName, scanFolder)) + { + validResult = true; + break; + } + } + } + + // The input has produced a valid relative path. However, the path might match multiple nested scan folders, + // so look to see if a higher-priority folder has a better match. + if (validResult) + { + QString overridingFile = m_platformConfig->GetOverridingFile(relativeName, scanFolder); + + if (!overridingFile.isEmpty()) + { + overridingFile = AssetUtilities::NormalizeFilePath(overridingFile); + validResult = m_platformConfig->ConvertToRelativePath(overridingFile, relativeName, scanFolder); + } + } + + if (!validResult) + { + // if we are here it means we have failed to determine the relativePath, so we will send back the original path + AZ_TracePrintf(AssetProcessor::DebugChannel, + "GenerateRelativeSourcePath found no valid result, returning original path: %s...\n", sourcePath.c_str()); + + rootFolder.clear(); + relativePath.clear(); + relativePath = sourcePath; + return false; + } + + relativePath = relativeName.toUtf8().data(); + rootFolder = scanFolder.toUtf8().data(); + + AZ_Assert(!relativePath.empty(), "ConvertToRelativePath returned true, but relativePath is empty"); + + return true; + } + bool AssetCatalog::GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullSourcePath) { ProcessGetFullSourcePathFromRelativeProductPathRequest(relPath, fullSourcePath); diff --git a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h index f515fa3658..13dc7892b1 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h +++ b/Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h @@ -95,6 +95,12 @@ namespace AssetProcessor const char* GetAbsoluteDevGameFolderPath() override; const char* GetAbsoluteDevRootFolderPath() override; bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& relativeProductPath) override; + + //! Given a partial or full source file path, respond with its relative path and the watch folder it is relative to. + //! The input source path does not need to exist, so this can be used for new files that haven't been saved yet. + bool GenerateRelativeSourcePath( + const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder) override; + bool GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullSourcePath) override; bool GetAssetInfoById(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath) override; bool GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override; diff --git a/Code/Tools/AssetProcessor/native/AssetManager/AssetRequestHandler.cpp b/Code/Tools/AssetProcessor/native/AssetManager/AssetRequestHandler.cpp index 97b28691dc..2d22d3d136 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/AssetRequestHandler.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/AssetRequestHandler.cpp @@ -104,6 +104,27 @@ namespace return GetRelativeProductPathFromFullSourceOrProductPathResponse(relPathFound, relProductPath); } + GenerateRelativeSourcePathResponse HandleGenerateRelativeSourcePathRequest( + MessageData messageData) + { + bool relPathFound = false; + AZStd::string relPath; + AZStd::string watchFolder; + + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + relPathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GenerateRelativeSourcePath, + messageData.m_message->m_sourcePath, relPath, watchFolder); + + if (!relPathFound) + { + AZ_TracePrintf( + AssetProcessor::ConsoleChannel, "Could not find relative source path for the source file (%s).", + messageData.m_message->m_sourcePath.c_str()); + } + + return GenerateRelativeSourcePathResponse(relPathFound, relPath, watchFolder); + } + SourceAssetInfoResponse HandleSourceAssetInfoRequest(MessageData messageData) { SourceAssetInfoResponse response; @@ -407,6 +428,7 @@ AssetRequestHandler::AssetRequestHandler() m_requestRouter.RegisterMessageHandler(&HandleGetFullSourcePathFromRelativeProductPathRequest); m_requestRouter.RegisterMessageHandler(&HandleGetRelativeProductPathFromFullSourceOrProductPathRequest); + m_requestRouter.RegisterMessageHandler(&HandleGenerateRelativeSourcePathRequest); m_requestRouter.RegisterMessageHandler(&HandleSourceAssetInfoRequest); m_requestRouter.RegisterMessageHandler(&HandleSourceAssetProductsInfoRequest); m_requestRouter.RegisterMessageHandler(&HandleGetScanFoldersRequest); diff --git a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h index 88f74c886b..3dc8bd7a00 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h +++ b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h @@ -57,6 +57,9 @@ namespace AzFramework class GetRelativeProductPathFromFullSourceOrProductPathRequest; class GetRelativeProductPathFromFullSourceOrProductPathResponse; + class GenerateRelativeSourcePathRequest; + class GenerateRelativeSourcePathResponse; + class GetFullSourcePathFromRelativeProductPathRequest; class GetFullSourcePathFromRelativeProductPathResponse; class AssetNotificationMessage; @@ -104,6 +107,8 @@ namespace AssetProcessor using GetAbsoluteAssetDatabaseLocationResponse = AzToolsFramework::AssetSystem::GetAbsoluteAssetDatabaseLocationResponse; using GetRelativeProductPathFromFullSourceOrProductPathRequest = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathRequest; using GetRelativeProductPathFromFullSourceOrProductPathResponse = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathResponse; + using GenerateRelativeSourcePathRequest = AzFramework::AssetSystem::GenerateRelativeSourcePathRequest; + using GenerateRelativeSourcePathResponse = AzFramework::AssetSystem::GenerateRelativeSourcePathResponse; using GetFullSourcePathFromRelativeProductPathRequest = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathRequest; using GetFullSourcePathFromRelativeProductPathResponse = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathResponse; diff --git a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp index 523e39d622..3d7cc3b8b4 100644 --- a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp +++ b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp @@ -291,7 +291,6 @@ namespace AssetProcessor } } - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(registry); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_EngineRegistry(registry, platform, specialization, &scratchBuffer); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_GemRegistries(registry, platform, specialization, &scratchBuffer); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectRegistry(registry, platform, specialization, &scratchBuffer); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp index e92e9afba5..303f0ad0e8 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp @@ -130,6 +130,9 @@ namespace AssetProcessor auto cacheRootKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_cache_path"; settingsRegistry->Set(cacheRootKey, m_data->m_temporarySourceDir.absoluteFilePath("Cache").toUtf8().constData()); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + settingsRegistry->Set(projectPathKey, "AutomatedTesting"); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry); AssetUtilities::ComputeProjectCacheRoot(m_data->m_cacheRootDir); QString normalizedCacheRoot = AssetUtilities::NormalizeDirectoryPath(m_data->m_cacheRootDir.absolutePath()); @@ -221,20 +224,28 @@ namespace AssetProcessor dbConn->SetScanFolder(newScanFolder); } - // build some default configs. - void BuildConfig(const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config) + virtual void AddScanFolders( + const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config, + const AZStd::vector& platforms) { - config.EnablePlatform({ "pc" ,{ "desktop", "renderer" } }, true); - config.EnablePlatform({ "es3" ,{ "mobile", "renderer" } }, true); - config.EnablePlatform({ "fandango" ,{ "console", "renderer" } }, false); - AZStd::vector platforms; - config.PopulatePlatformsForScanFolder(platforms); // PATH DisplayName PortKey root recurse platforms order AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder4"), "subfolder4", "subfolder4", false, false, platforms, -6), config, dbConn); // subfolder 4 overrides subfolder3 AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder3"), "subfolder3", "subfolder3", false, false, platforms, -5), config, dbConn); // subfolder 3 overrides subfolder2 AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder2"), "subfolder2", "subfolder2", false, true, platforms, -2), config, dbConn); // subfolder 2 overrides subfolder1 AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder1"), "subfolder1", "subfolder1", false, true, platforms, -1), config, dbConn); // subfolder1 overrides root AddScanFolder(ScanFolderInfo(tempPath.absolutePath(), "temp", "tempfolder", true, false, platforms, 0), config, dbConn); // add the root + } + + // build some default configs. + void BuildConfig(const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config) + { + config.EnablePlatform({ "pc" ,{ "desktop", "renderer" } }, true); + config.EnablePlatform({ "android" ,{ "mobile", "renderer" } }, true); + config.EnablePlatform({ "fandango" ,{ "console", "renderer" } }, false); + AZStd::vector platforms; + config.PopulatePlatformsForScanFolder(platforms); + + AddScanFolders(tempPath, dbConn, config, platforms); config.AddMetaDataType("exportsettings", QString()); @@ -243,22 +254,22 @@ namespace AssetProcessor AssetRecognizer rec; AssetPlatformSpec specpc; - AssetPlatformSpec speces3; + AssetPlatformSpec specandroid; - speces3.m_extraRCParams = "somerandomparam"; + specandroid.m_extraRCParams = "somerandomparam"; rec.m_name = "random files"; rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.random", AssetBuilderSDK::AssetBuilderPattern::Wildcard); rec.m_platformSpecs.insert("pc", specpc); config.AddRecognizer(rec); specpc.m_extraRCParams = ""; // blank must work - speces3.m_extraRCParams = "testextraparams"; + specandroid.m_extraRCParams = "testextraparams"; const char* builderTxt1Name = "txt files"; rec.m_name = builderTxt1Name; rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard); rec.m_platformSpecs.insert("pc", specpc); - rec.m_platformSpecs.insert("es3", speces3); + rec.m_platformSpecs.insert("android", specandroid); config.AddRecognizer(rec); @@ -269,7 +280,7 @@ namespace AssetProcessor ignore_rec.m_name = "ignore files"; ignore_rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.ignore", AssetBuilderSDK::AssetBuilderPattern::Wildcard); ignore_rec.m_platformSpecs.insert("pc", specpc); - ignore_rec.m_platformSpecs.insert("es3", ignore_spec); + ignore_rec.m_platformSpecs.insert("android", ignore_spec); config.AddRecognizer(ignore_rec); ExcludeAssetRecognizer excludeRecogniser; @@ -356,7 +367,8 @@ namespace AssetProcessor return false; } - // Calls the GetFullSourcePathFromRelativeProductPath function and checks the return results, returning true if it matches both of the expected results + // Calls the GetFullSourcePathFromRelativeProductPath function and checks the return results, returning true if it matches both of + // the expected results bool TestGetFullSourcePath(const QString& fileToCheck, const QDir& tempPath, bool expectToFind, const char* expectedPath) { bool fullPathfound = false; @@ -528,6 +540,177 @@ namespace AssetProcessor ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa/basefile.txt" })); } + class AssetCatalogTestRelativeSourcePath : public AssetCatalogTest + { + public: + QDir GetRoot() + { + // Return an OS-friendly absolute root directory for our tests ("C:/sourceRoot" or "/sourceRoot"). It doesn't + // need to exist, it just needs to be an absolute path. + return QDir::root().filePath("sourceRoot"); + } + + // Set up custom scan folders for the "relative source path" tests, so that we can try out specific combinations of watch folders + void AddScanFolders( + [[maybe_unused]] const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config, + const AZStd::vector& platforms) override + { + QDir root = GetRoot(); + + // This will set up the following watch folders, in highest to lowest priority: + + // /sourceRoot/recurseNested/nested (recurse) + // /sourceRoot/noRecurse (no recurse) + // /sourceRoot/recurseNotNested (recurse) + // /sourceRoot/recurseNested (recurse) + + AddScanFolder( + ScanFolderInfo(root.filePath("recurseNested/nested"), "nested", "nested", false, true, platforms, -4), config, dbConn); + AddScanFolder( + ScanFolderInfo(root.filePath("noRecurse"), "noRecurse", "noRecurse", false, false, platforms, -3), config, dbConn); + AddScanFolder( + ScanFolderInfo(root.filePath("recurseNotNested"), "recurseNotNested", "recurseNotNested", false, true, platforms, -2), + config, dbConn); + AddScanFolder( + ScanFolderInfo(root.filePath("recurseNested"), "recurseNested", "recurseNested", false, true, platforms, -1), + config, dbConn); + } + + // Calls the GenerateRelativeSourcePath function and validates that the results match the expected inputs. + void TestGetRelativeSourcePath( + const AZStd::string& sourcePath, bool expectedToFind, const AZStd::string& expectedPath, const AZStd::string& expectedRoot) + { + bool relPathFound = false; + AZStd::string relPath; + AZStd::string rootFolder; + + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + relPathFound, &AzToolsFramework::AssetSystem::AssetSystemRequest::GenerateRelativeSourcePath, sourcePath, + relPath, rootFolder); + + EXPECT_EQ(relPathFound, expectedToFind); + EXPECT_EQ(relPath, expectedPath); + EXPECT_EQ(rootFolder, expectedRoot); + } + }; + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_EmptySourcePath_ReturnsNoMatch) + { + // Test passes in an empty source path, which shouldn't produce a valid result. + // Input: empty source path + // Output: empty, not found result + TestGetRelativeSourcePath("", false, "", ""); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_AbsolutePathOutsideWatchFolders_ReturnsNoMatch) + { + // Test passes in an invalid absolute source path, which shouldn't produce a valid result. + // Input: "/sourceRoot/noWatchFolder/test.txt" + // Output: not found result, which also returns the input as the relative file name + QDir watchFolder = GetRoot().filePath("noWatchFolder/"); + QString fileToCheck = watchFolder.filePath("test.txt"); + + TestGetRelativeSourcePath(fileToCheck.toUtf8().constData(), false, fileToCheck.toUtf8().constData(), ""); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_AbsolutePathUnderWatchFolder_ReturnsRelativePath) + { + // Test passes in a valid absolute source path, which should produce a valid relative path + // Input: "/sourceRoot/noRecurse/test.txt" + // Output: "test.txt" in folder "/sourceRoot/noRecurse/" + QDir watchFolder = GetRoot().filePath("noRecurse/"); + QString fileToCheck = watchFolder.filePath("test.txt"); + + TestGetRelativeSourcePath(fileToCheck.toUtf8().constData(), true, "test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_AbsolutePathUnderNestedWatchFolders_ReturnsRelativePath) + { + // Test passes in a valid absolute source path that matches a watch folder and a nested watch folder. + // The output relative path should match the nested folder, because the nested folder has a higher priority registered with the AP. + // Input: "/sourceRoot/recurseNested/nested/test.txt" + // Output: "test.txt" in folder "/sourceRoot/recurseNested/nested/" + QDir watchFolder = GetRoot().filePath("recurseNested/nested/"); + QString fileToCheck = watchFolder.filePath("test.txt"); + + TestGetRelativeSourcePath(fileToCheck.toUtf8().constData(), true, "test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_BareFileNameValidInWatchFolder_ReturnsHighestPriorityWatchFolder) + { + // Test passes in a simple file name. The output should be relative to the highest-priority watch folder. + // Input: "test.txt" + // Output: "test.txt" in folder "/sourceRoot/recurseNested/nested/" + QDir watchFolder = GetRoot().filePath("recurseNested/nested/"); + + TestGetRelativeSourcePath("test.txt", true, "test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathValidInWatchFolder_ReturnsHighestPriorityWatchFolder) + { + // Test passes in a relative path. The output should preserve the relative path, but list it as relative to the highest-priority + // watch folder. + // Input: "a/b/c/test.txt" + // Output: "a/b/c/test.txt" in folder "/sourceRoot/recurseNested/nested/" + QDir watchFolder = GetRoot().filePath("recurseNested/nested/"); + + TestGetRelativeSourcePath("a/b/c/test.txt", true, "a/b/c/test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathNotInWatchFolder_ReturnsNoMatch) + { + // Test passes in a relative path that "backs up" two directories. This will be invalid, because no matter which watch directory + // we start at, the result will be outside of any watch directory. + // Input: "../../test.txt" + // Output: not found result, which also returns the input as the relative file name + TestGetRelativeSourcePath("../../test.txt", false, "../../test.txt", ""); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathValidFromNestedWatchFolder_ReturnsOuterFolder) + { + // Test passes in a relative path that "backs up" one directory. This will produce a valid result, because we can back up from + // the "recurseNested/nested/" watch folder to "recurseNested", which is also a valid watch folder. + // Input: "../test.txt" + // Output: "test.txt" in folder "/sourceRoot/recurseNested" + QDir watchFolder = GetRoot().filePath("recurseNested/"); + TestGetRelativeSourcePath("../test.txt", true, "test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathMovesToParentWatchFolder_ReturnsOuterFolder) + { + // Test passes in a relative path that backs up one directory and then forward into a directory. This will produce a valid + // result, because it can validly start in the highest-priority watch folder (recurseNested/nested), move back one into the + // outer watch folder (recurseNested), and then have a subdirectory within it. + // Note that it would also be valid to move from recurseNested to recurseNotNested, but that won't be the result of this test + // because that's a lower-priority match. + // Input: "../recurseNotNested/test.txt" + // Output: "recurseNotNested/test.txt" in folder "/sourceRoot/recurseNested/" + QDir watchFolder = GetRoot().filePath("recurseNested/"); + + TestGetRelativeSourcePath("../recurseNotNested/test.txt", true, "recurseNotNested/test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathMovesToSiblingWatchFolder_ReturnsSiblingFolder) + { + // Test passes in a relative path that backs up two directories and then forward into a directory. This will produce a valid + // result, because it can validly start in the recurseNested/nested folder, move back two folders, then forward into the sibling + // recurseNotNested folder. The result will be a relative path to the sibling folder. + // Input: "../../recurseNotNested/test.txt" + // Output: "test.txt" in folder "/sourceRoot/recurseNotNested/" + QDir watchFolder = GetRoot().filePath("recurseNotNested/"); + + TestGetRelativeSourcePath("../../recurseNotNested/test.txt", true, "test.txt", watchFolder.path().toUtf8().constData()); + } + + TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathBacksOutOfWatchFolder_ReturnsNoMatch) + { + // Test passes in a relative path that adds a directory, then "backs up" three directories. This will be invalid, because no + // matter which watch directory we start at, the result will be outside of any watch directory. + // Input: "../test.txt" + // Output: "test.txt" in folder "/sourceRoot/recurseNested" + TestGetRelativeSourcePath("a/../../../test.txt", false, "a/../../../test.txt", ""); + } + class AssetCatalogTest_GetFullSourcePath : public AssetCatalogTest { @@ -909,7 +1092,7 @@ namespace AssetProcessor { AssetCatalogTest::SetUp(); m_platforms.push_back("pc"); - m_platforms.push_back("es3"); + m_platforms.push_back("android"); // 4 products for one platform, 1 product for the other. m_platformToProductsForSourceWithDifferentProducts["pc"].push_back("subfolder3/basefilez.arc2"); @@ -917,7 +1100,7 @@ namespace AssetProcessor m_platformToProductsForSourceWithDifferentProducts["pc"].push_back("subfolder3/basefile.arc2"); m_platformToProductsForSourceWithDifferentProducts["pc"].push_back("subfolder3/basefile.azm2"); - m_platformToProductsForSourceWithDifferentProducts["es3"].push_back("subfolder3/es3exclusivefile.azm2"); + m_platformToProductsForSourceWithDifferentProducts["android"].push_back("subfolder3/androidexclusivefile.azm2"); m_sourceFileWithDifferentProductsPerPlatform = AZ::Uuid::CreateString("{38032FC9-2838-4D6A-9DA0-79E5E4F20C1B}"); m_sourceFileWithDependency = AZ::Uuid::CreateString("{807C4174-1D19-42AD-B8BC-A59291D9388C}"); @@ -930,7 +1113,7 @@ namespace AssetProcessor // resulting in image processing jobs having different products per platform. Because of this, the material jobs will then have different // dependencies per platform, because each material will depend on a referenced texture and all of that texture's mipmaps. - // Add a source file with 4 products on pc, but 1 on es3 + // Add a source file with 4 products on pc, but 1 on android bool result = AddSourceAndJobForMultiplePlatforms( "subfolder3", "MultiplatformFile.txt", @@ -945,7 +1128,7 @@ namespace AssetProcessor result = AddSourceAndJobForMultiplePlatforms("subfolder3", "FileWithDependency.txt", &(m_data->m_dbConn), sourceFileWithSameProductsJobsPerPlatform, m_platforms, m_sourceFileWithDependency); EXPECT_TRUE(result); - const AZStd::string fileWithDependencyProductPath = "subfolder3/es3exclusivefile.azm2"; + const AZStd::string fileWithDependencyProductPath = "subfolder3/androidexclusivefile.azm2"; for (const AZStd::string& platform : m_platforms) { diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp index c33943f9a7..04949c82a9 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp @@ -265,6 +265,9 @@ namespace AssetProcessorMessagesTests addPairFunc(new GetFullSourcePathFromRelativeProductPathRequest(), new GetFullSourcePathFromRelativeProductPathResponse()); addPairFunc(new GetRelativeProductPathFromFullSourceOrProductPathRequest(), new GetRelativeProductPathFromFullSourceOrProductPathResponse()); + addPairFunc( + new GenerateRelativeSourcePathRequest(), + new GenerateRelativeSourcePathResponse()); addPairFunc(new SourceAssetInfoRequest(), new SourceAssetInfoResponse()); addPairFunc(new SourceAssetProductsInfoRequest(), new SourceAssetProductsInfoResponse()); addPairFunc(new GetScanFoldersRequest(), new GetScanFoldersResponse()); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp index d04e13aef2..7b88321ad9 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp @@ -12,7 +12,7 @@ #include "AssetProcessorTest.h" - +#include #include #include "BaseAssetProcessorTest.h" @@ -67,6 +67,12 @@ namespace AssetProcessor static char processName[] = {"AssetProcessorBatch"}; static char* namePtr = &processName[0]; static char** paramStringArray = &namePtr; + + auto registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_application.reset(new UnitTestAppManager(&numParams, ¶mStringArray)); ASSERT_EQ(m_application->BeforeRun(), ApplicationManager::Status_Success); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h index c866a933dd..4ea0695f1c 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include // for the assert absorber. @@ -44,7 +45,18 @@ namespace AssetProcessor AZ::AllocatorInstance::Create(); } m_errorAbsorber = new UnitTestUtils::AssertAbsorber(); + m_application = AZStd::make_unique(); + + // Inject the AutomatedTesting project as a project path into test fixture + using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + constexpr auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + + "/project_path"; + if(auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry); + } } void TearDown() override diff --git a/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/assetBuilderSDKTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/assetBuilderSDKTest.cpp index c2931c6a09..671d8d96a9 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/assetBuilderSDKTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetBuilderSDK/assetBuilderSDKTest.cpp @@ -28,7 +28,7 @@ namespace AssetProcessor createJobsRequest.m_enabledPlatforms = { { "pc", {} - }, { "es3", {} + }, { "android", {} } }; ASSERT_EQ(createJobsRequest.GetEnabledPlatformsCount(), 2); @@ -48,19 +48,19 @@ namespace AssetProcessor ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_NONE); createJobsRequest.m_enabledPlatforms = { - { "es3", {} + { "android", {} } }; - ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_ES3); + ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_ANDROID); ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_NONE); createJobsRequest.m_enabledPlatforms = { { "pc", {} - }, { "es3", {} + }, { "android", {} } }; ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_PC); - ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ES3); + ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ANDROID); ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(2), AssetBuilderSDK::Platform_NONE); createJobsRequest.m_enabledPlatforms = { @@ -72,24 +72,24 @@ namespace AssetProcessor createJobsRequest.m_enabledPlatforms = { { "pc", {} - }, { "es3", {} + }, { "android", {} }, { "ios", {} - }, { "osx_gl", {} + }, { "mac", {} } }; ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_PC); - ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ES3); + ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ANDROID); ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(2), AssetBuilderSDK::Platform_IOS); - ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(3), AssetBuilderSDK::Platform_OSX); + ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(3), AssetBuilderSDK::Platform_MAC); ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(4), AssetBuilderSDK::Platform_NONE); createJobsRequest.m_enabledPlatforms = { { "pc", {} - }, { "es3", {} + }, { "android", {} } }; ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_PC); - ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ES3); + ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ANDROID); ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(2), AssetBuilderSDK::Platform_NONE); // using a deprecated API should have generated warnings. // but we can't test for it because these warnings are WarningOnce and some other unit test might have already triggered it @@ -106,23 +106,23 @@ namespace AssetProcessor } }; ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_PC)); - ASSERT_FALSE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ES3)); + ASSERT_FALSE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ANDROID)); createJobsRequest.m_enabledPlatforms = { { "pc", {} - }, { "es3", {} + }, { "android", {} } }; ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_PC)); - ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ES3)); + ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ANDROID)); createJobsRequest.m_enabledPlatforms = { { "pc", {} - }, { "es3", {} + }, { "android", {} } }; ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_PC)); - ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ES3)); + ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ANDROID)); // using a deprecated API should have generated warnings. // but we can't test for it because these warnings are WarningOnce and some other unit test might have already triggered it } @@ -133,9 +133,9 @@ namespace AssetProcessor UnitTestUtils::AssertAbsorber absorb; ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_PC)); - ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_ES3)); + ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_ANDROID)); ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_IOS)); - ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_OSX)); + ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_MAC)); ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_PROVO)); ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_SALEM)); ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_JASPER)); diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp index 07d1e48229..bd99cf7a94 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp @@ -204,6 +204,9 @@ void AssetProcessorManagerTest::SetUp() auto cacheRootKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_cache_path"; registry->Set(cacheRootKey, tempPath.absoluteFilePath("Cache").toUtf8().constData()); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_data->m_databaseLocationListener.BusConnect(); @@ -4017,15 +4020,15 @@ TEST_F(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesFor m_assetProcessorManager->m_allowModtimeSkippingFeature = true; AssetUtilities::SetUseFileHashOverride(true, true); - // Enable es3 platform after the initial SetUp has already processed the files for pc + // Enable android platform after the initial SetUp has already processed the files for pc QDir tempPath(m_tempDir.path()); - AssetBuilderSDK::PlatformInfo es3Platform("es3", { "host", "renderer" }); - m_config->EnablePlatform(es3Platform, true); + AssetBuilderSDK::PlatformInfo androidPlatform("android", { "host", "renderer" }); + m_config->EnablePlatform(androidPlatform, true); // There's no way to remove scanfolders and adding a new one after enabling the platform will cause the pc assets to build as well, which we don't want // Instead we'll just const cast the vector and modify the enabled platforms for the scanfolder auto& platforms = const_cast&>(m_config->GetScanFolderAt(0).GetPlatforms()); - platforms.push_back(es3Platform); + platforms.push_back(androidPlatform); // We need the builder fingerprints to be updated to reflect the newly enabled platform m_assetProcessorManager->ComputeBuilderDirty(); @@ -4033,10 +4036,10 @@ TEST_F(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesFor QSet filePaths = BuildFileSet(); SimulateAssetScanner(filePaths); - ExpectWork(4, 2); // CreateJobs = 4, 2 files * 2 platforms. ProcessJobs = 2, just the es3 platform jobs (pc is already processed) + ExpectWork(4, 2); // CreateJobs = 4, 2 files * 2 platforms. ProcessJobs = 2, just the android platform jobs (pc is already processed) - ASSERT_TRUE(m_data->m_processResults[0].m_destinationPath.contains("es3")); - ASSERT_TRUE(m_data->m_processResults[1].m_destinationPath.contains("es3")); + ASSERT_TRUE(m_data->m_processResults[0].m_destinationPath.contains("android")); + ASSERT_TRUE(m_data->m_processResults[1].m_destinationPath.contains("android")); } TEST_F(ModtimeScanningTest, ModtimeSkipping_ModifyTimestamp) diff --git a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp index 31e4996b1e..9c80b267eb 100644 --- a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp @@ -39,7 +39,6 @@ void PlatformConfigurationUnitTests::SetUp() m_qApp = new QCoreApplication(m_argc, m_argv); AssetProcessorTest::SetUp(); AssetUtilities::ResetAssetRoot(); - } void PlatformConfigurationUnitTests::TearDown() @@ -121,14 +120,14 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Regular_Platforms) // verify the data. ASSERT_NE(config.GetPlatformByIdentifier(AzToolsFramework::AssetSystem::GetHostAssetPlatform()), nullptr); - ASSERT_NE(config.GetPlatformByIdentifier("es3"), nullptr); + ASSERT_NE(config.GetPlatformByIdentifier("android"), nullptr); ASSERT_NE(config.GetPlatformByIdentifier("server"), nullptr); - ASSERT_TRUE(config.GetPlatformByIdentifier("es3")->HasTag("mobile")); - ASSERT_TRUE(config.GetPlatformByIdentifier("es3")->HasTag("renderer")); - ASSERT_TRUE(config.GetPlatformByIdentifier("es3")->HasTag("android")); + ASSERT_TRUE(config.GetPlatformByIdentifier("android")->HasTag("mobile")); + ASSERT_TRUE(config.GetPlatformByIdentifier("android")->HasTag("renderer")); + ASSERT_TRUE(config.GetPlatformByIdentifier("android")->HasTag("android")); ASSERT_TRUE(config.GetPlatformByIdentifier("server")->HasTag("server")); - ASSERT_FALSE(config.GetPlatformByIdentifier("es3")->HasTag("server")); + ASSERT_FALSE(config.GetPlatformByIdentifier("android")->HasTag("server")); ASSERT_FALSE(config.GetPlatformByIdentifier("server")->HasTag("renderer")); } @@ -398,7 +397,7 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolderP AZStd::vector platforms = config.GetScanFolderAt(0).GetPlatforms(); ASSERT_EQ(platforms.size(), 4); ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo(AzToolsFramework::AssetSystem::GetHostAssetPlatform(), AZStd::unordered_set{})) != platforms.end()); - ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("es3", AZStd::unordered_set{})) != platforms.end()); + ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("android", AZStd::unordered_set{})) != platforms.end()); ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("ios", AZStd::unordered_set{})) != platforms.end()); ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("server", AZStd::unordered_set{})) != platforms.end()); @@ -406,12 +405,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolderP platforms = config.GetScanFolderAt(1).GetPlatforms(); ASSERT_EQ(platforms.size(), 2); ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo(AzToolsFramework::AssetSystem::GetHostAssetPlatform(), AZStd::unordered_set{})) != platforms.end()); - ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("es3", AZStd::unordered_set{})) != platforms.end()); + ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("android", AZStd::unordered_set{})) != platforms.end()); ASSERT_EQ(config.GetScanFolderAt(2).GetDisplayName(), QString("folder1output")); platforms = config.GetScanFolderAt(2).GetPlatforms(); ASSERT_EQ(platforms.size(), 1); - ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("es3", AZStd::unordered_set{})) != platforms.end()); + ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("android", AZStd::unordered_set{})) != platforms.end()); ASSERT_EQ(config.GetScanFolderAt(3).GetDisplayName(), QString("folder2output")); platforms = config.GetScanFolderAt(3).GetPlatforms(); @@ -455,7 +454,7 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers) using namespace AzToolsFramework::AssetSystem; using namespace AssetProcessor; #if defined(AZ_PLATFORM_WINDOWS) - const char* platformWhichIsNotCurrentPlatform = "osx_gl"; + const char* platformWhichIsNotCurrentPlatform = "mac"; #else const char* platformWhichIsNotCurrentPlatform = "pc"; #endif @@ -476,27 +475,27 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers) ASSERT_EQ(recogs["i_caf"].m_patternMatcher.GetBuilderPattern().m_pattern, "*.i_caf"); ASSERT_EQ(recogs["i_caf"].m_patternMatcher.GetBuilderPattern().m_type, AssetBuilderSDK::AssetBuilderPattern::Wildcard); ASSERT_EQ(recogs["i_caf"].m_platformSpecs.size(), 2); - ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("es3")); + ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); ASSERT_FALSE(recogs["i_caf"].m_platformSpecs.contains("server")); // server has been set to skip. - ASSERT_EQ(recogs["i_caf"].m_platformSpecs["es3"].m_extraRCParams, "mobile"); + ASSERT_EQ(recogs["i_caf"].m_platformSpecs["android"].m_extraRCParams, "mobile"); ASSERT_EQ(recogs["i_caf"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "defaultparams"); ASSERT_TRUE(recogs.contains("caf")); - ASSERT_TRUE(recogs["caf"].m_platformSpecs.contains("es3")); + ASSERT_TRUE(recogs["caf"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["caf"].m_platformSpecs.contains("server")); ASSERT_TRUE(recogs["caf"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); ASSERT_EQ(recogs["caf"].m_platformSpecs.size(), 3); - ASSERT_EQ(recogs["caf"].m_platformSpecs["es3"].m_extraRCParams, "rendererparams"); + ASSERT_EQ(recogs["caf"].m_platformSpecs["android"].m_extraRCParams, "rendererparams"); ASSERT_EQ(recogs["caf"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams"); ASSERT_EQ(recogs["caf"].m_platformSpecs["server"].m_extraRCParams, "copy"); ASSERT_TRUE(recogs.contains("mov")); - ASSERT_TRUE(recogs["mov"].m_platformSpecs.contains("es3")); + ASSERT_TRUE(recogs["mov"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["mov"].m_platformSpecs.contains("server")); ASSERT_TRUE(recogs["mov"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); ASSERT_EQ(recogs["mov"].m_platformSpecs.size(), 3); - ASSERT_EQ(recogs["mov"].m_platformSpecs["es3"].m_extraRCParams, "platformspecificoverride"); + ASSERT_EQ(recogs["mov"].m_platformSpecs["android"].m_extraRCParams, "platformspecificoverride"); ASSERT_EQ(recogs["mov"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams"); ASSERT_EQ(recogs["mov"].m_platformSpecs["server"].m_extraRCParams, "copy"); @@ -504,27 +503,27 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers) // (but platforms can override it) ASSERT_TRUE(recogs.contains("rend")); ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); - ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains("es3")); + ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains("server")); ASSERT_FALSE(recogs["rend"].m_platformSpecs.contains(platformWhichIsNotCurrentPlatform)); // this is not an enabled platform and should not be there. ASSERT_EQ(recogs["rend"].m_platformSpecs.size(), 3); ASSERT_EQ(recogs["rend"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams"); - ASSERT_EQ(recogs["rend"].m_platformSpecs["es3"].m_extraRCParams, "rendererparams"); + ASSERT_EQ(recogs["rend"].m_platformSpecs["android"].m_extraRCParams, "rendererparams"); ASSERT_EQ(recogs["rend"].m_platformSpecs["server"].m_extraRCParams, ""); // default if not specified is empty string ASSERT_TRUE(recogs.contains("alldefault")); ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); - ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains("es3")); + ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains("server")); ASSERT_FALSE(recogs["alldefault"].m_platformSpecs.contains(platformWhichIsNotCurrentPlatform)); // this is not an enabled platform and should not be there. ASSERT_EQ(recogs["alldefault"].m_platformSpecs.size(), 3); ASSERT_EQ(recogs["alldefault"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, ""); - ASSERT_EQ(recogs["alldefault"].m_platformSpecs["es3"].m_extraRCParams, ""); + ASSERT_EQ(recogs["alldefault"].m_platformSpecs["android"].m_extraRCParams, ""); ASSERT_EQ(recogs["alldefault"].m_platformSpecs["server"].m_extraRCParams, ""); ASSERT_TRUE(recogs.contains("skipallbutone")); ASSERT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); - ASSERT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains("es3")); + ASSERT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["skipallbutone"].m_platformSpecs.contains("server")); // server is only one enabled (set to copy) ASSERT_EQ(recogs["skipallbutone"].m_platformSpecs.size(), 1); ASSERT_EQ(recogs["skipallbutone"].m_platformSpecs["server"].m_extraRCParams, "copy"); @@ -550,7 +549,7 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Overrides) // verify the data. ASSERT_NE(config.GetPlatformByIdentifier(AzToolsFramework::AssetSystem::GetHostAssetPlatform()), nullptr); - ASSERT_NE(config.GetPlatformByIdentifier("es3"), nullptr); + ASSERT_NE(config.GetPlatformByIdentifier("android"), nullptr); ASSERT_NE(config.GetPlatformByIdentifier("provo"), nullptr); // this override swaps server with provo in that it turns ON provo, turns off server ASSERT_EQ(config.GetPlatformByIdentifier("server"), nullptr); // this should be off due to overrides @@ -567,11 +566,11 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Overrides) ASSERT_EQ(recogs["i_caf"].m_patternMatcher.GetBuilderPattern().m_pattern, "*.i_caf"); ASSERT_EQ(recogs["i_caf"].m_patternMatcher.GetBuilderPattern().m_type, AssetBuilderSDK::AssetBuilderPattern::Wildcard); ASSERT_EQ(recogs["i_caf"].m_platformSpecs.size(), 3); - ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("es3")); + ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("android")); ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("provo")); ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); ASSERT_FALSE(recogs["i_caf"].m_platformSpecs.contains("server")); // server has been set to skip. - ASSERT_EQ(recogs["i_caf"].m_platformSpecs["es3"].m_extraRCParams, "mobile"); + ASSERT_EQ(recogs["i_caf"].m_platformSpecs["android"].m_extraRCParams, "mobile"); ASSERT_EQ(recogs["i_caf"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "defaultparams"); ASSERT_EQ(recogs["i_caf"].m_platformSpecs["provo"].m_extraRCParams, "copy"); diff --git a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.h b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.h index fe669460a8..0fb67ab947 100644 --- a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.h +++ b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.h @@ -37,6 +37,5 @@ private: int m_argc; char** m_argv; QCoreApplication* m_qApp; - }; diff --git a/Code/Tools/AssetProcessor/native/ui/ProductAssetTreeModel.cpp b/Code/Tools/AssetProcessor/native/ui/ProductAssetTreeModel.cpp index 5efef8166c..68ba64ea1a 100644 --- a/Code/Tools/AssetProcessor/native/ui/ProductAssetTreeModel.cpp +++ b/Code/Tools/AssetProcessor/native/ui/ProductAssetTreeModel.cpp @@ -14,6 +14,7 @@ #include "ProductAssetTreeItemData.h" #include +#include #include namespace AssetProcessor @@ -159,31 +160,33 @@ namespace AssetProcessor return; } + AZ::IO::Path productNamePath(product.m_productName, AZ::IO::PosixPathSeparator); - AZStd::vector tokens; - AzFramework::StringFunc::Tokenize(product.m_productName.c_str(), tokens, AZ_CORRECT_DATABASE_SEPARATOR, false, true); - - if (tokens.empty()) + if (productNamePath.empty()) { AZ_Warning("AssetProcessor", false, "Product id %d has an invalid name: %s", product.m_productID, product.m_productName.c_str()); return; } AssetTreeItem* parentItem = m_root.get(); - AZStd::string fullFolderName; - for (int i = 0; i < tokens.size() - 1; ++i) + AZ::IO::Path currentFullFolderPath; + const AZ::IO::PathView filename = productNamePath.Filename(); + const AZ::IO::PathView fullPathWithoutFilename = productNamePath.RemoveFilename(); + AZStd::fixed_string currentPath; + for (auto pathIt = fullPathWithoutFilename.begin(); pathIt != fullPathWithoutFilename.end(); ++pathIt) { - AzFramework::StringFunc::AssetDatabasePath::Join(fullFolderName.c_str(), tokens[i].c_str(), fullFolderName); - AssetTreeItem* nextParent = parentItem->GetChildFolder(tokens[i].c_str()); + currentPath = pathIt->FixedMaxPathString(); + currentFullFolderPath /= currentPath; + AssetTreeItem* nextParent = parentItem->GetChildFolder(currentPath.c_str()); if (!nextParent) { if (!modelIsResetting) { - QModelIndex parentIndex = parentItem == m_root.get() ? QModelIndex() : createIndex(parentItem->GetRow(), 0, parentItem); + QModelIndex parentIndex = createIndex(parentItem->GetRow(), 0, parentItem); beginInsertRows(parentIndex, parentItem->getChildCount(), parentItem->getChildCount()); } - nextParent = parentItem->CreateChild(ProductAssetTreeItemData::MakeShared(nullptr, fullFolderName, tokens[i].c_str(), true, AZ::Uuid::CreateNull())); - m_productToTreeItem[fullFolderName] = nextParent; + nextParent = parentItem->CreateChild(ProductAssetTreeItemData::MakeShared(nullptr, currentFullFolderPath.Native(), currentPath.c_str(), true, AZ::Uuid::CreateNull())); + m_productToTreeItem[currentFullFolderPath.Native()] = nextParent; // m_productIdToTreeItem is not used for folders, folders don't have product IDs. if (!modelIsResetting) @@ -205,12 +208,12 @@ namespace AssetProcessor if (!modelIsResetting) { - QModelIndex parentIndex = parentItem == m_root.get() ? QModelIndex() : createIndex(parentItem->GetRow(), 0, parentItem); + QModelIndex parentIndex = createIndex(parentItem->GetRow(), 0, parentItem); beginInsertRows(parentIndex, parentItem->getChildCount(), parentItem->getChildCount()); } AZStd::shared_ptr productItemData = - ProductAssetTreeItemData::MakeShared(&product, product.m_productName, tokens[tokens.size() - 1].c_str(), false, sourceId); + ProductAssetTreeItemData::MakeShared(&product, product.m_productName, AZStd::fixed_string(filename.Native()).c_str(), false, sourceId); m_productToTreeItem[product.m_productName] = parentItem->CreateChild(productItemData); m_productIdToTreeItem[product.m_productID] = m_productToTreeItem[product.m_productName]; diff --git a/Code/Tools/AssetProcessor/native/ui/SourceAssetTreeModel.cpp b/Code/Tools/AssetProcessor/native/ui/SourceAssetTreeModel.cpp index dda0a58837..69e60f6733 100644 --- a/Code/Tools/AssetProcessor/native/ui/SourceAssetTreeModel.cpp +++ b/Code/Tools/AssetProcessor/native/ui/SourceAssetTreeModel.cpp @@ -63,8 +63,7 @@ namespace AssetProcessor } - auto fullPath = AZ::IO::Path(scanFolder.m_scanFolder) / source.m_sourceName; - + AZ::IO::Path fullPath = AZ::IO::Path(scanFolder.m_scanFolder, AZ::IO::PosixPathSeparator) / source.m_sourceName; // It's common for Open 3D Engine game projects and scan folders to be in a subfolder // of the engine install. To improve readability of the source files, strip out @@ -78,34 +77,35 @@ namespace AssetProcessor AzFramework::StringFunc::Replace(fullPath.Native(), m_assetRoot.absolutePath().toUtf8(), ""); } - - AZStd::vector tokens; - AzFramework::StringFunc::Tokenize(fullPath.c_str(), tokens, AZ_CORRECT_DATABASE_SEPARATOR, false, true); - - if (tokens.empty()) + if (fullPath.empty()) { - AZ_Warning("AssetProcessor", false, "Source id %s has an invalid name: %s", - source.m_sourceGuid.ToString().c_str(), source.m_sourceName.c_str()); + AZ_Warning( + "AssetProcessor", false, "Source id %s has an invalid name: %s", source.m_sourceGuid.ToString().c_str(), + source.m_sourceName.c_str()); return; } QModelIndex newIndicesStart; AssetTreeItem* parentItem = m_root.get(); - AZStd::string fullFolderName; - for (int i = 0; i < tokens.size() - 1; ++i) + AZ::IO::Path currentFullFolderPath; + const AZ::IO::PathView filename = fullPath.Filename(); + const AZ::IO::PathView fullPathWithoutFilename = fullPath.RemoveFilename(); + AZStd::fixed_string currentPath; + for (auto pathIt = fullPathWithoutFilename.begin(); pathIt != fullPathWithoutFilename.end(); ++pathIt) { - AzFramework::StringFunc::AssetDatabasePath::Join(fullFolderName.c_str(), tokens[i].c_str(), fullFolderName); - AssetTreeItem* nextParent = parentItem->GetChildFolder(tokens[i].c_str()); + currentPath = pathIt->FixedMaxPathString(); + currentFullFolderPath /= currentPath; + AssetTreeItem* nextParent = parentItem->GetChildFolder(currentPath.c_str()); if (!nextParent) { if (!modelIsResetting) { - QModelIndex parentIndex = parentItem == m_root.get() ? QModelIndex() : createIndex(parentItem->GetRow(), 0, parentItem); + QModelIndex parentIndex = createIndex(parentItem->GetRow(), 0, parentItem); beginInsertRows(parentIndex, parentItem->getChildCount(), parentItem->getChildCount()); } - nextParent = parentItem->CreateChild(SourceAssetTreeItemData::MakeShared(nullptr, nullptr, fullFolderName, tokens[i].c_str(), true)); - m_sourceToTreeItem[fullFolderName] = nextParent; + nextParent = parentItem->CreateChild(SourceAssetTreeItemData::MakeShared(nullptr, nullptr, currentFullFolderPath.Native(), currentPath.c_str(), true)); + m_sourceToTreeItem[currentFullFolderPath.Native()] = nextParent; // Folders don't have source IDs, don't add to m_sourceIdToTreeItem if (!modelIsResetting) { @@ -117,12 +117,12 @@ namespace AssetProcessor if (!modelIsResetting) { - QModelIndex parentIndex = parentItem == m_root.get() ? QModelIndex() : createIndex(parentItem->GetRow(), 0, parentItem); + QModelIndex parentIndex = createIndex(parentItem->GetRow(), 0, parentItem); beginInsertRows(parentIndex, parentItem->getChildCount(), parentItem->getChildCount()); } m_sourceToTreeItem[source.m_sourceName] = - parentItem->CreateChild(SourceAssetTreeItemData::MakeShared(&source, &scanFolder, source.m_sourceName, tokens[tokens.size() - 1].c_str(), false)); + parentItem->CreateChild(SourceAssetTreeItemData::MakeShared(&source, &scanFolder, source.m_sourceName, AZStd::fixed_string(filename.Native()).c_str(), false)); m_sourceIdToTreeItem[source.m_sourceID] = m_sourceToTreeItem[source.m_sourceName]; if (!modelIsResetting) { diff --git a/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp index 5940caadb7..079cdb7c66 100644 --- a/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp @@ -51,6 +51,8 @@ namespace AssetProcessor public: using GetRelativeProductPathFromFullSourceOrProductPathRequest = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathRequest; using GetRelativeProductPathFromFullSourceOrProductPathResponse = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathResponse; + using GenerateRelativeSourcePathRequest = AzFramework::AssetSystem::GenerateRelativeSourcePathRequest; + using GenerateRelativeSourcePathResponse = AzFramework::AssetSystem::GenerateRelativeSourcePathResponse; using GetFullSourcePathFromRelativeProductPathRequest = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathRequest; using GetFullSourcePathFromRelativeProductPathResponse = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathResponse; }; @@ -88,34 +90,34 @@ namespace AssetProcessor //AZ_TracePrintf("test", "-------------------------\n"); } - void ComputeFingerprints(unsigned int& fingerprintForPC, unsigned int& fingerprintForES3, PlatformConfiguration& config, QString scanFolderPath, QString relPath) + void ComputeFingerprints(unsigned int& fingerprintForPC, unsigned int& fingerprintForANDROID, PlatformConfiguration& config, QString scanFolderPath, QString relPath) { QString extraInfoForPC; - QString extraInfoForES3; + QString extraInfoForANDROID; RecognizerPointerContainer output; QString filePath = scanFolderPath + "/" + relPath; config.GetMatchingRecognizers(filePath, output); for (const AssetRecognizer* assetRecogniser : output) { extraInfoForPC.append(assetRecogniser->m_platformSpecs["pc"].m_extraRCParams); - extraInfoForES3.append(assetRecogniser->m_platformSpecs["es3"].m_extraRCParams); + extraInfoForANDROID.append(assetRecogniser->m_platformSpecs["android"].m_extraRCParams); extraInfoForPC.append(assetRecogniser->m_version); - extraInfoForES3.append(assetRecogniser->m_version); + extraInfoForANDROID.append(assetRecogniser->m_version); } - //Calculating fingerprints for the file for pc and es3 platforms + //Calculating fingerprints for the file for pc and android platforms AZ::Uuid sourceId = AZ::Uuid("{2206A6E0-FDBC-45DE-B6FE-C2FC63020BD5}"); JobEntry jobEntryPC(scanFolderPath, relPath, relPath, 0, { "pc", {"desktop", "renderer"} }, "", 0, 1, sourceId); - JobEntry jobEntryES3(scanFolderPath, relPath, relPath, 0, { "es3", {"mobile", "renderer"} }, "", 0, 2, sourceId); + JobEntry jobEntryANDROID(scanFolderPath, relPath, relPath, 0, { "android", {"mobile", "renderer"} }, "", 0, 2, sourceId); JobDetails jobDetailsPC; jobDetailsPC.m_extraInformationForFingerprinting = extraInfoForPC.toUtf8().constData(); jobDetailsPC.m_jobEntry = jobEntryPC; - JobDetails jobDetailsES3; - jobDetailsES3.m_extraInformationForFingerprinting = extraInfoForES3.toUtf8().constData(); - jobDetailsES3.m_jobEntry = jobEntryES3; + JobDetails jobDetailsANDROID; + jobDetailsANDROID.m_extraInformationForFingerprinting = extraInfoForANDROID.toUtf8().constData(); + jobDetailsANDROID.m_jobEntry = jobEntryANDROID; fingerprintForPC = AssetUtilities::GenerateFingerprint(jobDetailsPC); - fingerprintForES3 = AssetUtilities::GenerateFingerprint(jobDetailsES3); + fingerprintForANDROID = AssetUtilities::GenerateFingerprint(jobDetailsANDROID); } } @@ -240,7 +242,7 @@ namespace AssetProcessor PlatformConfiguration config; config.EnablePlatform({ "pc",{ "desktop", "renderer" } }, true); - config.EnablePlatform({ "es3",{ "mobile", "renderer" } }, true); + config.EnablePlatform({ "android",{ "mobile", "renderer" } }, true); config.EnablePlatform({ "fandago",{ "console", "renderer" } }, false); AZStd::vector platforms; config.PopulatePlatformsForScanFolder(platforms); @@ -259,9 +261,9 @@ namespace AssetProcessor AssetRecognizer rec; AssetPlatformSpec specpc; - AssetPlatformSpec speces3; + AssetPlatformSpec specandroid; - speces3.m_extraRCParams = "somerandomparam"; + specandroid.m_extraRCParams = "somerandomparam"; rec.m_name = "random files"; rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.random", AssetBuilderSDK::AssetBuilderPattern::Wildcard); rec.m_platformSpecs.insert("pc", specpc); @@ -269,13 +271,13 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(mockAppManager.RegisterAssetRecognizerAsBuilder(rec)); specpc.m_extraRCParams = ""; // blank must work - speces3.m_extraRCParams = "testextraparams"; + specandroid.m_extraRCParams = "testextraparams"; const char* builderTxt1Name = "txt files"; rec.m_name = builderTxt1Name; rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard); rec.m_platformSpecs.insert("pc", specpc); - rec.m_platformSpecs.insert("es3", speces3); + rec.m_platformSpecs.insert("android", specandroid); config.AddRecognizer(rec); @@ -305,21 +307,21 @@ namespace AssetProcessor rec.m_testLockSource = false; specpc.m_extraRCParams = "pcparams"; - speces3.m_extraRCParams = "es3params"; + specandroid.m_extraRCParams = "androidparams"; rec.m_name = "xxx files"; rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.xxx", AssetBuilderSDK::AssetBuilderPattern::Wildcard); rec.m_platformSpecs.insert("pc", specpc); - rec.m_platformSpecs.insert("es3", speces3); + rec.m_platformSpecs.insert("android", specandroid); config.AddRecognizer(rec); mockAppManager.RegisterAssetRecognizerAsBuilder(rec); // two recognizers for the same pattern. rec.m_name = "xxx files 2 (builder2)"; specpc.m_extraRCParams = "pcparams2"; - speces3.m_extraRCParams = "es3params2"; + specandroid.m_extraRCParams = "androidparams2"; rec.m_platformSpecs.insert("pc", specpc); - rec.m_platformSpecs.insert("es3", speces3); + rec.m_platformSpecs.insert("android", specandroid); config.AddRecognizer(rec); mockAppManager.RegisterAssetRecognizerAsBuilder(rec); @@ -330,7 +332,7 @@ namespace AssetProcessor ignore_rec.m_name = "ignore files"; ignore_rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.ignore", AssetBuilderSDK::AssetBuilderPattern::Wildcard); ignore_rec.m_platformSpecs.insert("pc", specpc); - ignore_rec.m_platformSpecs.insert("es3", ignore_spec); + ignore_rec.m_platformSpecs.insert("android", ignore_spec); config.AddRecognizer(ignore_rec); mockAppManager.RegisterAssetRecognizerAsBuilder(ignore_rec); @@ -432,7 +434,7 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); - UNIT_TEST_EXPECT_TRUE(processResults.size() == 1); // 1, since we have one recognizer for .ignore, but the 'es3' platform is marked as skip + UNIT_TEST_EXPECT_TRUE(processResults.size() == 1); // 1, since we have one recognizer for .ignore, but the 'android' platform is marked as skip UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "pc")); @@ -455,16 +457,16 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); - QList es3JobsIndex; + QList androidJobsIndex; QList pcJobsIndex; for (int checkIdx = 0; checkIdx < 4; ++checkIdx) { @@ -662,19 +664,19 @@ namespace AssetProcessor // ---------- test successes ---------- - QStringList es3outs; - es3outs.push_back(cacheRoot.filePath(QString("es3/basefile.arc1"))); - es3outs.push_back(cacheRoot.filePath(QString("es3/basefile.arc2"))); + QStringList androidouts; + androidouts.push_back(cacheRoot.filePath(QString("android/basefile.arc1"))); + androidouts.push_back(cacheRoot.filePath(QString("android/basefile.arc2"))); // feed it the messages its waiting for (create the files) - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "products.")); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[1], "products.")) + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "products.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[1], "products.")) - //Invoke Asset Processed for es3 platform , txt files job description + //Invoke Asset Processed for android platform , txt files job description AssetBuilderSDK::ProcessJobResponse response; response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 1)); - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[1].toUtf8().constData(), AZ::Uuid::CreateNull(), 2)); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 1)); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[1].toUtf8().constData(), AZ::Uuid::CreateNull(), 2)); // make sure legacy SubIds get stored in the DB and in asset response messages. // also make sure they don't get filed for the wrong asset. @@ -693,8 +695,8 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(changedInputResults.size() == 1); // always RELATIVE, always with the product name. - UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "es3"); - UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_platform == "es3"); + UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "android"); + UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_platform == "android"); UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_data == "basefile.arc1"); UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_data == "basefile.arc2"); UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_type == AzFramework::AssetSystem::AssetNotificationMessage::AssetChanged); @@ -793,14 +795,14 @@ namespace AssetProcessor changedInputResults.clear(); assetMessages.clear(); - es3outs.clear(); - es3outs.push_back(cacheRoot.filePath(QString("es3/basefile.azm"))); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "products.")); + androidouts.clear(); + androidouts.push_back(cacheRoot.filePath(QString("android/basefile.azm"))); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "products.")); - //Invoke Asset Processed for es3 platform , txt files2 job description + //Invoke Asset Processed for android platform , txt files2 job description response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); @@ -812,7 +814,7 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(changedInputResults.size() == 1); // always RELATIVE, always with the product name. - UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "es3"); + UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "android"); UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_data == "basefile.azm"); changedInputResults.clear(); @@ -1002,11 +1004,11 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); // --------- same result as above ---------- - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0); @@ -1025,25 +1027,25 @@ namespace AssetProcessor // this time make different products: - QStringList oldes3outs; + QStringList oldandroidouts; QStringList oldpcouts; - oldes3outs = es3outs; + oldandroidouts = androidouts; oldpcouts.append(pcouts); - QStringList es3outs2; + QStringList androidouts2; QStringList pcouts2; - es3outs.clear(); + androidouts.clear(); pcouts.clear(); - es3outs.push_back(cacheRoot.filePath(QString("es3/basefilea.arc1"))); - es3outs2.push_back(cacheRoot.filePath(QString("es3/basefilea.azm"))); - // note that the ES3 outs have changed + androidouts.push_back(cacheRoot.filePath(QString("android/basefilea.arc1"))); + androidouts2.push_back(cacheRoot.filePath(QString("android/basefilea.azm"))); + // note that the android outs have changed // but the pc outs are still the same. pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.arc1"))); pcouts2.push_back(cacheRoot.filePath(QString("pc/basefile.azm"))); // feed it the messages its waiting for (create the files) - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "newfile.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "newfile.")); UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "newfile.")); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs2[0], "newfile.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts2[0], "newfile.")); UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts2[0], "newfile.")); QCoreApplication::processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, 50); @@ -1055,12 +1057,12 @@ namespace AssetProcessor response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); @@ -1083,12 +1085,12 @@ namespace AssetProcessor // The files removed should be the ones we did not emit this time // note that order isn't guarantee but an example output it this - // [0] Removed: ES3, basefile.arc1 - // [1] Removed: ES3, basefile.arc2 - // [2] Changed: ES3, basefilea.arc1 (added) + // [0] Removed: ANDROID, basefile.arc1 + // [1] Removed: ANDROID, basefile.arc2 + // [2] Changed: ANDROID, basefilea.arc1 (added) - // [3] Removed: ES3, basefile.azm - // [4] Changed: ES3, basefilea.azm (added) + // [3] Removed: ANDROID, basefile.azm + // [4] Changed: ANDROID, basefilea.azm (added) // [5] changed: PC, basefile.arc1 (changed) // [6] changed: PC, basefile.azm (changed) @@ -1110,18 +1112,18 @@ namespace AssetProcessor if (element.m_data == "basefilea.arc1") { UNIT_TEST_EXPECT_TRUE(element.m_type == AzFramework::AssetSystem::AssetNotificationMessage::AssetChanged); - UNIT_TEST_EXPECT_TRUE(element.m_platform == "es3"); + UNIT_TEST_EXPECT_TRUE(element.m_platform == "android"); } if (element.m_data == "basefile.arc2") { UNIT_TEST_EXPECT_TRUE(element.m_type == AzFramework::AssetSystem::AssetNotificationMessage::AssetRemoved); - UNIT_TEST_EXPECT_TRUE(element.m_platform == "es3"); + UNIT_TEST_EXPECT_TRUE(element.m_platform == "android"); } } // original products must no longer exist since it should have found and deleted them! - for (QString outFile: oldes3outs) + for (QString outFile: oldandroidouts) { UNIT_TEST_EXPECT_FALSE(QFile::exists(outFile)); } @@ -1145,11 +1147,11 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); // --------- same result as above ---------- - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // pc and es3 + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // pc and android UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0); @@ -1169,12 +1171,12 @@ namespace AssetProcessor response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); @@ -1205,11 +1207,11 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); // --------- same result as above ---------- - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0); @@ -1220,12 +1222,12 @@ namespace AssetProcessor response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); @@ -1243,9 +1245,9 @@ namespace AssetProcessor // deleting the fingerprint file should not have erased the products UNIT_TEST_EXPECT_TRUE(QFile::exists(pcouts[0])); - UNIT_TEST_EXPECT_TRUE(QFile::exists(es3outs[0])); + UNIT_TEST_EXPECT_TRUE(QFile::exists(androidouts[0])); UNIT_TEST_EXPECT_TRUE(QFile::exists(pcouts2[0])); - UNIT_TEST_EXPECT_TRUE(QFile::exists(es3outs2[0])); + UNIT_TEST_EXPECT_TRUE(QFile::exists(androidouts2[0])); changedInputResults.clear(); assetMessages.clear(); @@ -1304,9 +1306,9 @@ namespace AssetProcessor } UNIT_TEST_EXPECT_FALSE(QFile::exists(pcouts[0])); - UNIT_TEST_EXPECT_FALSE(QFile::exists(es3outs[0])); + UNIT_TEST_EXPECT_FALSE(QFile::exists(androidouts[0])); UNIT_TEST_EXPECT_FALSE(QFile::exists(pcouts2[0])); - UNIT_TEST_EXPECT_FALSE(QFile::exists(es3outs2[0])); + UNIT_TEST_EXPECT_FALSE(QFile::exists(androidouts2[0])); changedInputResults.clear(); assetMessages.clear(); @@ -1321,28 +1323,28 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); // --------- same result as above ---------- - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "newfile.")); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs2[0], "newfile.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "newfile.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts2[0], "newfile.")); UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts2[0], "newfile.")); // send both done messages simultaneously! response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData())); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData())); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); // send one failure only for PC : @@ -1420,12 +1422,12 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(changedInputResults.size() == 3); UNIT_TEST_EXPECT_TRUE(assetMessages.size() == 3); - // which should be for the ES3: + // which should be for the ANDROID: UNIT_TEST_EXPECT_TRUE(AssetUtilities::NormalizeFilePath(changedInputResults[0].first) == absolutePath); // always RELATIVE, always with the product name. UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_data == "basefilea.arc1" || assetMessages[0].m_data == "basefilea.azm"); - UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "es3"); + UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "android"); for (auto& payload : payloadList) { @@ -1526,28 +1528,28 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); // --------- same result as above ---------- - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0); - es3outs.clear(); - es3outs2.clear(); + androidouts.clear(); + androidouts2.clear(); pcouts.clear(); pcouts2.clear(); - es3outs.push_back(cacheRoot.filePath(QString("es3/basefilez.arc2"))); - es3outs2.push_back(cacheRoot.filePath(QString("es3/basefileaz.azm2"))); - // note that the ES3 outs have changed + androidouts.push_back(cacheRoot.filePath(QString("android/basefilez.arc2"))); + androidouts2.push_back(cacheRoot.filePath(QString("android/basefileaz.azm2"))); + // note that the android outs have changed // but the pc outs are still the same. pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.arc2"))); pcouts2.push_back(cacheRoot.filePath(QString("pc/basefile.azm2"))); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "newfile.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "newfile.")); UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "newfile.")); - UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs2[0], "newfile.")); + UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts2[0], "newfile.")); UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts2[0], "newfile.")); changedInputResults.clear(); assetMessages.clear(); @@ -1555,12 +1557,12 @@ namespace AssetProcessor // send all the done messages simultaneously: response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 1)); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 1)); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 2)); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 2)); QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response)); response.m_outputProducts.clear(); @@ -1620,11 +1622,11 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); // --------- same result as above ---------- - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0); @@ -1645,9 +1647,9 @@ namespace AssetProcessor absolutePath = watchFolderPath + "/" + relativePathFromWatchFolder; unsigned int fingerprintForPC = 0; - unsigned int fingerprintForES3 = 0; + unsigned int fingerprintForANDROID = 0; - ComputeFingerprints(fingerprintForPC, fingerprintForES3, config, watchFolderPath, relativePathFromWatchFolder); + ComputeFingerprints(fingerprintForPC, fingerprintForANDROID, config, watchFolderPath, relativePathFromWatchFolder); processResults.clear(); QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath)); @@ -1655,11 +1657,11 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // // 2 each for pc and es3,since we have two recognizer for .xxx file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // // 2 each for pc and android,since we have two recognizer for .xxx file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); @@ -1681,11 +1683,11 @@ namespace AssetProcessor // we never actually submitted any fingerprints or indicated success, so the same number of jobs should occur as before sortAssetToProcessResultList(processResults); - UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // // 2 each for pc and es3,since we have two recognizer for .xxx file + UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // // 2 each for pc and android,since we have two recognizer for .xxx file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc")); UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc")); @@ -1705,7 +1707,7 @@ namespace AssetProcessor // now re-perform the same test, this time only the pc ones should re-appear. // this should happen because we're changing the extra params, which should be part of the fingerprint // if this unit test fails, check to make sure that the extra params are being ingested into the fingerprint computation functions - // and also make sure that the jobs that are for the remaining es3 platform don't change. + // and also make sure that the jobs that are for the remaining android platform don't change. // store the UUID so that we can insert the new one with the same UUID AZStd::shared_ptr builderTxt2Builder; @@ -1743,12 +1745,12 @@ namespace AssetProcessor // --------------------- unsigned int newfingerprintForPC = 0; - unsigned int newfingerprintForES3 = 0; + unsigned int newfingerprintForANDROID = 0; - ComputeFingerprints(newfingerprintForPC, newfingerprintForES3, config, watchFolderPath, relativePathFromWatchFolder); + ComputeFingerprints(newfingerprintForPC, newfingerprintForANDROID, config, watchFolderPath, relativePathFromWatchFolder); UNIT_TEST_EXPECT_TRUE(newfingerprintForPC != fingerprintForPC);//Fingerprints should be different - UNIT_TEST_EXPECT_TRUE(newfingerprintForES3 == fingerprintForES3);//Fingerprints are same + UNIT_TEST_EXPECT_TRUE(newfingerprintForANDROID == fingerprintForANDROID);//Fingerprints are same config.RemoveRecognizer("xxx files 2 (builder2)"); mockAppManager.UnRegisterAssetRecognizerAsBuilder("xxx files 2 (builder2)"); @@ -1763,18 +1765,18 @@ namespace AssetProcessor absolutePath = AssetUtilities::NormalizeFilePath(absolutePath); QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath)); UNIT_TEST_EXPECT_TRUE(BlockUntil(idling, 5000)); - UNIT_TEST_EXPECT_TRUE(processResults.size() == 2); // pc and es3 + UNIT_TEST_EXPECT_TRUE(processResults.size() == 2); // pc and android UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier != processResults[1].m_jobEntry.m_platformInfo.m_identifier); - UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "pc") || (processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3")); - UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "pc") || (processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3")); + UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "pc") || (processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android")); + UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "pc") || (processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android")); unsigned int newfingerprintForPCAfterVersionChange = 0; - unsigned int newfingerprintForES3AfterVersionChange = 0; + unsigned int newfingerprintForANDROIDAfterVersionChange = 0; - ComputeFingerprints(newfingerprintForPCAfterVersionChange, newfingerprintForES3AfterVersionChange, config, watchFolderPath, relativePathFromWatchFolder); + ComputeFingerprints(newfingerprintForPCAfterVersionChange, newfingerprintForANDROIDAfterVersionChange, config, watchFolderPath, relativePathFromWatchFolder); UNIT_TEST_EXPECT_TRUE((newfingerprintForPCAfterVersionChange != fingerprintForPC) || (newfingerprintForPCAfterVersionChange != newfingerprintForPC));//Fingerprints should be different - UNIT_TEST_EXPECT_TRUE((newfingerprintForES3AfterVersionChange != fingerprintForES3) || (newfingerprintForES3AfterVersionChange != newfingerprintForES3));//Fingerprints should be different + UNIT_TEST_EXPECT_TRUE((newfingerprintForANDROIDAfterVersionChange != fingerprintForANDROID) || (newfingerprintForANDROIDAfterVersionChange != newfingerprintForANDROID));//Fingerprints should be different //------Test for Files which are excluded processResults.clear(); @@ -1919,7 +1921,7 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(processResults.size() == 0); // nothing to process - // we are aware that 4 products went missing (es3 and pc versions of the 2 files since we renamed the SOURCE folder) + // we are aware that 4 products went missing (android and pc versions of the 2 files since we renamed the SOURCE folder) UNIT_TEST_EXPECT_TRUE(assetMessages.size() == 4); for (auto element : assetMessages) { @@ -2178,8 +2180,8 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(assetMessages[2].m_assetId != AZ::Data::AssetId()); UNIT_TEST_EXPECT_TRUE(assetMessages[3].m_assetId != AZ::Data::AssetId()); - UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "es3"); - UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_platform == "es3"); + UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "android"); + UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_platform == "android"); UNIT_TEST_EXPECT_TRUE(assetMessages[2].m_platform == "pc"); UNIT_TEST_EXPECT_TRUE(assetMessages[3].m_platform == "pc"); @@ -2212,12 +2214,12 @@ namespace AssetProcessor mockAppManager.UnRegisterAllBuilders(); AssetRecognizer abt_rec1; - AssetPlatformSpec abt_speces3; + AssetPlatformSpec abt_specandroid; abt_rec1.m_name = "UnitTestTextBuilder1"; abt_rec1.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard); //abt_rec1.m_regexp.setPatternSyntax(QRegExp::Wildcard); //abt_rec1.m_regexp.setPattern("*.txt"); - abt_rec1.m_platformSpecs.insert("es3", speces3); + abt_rec1.m_platformSpecs.insert("android", specandroid); mockAppManager.RegisterAssetRecognizerAsBuilder(abt_rec1); AssetRecognizer abt_rec2; @@ -2266,8 +2268,8 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); - UNIT_TEST_EXPECT_TRUE(processResults.size() == 2); // 1 for pc and es3 - UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3"); + UNIT_TEST_EXPECT_TRUE(processResults.size() == 2); // 1 for pc and android + UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android"); UNIT_TEST_EXPECT_TRUE(processResults[1].m_jobEntry.m_platformInfo.m_identifier == "pc"); UNIT_TEST_EXPECT_TRUE(QString::compare(processResults[0].m_jobEntry.GetAbsoluteSourcePath(), absolutePath, Qt::CaseInsensitive) == 0); UNIT_TEST_EXPECT_TRUE(QString::compare(processResults[1].m_jobEntry.GetAbsoluteSourcePath(), absolutePath, Qt::CaseInsensitive) == 0); diff --git a/Code/Tools/AssetProcessor/native/unittests/ConnectionUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/ConnectionUnitTests.cpp index 29a0570d39..992cf09539 100644 --- a/Code/Tools/AssetProcessor/native/unittests/ConnectionUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/ConnectionUnitTests.cpp @@ -17,16 +17,16 @@ void ConnectionUnitTest::StartTest() m_testConnection.SetAssetPlatformsString("pc"); AzFramework::AssetSystem::AssetNotificationMessage testMessage; EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(0); - m_testConnection.SendPerPlatform(0, testMessage, "osx_gl"); + m_testConnection.SendPerPlatform(0, testMessage, "mac"); EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(1); m_testConnection.SendPerPlatform(0, testMessage, "pc"); - m_testConnection.SetAssetPlatformsString("pc,es3"); + m_testConnection.SetAssetPlatformsString("pc,android"); EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(1); m_testConnection.SendPerPlatform(0, testMessage, "pc"); EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(0); - m_testConnection.SendPerPlatform(0, testMessage, "osx_gl"); + m_testConnection.SendPerPlatform(0, testMessage, "mac"); EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(1); - m_testConnection.SendPerPlatform(0, testMessage, "es3"); + m_testConnection.SendPerPlatform(0, testMessage, "android"); EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(0); // Intended partial string match test - shouldn't send m_testConnection.SendPerPlatform(0, testMessage, "es"); diff --git a/Code/Tools/AssetProcessor/native/unittests/MockConnectionHandler.h b/Code/Tools/AssetProcessor/native/unittests/MockConnectionHandler.h index f0553d5e84..75ffdfe218 100644 --- a/Code/Tools/AssetProcessor/native/unittests/MockConnectionHandler.h +++ b/Code/Tools/AssetProcessor/native/unittests/MockConnectionHandler.h @@ -63,7 +63,7 @@ namespace AssetProcessor size_t SendPerPlatform(unsigned int serial, const AzFramework::AssetSystem::BaseAssetProcessorMessage& message, const QString& platform) override { - if (QString::compare(platform, "pc", Qt::CaseInsensitive) == 0 || QString::compare(platform, "es3", Qt::CaseInsensitive) == 0) + if (QString::compare(platform, "pc", Qt::CaseInsensitive) == 0 || QString::compare(platform, "android", Qt::CaseInsensitive) == 0) { return Send(serial, message); } @@ -72,7 +72,7 @@ namespace AssetProcessor size_t SendRawPerPlatform(unsigned int type, unsigned int serial, const QByteArray& data, const QString& platform) override { - if (QString::compare(platform, "pc", Qt::CaseInsensitive) == 0 || QString::compare(platform, "es3", Qt::CaseInsensitive) == 0) + if (QString::compare(platform, "pc", Qt::CaseInsensitive) == 0 || QString::compare(platform, "android", Qt::CaseInsensitive) == 0) { return SendRaw(type, serial, data); } diff --git a/Code/Tools/AssetProcessor/native/unittests/PlatformConfigurationUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/PlatformConfigurationUnitTests.cpp index e09a6366a1..ff29cf2ca9 100644 --- a/Code/Tools/AssetProcessor/native/unittests/PlatformConfigurationUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/PlatformConfigurationUnitTests.cpp @@ -64,7 +64,7 @@ void PlatformConfigurationTests::StartTest() PlatformConfiguration config; config.EnablePlatform({ "pc",{ "desktop", "host" } }, true); - config.EnablePlatform({ "es3",{ "mobile", "android" } }, true); + config.EnablePlatform({ "android",{ "mobile", "android" } }, true); config.EnablePlatform({ "fandago",{ "console" } }, false); AZStd::vector platforms; config.PopulatePlatformsForScanFolder(platforms); @@ -88,15 +88,15 @@ void PlatformConfigurationTests::StartTest() AssetRecognizer rec; AssetPlatformSpec specpc; - AssetPlatformSpec speces3; + AssetPlatformSpec specandroid; AssetPlatformSpec specfandago; specpc.m_extraRCParams = ""; // blank must work - speces3.m_extraRCParams = "testextraparams"; + specandroid.m_extraRCParams = "testextraparams"; rec.m_name = "txt files"; rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard); rec.m_platformSpecs.insert("pc", specpc); - rec.m_platformSpecs.insert("es3", speces3); + rec.m_platformSpecs.insert("android", specandroid); rec.m_platformSpecs.insert("fandago", specfandago); config.AddRecognizer(rec); @@ -111,7 +111,7 @@ void PlatformConfigurationTests::StartTest() UNIT_TEST_EXPECT_TRUE(config.GetEnabledPlatforms().size() == 2); UNIT_TEST_EXPECT_TRUE(config.GetEnabledPlatforms()[0].m_identifier == "pc"); - UNIT_TEST_EXPECT_TRUE(config.GetEnabledPlatforms()[1].m_identifier == "es3"); + UNIT_TEST_EXPECT_TRUE(config.GetEnabledPlatforms()[1].m_identifier == "android"); UNIT_TEST_EXPECT_TRUE(config.GetScanFolderCount() == 11); UNIT_TEST_EXPECT_FALSE(config.GetScanFolderAt(0).IsRoot()); diff --git a/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp index 0e771f7fd1..02b98e5e33 100644 --- a/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp @@ -239,14 +239,14 @@ void RCcontrollerUnitTests::RunRCControllerTests() createdJobs.push_back(job); } - // double them up for "es3" to make sure that platform is respected + // double them up for "android" to make sure that platform is respected for (QString name : tempJobNames) { AZ::Uuid uuidOfSource = AZ::Uuid::CreateName(name.toUtf8().constData()); RCJob* job0 = new RCJob(rcJobListModel); AssetProcessor::JobDetails jobDetails; jobDetails.m_jobEntry.m_databaseSourceName = jobDetails.m_jobEntry.m_pathRelativeToWatchFolder = name; - jobDetails.m_jobEntry.m_platformInfo = { "es3" ,{ "mobile", "renderer" } }; + jobDetails.m_jobEntry.m_platformInfo = { "android" ,{ "mobile", "renderer" } }; jobDetails.m_jobEntry.m_jobKey = "Compile Other Stuff"; jobDetails.m_jobEntry.m_sourceFileUUID = uuidOfSource; job0->Init(jobDetails); @@ -490,7 +490,7 @@ void RCcontrollerUnitTests::RunRCControllerTests() UNIT_TEST_EXPECT_FALSE(gotJobsInQueueCall); // submit same job but different platform: - details.m_jobEntry = JobEntry("d:/test", "test1.txt", "test1.txt", AZ::Uuid("{7954065D-CFD1-4666-9E4C-3F36F417C7AC}"), { "es3" ,{ "mobile", "renderer" } }, "Test Job", 1234, 3, sourceId); + details.m_jobEntry = JobEntry("d:/test", "test1.txt", "test1.txt", AZ::Uuid("{7954065D-CFD1-4666-9E4C-3F36F417C7AC}"), { "android" ,{ "mobile", "renderer" } }, "Test Job", 1234, 3, sourceId); m_rcController.JobSubmitted(details); QCoreApplication::processEvents(QEventLoop::AllEvents); diff --git a/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.cpp b/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.cpp index 1e10002b53..650f3120dd 100644 --- a/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.cpp @@ -54,7 +54,7 @@ namespace UnitTestUtils { void SleepForMinimumFileSystemTime() { - // note that on OSX, the file system has a resolution of 1 second, and since we're using modtime for a bunch of things, + // note that on Mac, the file system has a resolution of 1 second, and since we're using modtime for a bunch of things, // not the actual hash files, we have to wait different amount depending on the OS. #ifdef AZ_PLATFORM_WINDOWS int milliseconds = 1; diff --git a/Code/Tools/AssetProcessor/native/utilities/ApplicationManager.cpp b/Code/Tools/AssetProcessor/native/utilities/ApplicationManager.cpp index c85801a074..579c7f93d3 100644 --- a/Code/Tools/AssetProcessor/native/utilities/ApplicationManager.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/ApplicationManager.cpp @@ -622,13 +622,14 @@ bool ApplicationManager::Activate() { if (!AssetUtilities::ComputeAssetRoot(m_systemRoot)) { + AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to compute the asset root for the project, this application cannot launch until this is fixed."); return false; } auto projectName = AssetUtilities::ComputeProjectName(); if (projectName.isEmpty()) { - AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to detect name of current game project. Is bootstrap.cfg appropriately configured?"); + AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to detect name of current game project. Configure your game project name to launch this application."); return false; } diff --git a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp index a17819bba2..7c4f429da4 100644 --- a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp @@ -1191,6 +1191,7 @@ bool ApplicationManagerBase::Activate() QDir projectCache; if (!AssetUtilities::ComputeProjectCacheRoot(projectCache)) { + AZ_Error("AssetProcessor", false, "Could not compute project cache root, please configure your project correctly to launch Asset Processor."); return false; } @@ -1200,22 +1201,27 @@ bool ApplicationManagerBase::Activate() // Shutdown if the disk has less than 128MB of free space if (!CheckSufficientDiskSpace(projectCache.absolutePath(), 128 * 1024 * 1024, true)) { + // CheckSufficientDiskSpace reports an error if disk space is low. return false; } bool appInited = InitApplicationServer(); if (!appInited) { + AZ_Error( + "AssetProcessor", false, "InitApplicationServer failed, something internal to Asset Processor has failed, please report this to support if you encounter this error."); return false; } if (!InitAssetDatabase()) { + // AssetDatabaseConnection::OpenDatabase reports any errors it encounters. return false; } if (!ApplicationManager::Activate()) { + // ApplicationManager::Activate() reports any errors it encounters. return false; } @@ -1230,6 +1236,7 @@ bool ApplicationManagerBase::Activate() m_isCurrentlyLoadingGems = true; if (!ActivateModules()) { + // ActivateModules reports any errors it encounters. m_isCurrentlyLoadingGems = false; return false; } @@ -1299,6 +1306,7 @@ bool ApplicationManagerBase::Activate() { if (!m_applicationServer->startListening()) { + // startListening reports any errors it encounters. return false; } } diff --git a/Code/Tools/AssetProcessor/testdata/config_broken_badplatform/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_broken_badplatform/AssetProcessorPlatformConfig.setreg index 468ab68f5d..05ed19cb74 100644 --- a/Code/Tools/AssetProcessor/testdata/config_broken_badplatform/AssetProcessorPlatformConfig.setreg +++ b/Code/Tools/AssetProcessor/testdata/config_broken_badplatform/AssetProcessorPlatformConfig.setreg @@ -5,11 +5,11 @@ "Platform pc": { "tags": "tools,renderer" }, - "Platform osx_gl": { + "Platform mac": { "tags": "tools,renderer" }, "Platforms": { - "es3": "enabled" + "android": "enabled" }, "ScanFolder Game": { "watch": "@PROJECTROOT@", diff --git a/Code/Tools/AssetProcessor/testdata/config_broken_noscans/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_broken_noscans/AssetProcessorPlatformConfig.setreg index 507fe4afb1..23f5725548 100644 --- a/Code/Tools/AssetProcessor/testdata/config_broken_noscans/AssetProcessorPlatformConfig.setreg +++ b/Code/Tools/AssetProcessor/testdata/config_broken_noscans/AssetProcessorPlatformConfig.setreg @@ -5,7 +5,7 @@ "Platform pc": { "tags": "tools,renderer" }, - "Platform osx_gl": { + "Platform mac": { "tags": "tools,renderer" }, "RC i_caf": { diff --git a/Code/Tools/AssetProcessor/testdata/config_broken_recognizers/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_broken_recognizers/AssetProcessorPlatformConfig.setreg index 0e687062b2..32c0af0593 100644 --- a/Code/Tools/AssetProcessor/testdata/config_broken_recognizers/AssetProcessorPlatformConfig.setreg +++ b/Code/Tools/AssetProcessor/testdata/config_broken_recognizers/AssetProcessorPlatformConfig.setreg @@ -5,7 +5,7 @@ "Platform pc": { "tags": "tools,renderer" }, - "Platform osx_gl": { + "Platform mac": { "tags": "tools,renderer" }, "ScanFolder Game": { diff --git a/Code/Tools/AssetProcessor/testdata/config_regular/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_regular/AssetProcessorPlatformConfig.setreg index 1c5c487a46..c43996f3d5 100644 --- a/Code/Tools/AssetProcessor/testdata/config_regular/AssetProcessorPlatformConfig.setreg +++ b/Code/Tools/AssetProcessor/testdata/config_regular/AssetProcessorPlatformConfig.setreg @@ -5,17 +5,17 @@ "Platform pc": { "tags": "tools,renderer" }, - "Platform es3": { + "Platform android": { "tags": "android,mobile,renderer" }, - "Platform osx_gl": { + "Platform mac": { "tags": "tools,renderer" }, "Platform server": { "tags": "server" }, "Platforms": { - "es3": "enabled", + "android": "enabled", "server": "enabled" }, "Jobs": { @@ -56,7 +56,7 @@ "glob": "*.i_caf", "params": "defaultparams", "server": "skip", - "es3": "mobile", + "android": "mobile", "priority": 5, "checkServer": true }, @@ -68,7 +68,7 @@ "RC mov": { "glob": "*.mov", "params": "copy", - "es3": "platformspecificoverride", + "android": "platformspecificoverride", "renderer": "rendererparams" }, "RC rend": { diff --git a/Code/Tools/AssetProcessor/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg index e1c2d6e8cc..5fe1071fd5 100644 --- a/Code/Tools/AssetProcessor/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg +++ b/Code/Tools/AssetProcessor/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg @@ -5,13 +5,13 @@ "Platform pc": { "tags": "tools,renderer" }, - "Platform es3": { + "Platform android": { "tags": "android,mobile,renderer" }, "Platform ios": { "tags": "mobile,renderer" }, - "Platform osx_gl": { + "Platform mac": { "tags": "tools,renderer" }, "Platform server": { @@ -21,7 +21,7 @@ "tags": "console,renderer" }, "Platforms": { - "es3": "enabled", + "android": "enabled", "ios": "enabled", "server": "enabled" }, @@ -54,14 +54,14 @@ "display": "folder1output", "recursive": 1, "order": 50000, - "include": "es3" + "include": "android" }, "ScanFolder Folder2": { "watch": "@ENGINEROOT@/Folder2", "display": "folder2output", "recursive": 1, "order": 60000, - "exclude": "es3" + "exclude": "android" }, "ScanFolder Folder3": { "watch": "@ENGINEROOT@/Folder3", @@ -80,7 +80,7 @@ "glob": "*.i_caf", "params": "defaultparams", "server": "skip", - "es3": "mobile", + "android": "mobile", "test": "copy", "priority": 5 }, @@ -92,7 +92,7 @@ "RC mov": { "glob": "*.mov", "params": "copy", - "es3": "platformspecificoverride", + "android": "platformspecificoverride", "renderer": "rendererparams" }, "RC rend": { diff --git a/Code/Tools/DeltaCataloger/Tests/tests_main.cpp b/Code/Tools/DeltaCataloger/Tests/tests_main.cpp index 67d6767a17..887889edcb 100644 --- a/Code/Tools/DeltaCataloger/Tests/tests_main.cpp +++ b/Code/Tools/DeltaCataloger/Tests/tests_main.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,12 @@ public: protected: void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + AZ::ComponentApplication::Descriptor desc; desc.m_useExistingAllocator = true; desc.m_enableDrilling = false; // we already created a memory driller for the test (AllocatorsFixture) diff --git a/Code/Tools/GridHub/GridHub/gridhub.cpp b/Code/Tools/GridHub/GridHub/gridhub.cpp index 7f85ade238..4b2d8e425b 100644 --- a/Code/Tools/GridHub/GridHub/gridhub.cpp +++ b/Code/Tools/GridHub/GridHub/gridhub.cpp @@ -552,7 +552,7 @@ GridHubComponent::OnMemberJoined([[maybe_unused]] GridMate::GridSession* session switch( member->GetPlatformId() ) { case AZ::PlatformID::PLATFORM_WINDOWS_64: - case AZ::PlatformID::PLATFORM_APPLE_OSX: + case AZ::PlatformID::PLATFORM_APPLE_MAC: { GridMate::string localMachineName = GridMate::Utils::GetMachineAddress(); if( member->GetMachineName() == localMachineName ) diff --git a/Code/Tools/ProjectManager/CMakeLists.txt b/Code/Tools/ProjectManager/CMakeLists.txt index e2b5aaf696..a655600325 100644 --- a/Code/Tools/ProjectManager/CMakeLists.txt +++ b/Code/Tools/ProjectManager/CMakeLists.txt @@ -37,11 +37,8 @@ ly_add_target( PRIVATE PY_PACKAGE="${python_package_name}" INCLUDE_DIRECTORIES - PUBLIC - . PRIVATE Source - BUILD_DEPENDENCIES PRIVATE 3rdParty::Qt::Core diff --git a/Code/Tools/ProjectManager/Resources/AddOffset.svg b/Code/Tools/ProjectManager/Resources/AddOffset.svg new file mode 100644 index 0000000000..4c62234070 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/AddOffset.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/AddOffset_Hover.svg b/Code/Tools/ProjectManager/Resources/AddOffset_Hover.svg new file mode 100644 index 0000000000..a0e2a07eda --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/AddOffset_Hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/ArrowBack.svg b/Code/Tools/ProjectManager/Resources/ArrowBack.svg new file mode 100644 index 0000000000..749bb5a02e --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/ArrowBack.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/FolderOffset.svg b/Code/Tools/ProjectManager/Resources/FolderOffset.svg new file mode 100644 index 0000000000..a048fbcc39 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/FolderOffset.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/FolderOffset_Hover.svg b/Code/Tools/ProjectManager/Resources/FolderOffset_Hover.svg new file mode 100644 index 0000000000..fb13cd8558 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/FolderOffset_Hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index 2e60e84326..04d5e98a10 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -4,6 +4,12 @@ Add.svg + AddOffset.svg + AddOffset_Hover.svg + ArrowBack.svg + build.svg + FolderOffset.svg + FolderOffset_Hover.svg Select_Folder.svg o3de_editor.ico Windows.svg @@ -14,6 +20,9 @@ DefaultProjectImage.png ArrowDownLine.svg ArrowUpLine.svg + o3de.svg + menu.svg + menu_hover.svg Backgrounds/FirstTimeBackgroundImage.jpg ArrowDownLine.svg ArrowUpLine.svg diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 16ef48ee7c..a85b911c15 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -1,29 +1,69 @@ /************** General (MainWindow) **************/ QMainWindow { - background-color: #333333; + background:#131313 url(:/o3de.svg) no-repeat top left; + /* position the logo using padding and background-origin, Qt does not support background-position pixels */ + background-origin:content; + padding:25px 16px; + margin:0; } - QPushButton:focus { outline: none; border:1px solid #1e70eb; } +QTabBar { + background-color: transparent; +} +QTabWidget::tab-bar +{ + left: 78px; /* make room for the logo */ +} +QTabBar::tab { + height:82px; + background-color: transparent; + font-size:24px; + min-width:100px; + margin-right:40px; + border-bottom: 3px solid transparent; +} +QTabBar::tab:text +{ + text-align:left; +} +QTabWidget::pane { + background-color: #333333; + border:0 none; +} +QTabBar::tab:selected +{ + border-bottom: 3px solid #1e70eb; + color: #1e70eb; +} +QTabBar::tab:hover +{ + color: #1e70eb; +} +QTabBar::tab:pressed +{ + color: #0e60eb; +} + /************** General (Forms) **************/ #formLineEditWidget, #formBrowseEditWidget { - max-width: 780px; + max-width: 890px; } #formFrame { - max-width: 720px; + max-width: 840px; background-color: #444444; border:1px solid #dddddd; border-radius: 4px; padding: 0px 10px 2px 6px; margin-top:10px; - margin-left:30px; + margin-left:50px; } #formFrame[Focus="true"] { @@ -59,15 +99,268 @@ QPushButton:focus { padding-top: -4px; } + #formErrorLabel { color: #ec3030; font-size: 14px; - margin-left: 40px; + margin-left: 50px; } #formTitleLabel { font-size:21px; color:#ffffff; - margin: 10px 0 10px 30px; + margin: 24px 0 10px 50px; +} + +/************** General (Modal windows) **************/ + +#header { + background-color:#111111; + min-height:80px; + max-height:80px; +} + +#header QPushButton { + /* settings min/max lets us use a fixed size */ + min-width: 24px; + max-width: 24px; + min-height: 24px; + max-height: 24px; + margin: 20px 10px 0px 10px; + background:transparent url(:/ArrowBack.svg) no-repeat center; + background-origin:content; + qproperty-flat: true; + qproperty-iconSize: 50px; +} + +#header QPushButton:focus { + border:none; +} +#header QPushButton:hover { + background:#333333 url(:/ArrowBack.svg) no-repeat center; +} +#header QPushButton:pressed { + background:#222222 url(:/ArrowBack.svg) no-repeat center; +} + +#headerTitle { + font-size:14px; + text-align:left; + margin:0; + padding-top:10px; + padding-bottom:-5px; + min-height:15px; + max-height:15px; +} +#headerSubTitle { + font-size:24px; + text-align:left; + margin:0; + min-height:42px; + max-height:42px; +} + +#body { + background-color:#333333; +} +#footer { + /* settings min/max lets us use a fixed size */ + min-width: 50px; + min-height:54px; + max-height:54px; +} + +#footer > QPushButton { + qproperty-flat: true; + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #0095f2, stop: 1.0 #1e70eb); + border-radius: 3px; + min-height: 28px; + max-height: 28px; + min-width: 150px; + margin-right:30px; +} +#footer > QPushButton:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #10A5f2, stop: 1.0 #2e80eb); +} +#footer > QPushButton:pressed { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #0085e2, stop: 1.0 #0e60db); +} + +#footer > QPushButton[secondary="true"] { + margin-right: 10px; + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #888888, stop: 1.0 #555555); +} +#footer > QPushButton[secondary="true"]:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #999999, stop: 1.0 #666666); +} +#footer > QPushButton[secondary="true"]:pressed { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #555555, stop: 1.0 #777777); +} + +/************** Project Settings **************/ +#projectSettings { + margin-top:42px; +} + +#projectTemplate { + margin: 55px 0 0 50px; + max-width: 780px; + min-height:200px; + max-height:200px; +} +#projectTemplateLabel { + font-size:16px; + font-weight:100; +} + +#projectTemplateDetailsLabel { + font-size:14px; + min-height:40px; + margin-bottom:20px; +} + +#projectTemplateDetails { + background-color:#444444; + max-width:240px; + min-width:240px; + margin-left:30px; } +/************** Projects **************/ +#firstTimeContent > #titleLabel { + font-size:60px; + margin:73px 0px 0px 0px; + qproperty-indent: 0; +} + +#firstTimeContent > #introLabel { + font-size:14px; + margin:10px 0 60px 0; + qproperty-indent: 0; +} + +#firstTimeContent > QPushButton { + min-width: 210px; + max-width: 210px; + min-height: 276px; + max-height: 276px; + qproperty-flat: true; + background-origin:content; + font-size:14px; + border: 1px solid #ffffff; +} + +#firstTimeContent > QPushButton:hover { + border: 1px solid #1e70eb; + color: #1e70eb; +} + +#firstTimeContent > QPushButton:pressed { + border: 1px solid #0e60eb; + color: #0e60eb; +} + +#createProjectButton { + background:rgba(0,0,0,180) url(:/AddOffset.svg) no-repeat center center; +} +#createProjectButton:hover, +#createProjectButton:pressed { + background:rgba(0,0,0,180) url(:/AddOffset_Hover.svg) no-repeat center center; +} + +#addProjectButton { + background:rgba(0,0,0,180) url(:/FolderOffset.svg) no-repeat center center; +} +#addProjectButton:hover, +#addProjectButton:pressed { + background:rgba(0,0,0,180) url(:/FolderOffset_Hover.svg) no-repeat center center; +} + +#projectsContent > QFrame { + margin-top:60px; +} + +#projectsContent > QFrame > #titleLabel { + font-size:24px; + qproperty-indent: 0; +} + +#projectsContent > QScrollArea { + margin-top:40px; + margin-bottom:5px; +} + +#projectButton > #labelButton { + border:1px solid white; +} +#projectButton > #labelButton:hover, +#projectButton > #labelButton:pressed { + border:1px solid #1e70eb; +} + +#projectButton > QFrame { + margin-top:6px; +} + +#projectButton > QFrame > QLabel { + font-weight:bold; + font-size:14px; + qproperty-indent: 0; +} + +#projectMenuButton { + qproperty-flat: true; + background:transparent url(:/menu.svg) no-repeat center center; + max-width:30px; + min-width:30px; + max-height:14px; + min-height:14px; +} + +#projectsContent > QFrame > #newProjectButton { + min-width:150px; + max-width:150px; + min-height:26px; + max-height:26px; +} + +#labelButtonOverlay { + background-color: rgba(50,50,50,200); + min-width:210px; + max-width:210px;; + min-height:278px; + max-height:278px; +} + +/************** Gem Catalog **************/ + +#GemCatalogTitle { + font-size: 18px; +} + +/************** Gem Catalog (Inspector) **************/ + +#GemCatalogInspector { + background-color: #444444; +} + +/************** Gem Catalog (Filter/left pane) **************/ + +#GemCatalogFilterWidget { + background-color: #444444; +} + +#GemCatalogHeaderWidget { + background-color: #1E252F; +} + +#GemCatalogFilterCategoryTitle { + font-size: 12px; + font-weight: 600; +} diff --git a/Code/Tools/ProjectManager/Resources/build.svg b/Code/Tools/ProjectManager/Resources/build.svg new file mode 100644 index 0000000000..b6c3546443 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/build.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Code/Tools/ProjectManager/Resources/menu.svg b/Code/Tools/ProjectManager/Resources/menu.svg new file mode 100644 index 0000000000..a639c74ab4 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/menu.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/menu_hover.svg b/Code/Tools/ProjectManager/Resources/menu_hover.svg new file mode 100644 index 0000000000..4eea63faca --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/menu_hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Code/Tools/ProjectManager/Resources/o3de.svg b/Code/Tools/ProjectManager/Resources/o3de.svg new file mode 100644 index 0000000000..bb6e596a00 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/o3de.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/o3de_editor.ico b/Code/Tools/ProjectManager/Resources/o3de_editor.ico index 0680ceea19..e7b77c35bf 100644 --- a/Code/Tools/ProjectManager/Resources/o3de_editor.ico +++ b/Code/Tools/ProjectManager/Resources/o3de_editor.ico @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c042fce57915fc749abc7b37de765fd697c3c4d7de045a3d44805aa0ce29901a -size 107016 +oid sha256:d717f77fe01f45df934a61bbc215e5322447d21e16f3cebcf2a02f148178f266 +size 106449 diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index 03e6a34b89..60e351cdb4 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -14,11 +14,17 @@ #include #include #include +#include +#include #include +#include #include #include #include +#include +#include +#include namespace O3DE::ProjectManager { @@ -26,29 +32,34 @@ namespace O3DE::ProjectManager : ScreenWidget(parent) { QVBoxLayout* vLayout = new QVBoxLayout(); - setLayout(vLayout); + vLayout->setContentsMargins(0,0,0,0); + + m_header = new ScreenHeader(this); + m_header->setTitle(tr("Create a New Project")); + m_header->setSubTitle(tr("Enter Project Details")); + connect(m_header->backButton(), &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton); + vLayout->addWidget(m_header); - m_screensCtrl = new ScreensCtrl(); - vLayout->addWidget(m_screensCtrl); + m_stack = new QStackedWidget(this); + m_stack->setObjectName("body"); + m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Expanding)); + m_stack->addWidget(new NewProjectSettingsScreen()); + m_stack->addWidget(new GemCatalogScreen()); + vLayout->addWidget(m_stack); QDialogButtonBox* backNextButtons = new QDialogButtonBox(); + backNextButtons->setObjectName("footer"); vLayout->addWidget(backNextButtons); m_backButton = backNextButtons->addButton(tr("Back"), QDialogButtonBox::RejectRole); + m_backButton->setProperty("secondary", true); m_nextButton = backNextButtons->addButton(tr("Next"), QDialogButtonBox::ApplyRole); - connect(m_backButton, &QPushButton::pressed, this, &CreateProjectCtrl::HandleBackButton); - connect(m_nextButton, &QPushButton::pressed, this, &CreateProjectCtrl::HandleNextButton); + connect(m_backButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton); + connect(m_nextButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleNextButton); - m_screensOrder = - { - ProjectManagerScreen::NewProjectSettings, - ProjectManagerScreen::GemCatalog - }; - m_screensCtrl->BuildScreens(m_screensOrder); - m_screensCtrl->ForceChangeToScreen(ProjectManagerScreen::NewProjectSettings, false); - - UpdateNextButtonText(); + Update(); + setLayout(vLayout); } ProjectManagerScreen CreateProjectCtrl::GetScreenEnum() @@ -56,30 +67,31 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::CreateProject; } + void CreateProjectCtrl::NotifyCurrentScreen() + { + ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); + if (currentScreen) + { + currentScreen->NotifyCurrentScreen(); + } + } + void CreateProjectCtrl::HandleBackButton() { - if (!m_screensCtrl->GotoPreviousScreen()) + if (m_stack->currentIndex() > 0) { - emit GotoPreviousScreenRequest(); + m_stack->setCurrentIndex(m_stack->currentIndex() - 1); + Update(); } else { - UpdateNextButtonText(); + emit GotoPreviousScreenRequest(); } } void CreateProjectCtrl::HandleNextButton() { - ScreenWidget* currentScreen = m_screensCtrl->GetCurrentScreen(); + ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum(); - auto screenOrderIter = m_screensOrder.begin(); - for (; screenOrderIter != m_screensOrder.end(); ++screenOrderIter) - { - if (*screenOrderIter == screenEnum) - { - ++screenOrderIter; - break; - } - } if (screenEnum == ProjectManagerScreen::NewProjectSettings) { @@ -97,18 +109,21 @@ namespace O3DE::ProjectManager } } - if (screenOrderIter != m_screensOrder.end()) + if (m_stack->currentIndex() != m_stack->count() - 1) { - m_screensCtrl->ChangeToScreen(*screenOrderIter); - UpdateNextButtonText(); + m_stack->setCurrentIndex(m_stack->currentIndex() + 1); + Update(); } else { auto result = PythonBindingsInterface::Get()->CreateProject(m_projectTemplatePath, m_projectInfo); if (result.IsSuccess()) { + // automatically register the project + PythonBindingsInterface::Get()->AddProject(m_projectInfo.m_path); + // adding gems is not implemented yet because we don't know what targets to add or how to add them - emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome); + emit ChangeScreenRequest(ProjectManagerScreen::Projects); } else { @@ -117,14 +132,21 @@ namespace O3DE::ProjectManager } } - void CreateProjectCtrl::UpdateNextButtonText() + void CreateProjectCtrl::Update() { - QString nextButtonText = tr("Next"); - if (m_screensCtrl->GetCurrentScreen()->GetScreenEnum() == ProjectManagerScreen::GemCatalog) + ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); + if (currentScreen && currentScreen->GetScreenEnum() == ProjectManagerScreen::GemCatalog) + { + m_header->setTitle(tr("Create Project")); + m_header->setSubTitle(tr("Configure project with Gems")); + m_nextButton->setText(tr("Create Project")); + } + else { - nextButtonText = tr("Create Project"); + m_header->setTitle(tr("Create Project")); + m_header->setSubTitle(tr("Enter Project Details")); + m_nextButton->setText(tr("Next")); } - m_nextButton->setText(nextButtonText); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h index 213bff3bc2..355ba3941d 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h @@ -12,15 +12,18 @@ #pragma once #if !defined(Q_MOC_RUN) -#include "ProjectInfo.h" #include -#include -#include +#include #endif +QT_FORWARD_DECLARE_CLASS(QStackedWidget) +QT_FORWARD_DECLARE_CLASS(QPushButton) +QT_FORWARD_DECLARE_CLASS(QLabel) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(ScreenHeader) + class CreateProjectCtrl : public ScreenWidget { @@ -28,18 +31,20 @@ namespace O3DE::ProjectManager explicit CreateProjectCtrl(QWidget* parent = nullptr); ~CreateProjectCtrl() = default; ProjectManagerScreen GetScreenEnum() override; + void NotifyCurrentScreen() override; protected slots: void HandleBackButton(); void HandleNextButton(); private: - void UpdateNextButtonText(); + void Update(); + + QStackedWidget* m_stack; + ScreenHeader* m_header; - ScreensCtrl* m_screensCtrl; QPushButton* m_backButton; QPushButton* m_nextButton; - QVector m_screensOrder; QString m_projectTemplatePath; ProjectInfo m_projectInfo; diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp index f51996bd65..6342041da4 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp @@ -82,6 +82,16 @@ 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 0e91ec2d3b..36e329cdf6 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h @@ -26,7 +26,10 @@ namespace O3DE::ProjectManager public: explicit EngineSettingsScreen(QWidget* parent = nullptr); ~EngineSettingsScreen() = default; + ProjectManagerScreen GetScreenEnum() override; + QString GetTabText() override; + bool IsTab() override; protected slots: void OnTextChanged(); diff --git a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp deleted file mode 100644 index 8654b221fb..0000000000 --- a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include -#include -#include -#include -#include - -namespace O3DE::ProjectManager -{ - FirstTimeUseScreen::FirstTimeUseScreen(QWidget* parent) - : ScreenWidget(parent) - { - QVBoxLayout* vLayout = new QVBoxLayout(); - setLayout(vLayout); - vLayout->setContentsMargins(s_contentMargins, s_contentMargins, s_contentMargins, s_contentMargins); - - QLabel* titleLabel = new QLabel(this); - titleLabel->setText(tr("Ready. Set. Create!")); - titleLabel->setStyleSheet("font-size: 60px"); - vLayout->addWidget(titleLabel); - - QLabel* introLabel = new QLabel(this); - introLabel->setTextFormat(Qt::AutoText); - introLabel->setText(tr("

Welcome to O3DE! Start something new by creating a project. Not sure what to create?

Explore what\342\200\231s available by downloading our sample project.

")); - introLabel->setStyleSheet("font-size: 14px"); - vLayout->addWidget(introLabel); - - QHBoxLayout* buttonLayout = new QHBoxLayout(); - buttonLayout->setSpacing(s_buttonSpacing); - - m_createProjectButton = CreateLargeBoxButton(QIcon(":/Add.svg"), tr("Create Project"), this); - m_createProjectButton->setIconSize(QSize(s_iconSize, s_iconSize)); - buttonLayout->addWidget(m_createProjectButton); - - m_addProjectButton = CreateLargeBoxButton(QIcon(":/Select_Folder.svg"), tr("Add a Project"), this); - m_addProjectButton->setIconSize(QSize(s_iconSize, s_iconSize)); - buttonLayout->addWidget(m_addProjectButton); - - QSpacerItem* buttonSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Expanding, QSizePolicy::Minimum); - buttonLayout->addItem(buttonSpacer); - - vLayout->addItem(buttonLayout); - - QSpacerItem* verticalSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Minimum, QSizePolicy::Expanding); - vLayout->addItem(verticalSpacer); - - // Using border-image allows for scaling options background-image does not support - setStyleSheet("O3DE--ProjectManager--ScreenWidget { border-image: url(:/Backgrounds/FirstTimeBackgroundImage.jpg) repeat repeat; }"); - - connect(m_createProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleNewProjectButton); - connect(m_addProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleAddProjectButton); - } - - ProjectManagerScreen FirstTimeUseScreen::GetScreenEnum() - { - return ProjectManagerScreen::FirstTimeUse; - } - - void FirstTimeUseScreen::HandleNewProjectButton() - { - emit ResetScreenRequest(ProjectManagerScreen::CreateProject); - emit ChangeScreenRequest(ProjectManagerScreen::CreateProject); - } - void FirstTimeUseScreen::HandleAddProjectButton() - { - emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome); - } - - QPushButton* FirstTimeUseScreen::CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent) - { - QPushButton* largeBoxButton = new QPushButton(icon, text, parent); - - largeBoxButton->setFixedSize(s_boxButtonWidth, s_boxButtonHeight); - largeBoxButton->setFlat(true); - largeBoxButton->setFocusPolicy(Qt::FocusPolicy::NoFocus); - largeBoxButton->setStyleSheet("QPushButton { font-size: 14px; background-color: rgba(0, 0, 0, 191); }"); - - return largeBoxButton; - } - -} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h deleted file mode 100644 index 80a2310d7a..0000000000 --- a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ -#pragma once - -#if !defined(Q_MOC_RUN) -#include -#endif - -QT_FORWARD_DECLARE_CLASS(QIcon) -QT_FORWARD_DECLARE_CLASS(QPushButton) - -namespace O3DE::ProjectManager -{ - class FirstTimeUseScreen - : public ScreenWidget - { - public: - explicit FirstTimeUseScreen(QWidget* parent = nullptr); - ~FirstTimeUseScreen() = default; - ProjectManagerScreen GetScreenEnum() override; - - protected slots: - void HandleNewProjectButton(); - void HandleAddProjectButton(); - - private: - QPushButton* CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent = nullptr); - - QPushButton* m_createProjectButton; - QPushButton* m_addProjectButton; - - inline constexpr static int s_contentMargins = 80; - inline constexpr static int s_buttonSpacing = 30; - inline constexpr static int s_iconSize = 24; - inline constexpr static int s_spacerSize = 20; - inline constexpr static int s_boxButtonWidth = 210; - inline constexpr static int s_boxButtonHeight = 280; - }; - -} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/FormLineEditWidget.cpp b/Code/Tools/ProjectManager/Source/FormLineEditWidget.cpp index 7ef7e3c7d8..6c08393910 100644 --- a/Code/Tools/ProjectManager/Source/FormLineEditWidget.cpp +++ b/Code/Tools/ProjectManager/Source/FormLineEditWidget.cpp @@ -78,6 +78,14 @@ namespace O3DE::ProjectManager m_errorLabel->setText(labelText); } + void FormLineEditWidget::setErrorLabelVisible(bool visible) + { + m_errorLabel->setVisible(visible); + m_frame->setProperty("Valid", !visible); + + refreshStyle(); + } + QLineEdit* FormLineEditWidget::lineEdit() const { return m_lineEdit; diff --git a/Code/Tools/ProjectManager/Source/FormLineEditWidget.h b/Code/Tools/ProjectManager/Source/FormLineEditWidget.h index 3094442cbd..76534f46f7 100644 --- a/Code/Tools/ProjectManager/Source/FormLineEditWidget.h +++ b/Code/Tools/ProjectManager/Source/FormLineEditWidget.h @@ -39,6 +39,7 @@ namespace O3DE::ProjectManager //! Set the error message for to display when invalid. void setErrorLabelText(const QString& labelText); + void setErrorLabelVisible(bool visible); //! Returns a pointer to the underlying LineEdit. QLineEdit* lineEdit() const; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 6e9ad42017..6402121e4a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -25,10 +25,12 @@ namespace O3DE::ProjectManager hLayout->setMargin(0); setLayout(hLayout); - setStyleSheet("background-color: #1E252F;"); + setObjectName("GemCatalogHeaderWidget"); + + hLayout->addSpacing(7); QLabel* titleLabel = new QLabel(tr("Gem Catalog")); - titleLabel->setStyleSheet("font-size: 21px;"); + titleLabel->setObjectName("GemCatalogTitle"); hLayout->addWidget(titleLabel); hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); @@ -42,7 +44,7 @@ namespace O3DE::ProjectManager hLayout->addWidget(filterLineEdit); hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); - hLayout->addSpacerItem(new QSpacerItem(220, 0, QSizePolicy::Fixed)); + hLayout->addSpacerItem(new QSpacerItem(140, 0, QSizePolicy::Fixed)); setFixedHeight(60); } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp index 3ece7760cf..a6a4e95ff9 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp @@ -43,7 +43,6 @@ namespace O3DE::ProjectManager m_collapseButton->setFlat(true); m_collapseButton->setFocusPolicy(Qt::NoFocus); m_collapseButton->setFixedWidth(s_collapseButtonSize); - m_collapseButton->setStyleSheet("border: 0px; border-radius: 0px;"); connect(m_collapseButton, &QPushButton::clicked, this, [=]() { UpdateCollapseState(); @@ -52,7 +51,7 @@ namespace O3DE::ProjectManager // Category title QLabel* headerLabel = new QLabel(header); - headerLabel->setStyleSheet("font-size: 11pt;"); + headerLabel->setObjectName("GemCatalogFilterCategoryTitle"); collapseLayout->addWidget(headerLabel); vLayout->addLayout(collapseLayout); @@ -79,14 +78,14 @@ namespace O3DE::ProjectManager elementWidget->setLayout(elementLayout); QCheckBox* checkbox = new QCheckBox(elementNames[i]); - checkbox->setStyleSheet("font-size: 11pt;"); + checkbox->setStyleSheet("font-size: 12px;"); m_buttonGroup->addButton(checkbox); elementLayout->addWidget(checkbox); elementLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); QLabel* countLabel = new QLabel(QString::number(elementCounts[i])); - countLabel->setStyleSheet("font-size: 11pt; background-color: #333333; border-radius: 3px; color: #94D2FF;"); + countLabel->setStyleSheet("font-size: 12px; background-color: #333333; border-radius: 3px; color: #94D2FF;"); elementLayout->addWidget(countLabel); m_elementWidgets.push_back(elementWidget); @@ -110,6 +109,8 @@ namespace O3DE::ProjectManager } } + vLayout->addSpacing(5); + // Separating line QFrame* hLine = new QFrame(); hLine->setFrameShape(QFrame::HLine); @@ -181,6 +182,8 @@ namespace O3DE::ProjectManager : QScrollArea(parent) , m_filterProxyModel(filterProxyModel) { + setObjectName("GemCatalogFilterWidget"); + m_gemModel = m_filterProxyModel->GetSourceModel(); setWidgetResizable(true); @@ -195,7 +198,7 @@ namespace O3DE::ProjectManager mainWidget->setLayout(m_mainLayout); QLabel* filterByLabel = new QLabel("Filter by"); - filterByLabel->setStyleSheet("font-size: 15pt;"); + filterByLabel->setStyleSheet("font-size: 16px;"); m_mainLayout->addWidget(filterByLabel); AddGemOriginFilter(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp index 791085f47a..bc44928868 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp @@ -25,7 +25,7 @@ namespace O3DE::ProjectManager bool GemInfo::IsValid() const { - return !m_path.isEmpty() && !m_uuid.IsNull(); + return !m_name.isEmpty() && !m_path.isEmpty(); } QString GemInfo::GetPlatformString(Platform platform) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h index 06b0adad32..1032ca5eaf 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h @@ -64,7 +64,6 @@ namespace O3DE::ProjectManager QString m_path; QString m_name = "Unknown Gem Name"; QString m_displayName = "Unknown Gem Name"; - AZ::Uuid m_uuid; QString m_creator = "Unknown Creator"; GemOrigin m_gemOrigin = Local; bool m_isAdded = false; //! Is the gem currently added and enabled in the project? diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp index 6276ddc996..3ecc18231e 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp @@ -23,6 +23,7 @@ namespace O3DE::ProjectManager : QScrollArea(parent) , m_model(model) { + setObjectName("GemCatalogInspector"); setWidgetResizable(true); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -85,7 +86,7 @@ namespace O3DE::ProjectManager QLabel* GemInspector::CreateStyledLabel(QLayout* layout, int fontSize, const QString& colorCodeString) { QLabel* result = new QLabel(); - result->setStyleSheet(QString("font-size: %1pt; color: %2;").arg(QString::number(fontSize), colorCodeString)); + result->setStyleSheet(QString("font-size: %1px; color: %2;").arg(QString::number(fontSize), colorCodeString)); layout->addWidget(result); return result; } @@ -93,13 +94,13 @@ namespace O3DE::ProjectManager void GemInspector::InitMainWidget() { // Gem name, creator and summary - m_nameLabel = CreateStyledLabel(m_mainLayout, 17, s_headerColor); + m_nameLabel = CreateStyledLabel(m_mainLayout, 18, s_headerColor); m_creatorLabel = CreateStyledLabel(m_mainLayout, 12, s_creatorColor); m_mainLayout->addSpacing(5); // TODO: QLabel seems to have issues determining the right sizeHint() for our font with the given font size. // This results into squeezed elements in the layout in case the text is a little longer than a sentence. - m_summaryLabel = new QLabel();//CreateLabel(m_mainLayout, 12, s_textColor); + m_summaryLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); m_mainLayout->addWidget(m_summaryLabel); m_summaryLabel->setWordWrap(true); m_mainLayout->addSpacing(5); @@ -146,9 +147,9 @@ namespace O3DE::ProjectManager QLabel* additionalInfoLabel = CreateStyledLabel(m_mainLayout, 14, s_headerColor); additionalInfoLabel->setText("Additional Information"); - m_versionLabel = CreateStyledLabel(m_mainLayout, 11, s_textColor); - m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, 11, s_textColor); - m_binarySizeLabel = CreateStyledLabel(m_mainLayout, 11, s_textColor); + m_versionLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); + m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); + m_binarySizeLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); } GemInspector::GemsSubWidget::GemsSubWidget(QWidget* parent) @@ -159,8 +160,8 @@ namespace O3DE::ProjectManager m_layout->setMargin(0); setLayout(m_layout); - m_titleLabel = GemInspector::CreateStyledLabel(m_layout, 15, s_headerColor); - m_textLabel = GemInspector::CreateStyledLabel(m_layout, 9, s_textColor); + 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(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index a40e5eb447..57200e3b36 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -49,7 +49,7 @@ namespace O3DE::ProjectManager painter->setRenderHint(QPainter::Antialiasing); QRect fullRect, itemRect, contentRect; - CalcRects(options, modelIndex, fullRect, itemRect, contentRect); + CalcRects(options, fullRect, itemRect, contentRect); QFont standardFont(options.font); standardFont.setPixelSize(s_fontSize); @@ -99,7 +99,7 @@ namespace O3DE::ProjectManager painter->drawText(gemCreatorRect, Qt::TextSingleLine, gemCreator); // Gem summary - const QSize summarySize = QSize(contentRect.width() - s_summaryStartX - s_buttonWidth - s_itemMargins.right() * 4, contentRect.height()); + const QSize summarySize = QSize(contentRect.width() - s_summaryStartX - s_buttonWidth - s_itemMargins.right() * 3, contentRect.height()); const QRect summaryRect = QRect(/*topLeft=*/QPoint(contentRect.left() + s_summaryStartX, contentRect.top()), summarySize); painter->setFont(standardFont); @@ -134,12 +134,10 @@ namespace O3DE::ProjectManager return QStyledItemDelegate::editorEvent(event, model, option, modelIndex); } - void GemItemDelegate::CalcRects(const QStyleOptionViewItem& option, const QModelIndex& modelIndex, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const + void GemItemDelegate::CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const { - const bool isFirst = modelIndex.row() == 0; - outFullRect = QRect(option.rect); - outItemRect = QRect(outFullRect.adjusted(s_itemMargins.left(), isFirst ? s_itemMargins.top() * 2 : s_itemMargins.top(), -s_itemMargins.right(), -s_itemMargins.bottom())); + 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())); } @@ -194,12 +192,12 @@ namespace O3DE::ProjectManager painter->setBrush(m_buttonEnabledColor); painter->setPen(m_buttonEnabledColor); - circleCenter = buttonRect.center() + QPoint(buttonRect.width() / 2 - s_buttonBorderRadius, 1); + circleCenter = buttonRect.center() + QPoint(buttonRect.width() / 2 - s_buttonBorderRadius + 1, 1); buttonText = "Added"; } else { - circleCenter = buttonRect.center() + QPoint(-buttonRect.width() / 2 + s_buttonBorderRadius + 1, 1); + circleCenter = buttonRect.center() + QPoint(-buttonRect.width() / 2 + s_buttonBorderRadius, 1); buttonText = "Get"; } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h index d43b5d15f6..48f173ec3f 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h @@ -45,25 +45,25 @@ namespace O3DE::ProjectManager const QColor m_buttonEnabledColor = QColor("#00B931"); // Item - inline constexpr static int s_height = 135; // Gem item total height - inline constexpr static qreal s_gemNameFontSize = 16.0; - inline constexpr static qreal s_fontSize = 15.0; - inline constexpr static int s_summaryStartX = 200; + inline constexpr static int s_height = 105; // Gem item total height + inline constexpr static qreal s_gemNameFontSize = 13.0; + inline constexpr static qreal s_fontSize = 12.0; + inline constexpr static int s_summaryStartX = 150; // Margin and borders - inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/20, /*top=*/10, /*right=*/20, /*bottom=*/10); // Item border distances - inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/15, /*top=*/12, /*right=*/12, /*bottom=*/12); // Distances of the elements within an item to the item borders + inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/16, /*top=*/8, /*right=*/16, /*bottom=*/8); // Item border distances + inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/12, /*right=*/15, /*bottom=*/12); // Distances of the elements within an item to the item borders inline constexpr static int s_borderWidth = 4; // Button - inline constexpr static int s_buttonWidth = 70; - inline constexpr static int s_buttonHeight = 24; - inline constexpr static int s_buttonBorderRadius = 12; - inline constexpr static int s_buttonCircleRadius = s_buttonBorderRadius - 3; - inline constexpr static qreal s_buttonFontSize = 12.0; + inline constexpr static int s_buttonWidth = 55; + inline constexpr static int s_buttonHeight = 18; + inline constexpr static int s_buttonBorderRadius = 9; + inline constexpr static int s_buttonCircleRadius = s_buttonBorderRadius - 2; + inline constexpr static qreal s_buttonFontSize = 10.0; private: - void CalcRects(const QStyleOptionViewItem& option, const QModelIndex& modelIndex, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const; + 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 DrawPlatformIcons(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const; @@ -73,7 +73,7 @@ namespace O3DE::ProjectManager // Platform icons void AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath); - inline constexpr static int s_platformIconSize = 16; + inline constexpr static int s_platformIconSize = 12; QHash m_platformIcons; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp index 128fb93345..bc287e3c61 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp @@ -35,7 +35,7 @@ namespace O3DE::ProjectManager topLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); QLabel* showCountLabel = new QLabel(); - showCountLabel->setStyleSheet("font-size: 11pt; font: italic;"); + showCountLabel->setStyleSheet("font-size: 12px; font: italic;"); topLayout->addWidget(showCountLabel); connect(proxyModel, &GemSortFilterProxyModel::OnInvalidated, this, [=] { @@ -61,16 +61,17 @@ namespace O3DE::ProjectManager QHBoxLayout* columnHeaderLayout = new QHBoxLayout(); columnHeaderLayout->setAlignment(Qt::AlignLeft); - columnHeaderLayout->addSpacing(31); + const int gemNameStartX = GemItemDelegate::s_itemMargins.left() + GemItemDelegate::s_contentMargins.left() - 3; + columnHeaderLayout->addSpacing(gemNameStartX); QLabel* gemNameLabel = new QLabel(tr("Gem Name")); - gemNameLabel->setStyleSheet("font-size: 11pt;"); + gemNameLabel->setStyleSheet("font-size: 12px;"); columnHeaderLayout->addWidget(gemNameLabel); - columnHeaderLayout->addSpacing(111); + columnHeaderLayout->addSpacing(77); QLabel* gemSummaryLabel = new QLabel(tr("Gem Summary")); - gemSummaryLabel->setStyleSheet("font-size: 11pt;"); + gemSummaryLabel->setStyleSheet("font-size: 12px;"); columnHeaderLayout->addWidget(gemSummaryLabel); vLayout->addLayout(columnHeaderLayout); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 724a8fa630..df11c4c7a6 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -33,8 +33,6 @@ namespace O3DE::ProjectManager item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setData(gemInfo.m_name, RoleName); - const QString uuidString = gemInfo.m_uuid.ToString().c_str(); - item->setData(uuidString, RoleUuid); item->setData(gemInfo.m_creator, RoleCreator); item->setData(gemInfo.m_gemOrigin, RoleGemOrigin); item->setData(aznumeric_cast(gemInfo.m_platforms), RolePlatforms); @@ -53,7 +51,7 @@ namespace O3DE::ProjectManager appendRow(item); const QModelIndex modelIndex = index(rowCount()-1, 0); - m_uuidToIndexMap[uuidString] = modelIndex; + m_nameToIndexMap[gemInfo.m_name] = modelIndex; } void GemModel::Clear() @@ -76,11 +74,6 @@ namespace O3DE::ProjectManager return static_cast(modelIndex.data(RoleGemOrigin).toInt()); } - QString GemModel::GetUuidString(const QModelIndex& modelIndex) - { - return modelIndex.data(RoleUuid).toString(); - } - GemInfo::Platforms GemModel::GetPlatforms(const QModelIndex& modelIndex) { return static_cast(modelIndex.data(RolePlatforms).toInt()); @@ -111,10 +104,10 @@ namespace O3DE::ProjectManager return modelIndex.data(RoleDocLink).toString(); } - QModelIndex GemModel::FindIndexByUuidString(const QString& uuidString) const + QModelIndex GemModel::FindIndexByNameString(const QString& nameString) const { - const auto iterator = m_uuidToIndexMap.find(uuidString); - if (iterator != m_uuidToIndexMap.end()) + const auto iterator = m_nameToIndexMap.find(nameString); + if (iterator != m_nameToIndexMap.end()) { return iterator.value(); } @@ -122,11 +115,11 @@ namespace O3DE::ProjectManager return {}; } - void GemModel::FindGemNamesByUuidStrings(QStringList& inOutGemNames) + void GemModel::FindGemNamesByNameStrings(QStringList& inOutGemNames) { for (QString& dependingGemString : inOutGemNames) { - QModelIndex modelIndex = FindIndexByUuidString(dependingGemString); + QModelIndex modelIndex = FindIndexByNameString(dependingGemString); if (modelIndex.isValid()) { dependingGemString = GetName(modelIndex); @@ -147,7 +140,7 @@ namespace O3DE::ProjectManager return {}; } - FindGemNamesByUuidStrings(result); + FindGemNamesByNameStrings(result); return result; } @@ -164,7 +157,7 @@ namespace O3DE::ProjectManager return {}; } - FindGemNamesByUuidStrings(result); + FindGemNamesByNameStrings(result); return result; } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 480f4c74d3..0caa399b58 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -33,8 +33,8 @@ namespace O3DE::ProjectManager void AddGem(const GemInfo& gemInfo); void Clear(); - QModelIndex FindIndexByUuidString(const QString& uuidString) const; - void FindGemNamesByUuidStrings(QStringList& inOutGemNames); + 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); @@ -43,7 +43,6 @@ namespace O3DE::ProjectManager static QString GetName(const QModelIndex& modelIndex); static QString GetCreator(const QModelIndex& modelIndex); static GemInfo::GemOrigin GetGemOrigin(const QModelIndex& modelIndex); - static QString GetUuidString(const QModelIndex& modelIndex); static GemInfo::Platforms GetPlatforms(const QModelIndex& modelIndex); static GemInfo::Types GetTypes(const QModelIndex& modelIndex); static QString GetSummary(const QModelIndex& modelIndex); @@ -59,7 +58,6 @@ namespace O3DE::ProjectManager enum UserRole { RoleName = Qt::UserRole, - RoleUuid, RoleCreator, RoleGemOrigin, RolePlatforms, @@ -76,7 +74,7 @@ namespace O3DE::ProjectManager RoleTypes }; - QHash m_uuidToIndexMap; + QHash m_nameToIndexMap; QItemSelectionModel* m_selectionModel = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/LinkWidget.cpp b/Code/Tools/ProjectManager/Source/LinkWidget.cpp index a6308f6c62..160d9cf7c7 100644 --- a/Code/Tools/ProjectManager/Source/LinkWidget.cpp +++ b/Code/Tools/ProjectManager/Source/LinkWidget.cpp @@ -37,7 +37,7 @@ namespace O3DE::ProjectManager void LinkLabel::enterEvent([[maybe_unused]] QEvent* event) { - setStyleSheet("font-size: 9pt; color: #94D2FF; text-decoration: underline;"); + setStyleSheet("font-size: 10px; color: #94D2FF; text-decoration: underline;"); } void LinkLabel::leaveEvent([[maybe_unused]] QEvent* event) @@ -52,6 +52,6 @@ namespace O3DE::ProjectManager void LinkLabel::SetDefaultStyle() { - setStyleSheet("font-size: 9pt; color: #94D2FF;"); + setStyleSheet("font-size: 10px; color: #94D2FF;"); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp index ffbf1bf6fe..53400b3193 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp @@ -12,6 +12,10 @@ #include #include +#include +#include +#include +#include #include #include @@ -23,6 +27,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -31,64 +36,96 @@ namespace O3DE::ProjectManager NewProjectSettingsScreen::NewProjectSettingsScreen(QWidget* parent) : ScreenWidget(parent) { - QHBoxLayout* hLayout = new QHBoxLayout(); - this->setLayout(hLayout); - + QHBoxLayout* hLayout = new QHBoxLayout(this); + hLayout->setAlignment(Qt::AlignLeft); + hLayout->setContentsMargins(0,0,0,0); + + // if we don't provide a parent for this box layout the stylesheet doesn't take + // if we don't set this in a frame (just use a sub-layout) all the content will align incorrectly horizontally + QFrame* projectSettingsFrame = new QFrame(this); + projectSettingsFrame->setObjectName("projectSettings"); QVBoxLayout* vLayout = new QVBoxLayout(this); - QLabel* projectNameLabel = new QLabel(tr("Project Name"), this); - vLayout->addWidget(projectNameLabel); - - m_projectNameLineEdit = new QLineEdit(tr("New Project"), this); - vLayout->addWidget(m_projectNameLineEdit); - - QLabel* projectPathLabel = new QLabel(tr("Project Location"), this); - vLayout->addWidget(projectPathLabel); - + // you cannot remove content margins in qss + vLayout->setContentsMargins(0,0,0,0); + vLayout->setAlignment(Qt::AlignTop); { - QHBoxLayout* projectPathLayout = new QHBoxLayout(this); - - m_projectPathLineEdit = new QLineEdit(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), this); - projectPathLayout->addWidget(m_projectPathLineEdit); - - QPushButton* browseButton = new QPushButton(tr("Browse"), this); - connect(browseButton, &QPushButton::pressed, this, &NewProjectSettingsScreen::HandleBrowseButton); - projectPathLayout->addWidget(browseButton); - - vLayout->addLayout(projectPathLayout); + const QString defaultName{ "NewProject" }; + const QString defaultPath = QDir::toNativeSeparators(GetDefaultProjectPath() + "/" + defaultName); + + m_projectName = new FormLineEditWidget(tr("Project name"), defaultName, this); + connect(m_projectName->lineEdit(), &QLineEdit::textChanged, this, &NewProjectSettingsScreen::ValidateProjectPath); + vLayout->addWidget(m_projectName); + + m_projectPath = new FormBrowseEditWidget(tr("Project Location"), defaultPath, this); + m_projectPath->lineEdit()->setReadOnly(true); + connect(m_projectPath->lineEdit(), &QLineEdit::textChanged, this, &NewProjectSettingsScreen::ValidateProjectPath); + vLayout->addWidget(m_projectPath); + + // if we don't use a QFrame we cannot "contain" the widgets inside and move them around + // as a group + QFrame* projectTemplateWidget = new QFrame(this); + projectTemplateWidget->setObjectName("projectTemplate"); + QVBoxLayout* containerLayout = new QVBoxLayout(); + containerLayout->setAlignment(Qt::AlignTop); + { + QLabel* projectTemplateLabel = new QLabel(tr("Select a Project Template")); + projectTemplateLabel->setObjectName("projectTemplateLabel"); + containerLayout->addWidget(projectTemplateLabel); + + QLabel* projectTemplateDetailsLabel = new QLabel(tr("Project templates are pre-configured with relevant Gems that provide " + "additional functionality and content to the project.")); + projectTemplateDetailsLabel->setWordWrap(true); + projectTemplateDetailsLabel->setObjectName("projectTemplateDetailsLabel"); + containerLayout->addWidget(projectTemplateDetailsLabel); + + QHBoxLayout* templateLayout = new QHBoxLayout(this); + containerLayout->addItem(templateLayout); + + m_projectTemplateButtonGroup = new QButtonGroup(this); + m_projectTemplateButtonGroup->setObjectName("templateButtonGroup"); + auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates(); + if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty()) + { + for (auto projectTemplate : templatesResult.GetValue()) + { + QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this); + radioButton->setProperty(k_pathProperty, projectTemplate.m_path); + m_projectTemplateButtonGroup->addButton(radioButton); + + containerLayout->addWidget(radioButton); + } + + m_projectTemplateButtonGroup->buttons().first()->setChecked(true); + } + } + projectTemplateWidget->setLayout(containerLayout); + vLayout->addWidget(projectTemplateWidget); } + projectSettingsFrame->setLayout(vLayout); - QLabel* projectTemplateLabel = new QLabel(this); - projectTemplateLabel->setText("Project Template"); - vLayout->addWidget(projectTemplateLabel); + hLayout->addWidget(projectSettingsFrame); - QHBoxLayout* templateLayout = new QHBoxLayout(this); - vLayout->addItem(templateLayout); + QWidget* projectTemplateDetails = new QWidget(this); + projectTemplateDetails->setObjectName("projectTemplateDetails"); + hLayout->addWidget(projectTemplateDetails); - m_projectTemplateButtonGroup = new QButtonGroup(this); - auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates(); - if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty()) + this->setLayout(hLayout); + } + + QString NewProjectSettingsScreen::GetDefaultProjectPath() + { + QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); + if (engineInfoResult.IsSuccess()) { - for (auto projectTemplate : templatesResult.GetValue()) + QDir path(QDir::toNativeSeparators(engineInfoResult.GetValue().m_defaultProjectsFolder)); + if (path.exists()) { - QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this); - radioButton->setProperty(k_pathProperty, projectTemplate.m_path); - m_projectTemplateButtonGroup->addButton(radioButton); - - templateLayout->addWidget(radioButton); + defaultPath = path.absolutePath(); } - - m_projectTemplateButtonGroup->buttons().first()->setChecked(true); } - - QSpacerItem* verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - vLayout->addItem(verticalSpacer); - - hLayout->addItem(vLayout); - - QWidget* gemsListPlaceholder = new QWidget(this); - gemsListPlaceholder->setFixedWidth(250); - hLayout->addWidget(gemsListPlaceholder); + return defaultPath; } ProjectManagerScreen NewProjectSettingsScreen::GetScreenEnum() @@ -96,26 +133,21 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::NewProjectSettings; } - void NewProjectSettingsScreen::HandleBrowseButton() + void NewProjectSettingsScreen::ValidateProjectPath() { - QString defaultPath = m_projectPathLineEdit->text(); - if (defaultPath.isEmpty()) - { - defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - } + Validate(); + } - QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("New project path"), defaultPath)); - if (!directory.isEmpty()) - { - m_projectPathLineEdit->setText(directory); - } + void NewProjectSettingsScreen::NotifyCurrentScreen() + { + Validate(); } ProjectInfo NewProjectSettingsScreen::GetProjectInfo() { ProjectInfo projectInfo; - projectInfo.m_projectName = m_projectNameLineEdit->text(); - projectInfo.m_path = QDir::toNativeSeparators(m_projectPathLineEdit->text() + "/" + projectInfo.m_projectName); + projectInfo.m_projectName = m_projectName->lineEdit()->text(); + projectInfo.m_path = m_projectPath->lineEdit()->text(); return projectInfo; } @@ -126,24 +158,44 @@ namespace O3DE::ProjectManager bool NewProjectSettingsScreen::Validate() { - bool projectNameIsValid = true; - if (m_projectNameLineEdit->text().isEmpty()) - { - projectNameIsValid = false; - } - bool projectPathIsValid = true; - if (m_projectPathLineEdit->text().isEmpty()) + if (m_projectPath->lineEdit()->text().isEmpty()) { projectPathIsValid = false; + m_projectPath->setErrorLabelText(tr("Please provide a valid location.")); + } + else + { + QDir path(m_projectPath->lineEdit()->text()); + if (path.exists() && !path.isEmpty()) + { + projectPathIsValid = false; + m_projectPath->setErrorLabelText(tr("This folder exists and isn't empty. Please choose a different location.")); + } } - QDir path(QDir::toNativeSeparators(m_projectPathLineEdit->text() + "/" + m_projectNameLineEdit->text())); - if (path.exists() && !path.isEmpty()) + bool projectNameIsValid = true; + if (m_projectName->lineEdit()->text().isEmpty()) { - projectPathIsValid = false; + projectNameIsValid = false; + m_projectName->setErrorLabelText(tr("Please provide a project name.")); + } + else + { + // this validation should roughly match the utils.validate_identifier which the cli + // uses to validate project names + QRegExp validProjectNameRegex("[A-Za-z][A-Za-z0-9_-]{0,63}"); + const bool result = validProjectNameRegex.exactMatch(m_projectName->lineEdit()->text()); + if (!result) + { + projectNameIsValid = false; + m_projectName->setErrorLabelText(tr("Project names must start with a letter and consist of up to 64 letter, number, '_' or '-' characters")); + } + } + m_projectName->setErrorLabelVisible(!projectNameIsValid); + m_projectPath->setErrorLabelVisible(!projectPathIsValid); return projectNameIsValid && projectPathIsValid; } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h index 1cfd3c9c35..0560f8728d 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h @@ -17,10 +17,12 @@ #endif QT_FORWARD_DECLARE_CLASS(QButtonGroup) -QT_FORWARD_DECLARE_CLASS(QLineEdit) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(FormLineEditWidget) + QT_FORWARD_DECLARE_CLASS(FormBrowseEditWidget) + class NewProjectSettingsScreen : public ScreenWidget { @@ -34,12 +36,17 @@ namespace O3DE::ProjectManager bool Validate(); + void NotifyCurrentScreen() override; + protected slots: void HandleBrowseButton(); + void ValidateProjectPath(); private: - QLineEdit* m_projectNameLineEdit; - QLineEdit* m_projectPathLineEdit; + QString GetDefaultProjectPath(); + + FormLineEditWidget* m_projectName; + FormBrowseEditWidget* m_projectPath; QButtonGroup* m_projectTemplateButtonGroup; }; diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp index ec1acdad61..72ffa686c1 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp @@ -12,6 +12,7 @@ #include + #include #include #include @@ -31,31 +32,49 @@ namespace O3DE::ProjectManager LabelButton::LabelButton(QWidget* parent) : QLabel(parent) { + setObjectName("labelButton"); + m_overlayLabel = new QLabel("", this); + m_overlayLabel->setObjectName("labelButtonOverlay"); + m_overlayLabel->setWordWrap(true); + m_overlayLabel->setAlignment(Qt::AlignCenter); + m_overlayLabel->setVisible(false); } void LabelButton::mousePressEvent([[maybe_unused]] QMouseEvent* event) { - emit triggered(); + if(m_enabled) + { + emit triggered(); + } } - ProjectButton::ProjectButton(const QString& projectName, QWidget* parent) - : QFrame(parent) - , m_projectName(projectName) - , m_projectImagePath(":/Resources/DefaultProjectImage.png") + void LabelButton::SetEnabled(bool enabled) { - Setup(); + m_enabled = enabled; + m_overlayLabel->setVisible(!enabled); + } + + void LabelButton::SetOverlayText(const QString& text) + { + m_overlayLabel->setText(text); } - ProjectButton::ProjectButton(const QString& projectName, const QString& projectImage, QWidget* parent) + ProjectButton::ProjectButton(const ProjectInfo& projectInfo, QWidget* parent) : QFrame(parent) - , m_projectName(projectName) - , m_projectImagePath(projectImage) + , m_projectInfo(projectInfo) { + if (m_projectInfo.m_imagePath.isEmpty()) + { + m_projectInfo.m_imagePath = ":/DefaultProjectImage.png"; + } + Setup(); } void ProjectButton::Setup() { + setObjectName("projectButton"); + QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setSpacing(0); vLayout->setContentsMargins(0, 0, 0, 0); @@ -63,40 +82,58 @@ namespace O3DE::ProjectManager m_projectImageLabel = new LabelButton(this); m_projectImageLabel->setFixedSize(s_projectImageWidth, s_projectImageHeight); + m_projectImageLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); vLayout->addWidget(m_projectImageLabel); - m_projectImageLabel->setPixmap(QPixmap(m_projectImagePath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding)); + m_projectImageLabel->setPixmap( + QPixmap(m_projectInfo.m_imagePath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding)); QMenu* newProjectMenu = new QMenu(this); m_editProjectAction = newProjectMenu->addAction(tr("Edit Project Settings...")); - -#ifdef SHOW_ALL_PROJECT_ACTIONS - m_editProjectGemsAction = newProjectMenu->addAction(tr("Cutomize Gems...")); newProjectMenu->addSeparator(); m_copyProjectAction = newProjectMenu->addAction(tr("Duplicate")); newProjectMenu->addSeparator(); m_removeProjectAction = newProjectMenu->addAction(tr("Remove from O3DE")); - m_deleteProjectAction = newProjectMenu->addAction(tr("Delete the Project")); + m_deleteProjectAction = newProjectMenu->addAction(tr("Delete this Project")); + +#ifdef SHOW_ALL_PROJECT_ACTIONS + m_editProjectGemsAction = newProjectMenu->addAction(tr("Cutomize Gems...")); #endif - m_projectSettingsMenuButton = new QPushButton(this); - m_projectSettingsMenuButton->setText(m_projectName); - m_projectSettingsMenuButton->setMenu(newProjectMenu); - m_projectSettingsMenuButton->setFocusPolicy(Qt::FocusPolicy::NoFocus); - m_projectSettingsMenuButton->setStyleSheet("font-size: 14px; text-align:left;"); - vLayout->addWidget(m_projectSettingsMenuButton); + QFrame* footer = new QFrame(this); + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setContentsMargins(0, 0, 0, 0); + footer->setLayout(hLayout); + { + QLabel* projectNameLabel = new QLabel(m_projectInfo.m_displayName, this); + hLayout->addWidget(projectNameLabel); + + QPushButton* projectMenuButton = new QPushButton(this); + projectMenuButton->setObjectName("projectMenuButton"); + projectMenuButton->setMenu(newProjectMenu); + hLayout->addWidget(projectMenuButton); + } - setFixedSize(s_projectImageWidth, s_projectImageHeight + m_projectSettingsMenuButton->height()); + vLayout->addWidget(footer); - connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectName); }); - connect(m_editProjectAction, &QAction::triggered, [this]() { emit EditProject(m_projectName); }); + connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectInfo.m_path); }); + connect(m_editProjectAction, &QAction::triggered, [this]() { emit EditProject(m_projectInfo.m_path); }); + connect(m_copyProjectAction, &QAction::triggered, [this]() { emit CopyProject(m_projectInfo.m_path); }); + connect(m_removeProjectAction, &QAction::triggered, [this]() { emit RemoveProject(m_projectInfo.m_path); }); + connect(m_deleteProjectAction, &QAction::triggered, [this]() { emit DeleteProject(m_projectInfo.m_path); }); #ifdef SHOW_ALL_PROJECT_ACTIONS - connect(m_editProjectGemsAction, &QAction::triggered, [this]() { emit EditProjectGems(m_projectName); }); - connect(m_copyProjectAction, &QAction::triggered, [this]() { emit CopyProject(m_projectName); }); - connect(m_removeProjectAction, &QAction::triggered, [this]() { emit RemoveProject(m_projectName); }); - connect(m_deleteProjectAction, &QAction::triggered, [this]() { emit DeleteProject(m_projectName); }); + connect(m_editProjectGemsAction, &QAction::triggered, [this]() { emit EditProjectGems(m_projectInfo.m_path); }); #endif } + void ProjectButton::SetButtonEnabled(bool enabled) + { + m_projectImageLabel->SetEnabled(enabled); + } + + void ProjectButton::SetButtonOverlayText(const QString& text) + { + m_projectImageLabel->SetOverlayText(text); + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h index c1aee8e63e..e82b56b3fa 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h @@ -13,7 +13,8 @@ #pragma once #if !defined(Q_MOC_RUN) -#include +#include + #include #endif @@ -32,11 +33,18 @@ namespace O3DE::ProjectManager explicit LabelButton(QWidget* parent = nullptr); ~LabelButton() = default; + void SetEnabled(bool enabled); + void SetOverlayText(const QString& text); + signals: void triggered(); public slots: void mousePressEvent(QMouseEvent* event) override; + + private: + QLabel* m_overlayLabel; + bool m_enabled = true; }; class ProjectButton @@ -45,10 +53,12 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - explicit ProjectButton(const QString& projectName, QWidget* parent = nullptr); - explicit ProjectButton(const QString& projectName, const QString& projectImage, QWidget* parent = nullptr); + explicit ProjectButton(const ProjectInfo& m_projectInfo, QWidget* parent = nullptr); ~ProjectButton() = default; + void SetButtonEnabled(bool enabled); + void SetButtonOverlayText(const QString& text); + signals: void OpenProject(const QString& projectName); void EditProject(const QString& projectName); @@ -60,10 +70,8 @@ namespace O3DE::ProjectManager private: void Setup(); - QString m_projectName; - QString m_projectImagePath; + ProjectInfo m_projectInfo; LabelButton* m_projectImageLabel; - QPushButton* m_projectSettingsMenuButton; QAction* m_editProjectAction; QAction* m_editProjectGemsAction; QAction* m_copyProjectAction; diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp index eb79f2da1e..cb1398cc61 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp @@ -11,52 +11,60 @@ */ #include -#include +#include #include +#include #include +#include +#include #include -#include - namespace O3DE::ProjectManager { - ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath) + ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath, const AZ::IO::PathView& projectPath, ProjectManagerScreen startScreen) : QMainWindow(parent) - , m_ui(new Ui::ProjectManagerWindowClass()) { - m_ui->setupUi(this); - QLayout* layout = m_ui->centralWidget->layout(); - layout->setMargin(0); - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - m_pythonBindings = AZStd::make_unique(engineRootPath); - m_screensCtrl = new ScreensCtrl(); - m_ui->verticalLayout->addWidget(m_screensCtrl); + setWindowTitle(tr("O3DE Project Manager")); - connect(m_ui->projectsMenu, &QMenu::aboutToShow, this, &ProjectManagerWindow::HandleProjectsMenu); - connect(m_ui->engineMenu, &QMenu::aboutToShow, this, &ProjectManagerWindow::HandleEngineMenu); + ScreensCtrl* screensCtrl = new ScreensCtrl(); + // currently the tab order on the home page is based on the order of this list + QVector screenEnums = + { + ProjectManagerScreen::Projects, + ProjectManagerScreen::EngineSettings, + ProjectManagerScreen::CreateProject, + ProjectManagerScreen::UpdateProject + }; + screensCtrl->BuildScreens(screenEnums); + + setCentralWidget(screensCtrl); + + // setup stylesheets and hot reloading QDir rootDir = QString::fromUtf8(engineRootPath.Native().data(), aznumeric_cast(engineRootPath.Native().size())); const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources"); const auto qrcPath = QStringLiteral(":/ProjectManager/style"); AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRootPath); + // set stylesheet after creating the screens or their styles won't get updated AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("style:ProjectManager.qss")); - QVector screenEnums = + // always push the projects screen first so we have something to come back to + if (startScreen != ProjectManagerScreen::Projects) { - ProjectManagerScreen::FirstTimeUse, - ProjectManagerScreen::CreateProject, - ProjectManagerScreen::ProjectsHome, - ProjectManagerScreen::UpdateProject, - ProjectManagerScreen::EngineSettings - }; - m_screensCtrl->BuildScreens(screenEnums); - m_screensCtrl->ForceChangeToScreen(ProjectManagerScreen::FirstTimeUse, false); + screensCtrl->ForceChangeToScreen(ProjectManagerScreen::Projects); + } + screensCtrl->ForceChangeToScreen(startScreen); + + if (!projectPath.empty()) + { + const QString path = QString::fromUtf8(projectPath.Native().data(), aznumeric_cast(projectPath.Native().size())); + emit screensCtrl->NotifyCurrentProject(path); + } } ProjectManagerWindow::~ProjectManagerWindow() @@ -64,13 +72,4 @@ namespace O3DE::ProjectManager m_pythonBindings.reset(); } - void ProjectManagerWindow::HandleProjectsMenu() - { - m_screensCtrl->ChangeToScreen(ProjectManagerScreen::ProjectsHome); - } - void ProjectManagerWindow::HandleEngineMenu() - { - m_screensCtrl->ChangeToScreen(ProjectManagerScreen::EngineSettings); - } - } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h index d5c586e59b..758af8fc00 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h @@ -13,17 +13,10 @@ #if !defined(Q_MOC_RUN) #include - -#include - #include +#include #endif -namespace Ui -{ - class ProjectManagerWindowClass; -} - namespace O3DE::ProjectManager { class ProjectManagerWindow @@ -32,16 +25,11 @@ namespace O3DE::ProjectManager Q_OBJECT public: - explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath); + explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath, const AZ::IO::PathView& projectPath, + ProjectManagerScreen startScreen = ProjectManagerScreen::Projects); ~ProjectManagerWindow(); - protected slots: - void HandleProjectsMenu(); - void HandleEngineMenu(); - private: - QScopedPointer m_ui; - ScreensCtrl* m_screensCtrl; AZStd::unique_ptr m_pythonBindings; }; diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui deleted file mode 100644 index 633cd61182..0000000000 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui +++ /dev/null @@ -1,67 +0,0 @@ - - - ProjectManagerWindowClass - - - - 0 - 0 - 1200 - 800 - - - - - 0 - 0 - - - - O3DE Project Manager - - - - - - - - 0 - 0 - 1200 - 36 - - - - - 16 - - - - - Icon - - - - :/o3de_editor.ico:/o3de_editor.ico - - - - - Projects - - - - - Engine - - - - - - - - - - - - diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp new file mode 100644 index 0000000000..58e4c5c60f --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp @@ -0,0 +1,207 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + namespace ProjectUtils + { + static bool WarnDirectoryOverwrite(const QString& path, QWidget* parent) + { + if (!QDir(path).isEmpty()) + { + QMessageBox::StandardButton warningResult = QMessageBox::warning( + parent, + QObject::tr("Overwrite Directory"), + QObject::tr("Directory is not empty! Are you sure you want to overwrite it?"), + QMessageBox::No | QMessageBox::Yes + ); + + if (warningResult != QMessageBox::Yes) + { + return false; + } + } + + return true; + } + + static bool IsDirectoryDescedent(const QString& possibleAncestorPath, const QString& possibleDecedentPath) + { + QDir ancestor(possibleAncestorPath); + QDir descendent(possibleDecedentPath); + + do + { + if (ancestor == descendent) + { + return false; + } + + descendent.cdUp(); + } + while (!descendent.isRoot()); + + return true; + } + + static bool CopyDirectory(const QString& origPath, const QString& newPath) + { + QDir original(origPath); + if (!original.exists()) + { + return false; + } + + for (QString directory : original.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) + { + QString newDirectoryPath = newPath + QDir::separator() + directory; + original.mkpath(newDirectoryPath); + + if (!CopyDirectory(origPath + QDir::separator() + directory, newDirectoryPath)) + { + return false; + } + } + + for (QString file : original.entryList(QDir::Files)) + { + if (!QFile::copy(origPath + QDir::separator() + file, newPath + QDir::separator() + file)) + return false; + } + + return true; + } + + bool AddProjectDialog(QWidget* parent) + { + QString path = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(parent, QObject::tr("Select Project Directory"))); + if (!path.isEmpty()) + { + return RegisterProject(path); + } + + return false; + } + + bool RegisterProject(const QString& path) + { + return PythonBindingsInterface::Get()->AddProject(path); + } + + bool UnregisterProject(const QString& path) + { + return PythonBindingsInterface::Get()->RemoveProject(path); + } + + bool CopyProjectDialog(const QString& origPath, QWidget* parent) + { + bool copyResult = false; + + QDir parentOrigDir(origPath); + parentOrigDir.cdUp(); + QString newPath = QDir::toNativeSeparators( + QFileDialog::getExistingDirectory(parent, QObject::tr("Select New Project Directory"), parentOrigDir.path())); + if (!newPath.isEmpty()) + { + if (!WarnDirectoryOverwrite(newPath, parent)) + { + return false; + } + + // TODO: Block UX and Notify User they need to wait + + copyResult = CopyProject(origPath, newPath); + } + + return copyResult; + } + + bool CopyProject(const QString& origPath, const QString& newPath) + { + // Disallow copying from or into subdirectory + if (!IsDirectoryDescedent(origPath, newPath) || !IsDirectoryDescedent(newPath, origPath)) + { + return false; + } + + if (!CopyDirectory(origPath, newPath)) + { + // Cleanup whatever mess was made + DeleteProjectFiles(newPath, true); + return false; + } + + if (!RegisterProject(newPath)) + { + DeleteProjectFiles(newPath, true); + } + + return true; + } + + bool DeleteProjectFiles(const QString& path, bool force) + { + QDir projectDirectory(path); + if (projectDirectory.exists()) + { + // Check if there is an actual project hereor just force it + if (force || PythonBindingsInterface::Get()->GetProject(path).IsSuccess()) + { + return projectDirectory.removeRecursively(); + } + } + + return false; + } + + bool MoveProject(const QString& origPath, const QString& newPath, QWidget* parent) + { + if (!WarnDirectoryOverwrite(newPath, parent) || !UnregisterProject(origPath)) + { + return false; + } + + QDir directory; + if (directory.rename(origPath, newPath)) + { + return directory.rename(origPath, newPath); + } + + if (!RegisterProject(newPath)) + { + return false; + } + + return true; + } + + ProjectManagerScreen GetProjectManagerScreen(const QString& screen) + { + auto iter = s_ProjectManagerStringNames.find(screen); + if (iter != s_ProjectManagerStringNames.end()) + { + return iter.value(); + } + + return ProjectManagerScreen::Invalid; + } + + } // namespace ProjectUtils +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h new file mode 100644 index 0000000000..d556d682f2 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -0,0 +1,30 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once + +#include +#include + +namespace O3DE::ProjectManager +{ + namespace ProjectUtils + { + bool AddProjectDialog(QWidget* parent = nullptr); + bool RegisterProject(const QString& path); + bool UnregisterProject(const QString& path); + bool CopyProjectDialog(const QString& origPath, QWidget* parent = nullptr); + bool CopyProject(const QString& origPath, const QString& newPath); + bool DeleteProjectFiles(const QString& path, bool force = false); + bool MoveProject(const QString& origPath, const QString& newPath, QWidget* parent = nullptr); + ProjectManagerScreen GetProjectManagerScreen(const QString& screen); + } // namespace ProjectUtils +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.cpp deleted file mode 100644 index 411b46c55d..0000000000 --- a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace O3DE::ProjectManager -{ - ProjectsHomeScreen::ProjectsHomeScreen(QWidget* parent) - : ScreenWidget(parent) - { - QVBoxLayout* vLayout = new QVBoxLayout(); - setLayout(vLayout); - vLayout->setContentsMargins(s_contentMargins, s_contentMargins, s_contentMargins, s_contentMargins); - - QHBoxLayout* topLayout = new QHBoxLayout(); - - QLabel* titleLabel = new QLabel(this); - titleLabel->setText("My Projects"); - titleLabel->setStyleSheet("font-size: 24px"); - topLayout->addWidget(titleLabel); - - QSpacerItem* topSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Expanding, QSizePolicy::Minimum); - topLayout->addItem(topSpacer); - - QMenu* newProjectMenu = new QMenu(this); - m_createNewProjectAction = newProjectMenu->addAction("Create New Project"); - m_addExistingProjectAction = newProjectMenu->addAction("Add Existing Project"); - - QPushButton* newProjectMenuButton = new QPushButton(this); - newProjectMenuButton->setText("New Project..."); - newProjectMenuButton->setMenu(newProjectMenu); - newProjectMenuButton->setFixedWidth(s_newProjectButtonWidth); - newProjectMenuButton->setStyleSheet("font-size: 14px;"); - topLayout->addWidget(newProjectMenuButton); - - vLayout->addLayout(topLayout); - - // Get all projects and create a horizontal scrolling list of them - auto projectsResult = PythonBindingsInterface::Get()->GetProjects(); - if (projectsResult.IsSuccess() && !projectsResult.GetValue().isEmpty()) - { - QScrollArea* projectsScrollArea = new QScrollArea(this); - QWidget* scrollWidget = new QWidget(); - QGridLayout* projectGridLayout = new QGridLayout(); - scrollWidget->setLayout(projectGridLayout); - projectsScrollArea->setWidget(scrollWidget); - projectsScrollArea->setWidgetResizable(true); - - int gridIndex = 0; - for (auto project : projectsResult.GetValue()) - { - ProjectButton* projectButton; - QString projectPreviewPath = project.m_path + m_projectPreviewImagePath; - QFileInfo doesPreviewExist(projectPreviewPath); - if (doesPreviewExist.exists() && doesPreviewExist.isFile()) - { - projectButton = new ProjectButton(project.m_projectName, projectPreviewPath, this); - } - else - { - projectButton = new ProjectButton(project.m_projectName, this); - } - - // Create rows of projects buttons s_projectButtonRowCount buttons wide - projectGridLayout->addWidget(projectButton, gridIndex / s_projectButtonRowCount, gridIndex % s_projectButtonRowCount); - - connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsHomeScreen::HandleOpenProject); - connect(projectButton, &ProjectButton::EditProject, this, &ProjectsHomeScreen::HandleEditProject); - -#ifdef SHOW_ALL_PROJECT_ACTIONS - connect(projectButton, &ProjectButton::EditProjectGems, this, &ProjectsHomeScreen::HandleEditProjectGems); - connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsHomeScreen::HandleCopyProject); - connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsHomeScreen::HandleRemoveProject); - connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsHomeScreen::HandleDeleteProject); -#endif - ++gridIndex; - } - - vLayout->addWidget(projectsScrollArea); - } - - // Using border-image allows for scaling options background-image does not support - setStyleSheet("O3DE--ProjectManager--ScreenWidget { border-image: url(:/Backgrounds/FirstTimeBackgroundImage.jpg) repeat repeat; }"); - - connect(m_createNewProjectAction, &QAction::triggered, this, &ProjectsHomeScreen::HandleNewProjectButton); - connect(m_addExistingProjectAction, &QAction::triggered, this, &ProjectsHomeScreen::HandleAddProjectButton); - } - - ProjectManagerScreen ProjectsHomeScreen::GetScreenEnum() - { - return ProjectManagerScreen::ProjectsHome; - } - - void ProjectsHomeScreen::HandleNewProjectButton() - { - emit ResetScreenRequest(ProjectManagerScreen::CreateProject); - emit ChangeScreenRequest(ProjectManagerScreen::CreateProject); - } - void ProjectsHomeScreen::HandleAddProjectButton() - { - // Do nothing for now - } - void ProjectsHomeScreen::HandleOpenProject(const QString& projectPath) - { - // Open the editor with this project open - emit NotifyCurrentProject(projectPath); - } - void ProjectsHomeScreen::HandleEditProject(const QString& projectPath) - { - emit NotifyCurrentProject(projectPath); - emit ResetScreenRequest(ProjectManagerScreen::UpdateProject); - emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject); - } - void ProjectsHomeScreen::HandleEditProjectGems(const QString& projectPath) - { - emit NotifyCurrentProject(projectPath); - emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog); - } - void ProjectsHomeScreen::HandleCopyProject([[maybe_unused]] const QString& projectPath) - { - // Open file dialog and choose location for copied project then register copy with O3DE - } - void ProjectsHomeScreen::HandleRemoveProject([[maybe_unused]] const QString& projectPath) - { - // Unregister Project from O3DE - } - void ProjectsHomeScreen::HandleDeleteProject([[maybe_unused]] const QString& projectPath) - { - // Remove project from 03DE and delete from disk - ProjectsHomeScreen::HandleRemoveProject(projectPath); - } - -} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp new file mode 100644 index 0000000000..7b9e3ecb9d --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -0,0 +1,376 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define DISPLAY_PROJECT_DEV_DATA true + +namespace O3DE::ProjectManager +{ + ProjectsScreen::ProjectsScreen(QWidget* parent) + : ScreenWidget(parent) + { + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setAlignment(Qt::AlignTop); + vLayout->setContentsMargins(s_contentMargins, 0, s_contentMargins, 0); + setLayout(vLayout); + + m_background.load(":/Backgrounds/FirstTimeBackgroundImage.jpg"); + + m_stack = new QStackedWidget(this); + + m_firstTimeContent = CreateFirstTimeContent(); + m_stack->addWidget(m_firstTimeContent); + + m_projectsContent = CreateProjectsContent(); + m_stack->addWidget(m_projectsContent); + + vLayout->addWidget(m_stack); + } + + QFrame* ProjectsScreen::CreateFirstTimeContent() + { + QFrame* frame = new QFrame(this); + frame->setObjectName("firstTimeContent"); + { + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setAlignment(Qt::AlignTop); + frame->setLayout(layout); + + QLabel* titleLabel = new QLabel(tr("Ready. Set. Create."), this); + titleLabel->setObjectName("titleLabel"); + layout->addWidget(titleLabel); + + QLabel* introLabel = new QLabel(this); + introLabel->setObjectName("introLabel"); + introLabel->setText(tr("Welcome to O3DE! Start something new by creating a project. Not sure what to create? \nExplore what's " + "available by downloading our sample project.")); + layout->addWidget(introLabel); + + QHBoxLayout* buttonLayout = new QHBoxLayout(this); + buttonLayout->setAlignment(Qt::AlignLeft); + buttonLayout->setSpacing(s_spacerSize); + + // use a newline to force the text up + QPushButton* createProjectButton = new QPushButton(tr("Create a Project\n"), this); + createProjectButton->setObjectName("createProjectButton"); + buttonLayout->addWidget(createProjectButton); + + QPushButton* addProjectButton = new QPushButton(tr("Add a Project\n"), this); + addProjectButton->setObjectName("addProjectButton"); + buttonLayout->addWidget(addProjectButton); + + connect(createProjectButton, &QPushButton::clicked, this, &ProjectsScreen::HandleNewProjectButton); + connect(addProjectButton, &QPushButton::clicked, this, &ProjectsScreen::HandleAddProjectButton); + + layout->addLayout(buttonLayout); + } + + return frame; + } + + QFrame* ProjectsScreen::CreateProjectsContent() + { + QFrame* frame = new QFrame(this); + frame->setObjectName("projectsContent"); + { + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + layout->setContentsMargins(0, 0, 0, 0); + frame->setLayout(layout); + + QFrame* header = new QFrame(this); + QHBoxLayout* headerLayout = new QHBoxLayout(); + { + QLabel* titleLabel = new QLabel(tr("My Projects"), this); + titleLabel->setObjectName("titleLabel"); + headerLayout->addWidget(titleLabel); + + QMenu* newProjectMenu = new QMenu(this); + m_createNewProjectAction = newProjectMenu->addAction("Create New Project"); + m_addExistingProjectAction = newProjectMenu->addAction("Add Existing Project"); + + connect(m_createNewProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleNewProjectButton); + connect(m_addExistingProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleAddProjectButton); + + QPushButton* newProjectMenuButton = new QPushButton(tr("New Project..."), this); + newProjectMenuButton->setObjectName("newProjectButton"); + newProjectMenuButton->setMenu(newProjectMenu); + newProjectMenuButton->setDefault(true); + headerLayout->addWidget(newProjectMenuButton); + } + header->setLayout(headerLayout); + + layout->addWidget(header); + + // Get all projects and create a horizontal scrolling list of them + auto projectsResult = PythonBindingsInterface::Get()->GetProjects(); + if (projectsResult.IsSuccess() && !projectsResult.GetValue().isEmpty()) + { + QScrollArea* projectsScrollArea = new QScrollArea(this); + QWidget* scrollWidget = new QWidget(); + + FlowLayout* flowLayout = new FlowLayout(0, s_spacerSize, s_spacerSize); + scrollWidget->setLayout(flowLayout); + + projectsScrollArea->setWidget(scrollWidget); + projectsScrollArea->setWidgetResizable(true); + +#ifndef DISPLAY_PROJECT_DEV_DATA + for (auto project : projectsResult.GetValue()) +#else + ProjectInfo project = projectsResult.GetValue().at(0); + for (int i = 0; i < 15; i++) +#endif + { + ProjectButton* projectButton; + + QString projectPreviewPath = project.m_path + m_projectPreviewImagePath; + QFileInfo doesPreviewExist(projectPreviewPath); + if (doesPreviewExist.exists() && doesPreviewExist.isFile()) + { + project.m_imagePath = projectPreviewPath; + } + + projectButton = new ProjectButton(project, this); + + flowLayout->addWidget(projectButton); + + connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject); + connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject); + connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject); + connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject); + connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject); + +#ifdef SHOW_ALL_PROJECT_ACTIONS + connect(projectButton, &ProjectButton::EditProjectGems, this, &ProjectsScreen::HandleEditProjectGems); +#endif + } + + layout->addWidget(projectsScrollArea); + } + } + + return frame; + } + + ProjectManagerScreen ProjectsScreen::GetScreenEnum() + { + return ProjectManagerScreen::Projects; + } + + bool ProjectsScreen::IsTab() + { + return true; + } + + QString ProjectsScreen::GetTabText() + { + return tr("Projects"); + } + + void ProjectsScreen::paintEvent([[maybe_unused]] QPaintEvent* event) + { + // we paint the background here because qss does not support background cover scaling + QPainter painter(this); + + auto winSize = size(); + auto pixmapRatio = (float)m_background.width() / m_background.height(); + auto windowRatio = (float)winSize.width() / winSize.height(); + + if (pixmapRatio > windowRatio) + { + auto newWidth = (int)(winSize.height() * pixmapRatio); + auto offset = (newWidth - winSize.width()) / -2; + painter.drawPixmap(offset, 0, newWidth, winSize.height(), m_background); + } + else + { + auto newHeight = (int)(winSize.width() / pixmapRatio); + painter.drawPixmap(0, 0, winSize.width(), newHeight, m_background); + } + } + + void ProjectsScreen::HandleNewProjectButton() + { + emit ResetScreenRequest(ProjectManagerScreen::CreateProject); + emit ChangeScreenRequest(ProjectManagerScreen::CreateProject); + } + void ProjectsScreen::HandleAddProjectButton() + { + if (ProjectUtils::AddProjectDialog(this)) + { + emit ResetScreenRequest(ProjectManagerScreen::Projects); + emit ChangeScreenRequest(ProjectManagerScreen::Projects); + } + } + void ProjectsScreen::HandleOpenProject(const QString& projectPath) + { + if (!projectPath.isEmpty()) + { + AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); + AZStd::string executableFilename = "Editor"; + AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION); + auto cmdPath = AZ::IO::FixedMaxPathString::format("%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), projectPath.toStdString().c_str()); + + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; + processLaunchInfo.m_commandlineParameters = cmdPath; + bool launchSucceeded = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); + if (!launchSucceeded) + { + AZ_Error("ProjectManager", false, "Failed to launch editor"); + QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor, please verify the project settings are valid.")); + } + else + { + // prevent the user from accidentally pressing the button while the editor is launching + // and let them know what's happening + ProjectButton* button = qobject_cast(sender()); + if (button) + { + button->SetButtonEnabled(false); + button->SetButtonOverlayText(tr("Opening Editor...")); + } + + // enable the button after 3 seconds + constexpr int waitTimeInMs = 3000; + QTimer::singleShot(waitTimeInMs, this, [this, button] { + if (button) + { + button->SetButtonEnabled(true); + } + }); + } + } + else + { + AZ_Error("ProjectManager", false, "Cannot open editor because an empty project path was provided"); + QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor because the project path is invalid.")); + } + + } + void ProjectsScreen::HandleEditProject(const QString& projectPath) + { + emit NotifyCurrentProject(projectPath); + emit ResetScreenRequest(ProjectManagerScreen::UpdateProject); + emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject); + } + void ProjectsScreen::HandleEditProjectGems(const QString& projectPath) + { + emit NotifyCurrentProject(projectPath); + emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog); + } + void ProjectsScreen::HandleCopyProject(const QString& projectPath) + { + // Open file dialog and choose location for copied project then register copy with O3DE + if (ProjectUtils::CopyProjectDialog(projectPath, this)) + { + emit ResetScreenRequest(ProjectManagerScreen::Projects); + emit ChangeScreenRequest(ProjectManagerScreen::Projects); + } + } + void ProjectsScreen::HandleRemoveProject(const QString& projectPath) + { + // Unregister Project from O3DE and reload projects + if (ProjectUtils::UnregisterProject(projectPath)) + { + emit ResetScreenRequest(ProjectManagerScreen::Projects); + emit ChangeScreenRequest(ProjectManagerScreen::Projects); + } + } + void ProjectsScreen::HandleDeleteProject(const QString& projectPath) + { + QMessageBox::StandardButton warningResult = QMessageBox::warning( + this, tr("Delete Project"), tr("Are you sure?\nProject will be removed from O3DE and directory will be deleted!"), + QMessageBox::No | QMessageBox::Yes); + + if (warningResult == QMessageBox::Yes) + { + // Remove project from O3DE and delete from disk + HandleRemoveProject(projectPath); + ProjectUtils::DeleteProjectFiles(projectPath); + } + } + + void ProjectsScreen::NotifyCurrentScreen() + { + if (ShouldDisplayFirstTimeContent()) + { + m_stack->setCurrentWidget(m_firstTimeContent); + } + else + { + // refresh the projects content by re-creating it for now + if (m_projectsContent) + { + m_stack->removeWidget(m_projectsContent); + m_projectsContent->deleteLater(); + } + + m_projectsContent = CreateProjectsContent(); + + m_stack->addWidget(m_projectsContent); + m_stack->setCurrentWidget(m_projectsContent); + } + } + + bool ProjectsScreen::ShouldDisplayFirstTimeContent() + { + auto projectsResult = PythonBindingsInterface::Get()->GetProjects(); + if (!projectsResult.IsSuccess() || projectsResult.GetValue().isEmpty()) + { + return true; + } + + QSettings settings; + bool displayFirstTimeContent = settings.value("displayFirstTimeContent", true).toBool(); + if (displayFirstTimeContent) + { + settings.setValue("displayFirstTimeContent", false); + } + + return displayFirstTimeContent; + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.h b/Code/Tools/ProjectManager/Source/ProjectsScreen.h similarity index 69% rename from Code/Tools/ProjectManager/Source/ProjectsHomeScreen.h rename to Code/Tools/ProjectManager/Source/ProjectsScreen.h index e8d1ac4fb5..d88ba8398d 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsHomeScreen.h +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.h @@ -15,16 +15,26 @@ #include #endif +QT_FORWARD_DECLARE_CLASS(QPaintEvent) +QT_FORWARD_DECLARE_CLASS(QFrame) +QT_FORWARD_DECLARE_CLASS(QStackedWidget) + namespace O3DE::ProjectManager { - class ProjectsHomeScreen + class ProjectsScreen : public ScreenWidget { public: - explicit ProjectsHomeScreen(QWidget* parent = nullptr); - ~ProjectsHomeScreen() = default; + explicit ProjectsScreen(QWidget* parent = nullptr); + ~ProjectsScreen() = default; + ProjectManagerScreen GetScreenEnum() override; + QString GetTabText() override; + bool IsTab() override; + + protected: + void NotifyCurrentScreen() override; protected slots: void HandleNewProjectButton(); @@ -36,16 +46,24 @@ namespace O3DE::ProjectManager void HandleRemoveProject(const QString& projectPath); void HandleDeleteProject(const QString& projectPath); + void paintEvent(QPaintEvent* event) override; + private: + QFrame* CreateFirstTimeContent(); + QFrame* CreateProjectsContent(); + bool ShouldDisplayFirstTimeContent(); + QAction* m_createNewProjectAction; QAction* m_addExistingProjectAction; + QPixmap m_background; + QFrame* m_firstTimeContent; + QFrame* m_projectsContent; + QStackedWidget* m_stack; const QString m_projectPreviewImagePath = "/preview.png"; + inline constexpr static int s_contentMargins = 80; inline constexpr static int s_spacerSize = 20; - inline constexpr static int s_projectButtonRowCount = 4; - inline constexpr static int s_newProjectButtonWidth = 156; - }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 8c79a153c8..c0481d6c87 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -220,7 +220,7 @@ namespace RedirectOutput } } // namespace RedirectOutput -namespace O3DE::ProjectManager +namespace O3DE::ProjectManager { PythonBindings::PythonBindings(const AZ::IO::PathView& enginePath) : m_enginePath(enginePath) @@ -283,8 +283,11 @@ namespace O3DE::ProjectManager AZ_Warning("ProjectManagerWindow", result != -1, "Append to sys path failed"); // import required modules - m_registration = pybind11::module::import("cmake.Tools.registration"); - m_engineTemplate = pybind11::module::import("cmake.Tools.engine_template"); + m_register = pybind11::module::import("o3de.register"); + m_manifest = pybind11::module::import("o3de.manifest"); + m_engineTemplate = pybind11::module::import("o3de.engine_template"); + m_enableGemProject = pybind11::module::import("o3de.enable_gem"); + m_disableGemProject = pybind11::module::import("o3de.disable_gem"); return result == 0 && !PyErr_Occurred(); } catch ([[maybe_unused]] const std::exception& e) @@ -326,30 +329,30 @@ namespace O3DE::ProjectManager } } - AZ::Outcome PythonBindings::GetEngineInfo() + AZ::Outcome PythonBindings::GetEngineInfo() { EngineInfo engineInfo; bool result = ExecuteWithLock([&] { - pybind11::str enginePath = m_registration.attr("get_this_engine_path")(); + pybind11::str enginePath = m_manifest.attr("get_this_engine_path")(); - auto o3deData = m_registration.attr("load_o3de_manifest")(); + auto o3deData = m_manifest.attr("load_o3de_manifest")(); if (pybind11::isinstance(o3deData)) { - engineInfo.m_path = Py_To_String(enginePath); - engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]); - engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]); - engineInfo.m_defaultRestrictedFolder = Py_To_String(o3deData["default_restricted_folder"]); - engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]); - engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData,"third_party_path",""); + engineInfo.m_path = Py_To_String(enginePath); + engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]); + engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]); + engineInfo.m_defaultRestrictedFolder = Py_To_String(o3deData["default_restricted_folder"]); + engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]); + engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData,"third_party_path",""); } - auto engineData = m_registration.attr("get_engine_data")(pybind11::none(), enginePath); + auto engineData = m_manifest.attr("get_engine_json_data")(pybind11::none(), enginePath); if (pybind11::isinstance(engineData)) { try { - engineInfo.m_version = Py_To_String_Optional(engineData,"O3DEVersion","0.0.0.0"); - engineInfo.m_name = Py_To_String_Optional(engineData,"engine_name","O3DE"); + engineInfo.m_version = Py_To_String_Optional(engineData,"O3DEVersion","0.0.0.0"); + engineInfo.m_name = Py_To_String_Optional(engineData,"engine_name","O3DE"); } catch ([[maybe_unused]] const std::exception& e) { @@ -364,13 +367,13 @@ namespace O3DE::ProjectManager } else { - return AZ::Success(AZStd::move(engineInfo)); + return AZ::Success(AZStd::move(engineInfo)); } return AZ::Failure(); } - bool PythonBindings::SetEngineInfo(const EngineInfo& engineInfo) + bool PythonBindings::SetEngineInfo(const EngineInfo& engineInfo) { bool result = ExecuteWithLock([&] { pybind11::str enginePath = engineInfo.m_path.toStdString(); @@ -378,17 +381,18 @@ namespace O3DE::ProjectManager pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString(); pybind11::str defaultTemplatesFolder = engineInfo.m_defaultTemplatesFolder.toStdString(); - auto registrationResult = m_registration.attr("register")( + auto registrationResult = m_register.attr("register")( enginePath, // engine_path pybind11::none(), // project_path - pybind11::none(), // gem_path + pybind11::none(), // gem_path + pybind11::none(), // external_subdir_path pybind11::none(), // template_path pybind11::none(), // restricted_path pybind11::none(), // repo_uri pybind11::none(), // default_engines_folder defaultProjectsFolder, - defaultGemsFolder, - defaultTemplatesFolder + defaultGemsFolder, + defaultTemplatesFolder ); if (registrationResult.cast() != 0) @@ -396,13 +400,13 @@ namespace O3DE::ProjectManager result = false; } - auto manifest = m_registration.attr("load_o3de_manifest")(); + auto manifest = m_manifest.attr("load_o3de_manifest")(); if (pybind11::isinstance(manifest)) { try { manifest["third_party_path"] = engineInfo.m_thirdPartyPath.toStdString(); - m_registration.attr("save_o3de_manifest")(manifest); + m_manifest.attr("save_o3de_manifest")(manifest); } catch ([[maybe_unused]] const std::exception& e) { @@ -415,12 +419,12 @@ namespace O3DE::ProjectManager return result; } - AZ::Outcome PythonBindings::GetGem(const QString& path) + AZ::Outcome PythonBindings::GetGem(const QString& path) { GemInfo gemInfo = GemInfoFromPath(pybind11::str(path.toStdString())); if (gemInfo.IsValid()) { - return AZ::Success(AZStd::move(gemInfo)); + return AZ::Success(AZStd::move(gemInfo)); } else { @@ -428,19 +432,19 @@ namespace O3DE::ProjectManager } } - AZ::Outcome> PythonBindings::GetGems() + AZ::Outcome> PythonBindings::GetGems() { QVector gems; bool result = ExecuteWithLock([&] { - // external gems - for (auto path : m_registration.attr("get_gems")()) + // external gems + for (auto path : m_manifest.attr("get_gems")()) { gems.push_back(GemInfoFromPath(path)); } - // gems from the engine - for (auto path : m_registration.attr("get_engine_gems")()) + // gems from the engine + for (auto path : m_manifest.attr("get_engine_gems")()) { gems.push_back(GemInfoFromPath(path)); } @@ -452,18 +456,72 @@ namespace O3DE::ProjectManager } else { - return AZ::Success(AZStd::move(gems)); + return AZ::Success(AZStd::move(gems)); } } - AZ::Outcome PythonBindings::CreateProject(const QString& projectTemplatePath, const ProjectInfo& projectInfo) + bool PythonBindings::AddProject(const QString& path) + { + bool registrationResult = false; + bool result = ExecuteWithLock( + [&] + { + pybind11::str projectPath = path.toStdString(); + auto pythonRegistrationResult = m_register.attr("register")(pybind11::none(), projectPath); + + // Returns an exit code so boolify it then invert result + registrationResult = !pythonRegistrationResult.cast(); + }); + + return result && registrationResult; + } + + bool PythonBindings::RemoveProject(const QString& path) + { + bool registrationResult = false; + bool result = ExecuteWithLock( + [&] + { + pybind11::str projectPath = path.toStdString(); + auto pythonRegistrationResult = m_register.attr("register")( + pybind11::none(), // engine_path + projectPath, // project_path + pybind11::none(), // gem_path + pybind11::none(), // external_subdir_path + pybind11::none(), // template_path + pybind11::none(), // restricted_path + pybind11::none(), // repo_uri + pybind11::none(), // default_engines_folder + pybind11::none(), // default_projects_folder + pybind11::none(), // default_gems_folder + pybind11::none(), // default_templates_folder + pybind11::none(), // default_restricted_folder + pybind11::none(), // external_subdir_engine_path + pybind11::none(), // external_subdir_project_path + true, // remove + false // force + ); + + // Returns an exit code so boolify it then invert result + registrationResult = !pythonRegistrationResult.cast(); + }); + + return result && registrationResult; + } + + AZ::Outcome PythonBindings::CreateProject(const QString& projectTemplatePath, const ProjectInfo& projectInfo) { ProjectInfo createdProjectInfo; bool result = ExecuteWithLock([&] { - pybind11::str projectPath = projectInfo.m_path.toStdString(); + pybind11::str projectName = projectInfo.m_projectName.toStdString(); pybind11::str templatePath = projectTemplatePath.toStdString(); - auto createProjectResult = m_engineTemplate.attr("create_project")(projectPath, templatePath); + + auto createProjectResult = m_engineTemplate.attr("create_project")( + projectPath, + projectName, + templatePath + ); if (createProjectResult.cast() == 0) { createdProjectInfo = ProjectInfoFromPath(projectPath); @@ -476,16 +534,16 @@ namespace O3DE::ProjectManager } else { - return AZ::Success(AZStd::move(createdProjectInfo)); + return AZ::Success(AZStd::move(createdProjectInfo)); } } - AZ::Outcome PythonBindings::GetProject(const QString& path) + AZ::Outcome PythonBindings::GetProject(const QString& path) { ProjectInfo projectInfo = ProjectInfoFromPath(pybind11::str(path.toStdString())); if (projectInfo.IsValid()) { - return AZ::Success(AZStd::move(projectInfo)); + return AZ::Success(AZStd::move(projectInfo)); } else { @@ -496,30 +554,21 @@ namespace O3DE::ProjectManager GemInfo PythonBindings::GemInfoFromPath(pybind11::handle path) { GemInfo gemInfo; - gemInfo.m_path = Py_To_String(path); + gemInfo.m_path = Py_To_String(path); - auto data = m_registration.attr("get_gem_data")(pybind11::none(), path); + auto data = m_manifest.attr("get_gem_json_data")(pybind11::none(), path); if (pybind11::isinstance(data)) { try { // required - gemInfo.m_name = Py_To_String(data["Name"]); - gemInfo.m_uuid = AZ::Uuid(Py_To_String(data["Uuid"])); + gemInfo.m_name = Py_To_String(data["gem_name"]); // optional - gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name); - gemInfo.m_summary = Py_To_String_Optional(data, "Summary", ""); - gemInfo.m_version = Py_To_String_Optional(data, "Version", ""); + gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name); + gemInfo.m_summary = Py_To_String_Optional(data, "Summary", ""); + gemInfo.m_version = Py_To_String_Optional(data, "Version", ""); - if (data.contains("Dependencies")) - { - for (auto dependency : data["Dependencies"]) - { - const AZ::Uuid uuid = Py_To_String(dependency["Uuid"]); - gemInfo.m_dependingGemUuids.push_back(uuid.ToString().c_str()); - } - } if (data.contains("Tags")) { for (auto tag : data["Tags"]) @@ -543,13 +592,13 @@ namespace O3DE::ProjectManager projectInfo.m_path = Py_To_String(path); projectInfo.m_isNew = false; - auto projectData = m_registration.attr("get_project_data")(pybind11::none(), path); + auto projectData = m_manifest.attr("get_project_json_data")(pybind11::none(), path); if (pybind11::isinstance(projectData)) { try { - projectInfo.m_projectName = Py_To_String(projectData["project_name"]); - projectInfo.m_displayName = Py_To_String_Optional(projectData,"display_name", projectInfo.m_projectName); + projectInfo.m_projectName = Py_To_String(projectData["project_name"]); + projectInfo.m_displayName = Py_To_String_Optional(projectData,"display_name", projectInfo.m_projectName); } catch ([[maybe_unused]] const std::exception& e) { @@ -560,19 +609,19 @@ namespace O3DE::ProjectManager return projectInfo; } - AZ::Outcome> PythonBindings::GetProjects() + AZ::Outcome> PythonBindings::GetProjects() { QVector projects; bool result = ExecuteWithLock([&] { - // external projects - for (auto path : m_registration.attr("get_projects")()) + // external projects + for (auto path : m_manifest.attr("get_projects")()) { projects.push_back(ProjectInfoFromPath(path)); } - // projects from the engine - for (auto path : m_registration.attr("get_engine_projects")()) + // projects from the engine + for (auto path : m_manifest.attr("get_engine_projects")()) { projects.push_back(ProjectInfoFromPath(path)); } @@ -584,7 +633,7 @@ namespace O3DE::ProjectManager } else { - return AZ::Success(AZStd::move(projects)); + return AZ::Success(AZStd::move(projects)); } } @@ -594,13 +643,12 @@ namespace O3DE::ProjectManager pybind11::str pyGemPath = gemPath.toStdString(); pybind11::str pyProjectPath = projectPath.toStdString(); - m_registration.attr("add_gem_to_project")( + m_enableGemProject.attr("enable_gem_in_project")( pybind11::none(), // gem_name pyGemPath, - pybind11::none(), // gem_target pybind11::none(), // project_name pyProjectPath - ); + ); }); return result; @@ -612,19 +660,18 @@ namespace O3DE::ProjectManager pybind11::str pyGemPath = gemPath.toStdString(); pybind11::str pyProjectPath = projectPath.toStdString(); - m_registration.attr("remove_gem_to_project")( + m_disableGemProject.attr("disable_gem_in_project")( pybind11::none(), // gem_name pyGemPath, - pybind11::none(), // gem_target pybind11::none(), // project_name pyProjectPath - ); + ); }); return result; } - bool PythonBindings::UpdateProject([[maybe_unused]] const ProjectInfo& projectInfo) + bool PythonBindings::UpdateProject([[maybe_unused]] const ProjectInfo& projectInfo) { return false; } @@ -632,18 +679,18 @@ namespace O3DE::ProjectManager ProjectTemplateInfo PythonBindings::ProjectTemplateInfoFromPath(pybind11::handle path) { ProjectTemplateInfo templateInfo; - templateInfo.m_path = Py_To_String(path); + templateInfo.m_path = Py_To_String(path); - auto data = m_registration.attr("get_template_data")(pybind11::none(), path); + auto data = m_manifest.attr("get_template_json_data")(pybind11::none(), path); if (pybind11::isinstance(data)) { try { // required - templateInfo.m_displayName = Py_To_String(data["display_name"]); - templateInfo.m_name = Py_To_String(data["template_name"]); - templateInfo.m_summary = Py_To_String(data["summary"]); - + templateInfo.m_displayName = Py_To_String(data["display_name"]); + templateInfo.m_name = Py_To_String(data["template_name"]); + templateInfo.m_summary = Py_To_String(data["summary"]); + // optional if (data.contains("canonical_tags")) { @@ -669,12 +716,12 @@ namespace O3DE::ProjectManager return templateInfo; } - AZ::Outcome> PythonBindings::GetProjectTemplates() + AZ::Outcome> PythonBindings::GetProjectTemplates() { QVector templates; bool result = ExecuteWithLock([&] { - for (auto path : m_registration.attr("get_project_templates")()) + for (auto path : m_manifest.attr("get_project_templates")()) { templates.push_back(ProjectTemplateInfoFromPath(path)); } @@ -686,7 +733,7 @@ namespace O3DE::ProjectManager } else { - return AZ::Success(AZStd::move(templates)); + return AZ::Success(AZStd::move(templates)); } } } diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index 892e13a65b..18122f484b 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -12,7 +12,7 @@ #pragma once #include -#include +#include #include // Qt defines slots, which interferes with the use here. @@ -25,7 +25,7 @@ namespace O3DE::ProjectManager { - class PythonBindings + class PythonBindings : public PythonBindingsInterface::Registrar { public: @@ -46,6 +46,8 @@ namespace O3DE::ProjectManager AZ::Outcome CreateProject(const QString& projectTemplatePath, const ProjectInfo& projectInfo) override; AZ::Outcome GetProject(const QString& path) override; AZ::Outcome> GetProjects() override; + bool AddProject(const QString& path) override; + bool RemoveProject(const QString& path) override; bool UpdateProject(const ProjectInfo& projectInfo) override; bool AddGemToProject(const QString& gemPath, const QString& projectPath) override; bool RemoveGemFromProject(const QString& gemPath, const QString& projectPath) override; @@ -66,6 +68,9 @@ namespace O3DE::ProjectManager AZ::IO::FixedMaxPath m_enginePath; pybind11::handle m_engineTemplate; AZStd::recursive_mutex m_lock; - pybind11::handle m_registration; + pybind11::handle m_register; + pybind11::handle m_manifest; + pybind11::handle m_enableGemProject; + pybind11::handle m_disableGemProject; }; } diff --git a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h index b5c8f1a76a..a58eea0fe6 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h +++ b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h @@ -88,6 +88,20 @@ namespace O3DE::ProjectManager * @return an outcome with ProjectInfos on success */ virtual AZ::Outcome> GetProjects() = 0; + + /** + * Adds existing project on disk + * @param path the absolute path to the project + * @return true on success, false on failure + */ + virtual bool AddProject(const QString& path) = 0; + + /** + * Adds existing project on disk + * @param path the absolute path to the project + * @return true on success, false on failure + */ + virtual bool RemoveProject(const QString& path) = 0; /** * Update a project diff --git a/Code/Tools/ProjectManager/Source/ScreenDefs.h b/Code/Tools/ProjectManager/Source/ScreenDefs.h index 13289e2481..43ed303461 100644 --- a/Code/Tools/ProjectManager/Source/ScreenDefs.h +++ b/Code/Tools/ProjectManager/Source/ScreenDefs.h @@ -11,19 +11,39 @@ */ #pragma once +#include +#include +#include + namespace O3DE::ProjectManager { - enum ProjectManagerScreen + enum class ProjectManagerScreen { Invalid = -1, Empty, - FirstTimeUse, CreateProject, NewProjectSettings, GemCatalog, - ProjectsHome, + Projects, UpdateProject, ProjectSettings, EngineSettings }; + + static QHash s_ProjectManagerStringNames = { + { "Empty", ProjectManagerScreen::Empty}, + { "CreateProject", ProjectManagerScreen::CreateProject}, + { "NewProjectSettings", ProjectManagerScreen::NewProjectSettings}, + { "GemCatalog", ProjectManagerScreen::GemCatalog}, + { "Projects", ProjectManagerScreen::Projects}, + { "UpdateProject", ProjectManagerScreen::UpdateProject}, + { "ProjectSettings", ProjectManagerScreen::ProjectSettings}, + { "EngineSettings", ProjectManagerScreen::EngineSettings} + }; + + // need to define qHash for ProjectManagerScreen when using scoped enums + inline uint qHash(ProjectManagerScreen key, uint seed) + { + return ::qHash(static_cast(key), seed); + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreenFactory.cpp b/Code/Tools/ProjectManager/Source/ScreenFactory.cpp index d37ccdb59f..b2b4376e14 100644 --- a/Code/Tools/ProjectManager/Source/ScreenFactory.cpp +++ b/Code/Tools/ProjectManager/Source/ScreenFactory.cpp @@ -11,12 +11,11 @@ */ #include -#include #include #include #include #include -#include +#include #include #include @@ -28,9 +27,6 @@ namespace O3DE::ProjectManager switch(screen) { - case (ProjectManagerScreen::FirstTimeUse): - newScreen = new FirstTimeUseScreen(parent); - break; case (ProjectManagerScreen::CreateProject): newScreen = new CreateProjectCtrl(parent); break; @@ -40,8 +36,8 @@ namespace O3DE::ProjectManager case (ProjectManagerScreen::GemCatalog): newScreen = new GemCatalogScreen(parent); break; - case (ProjectManagerScreen::ProjectsHome): - newScreen = new ProjectsHomeScreen(parent); + case (ProjectManagerScreen::Projects): + newScreen = new ProjectsScreen(parent); break; case (ProjectManagerScreen::UpdateProject): newScreen = new UpdateProjectCtrl(parent); diff --git a/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.cpp new file mode 100644 index 0000000000..29b1eb6ff6 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.cpp @@ -0,0 +1,62 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + ScreenHeader::ScreenHeader(QWidget* parent) + : QFrame(parent) + { + setObjectName("header"); + + QHBoxLayout* layout = new QHBoxLayout(); + layout->setAlignment(Qt::AlignLeft); + layout->setContentsMargins(0,0,0,0); + + m_backButton = new QPushButton(); + m_backButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + layout->addWidget(m_backButton); + + QVBoxLayout* titleLayout = new QVBoxLayout(); + m_title = new QLabel(); + m_title->setObjectName("headerTitle"); + titleLayout->addWidget(m_title); + + m_subTitle = new QLabel(); + m_subTitle->setObjectName("headerSubTitle"); + titleLayout->addWidget(m_subTitle); + + layout->addLayout(titleLayout); + + setLayout(layout); + } + + void ScreenHeader::setTitle(const QString& text) + { + m_title->setText(text); + } + + void ScreenHeader::setSubTitle(const QString& text) + { + m_subTitle->setText(text); + } + + QPushButton* ScreenHeader::backButton() + { + return m_backButton; + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h new file mode 100644 index 0000000000..c5fdb56195 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h @@ -0,0 +1,42 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QPushButton) + +namespace O3DE::ProjectManager +{ + class ScreenHeader + : public QFrame + { + Q_OBJECT // AUTOMOC + + public: + ScreenHeader(QWidget* parent = nullptr); + + void setTitle(const QString& text); + void setSubTitle(const QString& text); + + QPushButton* backButton(); + + private: + QLabel* m_title; + QLabel* m_subTitle; + QPushButton* m_backButton; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreenWidget.h b/Code/Tools/ProjectManager/Source/ScreenWidget.h index e80747d67b..2ad6d30201 100644 --- a/Code/Tools/ProjectManager/Source/ScreenWidget.h +++ b/Code/Tools/ProjectManager/Source/ScreenWidget.h @@ -41,12 +41,27 @@ namespace O3DE::ProjectManager { return true; } + virtual bool IsTab() + { + return false; + } + virtual QString GetTabText() + { + return tr("Missing"); + } + + //! Notify this screen it is the current screen + virtual void NotifyCurrentScreen() + { + + } signals: void ChangeScreenRequest(ProjectManagerScreen screen); void GotoPreviousScreenRequest(); void ResetScreenRequest(ProjectManagerScreen screen); void NotifyCurrentProject(const QString& projectPath); + }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp index a77c434026..6206d4cee9 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp @@ -14,6 +14,7 @@ #include #include +#include #include namespace O3DE::ProjectManager @@ -21,17 +22,19 @@ namespace O3DE::ProjectManager ScreensCtrl::ScreensCtrl(QWidget* parent) : QWidget(parent) { + setObjectName("ScreensCtrl"); + QVBoxLayout* vLayout = new QVBoxLayout(); - vLayout->setMargin(0); - vLayout->setSpacing(0); vLayout->setContentsMargins(0, 0, 0, 0); setLayout(vLayout); m_screenStack = new QStackedWidget(); vLayout->addWidget(m_screenStack); - //Track the bottom of the stack - m_screenVisitOrder.push(ProjectManagerScreen::Invalid); + // add a tab widget at the bottom of the stack + m_tabWidget = new QTabWidget(); + m_screenStack->addWidget(m_tabWidget); + connect(m_tabWidget, &QTabWidget::currentChanged, this, &ScreensCtrl::TabChanged); } void ScreensCtrl::BuildScreens(QVector screens) @@ -57,7 +60,14 @@ namespace O3DE::ProjectManager ScreenWidget* ScreensCtrl::GetCurrentScreen() { - return reinterpret_cast(m_screenStack->currentWidget()); + if (m_screenStack->currentWidget() == m_tabWidget) + { + return reinterpret_cast(m_tabWidget->currentWidget()); + } + else + { + return reinterpret_cast(m_screenStack->currentWidget()); + } } bool ScreensCtrl::ChangeToScreen(ProjectManagerScreen screen) @@ -79,13 +89,28 @@ namespace O3DE::ProjectManager if (iterator != m_screenMap.end()) { ScreenWidget* currentScreen = GetCurrentScreen(); - if (currentScreen != iterator.value()) + ScreenWidget* newScreen = iterator.value(); + + if (currentScreen != newScreen) { if (addVisit) { - m_screenVisitOrder.push(currentScreen->GetScreenEnum()); + ProjectManagerScreen oldScreen = currentScreen->GetScreenEnum(); + m_screenVisitOrder.push(oldScreen); } - m_screenStack->setCurrentWidget(iterator.value()); + + if (newScreen->IsTab()) + { + m_tabWidget->setCurrentWidget(newScreen); + m_screenStack->setCurrentWidget(m_tabWidget); + } + else + { + m_screenStack->setCurrentWidget(newScreen); + } + + newScreen->NotifyCurrentScreen(); + return true; } } @@ -95,23 +120,56 @@ namespace O3DE::ProjectManager bool ScreensCtrl::GotoPreviousScreen() { - // Don't go back if we are on the first set screen - if (m_screenVisitOrder.top() != ProjectManagerScreen::Invalid) + if (!m_screenVisitOrder.isEmpty()) { // We do not check with screen if we can go back, we should always be able to go back - return ForceChangeToScreen(m_screenVisitOrder.pop(), false); + ProjectManagerScreen previousScreen = m_screenVisitOrder.pop(); + return ForceChangeToScreen(previousScreen, false); } return false; } void ScreensCtrl::ResetScreen(ProjectManagerScreen screen) { + bool shouldRestoreCurrentScreen = false; + if (GetCurrentScreen() && GetCurrentScreen()->GetScreenEnum() == screen) + { + shouldRestoreCurrentScreen = true; + } + int tabIndex = GetScreenTabIndex(screen); + // Delete old screen if it exists to start fresh DeleteScreen(screen); // Add new screen ScreenWidget* newScreen = BuildScreen(this, screen); - m_screenStack->addWidget(newScreen); + if (newScreen->IsTab()) + { + if (tabIndex > -1) + { + m_tabWidget->insertTab(tabIndex, newScreen, newScreen->GetTabText()); + } + else + { + m_tabWidget->addTab(newScreen, newScreen->GetTabText()); + } + if (shouldRestoreCurrentScreen) + { + m_tabWidget->setCurrentWidget(newScreen); + m_screenStack->setCurrentWidget(m_tabWidget); + newScreen->NotifyCurrentScreen(); + } + } + else + { + m_screenStack->addWidget(newScreen); + if (shouldRestoreCurrentScreen) + { + m_screenStack->setCurrentWidget(newScreen); + newScreen->NotifyCurrentScreen(); + } + } + m_screenMap.insert(screen, newScreen); connect(newScreen, &ScreenWidget::ChangeScreenRequest, this, &ScreensCtrl::ChangeToScreen); @@ -134,8 +192,21 @@ namespace O3DE::ProjectManager const auto iter = m_screenMap.find(screen); if (iter != m_screenMap.end()) { - m_screenStack->removeWidget(iter.value()); - iter.value()->deleteLater(); + ScreenWidget* screenToDelete = iter.value(); + if (screenToDelete->IsTab()) + { + int tabIndex = m_tabWidget->indexOf(screenToDelete); + if (tabIndex > -1) + { + m_tabWidget->removeTab(tabIndex); + } + } + else + { + // if the screen we delete is the current widget, a new one will + // be selected automatically (randomly?) + m_screenStack->removeWidget(screenToDelete); + } // Erase does not cause a rehash so interators remain valid m_screenMap.erase(iter); @@ -150,4 +221,27 @@ namespace O3DE::ProjectManager } } + void ScreensCtrl::TabChanged([[maybe_unused]] int index) + { + ScreenWidget* screen = reinterpret_cast(m_tabWidget->currentWidget()); + if (screen) + { + screen->NotifyCurrentScreen(); + } + } + + int ScreensCtrl::GetScreenTabIndex(ProjectManagerScreen screen) + { + const auto iter = m_screenMap.find(screen); + if (iter != m_screenMap.end()) + { + ScreenWidget* screenWidget = iter.value(); + if (screenWidget->IsTab()) + { + return m_tabWidget->indexOf(screenWidget); + } + } + + return -1; + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.h b/Code/Tools/ProjectManager/Source/ScreensCtrl.h index a9d1023b4b..3b51ed529a 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.h +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.h @@ -18,6 +18,8 @@ #include #endif +QT_FORWARD_DECLARE_CLASS(QTabWidget) + namespace O3DE::ProjectManager { class ScreenWidget; @@ -46,11 +48,15 @@ namespace O3DE::ProjectManager void ResetAllScreens(); void DeleteScreen(ProjectManagerScreen screen); void DeleteAllScreens(); + void TabChanged(int index); private: + int GetScreenTabIndex(ProjectManagerScreen screen); + QStackedWidget* m_screenStack; QHash m_screenMap; QStack m_screenVisitOrder; + QTabWidget* m_tabWidget; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/TagWidget.cpp b/Code/Tools/ProjectManager/Source/TagWidget.cpp index 3e80944204..628b682b95 100644 --- a/Code/Tools/ProjectManager/Source/TagWidget.cpp +++ b/Code/Tools/ProjectManager/Source/TagWidget.cpp @@ -18,9 +18,9 @@ namespace O3DE::ProjectManager TagWidget::TagWidget(const QString& text, QWidget* parent) : QLabel(text, parent) { - setFixedHeight(35); + setFixedHeight(24); setMargin(5); - setStyleSheet("font-size: 12pt; background-color: #333333; border-radius: 4px;"); + setStyleSheet("font-size: 12px; background-color: #333333; border-radius: 3px;"); } TagContainerWidget::TagContainerWidget(QWidget* parent) @@ -35,7 +35,7 @@ namespace O3DE::ProjectManager void TagContainerWidget::Update(const QStringList& tags) { QWidget* parentWidget = qobject_cast(parent()); - int width = 250; + int width = 200; if (parentWidget) { width = parentWidget->width(); diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index 84e3d8359d..b3180966ce 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -108,7 +108,7 @@ namespace O3DE::ProjectManager auto result = PythonBindingsInterface::Get()->UpdateProject(m_projectInfo); if (result) { - emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome); + emit ChangeScreenRequest(ProjectManagerScreen::Projects); } else { diff --git a/Code/Tools/ProjectManager/Source/main.cpp b/Code/Tools/ProjectManager/Source/main.cpp index 3d8bb71a0c..c597b8a729 100644 --- a/Code/Tools/ProjectManager/Source/main.cpp +++ b/Code/Tools/ProjectManager/Source/main.cpp @@ -15,13 +15,17 @@ #include #include #include +#include #include +#include #include #include #include +using namespace O3DE::ProjectManager; + int main(int argc, char* argv[]) { QApplication::setOrganizationName("O3DE"); @@ -35,7 +39,6 @@ int main(int argc, char* argv[]) QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware); - AZ::AllocatorInstance::Create(); int runSuccess = 0; { @@ -52,9 +55,37 @@ int main(int argc, char* argv[]) AzQtComponents::StyleManager styleManager(&app); styleManager.initialize(&app, engineRootPath); - O3DE::ProjectManager::ProjectManagerWindow window(nullptr, engineRootPath); + // Get the initial start screen if one is provided via command line + constexpr char optionPrefix[] = "--"; + AZ::CommandLine commandLine(optionPrefix); + commandLine.Parse(argc, argv); + + ProjectManagerScreen startScreen = ProjectManagerScreen::Projects; + if(commandLine.HasSwitch("screen")) + { + QString screenOption = commandLine.GetSwitchValue("screen", 0).c_str(); + ProjectManagerScreen screen = ProjectUtils::GetProjectManagerScreen(screenOption); + if (screen != ProjectManagerScreen::Invalid) + { + startScreen = screen; + } + } + + AZ::IO::FixedMaxPath projectPath; + if (commandLine.HasSwitch("project-path")) + { + projectPath = commandLine.GetSwitchValue("project-path", 0).c_str(); + } + + ProjectManagerWindow window(nullptr, engineRootPath, projectPath, startScreen); window.show(); + // somethings is preventing us from moving the window to the center of the + // primary screen - likely an Az style or component helper + constexpr int width = 1200; + constexpr int height = 800; + window.resize(width, height); + runSuccess = app.exec(); } AZ::AllocatorInstance::Destroy(); diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index a41ddad21e..feaea4c172 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -21,8 +21,6 @@ set(FILES Source/ScreenWidget.h Source/EngineInfo.h Source/EngineInfo.cpp - Source/FirstTimeUseScreen.h - Source/FirstTimeUseScreen.cpp Source/FormLineEditWidget.h Source/FormLineEditWidget.cpp Source/FormBrowseEditWidget.h @@ -33,20 +31,21 @@ set(FILES Source/ProjectManagerWindow.cpp Source/ProjectTemplateInfo.h Source/ProjectTemplateInfo.cpp - Source/ProjectManagerWindow.ui Source/PythonBindings.h Source/PythonBindings.cpp Source/PythonBindingsInterface.h Source/ProjectInfo.h Source/ProjectInfo.cpp + Source/ProjectUtils.h + Source/ProjectUtils.cpp Source/NewProjectSettingsScreen.h Source/NewProjectSettingsScreen.cpp Source/CreateProjectCtrl.h Source/CreateProjectCtrl.cpp Source/UpdateProjectCtrl.h Source/UpdateProjectCtrl.cpp - Source/ProjectsHomeScreen.h - Source/ProjectsHomeScreen.cpp + Source/ProjectsScreen.h + Source/ProjectsScreen.cpp Source/ProjectSettingsScreen.h Source/ProjectSettingsScreen.cpp Source/ProjectSettingsScreen.ui @@ -54,6 +53,8 @@ set(FILES Source/EngineSettingsScreen.cpp Source/ProjectButtonWidget.h Source/ProjectButtonWidget.cpp + Source/ScreenHeaderWidget.h + Source/ScreenHeaderWidget.cpp Source/LinkWidget.h Source/LinkWidget.cpp Source/TagWidget.h diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp index 3dc14814de..4ab41423fa 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp @@ -38,21 +38,8 @@ namespace AZ { namespace FbxSceneBuilder { - static AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler* g_fbxImporter = nullptr; static AZStd::vector g_componentDescriptors; - void Initialize() - { - // Currently it's still needed to explicitly create an instance of this instead of letting - // it be a normal component. This is because ResourceCompilerScene needs to return - // the list of available extensions before it can start the application. - if (!g_fbxImporter) - { - g_fbxImporter = aznew AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler(); - g_fbxImporter->Activate(); - } - } - void Reflect(AZ::SerializeContext* /*context*/) { // Descriptor registration is done in Reflect instead of Initialize because the ResourceCompilerScene initializes the libraries before @@ -64,6 +51,7 @@ namespace AZ { // Global importer and behavior g_componentDescriptors.push_back(FbxSceneBuilder::FbxImporter::CreateDescriptor()); + g_componentDescriptors.push_back(FbxSceneImporter::FbxImportRequestHandler::CreateDescriptor()); // Node and attribute importers g_componentDescriptors.push_back(AssImpBitangentStreamImporter::CreateDescriptor()); @@ -110,13 +98,6 @@ namespace AZ g_componentDescriptors.clear(); g_componentDescriptors.shrink_to_fit(); } - - if (g_fbxImporter) - { - g_fbxImporter->Deactivate(); - delete g_fbxImporter; - g_fbxImporter = nullptr; - } } } // namespace FbxSceneBuilder } // namespace SceneAPI @@ -125,7 +106,6 @@ namespace AZ extern "C" AZ_DLL_EXPORT void InitializeDynamicModule(void* env) { AZ::Environment::Attach(static_cast(env)); - AZ::SceneAPI::FbxSceneBuilder::Initialize(); } extern "C" AZ_DLL_EXPORT void Reflect(AZ::SerializeContext* context) { diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp index 155209f1b5..a8b059304d 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp @@ -10,12 +10,16 @@ * */ +#include +#include #include -#include +#include +#include +#include +#include #include #include #include -#include namespace AZ { @@ -23,10 +27,23 @@ namespace AZ { namespace FbxSceneImporter { - const char* FbxImportRequestHandler::s_extension = ".fbx"; + void SceneImporterSettings::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context); serializeContext) + { + serializeContext->Class() + ->Version(2) + ->Field("SupportedFileTypeExtensions", &SceneImporterSettings::m_supportedFileTypeExtensions); + } + } void FbxImportRequestHandler::Activate() { + if (auto* settingsRegistry = AZ::SettingsRegistry::Get()) + { + settingsRegistry->GetObject(m_settings, "/O3DE/SceneAPI/AssetImporter"); + } + BusConnect(); } @@ -37,21 +54,31 @@ namespace AZ void FbxImportRequestHandler::Reflect(ReflectContext* context) { + SceneImporterSettings::Reflect(context); + SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(1); + serializeContext->Class()->Version(1)->Attribute( + AZ::Edit::Attributes::SystemComponentTags, + AZStd::vector( + {AssetBuilderSDK::ComponentTags::AssetBuilder, + AssetImportRequest::GetAssetImportRequestComponentTag()})); + } } void FbxImportRequestHandler::GetSupportedFileExtensions(AZStd::unordered_set& extensions) { - extensions.insert(s_extension); + extensions.insert(m_settings.m_supportedFileTypeExtensions.begin(), m_settings.m_supportedFileTypeExtensions.end()); } Events::LoadingResult FbxImportRequestHandler::LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid, [[maybe_unused]] RequestingApplication requester) { - if (!AzFramework::StringFunc::Path::IsExtension(path.c_str(), s_extension)) + AZStd::string extension; + StringFunc::Path::GetExtension(path.c_str(), extension); + + if (!m_settings.m_supportedFileTypeExtensions.contains(extension)) { return Events::LoadingResult::Ignored; } @@ -73,6 +100,11 @@ namespace AZ return Events::LoadingResult::AssetFailure; } } + + void FbxImportRequestHandler::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided) + { + provided.emplace_back(AZ_CRC_CE("AssetImportRequestHandler")); + } } // namespace Import } // namespace SceneAPI } // namespace AZ diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h index 8b33051f1e..12c7c6f877 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h @@ -21,12 +21,21 @@ namespace AZ { namespace FbxSceneImporter { + struct SceneImporterSettings + { + AZ_TYPE_INFO(SceneImporterSettings, "{8BB6C7AD-BF99-44DC-9DA1-E7AD3F03DC10}"); + + static void Reflect(AZ::ReflectContext* context); + + AZStd::unordered_set m_supportedFileTypeExtensions; + }; + class FbxImportRequestHandler - : public SceneCore::BehaviorComponent + : public AZ::Component , public Events::AssetImportRequestBus::Handler { public: - AZ_COMPONENT(FbxImportRequestHandler, "{9F4B189C-0A96-4F44-A5F0-E087FF1561F8}", SceneCore::BehaviorComponent); + AZ_COMPONENT(FbxImportRequestHandler, "{9F4B189C-0A96-4F44-A5F0-E087FF1561F8}"); ~FbxImportRequestHandler() override = default; @@ -38,8 +47,13 @@ namespace AZ Events::LoadingResult LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid, RequestingApplication requester) override; + static void GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided); + private: - static const char* s_extension; + + SceneImporterSettings m_settings; + + static constexpr const char* SettingsFilename = "AssetImporterSettings.json"; }; } // namespace FbxSceneImporter } // namespace SceneAPI diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp index 193a1f9fd5..c0d1fc3bd0 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp @@ -37,7 +37,7 @@ namespace AZ SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(1); + serializeContext->Class()->Version(2); } } diff --git a/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp b/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp index 2cda1e68ae..791af4bf68 100644 --- a/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp +++ b/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp @@ -17,6 +17,13 @@ #include #include +#if AZ_TRAIT_COMPILER_SUPPORT_CSIGNAL +#include +#include +#include +#include +#endif // AZ_TRAIT_COMPILER_SUPPORT_CSIGNAL + namespace AZ { namespace AssImpSDKWrapper @@ -34,10 +41,31 @@ namespace AZ { } +#if AZ_TRAIT_COMPILER_SUPPORT_CSIGNAL + void signal_handler(int signal) + { + AZ_TracePrintf( + SceneAPI::Utilities::ErrorWindow, + "Failed to import scene with Asset Importer library. An %s has occured in the library, this scene file cannot be parsed by the library.", + signal == SIGABRT ? "assert" : "unknown error"); + } +#endif // AZ_TRAIT_COMPILER_SUPPORT_CSIGNAL + bool AssImpSceneWrapper::LoadSceneFromFile(const char* fileName) { AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "AssImpSceneWrapper::LoadSceneFromFile %s", fileName); AZ_TraceContext("Filename", fileName); + +#if AZ_TRAIT_COMPILER_SUPPORT_CSIGNAL + // Turn off the abort popup because it can disrupt automation. + // AssImp calls abort when asserts are enabled, and an assert is encountered. +#ifdef _WRITE_ABORT_MSG + _set_abort_behavior(0, _WRITE_ABORT_MSG); +#endif // #ifdef _WRITE_ABORT_MSG + // Instead, capture any calls to abort with a signal handler, and report them. + auto previous_handler = std::signal(SIGABRT, signal_handler); +#endif // AZ_TRAIT_COMPILER_SUPPORT_CSIGNAL + // aiProcess_JoinIdenticalVertices is not enabled because O3DE has a mesh optimizer that also does this, // this flag is disabled to keep AssImp output similar to FBX SDK to reduce downstream bugs for the initial AssImp release. // There's currently a minimum of properties and flags set to maximize compatibility with the existing node graph. @@ -49,6 +77,15 @@ namespace AZ | aiProcess_LimitBoneWeights //Limits the number of bones that can affect a vertex to a maximum value //dropping the least important and re-normalizing | aiProcess_GenNormals); //Generate normals for meshes + +#if AZ_TRAIT_COMPILER_SUPPORT_CSIGNAL + // Reset abort behavior for anything else that may call abort. + std::signal(SIGABRT, previous_handler); +#ifdef _WRITE_ABORT_MSG + _set_abort_behavior(1, _WRITE_ABORT_MSG); +#endif // #ifdef _WRITE_ABORT_MSG +#endif // AZ_TRAIT_COMPILER_SUPPORT_CSIGNAL + if (!m_assImpScene) { AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Failed to import Asset Importer Scene. Error returned: %s", m_importer.GetErrorString()); diff --git a/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h b/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h index a2f9450bce..2deed280db 100644 --- a/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h +++ b/Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h @@ -71,6 +71,11 @@ namespace AZ static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; using MutexType = AZStd::recursive_mutex; + static AZ::Crc32 GetAssetImportRequestComponentTag() + { + return AZ_CRC_CE("AssetImportRequest"); + } + virtual ~AssetImportRequest() = 0; //! Fills the given list with all available file extensions, excluding the extension for the manifest. diff --git a/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowHandler.cpp b/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowHandler.cpp index 322aa9ac51..640c092070 100644 --- a/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowHandler.cpp +++ b/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowHandler.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace AZ @@ -58,10 +59,11 @@ namespace AZ } else { - AzToolsFramework::Vector3PropertyHandler handler; - handler.ConsumeAttribute(widget->GetTranslationWidget(), attrib, attrValue, debugName); - handler.ConsumeAttribute(widget->GetRotationWidget(), attrib, attrValue, debugName); - handler.ConsumeAttribute(widget->GetScaleWidget(), attrib, attrValue, debugName); + AzToolsFramework::Vector3PropertyHandler vector3Handler; + vector3Handler.ConsumeAttribute(widget->GetTranslationWidget(), attrib, attrValue, debugName); + vector3Handler.ConsumeAttribute(widget->GetRotationWidget(), attrib, attrValue, debugName); + AzToolsFramework::doublePropertySpinboxHandler spinboxHandler; + spinboxHandler.ConsumeAttribute(widget->GetScaleWidget(), attrib, attrValue, debugName); } } diff --git a/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowWidget.cpp b/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowWidget.cpp index 10e0fd2a68..e8ecaa0c27 100644 --- a/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowWidget.cpp +++ b/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowWidget.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -47,7 +48,7 @@ namespace AZ ExpandedTransform::ExpandedTransform() : m_translation(0, 0, 0) , m_rotation(0, 0, 0) - , m_scale(1, 1, 1) + , m_scale(1) { } @@ -60,14 +61,14 @@ namespace AZ { m_translation = transform.GetTranslation(); m_rotation = transform.GetEulerDegrees(); - m_scale = transform.GetScale(); + m_scale = transform.GetUniformScale(); } void ExpandedTransform::GetTransform(AZ::Transform& transform) const { transform = Transform::CreateTranslation(m_translation); transform *= AZ::ConvertEulerDegreesToTransform(m_rotation); - transform.MultiplyByScale(m_scale); + transform.MultiplyByUniformScale(m_scale); } const AZ::Vector3& ExpandedTransform::GetTranslation() const @@ -90,12 +91,12 @@ namespace AZ m_rotation = rotation; } - const AZ::Vector3& ExpandedTransform::GetScale() const + const float ExpandedTransform::GetScale() const { return m_scale; } - void ExpandedTransform::SetScale(const AZ::Vector3& scale) + void ExpandedTransform::SetScale(const float scale) { m_scale = scale; } @@ -131,7 +132,7 @@ namespace AZ m_rotationWidget->setMaximum(360); m_rotationWidget->setSuffix(" degrees"); - m_scaleWidget = new AzQtComponents::VectorInput(this, 3); + m_scaleWidget = new AzToolsFramework::PropertyDoubleSpinCtrl(this); m_scaleWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); m_scaleWidget->setMinimum(0); m_scaleWidget->setMaximum(10000); @@ -191,13 +192,10 @@ namespace AZ AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestWrite, this); }); - QObject::connect(m_scaleWidget, &AzQtComponents::VectorInput::valueChanged, this, [this] + QObject::connect(m_scaleWidget, &AzToolsFramework::PropertyDoubleSpinCtrl::valueChanged, this, [this] { - AzQtComponents::VectorInput* widget = this->GetScaleWidget(); - AZ::Vector3 scale; - - PopulateVector3(widget, scale); - + AzToolsFramework::PropertyDoubleSpinCtrl* widget = this->GetScaleWidget(); + float scale = aznumeric_cast(widget->value()); m_transform.SetScale(scale); AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestWrite, this); }); @@ -224,9 +222,7 @@ namespace AZ m_rotationWidget->setValuebyIndex(m_transform.GetRotation().GetY(), 1); m_rotationWidget->setValuebyIndex(m_transform.GetRotation().GetZ(), 2); - m_scaleWidget->setValuebyIndex(m_transform.GetScale().GetX(), 0); - m_scaleWidget->setValuebyIndex(m_transform.GetScale().GetY(), 1); - m_scaleWidget->setValuebyIndex(m_transform.GetScale().GetZ(), 2); + m_scaleWidget->setValue(m_transform.GetScale()); blockSignals(false); } @@ -251,7 +247,7 @@ namespace AZ return m_rotationWidget; } - AzQtComponents::VectorInput* TransformRowWidget::GetScaleWidget() + AzToolsFramework::PropertyDoubleSpinCtrl* TransformRowWidget::GetScaleWidget() { return m_scaleWidget; } diff --git a/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowWidget.h b/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowWidget.h index dc3286f80e..3977d26c7c 100644 --- a/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowWidget.h +++ b/Code/Tools/SceneAPI/SceneUI/RowWidgets/TransformRowWidget.h @@ -21,6 +21,7 @@ #include #include #include + #endif namespace AzQtComponents @@ -28,6 +29,11 @@ namespace AzQtComponents class VectorInput; } +namespace AzToolsFramework +{ + class PropertyDoubleSpinCtrl; +} + namespace AZ { namespace SceneAPI @@ -51,14 +57,14 @@ namespace AZ const AZ::Vector3& GetRotation() const; void SetRotation(const AZ::Vector3& translation); - const AZ::Vector3& GetScale() const; - void SetScale(const AZ::Vector3& scale); + const float GetScale() const; + void SetScale(const float scale); private: AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING AZ::Vector3 m_translation; AZ::Vector3 m_rotation; - AZ::Vector3 m_scale; + float m_scale; AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING }; @@ -78,7 +84,7 @@ namespace AZ AzQtComponents::VectorInput* GetTranslationWidget(); AzQtComponents::VectorInput* GetRotationWidget(); - AzQtComponents::VectorInput* GetScaleWidget(); + AzToolsFramework::PropertyDoubleSpinCtrl* GetScaleWidget(); protected: ExpandedTransform m_transform; @@ -87,7 +93,7 @@ namespace AZ AzQtComponents::VectorInput* m_translationWidget; AzQtComponents::VectorInput* m_rotationWidget; - AzQtComponents::VectorInput* m_scaleWidget; + AzToolsFramework::PropertyDoubleSpinCtrl* m_scaleWidget; }; } // namespace SceneUI } // namespace SceneAPI diff --git a/Code/Tools/SceneAPI/SceneUI/Tests/RowWidgets/TransformRowWidgetTests.cpp b/Code/Tools/SceneAPI/SceneUI/Tests/RowWidgets/TransformRowWidgetTests.cpp index 05082f29fb..cda6582e63 100644 --- a/Code/Tools/SceneAPI/SceneUI/Tests/RowWidgets/TransformRowWidgetTests.cpp +++ b/Code/Tools/SceneAPI/SceneUI/Tests/RowWidgets/TransformRowWidgetTests.cpp @@ -30,7 +30,7 @@ namespace AZ Vector3 m_translation = Vector3(10.0f, 20.0f, 30.0f); Vector3 m_rotation = Vector3(30.0f, 45.0f, 60.0f); - Vector3 m_scale = Vector3(2.0f, 3.0f, 4.0f); + float m_scale = 3.0f; }; TEST_F(TransformRowWidgetTest, GetTranslation_TranslationInMatrix_TranslationCanBeRetrievedDirectly) @@ -83,26 +83,22 @@ namespace AZ TEST_F(TransformRowWidgetTest, GetScale_ScaleInMatrix_ScaleCanBeRetrievedDirectly) { - m_transform = Transform::CreateScale(m_scale); + m_transform = Transform::CreateUniformScale(m_scale); m_expanded.SetTransform(m_transform); - const Vector3& returned = m_expanded.GetScale(); - EXPECT_NEAR(m_scale.GetX(), returned.GetX(), 0.1f); - EXPECT_NEAR(m_scale.GetY(), returned.GetY(), 0.1f); - EXPECT_NEAR(m_scale.GetZ(), returned.GetZ(), 0.1f); + const float returned = m_expanded.GetScale(); + EXPECT_NEAR(m_scale, returned, 0.1f); } TEST_F(TransformRowWidgetTest, GetScale_ScaleInMatrix_ScaleCanBeRetrievedFromTransform) { - m_transform = Transform::CreateScale(m_scale); + m_transform = Transform::CreateUniformScale(m_scale); m_expanded.SetTransform(m_transform); Transform rebuild; m_expanded.GetTransform(rebuild); - Vector3 returned = rebuild.GetScale(); - EXPECT_NEAR(m_scale.GetX(), returned.GetX(), 0.1f); - EXPECT_NEAR(m_scale.GetY(), returned.GetY(), 0.1f); - EXPECT_NEAR(m_scale.GetZ(), returned.GetZ(), 0.1f); + float returned = rebuild.GetUniformScale(); + EXPECT_NEAR(m_scale, returned, 0.1f); } TEST_F(TransformRowWidgetTest, GetTransform_RotateAndTranslateInMatrix_ReconstructedTransformMatchesOriginal) @@ -121,7 +117,7 @@ namespace AZ { Quaternion quaternion = AZ::ConvertEulerDegreesToQuaternion(m_rotation); m_transform = Transform::CreateFromQuaternionAndTranslation(quaternion, m_translation); - m_transform.MultiplyByScale(m_scale); + m_transform.MultiplyByUniformScale(m_scale); m_expanded.SetTransform(m_transform); Transform rebuild; diff --git a/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp index 3fbf7cb25c..d06534e303 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.cpp +++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp @@ -244,7 +244,7 @@ namespace AZ } else { - return SavePrefab(templateId); + return SavePrefab(outputPath, templateId); } } @@ -318,7 +318,7 @@ namespace AZ nestedPrefabPath.ReplaceExtension("prefab"); auto prefabLoaderInterface = AZ::Interface::Get(); - nestedPrefabPath = prefabLoaderInterface->GetRelativePathToProject(nestedPrefabPath); + nestedPrefabPath = prefabLoaderInterface->GenerateRelativePath(nestedPrefabPath); AzToolsFramework::Prefab::TemplateId nestedTemplateId = prefabSystemComponentInterface->GetTemplateIdFromFilePath(nestedPrefabPath); @@ -439,17 +439,31 @@ namespace AZ AZ::Debug::Trace::Instance().Output("", "\n"); } - bool SliceConverter::SavePrefab(AzToolsFramework::Prefab::TemplateId templateId) + bool SliceConverter::SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId) { auto prefabLoaderInterface = AZ::Interface::Get(); - if (!prefabLoaderInterface->SaveTemplate(templateId)) + AZStd::string out; + if (prefabLoaderInterface->SaveTemplateToString(templateId, out)) { - AZ_Printf("Convert-Slice", " Could not save prefab - internal error (Json write operation failure).\n"); - return false; + IO::SystemFile outputFile; + if (!outputFile.Open( + AZStd::string(outputPath.Native()).c_str(), + IO::SystemFile::OpenMode::SF_OPEN_CREATE | + IO::SystemFile::OpenMode::SF_OPEN_CREATE_PATH | + IO::SystemFile::OpenMode::SF_OPEN_WRITE_ONLY)) + { + AZ_Error("Convert-Slice", false, " Unable to create output file '%.*s'.", AZ_STRING_ARG(outputPath.Native())); + return false; + } + + outputFile.Write(out.data(), out.size()); + outputFile.Close(); + return true; } - return true; + AZ_Printf("Convert-Slice", " Could not save prefab - internal error (Json write operation failure).\n"); + return false; } bool SliceConverter::ConnectToAssetProcessor() diff --git a/Code/Tools/SerializeContextTools/SliceConverter.h b/Code/Tools/SerializeContextTools/SliceConverter.h index 8dba6a0e55..bec893ff56 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.h +++ b/Code/Tools/SerializeContextTools/SliceConverter.h @@ -52,11 +52,11 @@ namespace AZ static bool ConvertNestedSlices( SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance, AZ::SerializeContext* serializeContext, bool isDryRun); - static bool SliceConverter::ConvertSliceInstance( + static bool ConvertSliceInstance( AZ::SliceComponent::SliceInstance& instance, AZ::Data::Asset& sliceAsset, AzToolsFramework::Prefab::TemplateReference nestedTemplate, AzToolsFramework::Prefab::Instance* topLevelInstance); static void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId); - static bool SavePrefab(AzToolsFramework::Prefab::TemplateId templateId); + static bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId); }; } // namespace SerializeContextTools } // namespace AZ diff --git a/Gems/AWSClientAuth/Code/CMakeLists.txt b/Gems/AWSClientAuth/Code/CMakeLists.txt index e9f2a4ed84..40f6e0fe36 100644 --- a/Gems/AWSClientAuth/Code/CMakeLists.txt +++ b/Gems/AWSClientAuth/Code/CMakeLists.txt @@ -29,6 +29,9 @@ ly_add_target( Gem::HttpRequestor 3rdParty::AWSNativeSDK::AWSClientAuth 3rdParty::AWSNativeSDK::Core + RUNTIME_DEPENDENCIES + Gem::AWSCore + Gem::HttpRequestor ) ly_add_target( @@ -44,13 +47,19 @@ ly_add_target( AZ::AzCore AZ::AzFramework Gem::AWSCore - Gem::HttpRequestor 3rdParty::AWSNativeSDK::AWSClientAuth 3rdParty::AWSNativeSDK::Core PUBLIC Gem::AWSClientAuth.Static + RUNTIME_DEPENDENCIES + Gem::AWSCore + Gem::HttpRequestor ) +# servers and clients use the above module. +ly_create_alias(NAME AWSClientAuth.Servers NAMESPACE Gem TARGETS Gem::AWSClientAuth) +ly_create_alias(NAME AWSClientAuth.Clients NAMESPACE Gem TARGETS Gem::AWSClientAuth) + ################################################################################ # Tests ################################################################################ @@ -71,10 +80,14 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) 3rdParty::AWSNativeSDK::AWSClientAuth AZ::AzCore AZ::AzFramework - Gem::AWSCore - Gem::AWSClientAuth.Static AZ::AWSNativeSDKInit + Gem::AWSClientAuth.Static + Gem::AWSCore Gem::HttpRequestor + RUNTIME_DEPENDENCIES + Gem::AWSCore + AZ::AWSNativeSDKInit + Gem::HttpRequestor ) ly_add_googletest( NAME Gem::AWSClientAuth.Tests diff --git a/Gems/AWSCore/Code/CMakeLists.txt b/Gems/AWSCore/Code/CMakeLists.txt index a58b02d1d4..46046c0791 100644 --- a/Gems/AWSCore/Code/CMakeLists.txt +++ b/Gems/AWSCore/Code/CMakeLists.txt @@ -45,6 +45,10 @@ ly_add_target( Gem::AWSCore.Static ) +# clients and servers will use the above Gem::AWSCore module. +ly_create_alias(NAME AWSCore.Servers NAMESPACE Gem TARGETS Gem::AWSCore) +ly_create_alias(NAME AWSCore.Clients NAMESPACE Gem TARGETS Gem::AWSCore) + if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME AWSCore.Editor.Static STATIC @@ -99,6 +103,11 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Gem::AWSCore.Editor.Static ) ly_add_dependencies(AWSCore.Editor AWSCore.ResourceMappingTool) + + # Builders and Tools (such as the Editor use AWSCore.Editor) use the .Editor module above. + ly_create_alias(NAME AWSCore.Tools NAMESPACE Gem TARGETS Gem::AWSCore.Editor) + ly_create_alias(NAME AWSCore.Builders NAMESPACE Gem TARGETS Gem::AWSCore.Editor) + endif() ################################################################################ diff --git a/Gems/AWSCore/Code/Include/Private/AWSCoreInternalBus.h b/Gems/AWSCore/Code/Include/Private/AWSCoreInternalBus.h index 738d41c796..27487ed481 100644 --- a/Gems/AWSCore/Code/Include/Private/AWSCoreInternalBus.h +++ b/Gems/AWSCore/Code/Include/Private/AWSCoreInternalBus.h @@ -38,6 +38,11 @@ namespace AWSCore //! @return The path of AWS resource mapping config file virtual AZStd::string GetResourceMappingConfigFilePath() const = 0; + //! GetResourceMappingConfigFolderPath + //! Get the path of AWS resource mapping config folder + //! @return The path of AWS resource mapping config folder + virtual AZStd::string GetResourceMappingConfigFolderPath() const = 0; + //! ReloadConfiguration //! Reload AWSCore configuration without restarting application virtual void ReloadConfiguration() = 0; diff --git a/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h b/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h index bd30af3ce7..92082617b7 100644 --- a/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h +++ b/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h @@ -53,6 +53,7 @@ namespace AWSCore // AWSCoreInternalRequestBus interface implementation AZStd::string GetResourceMappingConfigFilePath() const override; + AZStd::string GetResourceMappingConfigFolderPath() const override; AZStd::string GetProfileName() const override; void ReloadConfiguration() override; diff --git a/Gems/AWSCore/Code/Include/Private/Editor/AWSCoreEditorManager.h b/Gems/AWSCore/Code/Include/Private/Editor/AWSCoreEditorManager.h index 721cd6dd6a..98467727ea 100644 --- a/Gems/AWSCore/Code/Include/Private/Editor/AWSCoreEditorManager.h +++ b/Gems/AWSCore/Code/Include/Private/Editor/AWSCoreEditorManager.h @@ -18,7 +18,7 @@ namespace AWSCore class AWSCoreEditorManager { public: - static constexpr const char CLOUD_SERVICES_MENU_TEXT[] = "&Cloud services"; + static constexpr const char AWS_MENU_TEXT[] = "&AWS"; AWSCoreEditorManager(); virtual ~AWSCoreEditorManager(); diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Constants/AWSCoreEditorMenuLinks.h b/Gems/AWSCore/Code/Include/Private/Editor/Constants/AWSCoreEditorMenuLinks.h new file mode 100644 index 0000000000..46acfbd4a3 --- /dev/null +++ b/Gems/AWSCore/Code/Include/Private/Editor/Constants/AWSCoreEditorMenuLinks.h @@ -0,0 +1,53 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +namespace AWSCore +{ + static constexpr const char NewToAWSUrl[] = "https://docs.o3de.org/docs/user-guide/gems/reference/aws/"; + + static constexpr const char AWSAndScriptCanvasUrl[] = "https://docs.o3de.org/docs/user-guide/components/reference/aws/"; + static constexpr const char AWSAndComponentsUrl[] = "https://docs.o3de.org/docs/user-guide/components/reference/aws/"; + static constexpr const char CallAWSResourcesUrl[] = "https://docs.o3de.org/docs/user-guide/components/reference/aws/"; + + static constexpr const char AWSCredentialConfigurationUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-core/configuring-credentials/"; + + static constexpr const char AWSClientAuthGemOverviewUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/"; + static constexpr const char AWSClientAuthCDKAndResourcesUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/"; + static constexpr const char AWSClientAuthScriptCanvasAndLuaUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/"; + static constexpr const char AWSClientAuth3rdPartyAuthProviderUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/"; + static constexpr const char AWSClientAuthCustomAuthProviderUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/"; + static constexpr const char AWSClientAuthPlatformSpecificUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/"; + static constexpr const char AWSClientAuthAPIReferenceUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/"; + + static constexpr const char AWSMetricsGemOverviewUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/"; + static constexpr const char AWSMetricsSetupGemUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/"; + static constexpr const char AWSMetricsScriptingUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/"; + static constexpr const char AWSMetricsAPIReferenceUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/"; + static constexpr const char AWSMetricsAdvancedTopicsUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/"; + static constexpr const char AWSMetricsSettingsUrl[] = + "https://docs.o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/"; +} // namespace AWSCore diff --git a/Gems/AWSCore/Code/Include/Private/Editor/Constants/AWSCoreEditorMenuNames.h b/Gems/AWSCore/Code/Include/Private/Editor/Constants/AWSCoreEditorMenuNames.h new file mode 100644 index 0000000000..a9a8198e52 --- /dev/null +++ b/Gems/AWSCore/Code/Include/Private/Editor/Constants/AWSCoreEditorMenuNames.h @@ -0,0 +1,44 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +namespace AWSCore +{ + static constexpr const char NewToAWSActionText[] = "Getting started with AWS?"; + + static constexpr const char AWSAndO3DEGlobalDocsText[] = "AWS & O3DE global docs"; + static constexpr const char AWSAndScriptCanvasActionText[] = "AWS && ScriptCanvas"; + static constexpr const char AWSAndComponentsActionText[] = "AWS & Components"; + static constexpr const char CallAWSResourcesActionText[] = "Call AWS resources"; + + static constexpr const char AWSCredentialConfigurationActionText[] = "AWS credential configuration"; + + static constexpr const char AWSResourceMappingToolActionText[] = "AWS Resource Mapping Tool..."; + + static constexpr const char AWSClientAuthActionText[] = "Client Auth"; + static constexpr const char AWSClientAuthGemOverviewActionText[] = "Gem Overview"; + static constexpr const char AWSClientAuthCDKAndResourcesActionText[] = "CDK Application and Resource Mappings"; + static constexpr const char AWSClientAuthScriptCanvasAndLuaActionText[] = "Script Canvas and Lua"; + static constexpr const char AWSClientAuth3rdPartyAuthProviderActionText[] = "3rd Party developer Authentication Provider support"; + static constexpr const char AWSClientAuthCustomAuthProviderActionText[] = "Custom developer Authentication Provider support"; + static constexpr const char AWSClientAuthPlatformSpecificActionText[] = "Platform specific Callouts"; + static constexpr const char AWSClientAuthAPIReferenceActionText[] = "API Reference"; + + static constexpr const char AWSMetricsActionText[] = "Metrics"; + static constexpr const char AWSMetricsGemOverviewActionText[] = "Metrics Overview"; + static constexpr const char AWSMetricsSetupGemActionText[] = "Setup Metrics Gem"; + static constexpr const char AWSMetricsScriptingActionText[] = "Scripting with AWS Metrics"; + static constexpr const char AWSMetricsAPIReferenceActionText[] = "C++ API with AWS Metrics Gem"; + static constexpr const char AWSMetricsAdvancedTopicsActionText[] = "Advanced topics"; + static constexpr const char AWSMetricsSettingsActionText[] = "Metrics Settings"; +} // namespace AWSCore diff --git a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h index 19f241e368..c892f86b66 100644 --- a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h +++ b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreEditorMenu.h @@ -35,29 +35,23 @@ namespace AWSCore static constexpr const char AWSResourceMappingToolIsRunningText[] = "Resource Mapping Tool is running..."; static constexpr const char AWSResourceMappingToolLogWarningText[] = "Failed to launch Resource Mapping Tool, please check logs for details."; - static constexpr const char AWSResourceMappingToolActionText[] = "AWS Resource Mapping Tool..."; - static constexpr const char CredentialConfigurationActionText[] = "Credential Configuration"; - static constexpr const char CredentialConfigurationUrl[] = "https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/credentials.html"; - static constexpr const char NewToAWSActionText[] = "New to AWS?"; - static constexpr const char NewToAWSUrl[] = "https://o3deorg.netlify.app/docs/user-guide/gems/reference/aws"; - static constexpr const char AWSAndScriptCanvasActionText[] = "AWS && ScriptCanvas"; - static constexpr const char AWSAndScriptCanvasUrl[] = "https://o3deorg.netlify.app/docs/user-guide/gems/reference/aws"; - static constexpr const char AWSClientAuthActionText[] = "Client Auth"; - static constexpr const char AWSMetricsActionText[] = "Metrics"; AWSCoreEditorMenu(const QString& text); ~AWSCoreEditorMenu(); private: + QAction* AddExternalLinkAction(const AZStd::string& name, const AZStd::string& url, const AZStd::string& icon = ""); + void InitializeResourceMappingToolAction(); void InitializeAWSDocActions(); + void InitializeAWSGlobalDocsSubMenu(); void InitializeAWSFeatureGemActions(); // AWSCoreEditorRequestBus interface implementation void SetAWSClientAuthEnabled() override; void SetAWSMetricsEnabled() override; - void SetAWSFeatureActionsEnabled(const AZStd::string actionText); + QMenu* SetAWSFeatureSubMenu(const AZStd::string& menuText); // To improve experience, use process watcher to keep track of ongoing tool process AZStd::unique_ptr m_resourceMappingToolWatcher; diff --git a/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp b/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp index 3c0f48c058..b22749dbaa 100644 --- a/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp +++ b/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp @@ -58,6 +58,19 @@ namespace AWSCore return configFilePath; } + AZStd::string AWSCoreConfiguration::GetResourceMappingConfigFolderPath() const + { + if (m_sourceProjectFolder.empty()) + { + AZ_Warning(AWSCoreConfigurationName, false, ProjectSourceFolderNotFoundErrorMessage); + return ""; + } + AZStd::string configFolderPath = AZStd::string::format( + "%s/%s", m_sourceProjectFolder.c_str(), AWSCoreResourceMappingConfigFolderName); + AzFramework::StringFunc::Path::Normalize(configFolderPath); + return configFolderPath; + } + void AWSCoreConfiguration::InitConfig() { InitSourceProjectFolderPath(); @@ -123,7 +136,7 @@ namespace AWSCore auto profileNamePath = AZStd::string::format("%s%s", AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCoreProfileNameKey); m_settingsRegistry.Remove(profileNamePath); - m_profileName.clear(); + m_profileName = AWSCoreDefaultProfileName; auto resourceMappingConfigFileNamePath = AZStd::string::format("%s%s", AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCoreResourceMappingConfigFileNameKey); diff --git a/Gems/AWSCore/Code/Source/Editor/AWSCoreEditorManager.cpp b/Gems/AWSCore/Code/Source/Editor/AWSCoreEditorManager.cpp index 1e3e44255a..89956d61b8 100644 --- a/Gems/AWSCore/Code/Source/Editor/AWSCoreEditorManager.cpp +++ b/Gems/AWSCore/Code/Source/Editor/AWSCoreEditorManager.cpp @@ -16,7 +16,7 @@ namespace AWSCore { AWSCoreEditorManager::AWSCoreEditorManager() - : m_awsCoreEditorMenu(new AWSCoreEditorMenu(CLOUD_SERVICES_MENU_TEXT)) + : m_awsCoreEditorMenu(new AWSCoreEditorMenu(AWS_MENU_TEXT)) { } diff --git a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp index 754cc32751..c319788547 100644 --- a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp +++ b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp @@ -13,10 +13,13 @@ #include #include #include +#include #include #include #include +#include +#include #include #include @@ -36,8 +39,8 @@ namespace AWSCore : QMenu(text) , m_resourceMappingToolWatcher(nullptr) { - InitializeResourceMappingToolAction(); InitializeAWSDocActions(); + InitializeResourceMappingToolAction(); this->addSeparator(); InitializeAWSFeatureGemActions(); @@ -58,6 +61,21 @@ namespace AWSCore this->clear(); } + QAction* AWSCoreEditorMenu::AddExternalLinkAction( + const AZStd::string& name, const AZStd::string& url, const AZStd::string& icon) + { + QAction* linkAction = new QAction(QObject::tr(name.c_str())); + QObject::connect(linkAction, &QAction::triggered, this, + [url]() { + QDesktopServices::openUrl(QUrl(url.c_str())); + }); + if (!icon.empty()) + { + linkAction->setIcon(QIcon(icon.c_str())); + } + return linkAction; + } + void AWSCoreEditorMenu::InitializeResourceMappingToolAction() { #ifdef AWSCORE_EDITOR_RESOURCE_MAPPING_TOOL_ENABLED @@ -103,21 +121,21 @@ namespace AWSCore void AWSCoreEditorMenu::InitializeAWSDocActions() { - QAction* credentialConfiguration = new QAction(QObject::tr(CredentialConfigurationActionText)); - QObject::connect(credentialConfiguration, &QAction::triggered, this, []() { - QDesktopServices::openUrl(QUrl(CredentialConfigurationUrl)); - }); - this->addAction(credentialConfiguration); + this->addAction(AddExternalLinkAction(NewToAWSActionText, NewToAWSUrl, ":/Notifications/link.svg")); + + InitializeAWSGlobalDocsSubMenu(); - QAction* newToAWS = new QAction(QObject::tr(NewToAWSActionText)); - QObject::connect(newToAWS, &QAction::triggered, this, []() { - QDesktopServices::openUrl(QUrl(NewToAWSUrl)); }); - this->addAction(newToAWS); + this->addAction(AddExternalLinkAction( + AWSCredentialConfigurationActionText, AWSCredentialConfigurationUrl, ":/Notifications/link.svg")); + } + + void AWSCoreEditorMenu::InitializeAWSGlobalDocsSubMenu() + { + QMenu* globalDocsMenu = this->addMenu(QObject::tr(AWSAndO3DEGlobalDocsText)); - QAction* awsAndScriptCanvas = new QAction(QObject::tr(AWSAndScriptCanvasActionText)); - QObject::connect(awsAndScriptCanvas, &QAction::triggered, this, []() { - QDesktopServices::openUrl(QUrl(AWSAndScriptCanvasUrl)); }); - this->addAction(awsAndScriptCanvas); + globalDocsMenu->addAction(AddExternalLinkAction(AWSAndScriptCanvasActionText, AWSAndScriptCanvasUrl, ":/Notifications/link.svg")); + globalDocsMenu->addAction(AddExternalLinkAction(AWSAndComponentsActionText, AWSAndComponentsUrl, ":/Notifications/link.svg")); + globalDocsMenu->addAction(AddExternalLinkAction(CallAWSResourcesActionText, CallAWSResourcesUrl, ":/Notifications/link.svg")); } void AWSCoreEditorMenu::InitializeAWSFeatureGemActions() @@ -135,25 +153,67 @@ namespace AWSCore void AWSCoreEditorMenu::SetAWSClientAuthEnabled() { - SetAWSFeatureActionsEnabled(AWSClientAuthActionText); + // TODO: instead of creating submenu in core editor, aws feature gem should return submenu component directly + QMenu* subMenu = SetAWSFeatureSubMenu(AWSClientAuthActionText); + + subMenu->addAction(AddExternalLinkAction( + AWSClientAuthGemOverviewActionText, AWSClientAuthGemOverviewUrl, ":/Notifications/link.svg")); + subMenu->addAction(AddExternalLinkAction( + AWSClientAuthCDKAndResourcesActionText, AWSClientAuthCDKAndResourcesUrl, ":/Notifications/link.svg")); + subMenu->addAction(AddExternalLinkAction( + AWSClientAuthScriptCanvasAndLuaActionText, AWSClientAuthScriptCanvasAndLuaUrl, ":/Notifications/link.svg")); + subMenu->addAction(AddExternalLinkAction( + AWSClientAuth3rdPartyAuthProviderActionText, AWSClientAuth3rdPartyAuthProviderUrl, ":/Notifications/link.svg")); + subMenu->addAction(AddExternalLinkAction( + AWSClientAuthCustomAuthProviderActionText, AWSClientAuthCustomAuthProviderUrl, ":/Notifications/link.svg")); + subMenu->addAction(AddExternalLinkAction( + AWSClientAuthPlatformSpecificActionText, AWSClientAuthPlatformSpecificUrl, ":/Notifications/link.svg")); + subMenu->addAction(AddExternalLinkAction( + AWSClientAuthAPIReferenceActionText, AWSClientAuthAPIReferenceUrl, ":/Notifications/link.svg")); } void AWSCoreEditorMenu::SetAWSMetricsEnabled() { - SetAWSFeatureActionsEnabled(AWSMetricsActionText); + // TODO: instead of creating submenu in core editor, aws feature gem should return submenu component directly + QMenu* subMenu = SetAWSFeatureSubMenu(AWSMetricsActionText); + + subMenu->addAction(AddExternalLinkAction( + AWSMetricsGemOverviewActionText, AWSMetricsGemOverviewUrl, ":/Notifications/link.svg")); + subMenu->addAction(AddExternalLinkAction( + AWSMetricsSetupGemActionText, AWSMetricsSetupGemUrl, ":/Notifications/link.svg")); + subMenu->addAction(AddExternalLinkAction( + AWSMetricsScriptingActionText, AWSMetricsScriptingUrl, ":/Notifications/link.svg")); + subMenu->addAction(AddExternalLinkAction( + AWSMetricsAPIReferenceActionText, AWSMetricsAPIReferenceUrl, ":/Notifications/link.svg")); + subMenu->addAction(AddExternalLinkAction( + AWSMetricsAdvancedTopicsActionText, AWSMetricsAdvancedTopicsUrl, ":/Notifications/link.svg")); + + AZStd::string priorAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devroot@"); + AZStd::string configFilePath = priorAlias + "\\Gems\\AWSMetrics\\Code\\" + AZ::SettingsRegistryInterface::RegistryFolder; + AzFramework::StringFunc::Path::Normalize(configFilePath); + + QAction* settingsAction = new QAction(QObject::tr(AWSMetricsSettingsActionText)); + QObject::connect(settingsAction, &QAction::triggered, this, + [configFilePath](){ + QDesktopServices::openUrl(QUrl::fromLocalFile(configFilePath.c_str())); + }); + subMenu->addAction(settingsAction); } - void AWSCoreEditorMenu::SetAWSFeatureActionsEnabled(const AZStd::string actionText) + QMenu* AWSCoreEditorMenu::SetAWSFeatureSubMenu(const AZStd::string& menuText) { auto actionList = this->actions(); for (QList::iterator itr = actionList.begin(); itr != actionList.end(); itr++) { - if (QString::compare((*itr)->text(), actionText.c_str()) == 0) + if (QString::compare((*itr)->text(), menuText.c_str()) == 0) { - (*itr)->setIcon(QIcon(QString(":/Notifications/checkmark.svg"))); - (*itr)->setEnabled(true); - break; + QMenu* subMenu = new QMenu(QObject::tr(menuText.c_str())); + subMenu->setIcon(QIcon(QString(":/Notifications/checkmark.svg"))); + this->insertMenu(*itr, subMenu); + this->removeAction(*itr); + return subMenu; } } + return nullptr; } } // namespace AWSCore diff --git a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp index fb46a8a700..18d437a66c 100644 --- a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp +++ b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp @@ -14,6 +14,7 @@ #include #include +#include #include namespace AWSCore @@ -108,17 +109,24 @@ namespace AWSCore { return ""; } + + AZStd::string profileName = "default"; + AWSCoreInternalRequestBus::BroadcastResult(profileName, &AWSCoreInternalRequests::GetProfileName); + + AZStd::string configPath = ""; + AWSCoreInternalRequestBus::BroadcastResult(configPath, &AWSCoreInternalRequests::GetResourceMappingConfigFolderPath); + if (m_isDebug) { return AZStd::string::format( - "%s debug %s --binaries_path %s --debug", - m_enginePythonEntryPath.c_str(), m_toolScriptPath.c_str(), m_toolQtBinDirectoryPath.c_str()); + "%s debug %s --binaries_path %s --debug --profile %s --config_path %s", m_enginePythonEntryPath.c_str(), + m_toolScriptPath.c_str(), m_toolQtBinDirectoryPath.c_str(), profileName.c_str(), configPath.c_str()); } else { return AZStd::string::format( - "%s %s --binaries_path %s", - m_enginePythonEntryPath.c_str(), m_toolScriptPath.c_str(), m_toolQtBinDirectoryPath.c_str()); + "%s %s --binaries_path %s --profile %s --config_path %s", m_enginePythonEntryPath.c_str(), + m_toolScriptPath.c_str(), m_toolQtBinDirectoryPath.c_str(), profileName.c_str(), configPath.c_str()); } } diff --git a/Gems/AWSCore/Code/Tests/AWSCoreEditorSystemComponentTest.cpp b/Gems/AWSCore/Code/Tests/AWSCoreEditorSystemComponentTest.cpp index e9c249f1c8..ff78d8e252 100644 --- a/Gems/AWSCore/Code/Tests/AWSCoreEditorSystemComponentTest.cpp +++ b/Gems/AWSCore/Code/Tests/AWSCoreEditorSystemComponentTest.cpp @@ -85,7 +85,7 @@ TEST_F(AWSCoreEditorSystemComponentTest, NotifyMainWindowInitialized_HaveDummyMe testMenuBar->addMenu("dummy menu"); AzToolsFramework::EditorEvents::Bus::Broadcast(&AzToolsFramework::EditorEvents::NotifyMainWindowInitialized, &testMainWindow); EXPECT_TRUE(testMenuBar->actions().size() == 2); - EXPECT_TRUE(QString::compare(testMenuBar->actions()[1]->text(), AWSCoreEditorManager::CLOUD_SERVICES_MENU_TEXT) == 0); + EXPECT_TRUE(QString::compare(testMenuBar->actions()[1]->text(), AWSCoreEditorManager::AWS_MENU_TEXT) == 0); } TEST_F(AWSCoreEditorSystemComponentTest, NotifyMainWindowInitialized_HaveHelpMenuInMenuBar_ExpectedMenuGetsAddedAtFront) @@ -95,5 +95,5 @@ TEST_F(AWSCoreEditorSystemComponentTest, NotifyMainWindowInitialized_HaveHelpMen testMenuBar->addMenu(AWSCoreEditorSystemComponent::EDITOR_HELP_MENU_TEXT); AzToolsFramework::EditorEvents::Bus::Broadcast(&AzToolsFramework::EditorEvents::NotifyMainWindowInitialized, &testMainWindow); EXPECT_TRUE(testMenuBar->actions().size() == 2); - EXPECT_TRUE(QString::compare(testMenuBar->actions()[0]->text(), AWSCoreEditorManager::CLOUD_SERVICES_MENU_TEXT) == 0); + EXPECT_TRUE(QString::compare(testMenuBar->actions()[0]->text(), AWSCoreEditorManager::AWS_MENU_TEXT) == 0); } diff --git a/Gems/AWSCore/Code/Tests/Credential/AWSDefaultCredentialHandlerTest.cpp b/Gems/AWSCore/Code/Tests/Credential/AWSDefaultCredentialHandlerTest.cpp index 297e56fc33..7f00adaae7 100644 --- a/Gems/AWSCore/Code/Tests/Credential/AWSDefaultCredentialHandlerTest.cpp +++ b/Gems/AWSCore/Code/Tests/Credential/AWSDefaultCredentialHandlerTest.cpp @@ -34,7 +34,8 @@ public: MOCK_METHOD0(GetAWSCredentials, Aws::Auth::AWSCredentials()); }; -class AWSDefaultCredentialHandlerMock : public AWSDefaultCredentialHandler +class AWSDefaultCredentialHandlerMock + : public AWSDefaultCredentialHandler { public: void SetupMocks( @@ -76,6 +77,7 @@ public: // AWSCoreInternalRequestBus interface implementation AZStd::string GetProfileName() const override { return m_profileName; } AZStd::string GetResourceMappingConfigFilePath() const override { return ""; } + AZStd::string GetResourceMappingConfigFolderPath() const override { return ""; } void ReloadConfiguration() override {} std::shared_ptr m_environmentCredentialsProviderMock; diff --git a/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreEditorMenuTest.cpp b/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreEditorMenuTest.cpp index 7a578d2bbe..bde2a43993 100644 --- a/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreEditorMenuTest.cpp +++ b/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreEditorMenuTest.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -35,6 +36,7 @@ class AWSCoreEditorMenuTest { AWSCoreEditorUIFixture::SetUp(); AWSCoreFixture::SetUp(); + m_localFileIO->SetAlias("@devroot@", "dummy engine root"); } void TearDown() override @@ -77,12 +79,12 @@ TEST_F(AWSCoreEditorMenuTest, AWSCoreEditorMenu_BroadcastFeatureGemsAreEnabled_C QList actualActions = testMenu.actions(); for (QList::iterator itr = actualActions.begin(); itr != actualActions.end(); itr++) { - if (QString::compare((*itr)->text(), AWSCoreEditorMenu::AWSClientAuthActionText) == 0) + if (QString::compare((*itr)->text(), AWSClientAuthActionText) == 0) { EXPECT_TRUE((*itr)->isEnabled()); } - if (QString::compare((*itr)->text(), AWSCoreEditorMenu::AWSMetricsActionText) == 0) + if (QString::compare((*itr)->text(), AWSMetricsActionText) == 0) { EXPECT_TRUE((*itr)->isEnabled()); } diff --git a/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp b/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp index 3adebc9a24..b46a840d40 100644 --- a/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp +++ b/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp @@ -119,6 +119,7 @@ public: // AWSCoreInternalRequestBus interface implementation AZStd::string GetProfileName() const override { return ""; } AZStd::string GetResourceMappingConfigFilePath() const override { return m_normalizedConfigFilePath; } + AZStd::string GetResourceMappingConfigFolderPath() const override { return m_normalizedConfigFolderPath; } void ReloadConfiguration() override { m_reloadConfigurationCounter++; } AZStd::unique_ptr m_resourceMappingManager; diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py index ee305c750a..b679921196 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py @@ -48,11 +48,14 @@ class ConfigurationManager(object): def configuration(self, new_configuration: ConfigurationManager) -> None: self._configuration = new_configuration - def setup(self) -> None: + def setup(self, config_path: str) -> None: logger.info("Setting up default configuration ...") - # TODO: remove config directory and files default setup once integrating with user input try: - self._configuration.config_directory = file_utils.get_current_directory_path() + normalized_config_path: str = file_utils.normalize_file_path(config_path); + if normalized_config_path: + self._configuration.config_directory = normalized_config_path + else: + self._configuration.config_directory = file_utils.get_current_directory_path() self._configuration.config_files = \ file_utils.find_files_with_suffix_under_directory(self._configuration.config_directory, constants.RESOURCE_MAPPING_CONFIG_FILE_NAME_SUFFIX) diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py index 0ee7455e6c..e99bf5d441 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py @@ -13,13 +13,16 @@ from argparse import (ArgumentParser, Namespace) import logging import sys +from utils import aws_utils from utils import environment_utils from utils import file_utils # arguments setup argument_parser: ArgumentParser = ArgumentParser() argument_parser.add_argument('--binaries_path', help='Path to QT Binaries necessary for PySide.') +argument_parser.add_argument('--config_path', help='Path to resource mapping config directory.') argument_parser.add_argument('--debug', action='store_true', help='Execute on debug mode to enable DEBUG logging level') +argument_parser.add_argument('--profile', default='default', help='Named AWS profile to use for querying AWS resources') arguments: Namespace = argument_parser.parse_args() # logging setup @@ -70,9 +73,12 @@ if __name__ == "__main__": except FileNotFoundError: logger.warning("Failed to load style sheet for resource mapping tool") + logger.info("Initializing boto3 default session ...") + aws_utils.setup_default_session(arguments.profile) + logger.info("Initializing configuration manager ...") configuration_manager: ConfigurationManager = ConfigurationManager() - configuration_manager.setup() + configuration_manager.setup(arguments.config_path) logger.info("Initializing thread manager ...") thread_manager: ThreadManager = ThreadManager() diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_configuration_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_configuration_manager.py index a9dcf97af9..552f9fff01 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_configuration_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_configuration_manager.py @@ -43,7 +43,7 @@ class TestConfigurationManager(TestCase): mock_find_files_with_suffix_under_directory: MagicMock, mock_get_default_account_id: MagicMock, mock_get_default_region: MagicMock) -> None: - TestConfigurationManager._expected_configuration_manager.setup() + TestConfigurationManager._expected_configuration_manager.setup("") mock_get_current_directory_path.assert_called_once() mock_check_path_exists.assert_called_once_with(TestConfigurationManager._expected_directory_path) mock_find_files_with_suffix_under_directory.assert_called_once_with( @@ -58,3 +58,29 @@ class TestConfigurationManager(TestCase): TestConfigurationManager._expected_account_id assert TestConfigurationManager._expected_configuration_manager.configuration.region == \ TestConfigurationManager._expected_region + + @patch("utils.aws_utils.get_default_region", return_value=_expected_region) + @patch("utils.aws_utils.get_default_account_id", return_value=_expected_account_id) + @patch("utils.file_utils.find_files_with_suffix_under_directory", return_value=_expected_config_files) + @patch("utils.file_utils.check_path_exists", return_value=True) + @patch("utils.file_utils.normalize_file_path", return_value=_expected_directory_path) + def test_setup_get_configuration_setup_with_path_as_expected(self, mock_normalize_file_path: MagicMock, + mock_check_path_exists: MagicMock, + mock_find_files_with_suffix_under_directory: MagicMock, + mock_get_default_account_id: MagicMock, + mock_get_default_region: MagicMock) -> None: + TestConfigurationManager._expected_configuration_manager.setup(TestConfigurationManager._expected_directory_path) + mock_normalize_file_path.assert_called_once() + mock_check_path_exists.assert_called_once_with(TestConfigurationManager._expected_directory_path) + mock_find_files_with_suffix_under_directory.assert_called_once_with( + TestConfigurationManager._expected_directory_path, constants.RESOURCE_MAPPING_CONFIG_FILE_NAME_SUFFIX) + mock_get_default_account_id.assert_called_once() + mock_get_default_region.assert_called_once() + assert TestConfigurationManager._expected_configuration_manager.configuration.config_directory == \ + TestConfigurationManager._expected_directory_path + assert TestConfigurationManager._expected_configuration_manager.configuration.config_files == \ + TestConfigurationManager._expected_config_files + assert TestConfigurationManager._expected_configuration_manager.configuration.account_id == \ + TestConfigurationManager._expected_account_id + assert TestConfigurationManager._expected_configuration_manager.configuration.region == \ + TestConfigurationManager._expected_region diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_view_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_view_manager.py index eb8304182f..e5c84c6579 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_view_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_view_manager.py @@ -36,7 +36,7 @@ class TestViewManager(TestCase): main_window_patcher: patch = patch("manager.view_manager.QMainWindow") cls._mock_main_window = main_window_patcher.start() - window_icon_patcher: patch = patch("manager.view_manager.QPixmap") + window_icon_patcher: patch = patch("manager.view_manager.QIcon") window_icon_patcher.start() stacked_pages_patcher: patch = patch("manager.view_manager.QStackedWidget") diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/utils/test_aws_utils.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/utils/test_aws_utils.py index 7244431e76..51bd6ade23 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/utils/test_aws_utils.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/utils/test_aws_utils.py @@ -37,13 +37,12 @@ class TestAWSUtils(TestCase): .build() def setUp(self) -> None: - client_patcher: patch = patch("boto3.client") - self.addCleanup(client_patcher.stop) - self._mock_client: MagicMock = client_patcher.start() - session_patcher: patch = patch("boto3.session.Session") self.addCleanup(session_patcher.stop) self._mock_session: MagicMock = session_patcher.start() + self._mock_client: MagicMock = self._mock_session.return_value.client + + aws_utils.setup_default_session("default") def test_get_default_account_id_return_expected_account_id(self) -> None: mocked_sts_client: MagicMock = self._mock_client.return_value diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/aws_utils.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/aws_utils.py index 329d3ff44e..b0c3c9c1b3 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/aws_utils.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/aws_utils.py @@ -26,6 +26,8 @@ aws account, region, resources, etc. _PAGINATION_MAX_ITEMS: int = 10 _PAGINATION_PAGE_SIZE: int = 10 +default_session: boto3.session.Session = None + class AWSConstants(object): CLOUDFORMATION_SERVICE_NAME: str = "cloudformation" @@ -53,15 +55,20 @@ def _close_client_connection(client: BaseClient) -> None: def _initialize_boto3_aws_client(service: str, region: str = "") -> BaseClient: if region: - boto3_client: BaseClient = boto3.client(service, region_name=region) + boto3_client: BaseClient = default_session.client(service, region_name=region) else: - boto3_client: BaseClient = boto3.client(service) + boto3_client: BaseClient = default_session.client(service) boto3_client.meta.events.register( f"after-call.{service}.*", lambda **kwargs: _close_client_connection(boto3_client) ) return boto3_client +def setup_default_session(profile: str) -> None: + global default_session + default_session = boto3.session.Session(profile_name=profile) + + def get_default_account_id() -> str: sts_client: BaseClient = _initialize_boto3_aws_client(AWSConstants.STS_SERVICE_NAME) try: @@ -72,7 +79,7 @@ def get_default_account_id() -> str: def get_default_region() -> str: - region: str = boto3.session.Session().region_name + region: str = default_session.region_name if region: return region diff --git a/Gems/AWSCore/Code/awscore_editor_files.cmake b/Gems/AWSCore/Code/awscore_editor_files.cmake index 652f0455e1..13bfbb6102 100644 --- a/Gems/AWSCore/Code/awscore_editor_files.cmake +++ b/Gems/AWSCore/Code/awscore_editor_files.cmake @@ -12,6 +12,8 @@ set(FILES Include/Private/AWSCoreEditorSystemComponent.h Include/Private/Editor/AWSCoreEditorManager.h + Include/Private/Editor/Constants/AWSCoreEditorMenuLinks.h + Include/Private/Editor/Constants/AWSCoreEditorMenuNames.h Include/Private/Editor/UI/AWSCoreEditorMenu.h Include/Private/Editor/UI/AWSCoreResourceMappingToolAction.h Source/AWSCoreEditorSystemComponent.cpp diff --git a/Gems/AWSMetrics/Code/CMakeLists.txt b/Gems/AWSMetrics/Code/CMakeLists.txt index ffa9ac0408..aa790371d2 100644 --- a/Gems/AWSMetrics/Code/CMakeLists.txt +++ b/Gems/AWSMetrics/Code/CMakeLists.txt @@ -23,6 +23,7 @@ ly_add_target( PRIVATE AZ::AzCore AZ::AzFramework + PUBLIC Gem::AWSCore ) @@ -40,10 +41,15 @@ ly_add_target( PRIVATE AZ::AzCore AZ::AzFramework - Gem::AWSCore Gem::AWSMetrics.Static + RUNTIME_DEPENDENCIES + Gem::AWSCore ) +# Servers and Clients use the above metrics module +ly_create_alias(NAME AWSMetrics.Servers NAMESPACE Gem TARGETS Gem::AWSMetrics) +ly_create_alias(NAME AWSMetrics.Clients NAMESPACE Gem TARGETS Gem::AWSMetrics) + ################################################################################ # Tests ################################################################################ @@ -63,8 +69,9 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest AZ::AzCore AZ::AzFramework - Gem::AWSCore Gem::AWSMetrics.Static + RUNTIME_DEPENDENCIES + Gem::AWSCore ) ly_add_googletest( NAME Gem::AWSMetrics.Tests diff --git a/Gems/AWSMetrics/cdk/aws_metrics/batch_processing.py b/Gems/AWSMetrics/cdk/aws_metrics/batch_processing.py index fe85615d8d..22f6ad6903 100755 --- a/Gems/AWSMetrics/cdk/aws_metrics/batch_processing.py +++ b/Gems/AWSMetrics/cdk/aws_metrics/batch_processing.py @@ -111,8 +111,7 @@ class BatchProcessing: self._events_firehose_delivery_stream = kinesisfirehose.CfnDeliveryStream( self._stack, - id='EventsFirehoseDeliveryStream', - delivery_stream_name=f'{self._stack.stack_name}-EventsFirehoseDeliveryStream', + id=f'{self._stack.stack_name}-EventsFirehoseDeliveryStream', delivery_stream_type='KinesisStreamAsSource', kinesis_stream_source_configuration=kinesisfirehose.CfnDeliveryStream.KinesisStreamSourceConfigurationProperty( kinesis_stream_arn=self._input_stream_arn, @@ -327,7 +326,7 @@ class BatchProcessing: @property def delivery_stream_name(self) -> kinesisfirehose.CfnDeliveryStream.delivery_stream_name: - return self._events_firehose_delivery_stream.delivery_stream_name + return self._events_firehose_delivery_stream.ref @property def delivery_stream_role_arn(self) -> iam.Role.role_arn: diff --git a/Gems/AWSMetrics/cdk/aws_metrics/data_ingestion.py b/Gems/AWSMetrics/cdk/aws_metrics/data_ingestion.py index a3ab99fccf..fbd332a577 100755 --- a/Gems/AWSMetrics/cdk/aws_metrics/data_ingestion.py +++ b/Gems/AWSMetrics/cdk/aws_metrics/data_ingestion.py @@ -73,14 +73,14 @@ class DataIngestion: api_id_output = core.CfnOutput( self._stack, - id='RestApiId', + 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( self._stack, - id='DeploymentStage', + id='RESTApiStage', description='Stage for the REST API deployment', export_name=f"{application_name}:DeploymentStage", value=self._rest_api.deployment_stage.stage_name) diff --git a/Gems/Achievements/Code/CMakeLists.txt b/Gems/Achievements/Code/CMakeLists.txt index b49409bd5e..4b2aa07dab 100644 --- a/Gems/Achievements/Code/CMakeLists.txt +++ b/Gems/Achievements/Code/CMakeLists.txt @@ -44,3 +44,6 @@ ly_add_target( PRIVATE Gem::Achievements.Static ) + +# we'll load the above "Gem::Achievements" module in clients only. +ly_create_alias(NAME Achievements.Clients NAMESPACE Gem TARGETS Gem::Achievements) diff --git a/Gems/AssetMemoryAnalyzer/Code/CMakeLists.txt b/Gems/AssetMemoryAnalyzer/Code/CMakeLists.txt index df97feaa3f..bdf76eca60 100644 --- a/Gems/AssetMemoryAnalyzer/Code/CMakeLists.txt +++ b/Gems/AssetMemoryAnalyzer/Code/CMakeLists.txt @@ -43,6 +43,10 @@ ly_add_target( Gem::ImGui ) +# AssetMemoryAnalyzer is available in clients and servers. +ly_create_alias(NAME AssetMemoryAnalyzer.Clients NAMESPACE Gem TARGETS Gem::AssetMemoryAnalyzer) +ly_create_alias(NAME AssetMemoryAnalyzer.Servers NAMESPACE Gem TARGETS Gem::AssetMemoryAnalyzer) + ################################################################################ # Tests ################################################################################ @@ -65,3 +69,4 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAME Gem::AssetMemoryAnalyzer.Tests ) endif() + diff --git a/Gems/AssetValidation/Code/CMakeLists.txt b/Gems/AssetValidation/Code/CMakeLists.txt index 983ddd9e9d..f62baf57f5 100644 --- a/Gems/AssetValidation/Code/CMakeLists.txt +++ b/Gems/AssetValidation/Code/CMakeLists.txt @@ -65,3 +65,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) NAME Gem::AssetValidation.Tests ) endif() + +# AssetValidation should be active in all clients plus tools +ly_create_alias(NAME AssetValidation.Clients NAMESPACE Gem TARGETS Gem::AssetValidation) +ly_create_alias(NAME AssetValidation.Tools NAMESPACE Gem TARGETS Gem::AssetValidation) + diff --git a/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h b/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h index ab9e65b896..d67260de3e 100644 --- a/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h +++ b/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h @@ -152,8 +152,11 @@ struct AssetValidationTest { AZ::SettingsRegistry::Register(&m_registry); - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(m_registry); + auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + + "/project_path"; + m_registry.Set(projectPathKey, "AutomatedTesting"); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry); + // Set the engine root to the temporary directory and re-update the runtime file paths auto enginePathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/engine_path"; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp index 04b015a66f..bf584c4d63 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp @@ -108,11 +108,11 @@ namespace ImageProcessingAtomEditor { readableString = "PC"; } - else if (platformStrLowerCase == "es3") + else if (platformStrLowerCase == "android") { readableString = "Android"; } - else if (platformStrLowerCase == "osx_gl") + else if (platformStrLowerCase == "mac") { readableString = "macOS"; } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/Mac/ImageProcessing_Traits_Mac.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/Mac/ImageProcessing_Traits_Mac.h index 28c1e78bb6..ef0e7f9619 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/Mac/ImageProcessing_Traits_Mac.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/Mac/ImageProcessing_Traits_Mac.h @@ -12,7 +12,7 @@ #pragma once #define AZ_TRAIT_IMAGEPROCESSING_BESSEL_FUNCTION_FIRST_ORDER j1 -#define AZ_TRAIT_IMAGEPROCESSING_DEFAULT_PLATFORM "osx_gl" +#define AZ_TRAIT_IMAGEPROCESSING_DEFAULT_PLATFORM "mac" #define AZ_TRAIT_IMAGEPROCESSING_DEFINE_DIRECT3D_CONSTANTS 1 #define AZ_TRAIT_IMAGEPROCESSING_PVRTEXLIB_USE_WINDLL_IMPORT 0 #define AZ_TRAIT_IMAGEPROCESSING_SQUISH_DO_NOT_USE_FASTCALL 1 diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/iOS/ImageProcessing_Traits_iOS.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/iOS/ImageProcessing_Traits_iOS.h index 28c1e78bb6..ef0e7f9619 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/iOS/ImageProcessing_Traits_iOS.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Platform/iOS/ImageProcessing_Traits_iOS.h @@ -12,7 +12,7 @@ #pragma once #define AZ_TRAIT_IMAGEPROCESSING_BESSEL_FUNCTION_FIRST_ORDER j1 -#define AZ_TRAIT_IMAGEPROCESSING_DEFAULT_PLATFORM "osx_gl" +#define AZ_TRAIT_IMAGEPROCESSING_DEFAULT_PLATFORM "mac" #define AZ_TRAIT_IMAGEPROCESSING_DEFINE_DIRECT3D_CONSTANTS 1 #define AZ_TRAIT_IMAGEPROCESSING_PVRTEXLIB_USE_WINDLL_IMPORT 0 #define AZ_TRAIT_IMAGEPROCESSING_SQUISH_DO_NOT_USE_FASTCALL 1 diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_24bit.tif.exportsettings b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_24bit.tif.exportsettings index 0417122033..7bce3041b9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_24bit.tif.exportsettings +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/1024x1024_24bit.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /bumptype=none /M=62,18,32,83,50,50 /preset=Diffuse_highQ /mipgentype=kaiser /reduce="es3:0,ios:3,osx_gl:0,pc:4,provo:1" /ser=0 +/autooptimizefile=0 /bumptype=none /M=62,18,32,83,50,50 /preset=Diffuse_highQ /mipgentype=kaiser /reduce="android:0,ios:3,mac:0,pc:4,provo:1" /ser=0 diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset index 0b68493198..3a5122f18e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset @@ -25,7 +25,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}", "Name": "Albedo", "RGB_Weight": "CIEXYZ", @@ -67,7 +67,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}", "Name": "Albedo", "RGB_Weight": "CIEXYZ", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset index 3773857e0a..692ef99b1c 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset @@ -23,7 +23,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}", "Name": "AlbedoWithCoverage", "RGB_Weight": "CIEXYZ", @@ -61,7 +61,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}", "Name": "AlbedoWithCoverage", "RGB_Weight": "CIEXYZ", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset index 530e36038d..4ebe773f0e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset @@ -23,7 +23,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}", "Name": "AlbedoWithGenericAlpha", "RGB_Weight": "CIEXYZ", @@ -61,7 +61,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}", "Name": "AlbedoWithGenericAlpha", "RGB_Weight": "CIEXYZ", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset index 6d6c156683..6049ef5bd4 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset @@ -23,7 +23,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{7BB7BC6C-D3DA-4184-AC42-BCD8C57DE565}", "Name": "AlbedoWithOpacity", "RGB_Weight": "CIEXYZ", @@ -61,7 +61,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{7BB7BC6C-D3DA-4184-AC42-BCD8C57DE565}", "Name": "AlbedoWithOpacity", "RGB_Weight": "CIEXYZ", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset index 4e69ae67f2..56dec20f3e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset @@ -17,7 +17,7 @@ "PixelFormat": "BC4" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}", "Name": "AmbientOcclusion", "SourceColor": "Linear", @@ -43,7 +43,7 @@ ], "PixelFormat": "EAC_R11" }, - "osx_gl": { + "mac": { "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}", "Name": "AmbientOcclusion", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset index f37acd2f9d..2280a06302 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset @@ -11,7 +11,7 @@ "IsPowerOf2": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", "Name": "CloudShadows", "DestColor": "Linear", @@ -25,7 +25,7 @@ "PixelFormat": "EAC_R11", "IsPowerOf2": true }, - "osx_gl": { + "mac": { "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", "Name": "CloudShadows", "DestColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ColorChart.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ColorChart.preset index 46327e87ed..5f0480cee7 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ColorChart.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ColorChart.preset @@ -15,7 +15,7 @@ "IsColorChart": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{0A17A85F-07EE-48A0-8BF8-D42F0A5E0B3C}", "Name": "ColorChart", "SourceColor": "Linear", @@ -37,7 +37,7 @@ "PixelFormat": "R8G8B8X8", "IsColorChart": true }, - "osx_gl": { + "mac": { "UUID": "{0A17A85F-07EE-48A0-8BF8-D42F0A5E0B3C}", "Name": "ColorChart", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ConvolvedCubemap.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ConvolvedCubemap.preset index fe87f49426..abdf6501be 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ConvolvedCubemap.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ConvolvedCubemap.preset @@ -30,7 +30,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{2174E04B-73BB-4DF1-8961-4900DC3C9D72}", "Name": "ConvolvedCubemap", "SourceColor": "Linear", @@ -82,7 +82,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{2174E04B-73BB-4DF1-8961-4900DC3C9D72}", "Name": "ConvolvedCubemap", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset index 2c47f9eaed..f1e43e74b1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset @@ -18,7 +18,7 @@ "NumberResidentMips": 255 }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}", "Name": "Decal_AlbedoWithOpacity", "FileMasks": [ @@ -46,7 +46,7 @@ // Decal Texture Arrays need all mips available immediately for packing. "NumberResidentMips": 255 }, - "osx_gl": { + "mac": { "UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}", "Name": "Decal_AlbedoWithOpacity", "FileMasks": [ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset index 23ec2347cd..991692c5cc 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset @@ -18,7 +18,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{5096FC7B-0B2D-4466-9943-AD59922968E8}", "Name": "Detail_MergedAlbedoNormalsSmoothness", "SourceColor": "Linear", @@ -46,7 +46,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{5096FC7B-0B2D-4466-9943-AD59922968E8}", "Name": "Detail_MergedAlbedoNormalsSmoothness", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness_Lossless.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness_Lossless.preset index 3145c5cf8a..fec11218e8 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness_Lossless.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness_Lossless.preset @@ -17,7 +17,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{B6FC1AEF-907C-4157-9A1A-D9960F0E5B9A}", "Name": "Detail_MergedAlbedoNormalsSmoothness_Lossless", "SourceColor": "Linear", @@ -43,7 +43,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{B6FC1AEF-907C-4157-9A1A-D9960F0E5B9A}", "Name": "Detail_MergedAlbedoNormalsSmoothness_Lossless", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset index 86ba9d74c0..520e4ae193 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset @@ -28,7 +28,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{D7B4BEA6-6427-4295-B61B-62776D0056DE}", "Name": "Displacement", "SourceColor": "Linear", @@ -77,7 +77,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{D7B4BEA6-6427-4295-B61B-62776D0056DE}", "Name": "Displacement", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset index 5a98d2cd30..ffb16482fd 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset @@ -18,7 +18,7 @@ "DiscardAlpha": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}", "Name": "Emissive", "RGB_Weight": "CIEXYZ", @@ -46,7 +46,7 @@ "PixelFormat": "ASTC_6x6", "DiscardAlpha": true }, - "osx_gl": { + "mac": { "UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}", "Name": "Emissive", "RGB_Weight": "CIEXYZ", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Gradient.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Gradient.preset index 9cf32093d1..33d7babf00 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Gradient.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Gradient.preset @@ -11,7 +11,7 @@ "IsPowerOf2": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{0D26B387-2FBA-456D-AB8E-613020BCC7F8}", "Name": "Gradient", "SourceColor": "Linear", @@ -25,7 +25,7 @@ "DestColor": "Linear", "IsPowerOf2": true }, - "osx_gl": { + "mac": { "UUID": "{0D26B387-2FBA-456D-AB8E-613020BCC7F8}", "Name": "Gradient", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset index c77c77b988..f06682be42 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset @@ -18,7 +18,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{B6B04FD3-BD7B-44AC-AD93-6FECD2BD4D76}", "Name": "Greyscale", "SourceColor": "Linear", @@ -46,7 +46,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{B6B04FD3-BD7B-44AC-AD93-6FECD2BD4D76}", "Name": "Greyscale", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLDiffuse.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLDiffuse.preset index fb4155a974..8bd6b348d1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLDiffuse.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLDiffuse.preset @@ -28,7 +28,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}", "Name": "IBLDiffuse", "FileMasks": [ @@ -74,7 +74,7 @@ "SubId": 3000 } }, - "osx_gl": { + "mac": { "UUID": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}", "Name": "IBLDiffuse", "FileMasks": [ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset index 530eb3d048..402fc470eb 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset @@ -26,7 +26,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{E6441EAC-9843-484B-8EFC-C03B2935B48D}", "Name": "IBLSkybox", "FileMasks": [ @@ -68,7 +68,7 @@ "IBLDiffusePreset": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}" } }, - "osx_gl": { + "mac": { "UUID": "{E6441EAC-9843-484B-8EFC-C03B2935B48D}", "Name": "IBLSkybox", "FileMasks": [ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset index db5a9276bd..d940f425c2 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset @@ -30,7 +30,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", "Name": "IBLSpecular", "FileMasks": [ @@ -80,7 +80,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", "Name": "IBLSpecular", "FileMasks": [ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings b/Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings index c8d921a8ff..82a57dd614 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings @@ -5,7 +5,7 @@ "ClassData": { "AnalysisFingerprint": "2", "BuildSettings": { - "es3": { + "android": { "GlossScale": 16.0, "GlossBias": 0.0, "Streaming": false, @@ -17,7 +17,7 @@ "Streaming": false, "Enable": true }, - "osx_gl": { + "mac": { "GlossScale": 16.0, "GlossBias": 0.0, "Streaming": false, diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_R32F.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_R32F.preset new file mode 100644 index 0000000000..1bb23c6e96 --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_R32F.preset @@ -0,0 +1,45 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "FileMasks": ["_lutr32f"], + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + }, + "PlatformsPresets": { + "es3": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + }, + "ios": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + }, + "osx_gl": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + }, + "provo": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + } + } + } +} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG16.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG16.preset index 6bfb697a5e..183653d111 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG16.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG16.preset @@ -11,7 +11,7 @@ "PixelFormat": "R16G16" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{D55CBCD3-AF2D-4515-98AB-E278F6B3B5F6}", "Name": "LUT_RG16", "SourceColor": "Linear", @@ -25,7 +25,7 @@ "DestColor": "Linear", "PixelFormat": "R16G16" }, - "osx_gl": { + "mac": { "UUID": "{D55CBCD3-AF2D-4515-98AB-E278F6B3B5F6}", "Name": "LUT_RG16", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG32F.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG32F.preset index a010d26a9c..2cf0c6ca0a 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG32F.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG32F.preset @@ -12,7 +12,7 @@ "PixelFormat": "R32G32F" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{52470B8B-0798-4E03-B0D3-039D5141CFEC}", "Name": "LUT_RG32F", "SourceColor": "Linear", @@ -26,7 +26,7 @@ "DestColor": "Linear", "PixelFormat": "R32G32F" }, - "osx_gl": { + "mac": { "UUID": "{52470B8B-0798-4E03-B0D3-039D5141CFEC}", "Name": "LUT_RG32F", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG8.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG8.preset index ca636f486a..9838d532b2 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG8.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG8.preset @@ -14,7 +14,7 @@ "PixelFormat": "R8G8" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{3791319D-043B-4011-8B6F-3DE96D0C4309}", "Name": "LUT_RG8", "SourceColor": "Linear", @@ -34,7 +34,7 @@ ], "PixelFormat": "R8G8" }, - "osx_gl": { + "mac": { "UUID": "{3791319D-043B-4011-8B6F-3DE96D0C4309}", "Name": "LUT_RG8", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16.preset new file mode 100644 index 0000000000..f36d566d7e --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16.preset @@ -0,0 +1,59 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + }, + "PlatformsPresets": { + "es3": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + }, + "ios": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + }, + "osx_gl": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + }, + "provo": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + } + } + } +} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16F.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16F.preset new file mode 100644 index 0000000000..367c5101b3 --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16F.preset @@ -0,0 +1,59 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + }, + "PlatformsPresets": { + "es3": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + }, + "ios": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + }, + "osx_gl": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + }, + "provo": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + } + } + } +} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA32F.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA32F.preset index 717ece058d..3a456825bf 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA32F.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA32F.preset @@ -12,7 +12,7 @@ "PixelFormat": "R32G32B32A32F" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{AC4C49D4-2C70-425A-8DBF-E7FB2C61CF8D}", "Name": "LUT_RGBA32F", "SourceColor": "Linear", @@ -26,7 +26,7 @@ "DestColor": "Linear", "PixelFormat": "R32G32B32A32F" }, - "osx_gl": { + "mac": { "UUID": "{AC4C49D4-2C70-425A-8DBF-E7FB2C61CF8D}", "Name": "LUT_RGBA32F", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA8.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA8.preset index 6dbb29f830..49bf33dd84 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA8.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA8.preset @@ -10,7 +10,7 @@ "DestColor": "Linear" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{3A6BB297-B610-4EA5-8DA4-610FB12B9EC0}", "Name": "LUT_RGBA8", "SourceColor": "Linear", @@ -22,7 +22,7 @@ "SourceColor": "Linear", "DestColor": "Linear" }, - "osx_gl": { + "mac": { "UUID": "{3A6BB297-B610-4EA5-8DA4-610FB12B9EC0}", "Name": "LUT_RGBA8", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LayerMask.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LayerMask.preset index 9c57f80709..5ce06aaea2 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LayerMask.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LayerMask.preset @@ -15,7 +15,7 @@ "PixelFormat": "R8G8B8X8" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{B1AC2F76-CB1A-46A8-B92D-B8DFBB564FCF}", "Name": "LayerMask", "SourceColor": "Linear", @@ -37,7 +37,7 @@ ], "PixelFormat": "R8G8B8X8" }, - "osx_gl": { + "mac": { "UUID": "{B1AC2F76-CB1A-46A8-B92D-B8DFBB564FCF}", "Name": "LayerMask", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset index 84294dfbcc..9f4b5bf68d 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset @@ -9,7 +9,7 @@ "PixelFormat": "BC1" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", "Name": "LensOptics", "PixelFormat": "ETC2" @@ -19,7 +19,7 @@ "Name": "LensOptics", "PixelFormat": "ASTC_4x4" }, - "osx_gl": { + "mac": { "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", "Name": "LensOptics", "PixelFormat": "BC1" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset index 8c98394d36..ede264a78e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset @@ -14,7 +14,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", "Name": "LightProjector", "DestColor": "Linear", @@ -34,7 +34,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", "Name": "LightProjector", "DestColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LoadingScreen.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LoadingScreen.preset index f64c48eb95..ad0e2ddf06 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LoadingScreen.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LoadingScreen.preset @@ -9,7 +9,7 @@ "PixelFormat": "R8G8B8X8" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{9ED87726-12AB-4BE0-9397-AD62AE56D9E2}", "Name": "LoadingScreen", "PixelFormat": "R8G8B8X8" @@ -19,7 +19,7 @@ "Name": "LoadingScreen", "PixelFormat": "R8G8B8X8" }, - "osx_gl": { + "mac": { "UUID": "{9ED87726-12AB-4BE0-9397-AD62AE56D9E2}", "Name": "LoadingScreen", "PixelFormat": "R8G8B8X8" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset index a402a2636c..9370de063d 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset @@ -15,7 +15,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}", "Name": "Minimap", "SuppressEngineReduce": true, @@ -37,7 +37,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}", "Name": "Minimap", "SuppressEngineReduce": true, diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset index f5ecc58d1a..459cd5b1fb 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset @@ -14,7 +14,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}", "Name": "MuzzleFlash", "SuppressEngineReduce": true, @@ -34,7 +34,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}", "Name": "MuzzleFlash", "SuppressEngineReduce": true, diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset index 104f3b4a39..04307eada4 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset @@ -27,7 +27,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{508B21D5-5250-4003-97EC-1CF28D571ACF}", "Name": "Normals", "SourceColor": "Linear", @@ -75,7 +75,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{508B21D5-5250-4003-97EC-1CF28D571ACF}", "Name": "Normals", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsFromDisplacement.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsFromDisplacement.preset index c513720b68..46e97c3443 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsFromDisplacement.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsFromDisplacement.preset @@ -19,7 +19,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{8AE5D8D7-ECF8-4B7D-91DE-8F787E3B4210}", "Name": "NormalsFromDisplacement", "SourceColor": "Linear", @@ -49,7 +49,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{8AE5D8D7-ECF8-4B7D-91DE-8F787E3B4210}", "Name": "NormalsFromDisplacement", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness.preset index e773f7d910..b8f6e38ac1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness.preset @@ -25,7 +25,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{6EE749F4-846E-4F7A-878C-F211F85EA59F}", "Name": "NormalsWithSmoothness", "SourceColor": "Linear", @@ -67,7 +67,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{6EE749F4-846E-4F7A-878C-F211F85EA59F}", "Name": "NormalsWithSmoothness", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness_Legacy.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness_Legacy.preset index 4cf7af6f29..58bb02cd72 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness_Legacy.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness_Legacy.preset @@ -22,7 +22,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{A92541B8-2E70-4EF1-BA88-1DC1EA2A2341}", "Name": "NormalsWithSmoothness_Legacy", "SourceColor": "Linear", @@ -58,7 +58,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{A92541B8-2E70-4EF1-BA88-1DC1EA2A2341}", "Name": "NormalsWithSmoothness_Legacy", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset index 265379d053..bbd7fd5db9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset @@ -27,7 +27,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{F3D5E572-A3CF-435A-A2AB-75D2B6907847}", "Name": "Opacity", "SourceColor": "Linear", @@ -73,7 +73,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{F3D5E572-A3CF-435A-A2AB-75D2B6907847}", "Name": "Opacity", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage.preset index 03744dee9e..e51848a116 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage.preset @@ -8,7 +8,7 @@ "Name": "ReferenceImage" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{C659D222-F56B-4B61-A2F8-C1FA547F3C39}", "Name": "ReferenceImage" }, @@ -16,7 +16,7 @@ "UUID": "{C659D222-F56B-4B61-A2F8-C1FA547F3C39}", "Name": "ReferenceImage" }, - "osx_gl": { + "mac": { "UUID": "{C659D222-F56B-4B61-A2F8-C1FA547F3C39}", "Name": "ReferenceImage" }, diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinear.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinear.preset index 4d75e7ae1d..d9b9c17d07 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinear.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinear.preset @@ -13,7 +13,7 @@ "DiscardAlpha": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{46D9F30F-793C-4449-BCEF-12A396E61B2C}", "Name": "ReferenceImage_HDRLinear", "SourceColor": "Linear", @@ -31,7 +31,7 @@ "PixelFormat": "R9G9B9E5", "DiscardAlpha": true }, - "osx_gl": { + "mac": { "UUID": "{46D9F30F-793C-4449-BCEF-12A396E61B2C}", "Name": "ReferenceImage_HDRLinear", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinearUncompressed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinearUncompressed.preset index 8344102425..8a5a83afa3 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinearUncompressed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinearUncompressed.preset @@ -13,7 +13,7 @@ "DiscardAlpha": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{EEF24422-C8F0-4ECE-B32A-C70DB8129466}", "Name": "ReferenceImage_HDRLinearUncompressed", "SourceColor": "Linear", @@ -31,7 +31,7 @@ "PixelFormat": "R16G16B16A16F", "DiscardAlpha": true }, - "osx_gl": { + "mac": { "UUID": "{EEF24422-C8F0-4ECE-B32A-C70DB8129466}", "Name": "ReferenceImage_HDRLinearUncompressed", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_Linear.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_Linear.preset index 515e9b0512..bf72f21b06 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_Linear.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_Linear.preset @@ -11,7 +11,7 @@ "SuppressEngineReduce": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{02C3D9F5-3637-49BA-A48A-D68D629A4D14}", "Name": "ReferenceImage_Linear", "SourceColor": "Linear", @@ -25,7 +25,7 @@ "DestColor": "Linear", "SuppressEngineReduce": true }, - "osx_gl": { + "mac": { "UUID": "{02C3D9F5-3637-49BA-A48A-D68D629A4D14}", "Name": "ReferenceImage_Linear", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset index 58e283add7..1844e0186e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset @@ -34,7 +34,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}", "Name": "Reflectance", "SourceColor": "Linear", @@ -92,7 +92,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}", "Name": "Reflectance", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset index e386d08a35..e51cc7122b 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset @@ -16,7 +16,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{851128B5-7454-42C4-83CE-FCFE071834C5}", "Name": "ReflectanceWithSmoothness_Legacy", "FileMasks": [ @@ -40,7 +40,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{851128B5-7454-42C4-83CE-FCFE071834C5}", "Name": "ReflectanceWithSmoothness_Legacy", "FileMasks": [ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset index 767b0b67eb..07cc39c955 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset @@ -18,7 +18,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{9B2114F5-118A-4B3A-9CFE-97FA01EC8CFE}", "Name": "Reflectance_Linear", "DestColor": "Linear", @@ -46,7 +46,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{9B2114F5-118A-4B3A-9CFE-97FA01EC8CFE}", "Name": "Reflectance_Linear", "DestColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Font.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Font.preset index b2bbf905db..f76741148c 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Font.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Font.preset @@ -12,7 +12,7 @@ "IsPowerOf2": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{F34E3711-5F34-4DBC-8F5D-6340D3989F4B}", "Name": "SF_Font", "SourceColor": "Linear", @@ -28,7 +28,7 @@ "SuppressEngineReduce": true, "IsPowerOf2": true }, - "osx_gl": { + "mac": { "UUID": "{F34E3711-5F34-4DBC-8F5D-6340D3989F4B}", "Name": "SF_Font", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Gradient.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Gradient.preset index 41ba10f55c..aff25dc83d 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Gradient.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Gradient.preset @@ -12,7 +12,7 @@ "IsPowerOf2": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{E7F9DF56-DCB0-4683-96EE-F04DA547BE24}", "Name": "SF_Gradient", "SourceColor": "Linear", @@ -28,7 +28,7 @@ "SuppressEngineReduce": true, "IsPowerOf2": true }, - "osx_gl": { + "mac": { "UUID": "{E7F9DF56-DCB0-4683-96EE-F04DA547BE24}", "Name": "SF_Gradient", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset index e36e42860d..46a32ce5d4 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset @@ -13,7 +13,7 @@ "IsPowerOf2": true }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{189A42CB-AEE3-4B80-B276-0FDB0ECA140C}", "Name": "SF_Image", "SourceColor": "Linear", @@ -31,7 +31,7 @@ "PixelFormat": "PVRTC4", "IsPowerOf2": true }, - "osx_gl": { + "mac": { "UUID": "{189A42CB-AEE3-4B80-B276-0FDB0ECA140C}", "Name": "SF_Image", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset index fa2fe2ae72..0ba70d2ca3 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset @@ -12,7 +12,7 @@ "PixelFormat": "BC1" }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", "Name": "SF_Image_nonpower2", "SourceColor": "Linear", @@ -28,7 +28,7 @@ "SuppressEngineReduce": true, "PixelFormat": "PVRTC4" }, - "osx_gl": { + "mac": { "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", "Name": "SF_Image_nonpower2", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Skybox.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Skybox.preset index 9102bd53bb..4f71855ecf 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Skybox.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Skybox.preset @@ -21,7 +21,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{F359CD3B-37E6-4627-B4F6-2DFC2C0E3C1C}", "Name": "Skybox", "FileMasks": [ @@ -55,7 +55,7 @@ "RequiresConvolve": false } }, - "osx_gl": { + "mac": { "UUID": "{F359CD3B-37E6-4627-B4F6-2DFC2C0E3C1C}", "Name": "Skybox", "FileMasks": [ diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset index 19881f93d7..84a70935f1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset @@ -16,7 +16,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{88D07159-2FC0-4CBE-82CC-A9DC258C9351}", "Name": "Terrain_Albedo", "SourceColor": "Linear", @@ -40,7 +40,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{88D07159-2FC0-4CBE-82CC-A9DC258C9351}", "Name": "Terrain_Albedo", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset index 2fcbb012d4..1d83737ef9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset @@ -15,7 +15,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{7827AA52-0A7B-43E7-8CD4-55E0BC513AF1}", "Name": "Terrain_Albedo_HighPassed", "SourceColor": "Linear", @@ -37,7 +37,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{7827AA52-0A7B-43E7-8CD4-55E0BC513AF1}", "Name": "Terrain_Albedo_HighPassed", "SourceColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Uncompressed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Uncompressed.preset index d0dbbcba6f..6e28cafe11 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Uncompressed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Uncompressed.preset @@ -13,7 +13,7 @@ } }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{E996A696-991C-4FFC-B270-F5AD408B0618}", "Name": "Uncompressed", "PixelFormat": "R8G8B8X8", @@ -31,7 +31,7 @@ "MipGenType": "Box" } }, - "osx_gl": { + "mac": { "UUID": "{E996A696-991C-4FFC-B270-F5AD408B0618}", "Name": "Uncompressed", "PixelFormat": "R8G8B8X8", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset index 01595a3425..6f70e8f14f 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset @@ -13,7 +13,7 @@ "FileMasks": [ "_ui" ] }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}", "Name": "UserInterface_Compressed", "SuppressEngineReduce": true, @@ -25,7 +25,7 @@ "SuppressEngineReduce": true, "PixelFormat": "ASTC_6x6" }, - "osx_gl": { + "mac": { "UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}", "Name": "UserInterface_Compressed", "SuppressEngineReduce": true, diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Lossless.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Lossless.preset index 78c63790ab..39066b242b 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Lossless.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Lossless.preset @@ -13,7 +13,7 @@ "FileMasks": [ "_ui" ] }, "PlatformsPresets": { - "es3": { + "android": { "UUID": "{83003128-F63E-422B-AEC2-68F0A947225F}", "Name": "UserInterface_Lossless", "SuppressEngineReduce": true, @@ -25,7 +25,7 @@ "SuppressEngineReduce": true, "PixelFormat": "R8G8B8A8" }, - "osx_gl": { + "mac": { "UUID": "{83003128-F63E-422B-AEC2-68F0A947225F}", "Name": "UserInterface_Lossless", "SuppressEngineReduce": true, diff --git a/Gems/Atom/Asset/ImageProcessingAtom/gem.json b/Gems/Atom/Asset/ImageProcessingAtom/gem.json new file mode 100644 index 0000000000..86256bff9d --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "ImageProcessingAtom", + "display_name": "Atom Image Processing", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/Atom/Asset/Shader/Code/CMakeLists.txt b/Gems/Atom/Asset/Shader/Code/CMakeLists.txt index a06aa79d24..dd8afec534 100644 --- a/Gems/Atom/Asset/Shader/Code/CMakeLists.txt +++ b/Gems/Atom/Asset/Shader/Code/CMakeLists.txt @@ -101,3 +101,36 @@ ly_add_target( 3rdParty::SPIRVCross 3rdParty::azslc ) + +################################################################################ +# Tests +################################################################################ +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + + ly_add_target( + NAME Atom_Asset_Shader.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + atom_asset_shader_builders_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + . + Source/Editor + Tests + BUILD_DEPENDENCIES + PRIVATE + AZ::AtomCore + AZ::AzTest + AZ::AzFramework + AZ::AzToolsFramework + Legacy::CryCommon + Gem::Atom_RPI.Public + Gem::Atom_RHI.Public + Gem::Atom_RPI.Edit + Gem::Atom_Asset_Shader.Static + ) + ly_add_googletest( + NAME Gem::Atom_Asset_Shader.Tests + ) + +endif() diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp index 1e471f8644..14193d774f 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp @@ -60,33 +60,31 @@ namespace AZ void PreprocessorOptions::RemovePredefinedMacros(const AZStd::vector& macroNames) { + for (const auto& macroName : macroNames) + { m_predefinedMacros.erase( AZStd::remove_if( m_predefinedMacros.begin(), m_predefinedMacros.end(), - [&](const AZStd::string& predefinedMacro) - { - for (const auto& macroName : macroNames) + [&](const AZStd::string& predefinedMacro) { + // Haystack, needle, bCaseSensitive + if (!AzFramework::StringFunc::StartsWith(predefinedMacro, macroName, true)) { - // Haystack, needle, bCaseSensitive - if (!AzFramework::StringFunc::StartsWith(predefinedMacro, macroName, true)) - { - return false; - } - // If found, let's make sure it is not just a substring. - if (predefinedMacro.size() == macroName.size()) - { - return true; - } - // The predefinedMacro can be a string like "macro=value". If we find '=' it is a match. - if (predefinedMacro.c_str()[macroName.size()] == '=') - { - return true; - } return false; } + // If found, let's make sure it is not just a substring. + if (predefinedMacro.size() == macroName.size()) + { + return true; + } + // The predefinedMacro can be a string like "macro=value". If we find '=' it is a match. + if (predefinedMacro.c_str()[macroName.size()] == '=') + { + return true; + } return false; }), m_predefinedMacros.end()); + } } //! Binder helper to Matsui C-Pre-Processor library diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder2.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder2.cpp index 1668b57866..5758db1da2 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder2.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder2.cpp @@ -344,8 +344,7 @@ namespace AZ AZStd::string prependedAzslFilePath = RHI::PrependFile(args); if (prependedAzslFilePath == azslFullPath) { - // For some reason the combined azsl file was not created in the temporary - // directory assigned to this job. + // The specific error is already reported by RHI::PrependFile(). response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; return; } diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp index 89ca76bd01..c256caac4a 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp @@ -736,13 +736,13 @@ namespace AZ { platformId = AzFramework::PlatformId::PC; } - else if (platformIdentifier == "osx_gl") + else if (platformIdentifier == "mac") { - platformId = AzFramework::PlatformId::OSX; + platformId = AzFramework::PlatformId::MAC_ID; } - else if (platformIdentifier == "es3") + else if (platformIdentifier == "android") { - platformId = AzFramework::PlatformId::ES3; + platformId = AzFramework::PlatformId::ANDROID_ID; } else if (platformIdentifier == "ios") { @@ -788,13 +788,13 @@ namespace AZ { platformId = AzFramework::PlatformId::PC; } - else if (platform == "osx_gl") + else if (platform == "mac") { - platformId = AzFramework::PlatformId::OSX; + platformId = AzFramework::PlatformId::MAC_ID; } - else if (platform == "es3") + else if (platform == "android") { - platformId = AzFramework::PlatformId::ES3; + platformId = AzFramework::PlatformId::ANDROID_ID; } else if (platform == "ios") { diff --git a/Gems/Atom/Asset/Shader/Code/Tests/Common/ShaderBuilderTestFixture.cpp b/Gems/Atom/Asset/Shader/Code/Tests/Common/ShaderBuilderTestFixture.cpp new file mode 100644 index 0000000000..276c225e05 --- /dev/null +++ b/Gems/Atom/Asset/Shader/Code/Tests/Common/ShaderBuilderTestFixture.cpp @@ -0,0 +1,41 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "ShaderBuilderTestFixture.h" + +#include +#include + +namespace UnitTest +{ + void ShaderBuilderTestFixture::SetUp() + { + AllocatorsTestFixture::SetUp(); + + AZ::AllocatorInstance::Create(); + AZ::AllocatorInstance::Create(); + + AZ::NameDictionary::Create(); + } + + void ShaderBuilderTestFixture::TearDown() + { + AZ::NameDictionary::Destroy(); + + AZ::AllocatorInstance::Destroy(); + AZ::AllocatorInstance::Destroy(); + + AllocatorsTestFixture::TearDown(); + } + +} + diff --git a/Gems/Atom/Asset/Shader/Code/Tests/Common/ShaderBuilderTestFixture.h b/Gems/Atom/Asset/Shader/Code/Tests/Common/ShaderBuilderTestFixture.h new file mode 100644 index 0000000000..450cc6bde5 --- /dev/null +++ b/Gems/Atom/Asset/Shader/Code/Tests/Common/ShaderBuilderTestFixture.h @@ -0,0 +1,34 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace UnitTest +{ + /** + * Unit test fixture for setting up memory allocation pools and the AZ::Name dictionary. + * In the future will be extended as needed. + */ + class ShaderBuilderTestFixture + : public AllocatorsTestFixture + { + protected: + /////////////////////////////////////////////////////////////////////// + // AllocatorsTestFixture overrides + void SetUp() override; + void TearDown() override; + /////////////////////////////////////////////////////////////////////// + }; +} // namespace UnitTest + diff --git a/Gems/Atom/Asset/Shader/Code/Tests/SupervariantCmdArgumentTests.cpp b/Gems/Atom/Asset/Shader/Code/Tests/SupervariantCmdArgumentTests.cpp new file mode 100644 index 0000000000..2e5ee3fc09 --- /dev/null +++ b/Gems/Atom/Asset/Shader/Code/Tests/SupervariantCmdArgumentTests.cpp @@ -0,0 +1,523 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include + +#include +#include +#include + +#include + +#include "Common/ShaderBuilderTestFixture.h" + +namespace UnitTest +{ + using namespace AZ; + + struct KeyValueView + { + AZStd::string_view m_key; + AZStd::string_view m_value; + }; + + class SupervariantCmdArgumentTests : public ShaderBuilderTestFixture + { + protected: + static constexpr char MCPP_MACRO1[] = "MACRO1"; + static constexpr char MCPP_VALUE1[] = "VALUE1a"; + static constexpr char MCPP_NEW_VALUE1[] = "VALUE1b"; // Missing A is not a typo + + static constexpr char MCPP_MACRO2[] = "MACRO2"; + static constexpr char MCPP_VALUE2[] = "VALUE2"; + + static constexpr char MCPP_MACRO3[] = "MACRO3"; + static constexpr char MCPP_VALUE3[] = "VALUE3a"; + static constexpr char MCPP_NEW_VALUE3[] = "VALUE3b"; + + static constexpr char MCPP_MACRO4[] = "MACRO4"; + + static constexpr char MCPP_MACRO5[] = "MACRO5"; + + static constexpr char MCPP_MACRO6[] = "MACRO6"; + static constexpr char MCPP_VALUE6[] = "VALUE6"; + + static constexpr char AZSLC_ARG1[] = "--azsl1"; + + static constexpr char AZSLC_ARG2[] = "--azsl2"; + static constexpr char AZSLC_VAL2[] = "open,source"; + static constexpr char AZSLC_NEW_VAL2a[] = "closed,binary"; + static constexpr char AZSLC_NEW_VAL2b[] = "closed,source"; + + static constexpr char AZSLC_ARG3[] = "--azsl3"; + static constexpr char AZSLC_VAL3[] = "blue"; + + static constexpr char AZSLC_ARG4[] = "-azsl4"; + + static constexpr char AZSLC_ARG5[] = "--azsl5"; + static constexpr char AZSLC_VAL5[] = "smith,wick,john,45,-1,-1"; + static constexpr char AZSLC_NEW_VAL5[] = "apple,seed,crisp,-1,2,0"; + + static constexpr char AZSLC_ARG6[] = "--azsl6"; + + static constexpr char AZSLC_ARG7[] = "--azsl7"; + + //! Helper function. + //! Given an input list of {Key, Value} pairs returns a list of strings where each string is of the form: "Key=Value". + AZStd::vector CreateListOfStringsFromListOfKeyValues(AZStd::array_view listOfKeyValues) const + { + AZStd::vector listOfStrings; + for (const auto& keyValue : listOfKeyValues) + { + if (keyValue.m_value.empty()) + { + listOfStrings.push_back(keyValue.m_key); + } + else + { + listOfStrings.push_back(AZStd::string::format("%s=%s", keyValue.m_key.data(), keyValue.m_value.data())); + } + } + return listOfStrings; + } + + //! Helper function. + //! Given an input list of {Key, Value} pairs returns a list of strings where each string is of the form: "Key1", "Value1", "Key2", "Value2". + AZStd::vector CreateListOfSingleStringsFromListOfKeyValues(AZStd::array_view listOfKeyValues) const + { + AZStd::vector listOfStrings; + for (const auto& keyValue : listOfKeyValues) + { + listOfStrings.push_back(keyValue.m_key); + if (!keyValue.m_value.empty()) + { + listOfStrings.push_back(keyValue.m_value); + } + } + return listOfStrings; + } + + //! Helper function. + //! @param outputString: [out] The string " @argName" gets appended to it (The space is intentional). + //! Alternatively, if @argValue is NOT empty, then the string " @argName=@argValue" is + //! appended to it. + //! @param argName: A typical command line argument. "-p" or "--some". + //! @param argValue: A string representing the value that should be appended to @argName. + void AppendCmdLineArgument(AZStd::string& outputString, AZStd::string_view argName, AZStd::string_view argValue) const + { + if (argValue.empty()) + { + outputString += AZStd::string::format(" %s", argName.data()); + } + else + { + outputString += AZStd::string::format(" %s=%s", argName.data(), argValue.data()); + } + } + + //! Helper function. + //! Similar to above, but assumes that @argName refers to just the name of a macro definition so the appended string will always start + //! with "-D". + void AppendMacroDefinitionArgument(AZStd::string& outputString, AZStd::string_view argName, AZStd::string_view argValue) const + { + AppendCmdLineArgument(outputString, AZStd::string::format("-D%s", argName.data()), argValue); + } + + //! A helper made of helpers. + //! Returns a command line string that results of concatenating the input list of {Key, Value} pairs (with '='). + //! Example of a returned string: + //! "key1=value1 key2 key3 key4=value" + AZStd::string CreateCmdLineStringFromListOfKeyValues(AZStd::array_view listOfKeyValues) const + { + AZStd::string cmdLineString; + for (const auto& keyValueView : listOfKeyValues) + { + AppendCmdLineArgument(cmdLineString, keyValueView.m_key, keyValueView.m_value); + } + return cmdLineString; + } + + //! A helper made of helpers. + //! Returns a command line string of macro definitions that results of concatenating the input list of {Key, Value} pairs. + //! Example of a returned string: + //! "-Dkey1=value1 -Dkey2 -Dkey3 -Dkey4=value" + AZStd::string CreateMacroDefinitionCmdLineStringFromListOfKeyValues(AZStd::array_view listOfKeyValues) const + { + AZStd::string cmdLineString; + for (const auto& keyValueView : listOfKeyValues) + { + AppendMacroDefinitionArgument(cmdLineString, keyValueView.m_key, keyValueView.m_value); + } + return cmdLineString; + } + + //! @param includePaths A List of folder paths + //! @param predefinedMacros A List of strings with format: "name[=value]" + ShaderBuilder::PreprocessorOptions CreatePreprocessorOptions( + AZStd::array_view includePaths, AZStd::array_view predefinedMacros) const + { + ShaderBuilder::PreprocessorOptions preprocessorOptions; + + preprocessorOptions.m_projectIncludePaths.reserve(includePaths.size()); + for (const auto& path : includePaths) + { + preprocessorOptions.m_projectIncludePaths.push_back(path); + } + + preprocessorOptions.m_predefinedMacros.reserve(predefinedMacros.size()); + for (const auto& macro : predefinedMacros) + { + preprocessorOptions.m_predefinedMacros.push_back(macro); + } + + return preprocessorOptions; + } + + //! @param azslcAdditionalFreeArguments: A string representing series of command line arguments for AZSLc. + //! @param dxcAdditionalFreeArguments: A string representing series of command line arguments for DXC. + RHI::ShaderCompilerArguments CreateShaderCompilerArguments( + AZStd::string_view azslcAdditionalFreeArguments, AZStd::string_view dxcAdditionalFreeArguments) const + { + RHI::ShaderCompilerArguments shaderCompilerArguments; + shaderCompilerArguments.m_azslcWarningLevel = 1; + shaderCompilerArguments.m_azslcAdditionalFreeArguments = azslcAdditionalFreeArguments; + shaderCompilerArguments.m_defaultMatrixOrder = RHI::MatrixOrder::Row; + shaderCompilerArguments.m_dxcAdditionalFreeArguments = dxcAdditionalFreeArguments; + + return shaderCompilerArguments; + } + + + //! @param includePaths A List of folder paths + //! @param predefinedMacros A List of strings with format: "name[=value]" + //! @param azslcAdditionalFreeArguments A string representing series of command line arguments for AZSLc. + //! @param dxcAdditionalFreeArguments: A string representing series of command line arguments for DXC. + ShaderBuilder::GlobalBuildOptions CreateGlobalBuildOptions( + AZStd::array_view includePaths, + AZStd::array_view predefinedMacros, + AZStd::string_view azslcAdditionalFreeArguments, + AZStd::string_view dxcAdditionalFreeArguments) const + { + ShaderBuilder::GlobalBuildOptions globalBuildOptions; + globalBuildOptions.m_preprocessorSettings = CreatePreprocessorOptions(includePaths, predefinedMacros); + globalBuildOptions.m_compilerArguments = + CreateShaderCompilerArguments(azslcAdditionalFreeArguments, dxcAdditionalFreeArguments); + return globalBuildOptions; + } + + //! @param name Name of the supervariant. + //! @param plusArguments A string with command line arguments that contains both C-preprocessor macro definitions + //! and other command line arguments for AZSLc. + //! @param minusArguments A string with command line arguments that should be removed from the finalized command line arguments. + //! it can contain both, C-preprocessor macro definitions and other command line arguments for AZSLc. + RPI::ShaderSourceData::SupervariantInfo CreateSupervariantInfo( + AZStd::string_view name, AZStd::string_view plusArguments, AZStd::string_view minusArguments) const + { + RPI::ShaderSourceData::SupervariantInfo supervariantInfo; + supervariantInfo.m_name = name; + supervariantInfo.m_plusArguments = plusArguments; + supervariantInfo.m_minusArguments = minusArguments; + return supervariantInfo; + } + + bool StringContainsAllSubstrings(AZStd::string_view haystack, AZStd::array_view substrings) + { + return AZStd::all_of(AZ_BEGIN_END(substrings), + [&](AZStd::string_view needle) -> bool + { + return (haystack.find(needle) != AZStd::string::npos); + } + ); + } + + bool StringDoesNotContainAnyOneOfTheSubstrings(AZStd::string_view haystack, AZStd::array_view substrings) + { + return AZStd::all_of(AZ_BEGIN_END(substrings), [&](AZStd::string_view needle) -> bool { + return (haystack.find(needle) == AZStd::string::npos); + }); + } + + //! @returns: True if all strings in @substring appear in @vectorOfString. + //! @remark: Keep in mind that this is not the same as saying that all strings in @vectorOfStrings appear in @substrings. + bool VectorContainsAllSubstrings( + AZStd::array_view vectorOfStrings, AZStd::array_view substrings) + { + return AZStd::all_of( + AZ_BEGIN_END(substrings), + [&](AZStd::string_view needle) -> bool { + bool res = AZStd::any_of(AZ_BEGIN_END(vectorOfStrings), + [&](AZStd::string_view haystack) -> bool + { + return haystack.find(needle) != AZStd::string::npos; + } + ); + return res; + } + ); + } + + //! @returns: True only if None of the strings in @vectorOfStrings contains any of the strings in @substrings. + bool VectorDoesNotContainAnyOneOfTheSubstrings(AZStd::array_view vectorOfStrings, AZStd::array_view substrings) + { + return AZStd::all_of(AZ_BEGIN_END(vectorOfStrings), [&](AZStd::string_view haystack) -> bool { + return StringDoesNotContainAnyOneOfTheSubstrings(haystack, substrings); + }); + } + + }; // class SupervariantCmdArgumentTests + + + TEST_F(SupervariantCmdArgumentTests, CommandLineArgumentUtils_ValidateHelperFunctions) + { + // In this test the idea is to validate the static helper functions in AZ::RHI::ShaderCompilerArguments class + // that are useful for command line argument manipulation, etc. + AZStd::vector argumentList = { + {AZSLC_ARG1, ""}, {AZSLC_ARG2, AZSLC_VAL2}, {AZSLC_ARG3, AZSLC_VAL3}, {AZSLC_ARG4, ""}, {AZSLC_ARG5, AZSLC_VAL5}, + }; + + auto argumentsAsString = CreateCmdLineStringFromListOfKeyValues(argumentList); + auto listOfArgumentNames = AZ::RHI::CommandLineArgumentUtils::GetListOfArgumentNames(argumentsAsString); + + EXPECT_TRUE(AZStd::all_of(AZ_BEGIN_END(argumentList), [&](const KeyValueView& needle) -> bool { + return (AZStd::find(AZ_BEGIN_END(listOfArgumentNames), needle.m_key) != listOfArgumentNames.end()) && + // Make sure the values did not make into the expected list of keys. + (AZStd::find(AZ_BEGIN_END(listOfArgumentNames), needle.m_value) == listOfArgumentNames.end()); + })); + + AZStd::vector listOfArgumentsToRemove = { AZSLC_ARG4, AZSLC_ARG2 }; + auto stringWithRemovedArguments = + AZ::RHI::CommandLineArgumentUtils::RemoveArgumentsFromCommandLineString(listOfArgumentsToRemove, argumentsAsString); + EXPECT_TRUE(AZStd::all_of(AZ_BEGIN_END(listOfArgumentsToRemove), [&](const AZStd::string& needle) -> bool { + return stringWithRemovedArguments.find(needle) == AZStd::string::npos; + })); + + AZStd::vector listOfSurvivingArguments = {AZSLC_ARG1, AZSLC_ARG3, AZSLC_ARG5}; + EXPECT_TRUE(AZStd::all_of(AZ_BEGIN_END(listOfSurvivingArguments), [&](const AZStd::string& needle) -> bool { + return stringWithRemovedArguments.find(needle) != AZStd::string::npos; + })); + + auto stringWithoutExtraSpaces = + AZ::RHI::CommandLineArgumentUtils::RemoveExtraSpaces(" --arg1 -arg2 --arg3=foo --arg4=bar "); + EXPECT_EQ(stringWithoutExtraSpaces, AZStd::string("--arg1 -arg2 --arg3=foo --arg4=bar")); + + auto stringAsMergedArguments = + AZ::RHI::CommandLineArgumentUtils::MergeCommandLineArguments("--arg1 -arg2 --arg3=foo", "--arg3=bar --arg4"); + EXPECT_EQ(stringAsMergedArguments, AZStd::string("--arg1 -arg2 --arg3=bar --arg4")); + + EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("-DMACRO")); + EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("-D MACRO")); + EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -D MACRO")); + EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p -DMACRO --more")); + EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p -D MACRO=VALUE --more")); + EXPECT_FALSE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p --more")); + EXPECT_FALSE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p --more --DFAKE")); + EXPECT_FALSE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--DFAKE1 --help -p --more --D FAKE2")); + } + + TEST_F(SupervariantCmdArgumentTests, ShaderCompilerArguments_ValidateCommandLineArgumentsMerge) + { + // In this test we validate that AZ::RHI::ShaderCompilerArguments::Merge() works as expected + // by merging AZSLC & DXC arguments giving higher priority to the arguments in the "right". + + auto shaderCompilerArgumentsLeft = CreateShaderCompilerArguments( + "--azsl1 --azsl2=avalue2a -azsl3 --azsl4=avalue4a", + "--dxc1=dvalue1a -dxc2 --dxc3=dvalue3a --dxc4"); + auto shaderCompilerArgumentsRight = CreateShaderCompilerArguments( + "--azsl1 --azsl2=avalue2b -azsl3 --azsl4=avalue4a --azsl5", + "--dxc1=dvalue1a -dxc2 --dxc3=dvalue3b --dxc4 --dxc5=dvalue5a"); + + shaderCompilerArgumentsLeft.Merge(shaderCompilerArgumentsRight); + EXPECT_EQ(shaderCompilerArgumentsLeft.m_azslcAdditionalFreeArguments, "--azsl1 --azsl2=avalue2b -azsl3 --azsl4=avalue4a --azsl5"); + EXPECT_EQ(shaderCompilerArgumentsLeft.m_dxcAdditionalFreeArguments, "--dxc1=dvalue1a -dxc2 --dxc3=dvalue3b --dxc4 --dxc5=dvalue5a"); + } + + + TEST_F(SupervariantCmdArgumentTests, SupervariantInfo_ValidateMemberFunctions) + { + // In this test all member functions of the ShaderSourceData::SupervariantInfo class + // are validated. + + AZStd::vector mcppMacrosList = { + {MCPP_MACRO1, MCPP_VALUE1}, + {MCPP_MACRO2, MCPP_VALUE2}, + {MCPP_MACRO3, MCPP_VALUE3}, + {MCPP_MACRO4, ""}, + }; + + AZStd::string argumentsToAddOrReplace; + AppendMacroDefinitionArgument(argumentsToAddOrReplace, MCPP_MACRO3, MCPP_NEW_VALUE3); + AppendCmdLineArgument(argumentsToAddOrReplace, AZSLC_ARG2, AZSLC_NEW_VAL2a); + AppendMacroDefinitionArgument(argumentsToAddOrReplace, MCPP_MACRO1, MCPP_NEW_VALUE1); + AppendCmdLineArgument(argumentsToAddOrReplace, AZSLC_ARG5, AZSLC_NEW_VAL5); + AppendMacroDefinitionArgument(argumentsToAddOrReplace, MCPP_MACRO5, ""); + AppendCmdLineArgument(argumentsToAddOrReplace, AZSLC_ARG6, ""); + + AZStd::string argumentsToRemove; + AppendCmdLineArgument(argumentsToRemove, AZSLC_ARG3, ""); + AppendMacroDefinitionArgument(argumentsToRemove, MCPP_MACRO2, ""); + AppendCmdLineArgument(argumentsToRemove, AZSLC_ARG4, ""); + AppendMacroDefinitionArgument(argumentsToRemove, MCPP_MACRO4, ""); + + auto supervariantInfo = CreateSupervariantInfo("Dummy", argumentsToAddOrReplace, argumentsToRemove); + + auto macroListToRemove = supervariantInfo.GetCombinedListOfMacroDefinitionNamesToRemove(); + AZStd::vector macroNamesToRemoveThatMustBePresent = { MCPP_MACRO1, MCPP_MACRO2, MCPP_MACRO3, MCPP_MACRO4, MCPP_MACRO5 }; + EXPECT_EQ(macroListToRemove.size(), macroNamesToRemoveThatMustBePresent.size()); + EXPECT_TRUE( + VectorContainsAllSubstrings(macroListToRemove, macroNamesToRemoveThatMustBePresent) + ); + + auto macroListToAdd = supervariantInfo.GetMacroDefinitionsToAdd(); + AZStd::vector macroNamesToAddThatMustBePresent = {MCPP_MACRO1, MCPP_MACRO3, MCPP_MACRO5}; + EXPECT_EQ(macroListToAdd.size(), macroNamesToAddThatMustBePresent.size()); + EXPECT_TRUE(VectorContainsAllSubstrings(macroListToAdd, macroNamesToAddThatMustBePresent)); + + // The result of GetCustomizedArgumentsForAzslc() is the most important value to test + AZStd::vector freeAzslcArgumentList = { + {AZSLC_ARG1, ""}, {AZSLC_ARG2, AZSLC_VAL2}, {AZSLC_ARG3, AZSLC_VAL3}, {AZSLC_ARG4, ""}, {AZSLC_ARG5, AZSLC_VAL5}, + }; + AZStd::string azslcArgs = CreateCmdLineStringFromListOfKeyValues(freeAzslcArgumentList); + AZStd::string customizedAzslcArgs = supervariantInfo.GetCustomizedArgumentsForAzslc(azslcArgs); + + AZStd::vector stringsThatMustBePresent = { + AZSLC_ARG1, AZSLC_ARG2, AZSLC_NEW_VAL2a, AZSLC_ARG5, AZSLC_NEW_VAL5, AZSLC_ARG6}; + EXPECT_TRUE(StringContainsAllSubstrings(customizedAzslcArgs, stringsThatMustBePresent)); + + AZStd::vector stringsThatCanNotBePresent = { AZSLC_ARG3, AZSLC_VAL3, AZSLC_ARG4, + // Because GetCustomizedArgumentsForAzslc() only returns arguments for AZSLc, none of the macro related + // arguments can be present + MCPP_MACRO1, MCPP_VALUE1, MCPP_NEW_VALUE1, + MCPP_MACRO2, MCPP_VALUE2, + MCPP_MACRO3, MCPP_VALUE3, MCPP_NEW_VALUE3, + MCPP_MACRO4, + MCPP_MACRO5 + }; + + EXPECT_TRUE( + StringDoesNotContainAnyOneOfTheSubstrings(customizedAzslcArgs, stringsThatCanNotBePresent) + ); + } + + + TEST_F(SupervariantCmdArgumentTests, ShaderAssetBuilder_ValidateInfluenceOfSupervariantInfoOnGlobalBuildOptions) + { + // In this test we validate how the ShaderAssetBuilder configure the commmand line arguments it passes + // to MCPP, AZSLc & DXC. It basically starts with a GlobalBuildOptions, that gets further customized by + // the ShaderCompilerArguments from ShaderSourceData(.shader file) and later further customized + // by each SupervariantInfo in ShaderSourceData. + + // The first step is to define the initial values of the GlobalBuildOptions. + AZStd::vector globalMcppMacrosList = { + {MCPP_MACRO1, MCPP_VALUE1}, + {MCPP_MACRO2, MCPP_VALUE2}, + {MCPP_MACRO3, MCPP_VALUE3}, + {MCPP_MACRO4, ""}, + }; + + AZStd::vector globalAzslArguments = { + {AZSLC_ARG1, ""}, + {AZSLC_ARG2, AZSLC_VAL2}, + {AZSLC_ARG3, AZSLC_VAL3}, + {AZSLC_ARG4, ""}, + {AZSLC_ARG5, AZSLC_VAL5}, + }; + + auto globalBuildOptions = CreateGlobalBuildOptions( + AZStd::vector(), CreateListOfStringsFromListOfKeyValues(globalMcppMacrosList), + CreateCmdLineStringFromListOfKeyValues(globalAzslArguments), + "" /* Don't care about DXC in this test */); + + // The second step is to load the Shader Compiler Arguments from the .shader file. + // These arguments will be merged in @globalBuildOptions, but the .shader arguments have + // higher priority. + AZStd::vector shaderAzslArguments = { + {AZSLC_ARG2, AZSLC_NEW_VAL2a}, + {AZSLC_ARG6, ""}, + }; + auto shaderCompilerArguments = CreateShaderCompilerArguments( + CreateCmdLineStringFromListOfKeyValues(shaderAzslArguments), "" /* Don't care about DXC in this test */); + globalBuildOptions.m_compilerArguments.Merge(shaderCompilerArguments); + + // Let's create the dummy supervariant. It will have some MCPP & AZSLc arguments to be added/replaced AND other MCPP & AZSLc arguments to be removed. + AZStd::vector supervariantAzslArgumentsToAdd = { + {AZSLC_ARG2, AZSLC_NEW_VAL2b}, + {AZSLC_ARG7, ""}, + }; + AZStd::vector supervariantMacroDefinitionsToAdd = { + {MCPP_MACRO1, MCPP_NEW_VALUE1}, + {MCPP_MACRO3, MCPP_NEW_VALUE3}, + {MCPP_MACRO5, ""}, + }; + auto supervariantArgumentsToAdd = CreateCmdLineStringFromListOfKeyValues(supervariantAzslArgumentsToAdd) + + CreateMacroDefinitionCmdLineStringFromListOfKeyValues(supervariantMacroDefinitionsToAdd); + + AZStd::vector supervariantAzslArgumentsToRemove = { + {AZSLC_ARG4, ""}, + {AZSLC_ARG1, ""}, + }; + AZStd::vector supervariantMacrosToRemove = { + {MCPP_MACRO2, ""}, + {MCPP_MACRO4, ""}, + }; + auto supervariantArgumentsToRemove = CreateCmdLineStringFromListOfKeyValues(supervariantAzslArgumentsToRemove) + + CreateMacroDefinitionCmdLineStringFromListOfKeyValues(supervariantMacrosToRemove); + + //CreateMacroDefinitionCmdLineStringFromListOfKeyValues + auto supervariantInfo = CreateSupervariantInfo("Dummy", + supervariantArgumentsToAdd, // These arguments will be added or replace existing ones. + supervariantArgumentsToRemove); // These arguments must be removed. + + AZStd::vector macroDefinitionNamesToRemove = supervariantInfo.GetCombinedListOfMacroDefinitionNamesToRemove(); + globalBuildOptions.m_preprocessorSettings.RemovePredefinedMacros(macroDefinitionNamesToRemove); + AZStd::vector macroDefinitionsToAdd = supervariantInfo.GetMacroDefinitionsToAdd(); + globalBuildOptions.m_preprocessorSettings.m_predefinedMacros.insert( + globalBuildOptions.m_preprocessorSettings.m_predefinedMacros.end(), macroDefinitionsToAdd.begin(), macroDefinitionsToAdd.end()); + + // Validate macro definitions that must be present. + EXPECT_TRUE( + VectorContainsAllSubstrings( + globalBuildOptions.m_preprocessorSettings.m_predefinedMacros, + AZStd::vector({MCPP_MACRO1, MCPP_NEW_VALUE1, MCPP_MACRO3, MCPP_NEW_VALUE3, MCPP_MACRO5})) + ); + + // Validate macro definitions that can't be present. + EXPECT_TRUE( + VectorDoesNotContainAnyOneOfTheSubstrings( + globalBuildOptions.m_preprocessorSettings.m_predefinedMacros, + AZStd::vector({MCPP_MACRO2, MCPP_VALUE3, MCPP_MACRO4})) + ); + + AZStd::string azslcArgsFromGlobalBuildOptions = globalBuildOptions.m_compilerArguments.MakeAdditionalAzslcCommandLineString(); + + // The result of GetCustomizedArgumentsForAzslc() is the most important value to test + AZStd::string customizedAzslcArgs = supervariantInfo.GetCustomizedArgumentsForAzslc(azslcArgsFromGlobalBuildOptions); + + EXPECT_TRUE( + StringContainsAllSubstrings(customizedAzslcArgs, CreateListOfSingleStringsFromListOfKeyValues(supervariantAzslArgumentsToAdd)) + ); + + EXPECT_TRUE( + StringDoesNotContainAnyOneOfTheSubstrings(customizedAzslcArgs, CreateListOfSingleStringsFromListOfKeyValues(supervariantAzslArgumentsToRemove)) + ); + + EXPECT_TRUE( + StringContainsAllSubstrings(customizedAzslcArgs, AZStd::vector({AZSLC_ARG3, AZSLC_VAL3, AZSLC_ARG5, AZSLC_VAL5})) + ); + } + + +} //namespace UnitTest + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); + diff --git a/Gems/Atom/Asset/Shader/Code/atom_asset_shader_builders_tests_files.cmake b/Gems/Atom/Asset/Shader/Code/atom_asset_shader_builders_tests_files.cmake new file mode 100644 index 0000000000..9f22f9f632 --- /dev/null +++ b/Gems/Atom/Asset/Shader/Code/atom_asset_shader_builders_tests_files.cmake @@ -0,0 +1,16 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Tests/Common/ShaderBuilderTestFixture.h + Tests/Common/ShaderBuilderTestFixture.cpp + Tests/SupervariantCmdArgumentTests.cpp +) diff --git a/Gems/Atom/Asset/Shader/gem.json b/Gems/Atom/Asset/Shader/gem.json new file mode 100644 index 0000000000..71c741f436 --- /dev/null +++ b/Gems/Atom/Asset/Shader/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "AtomShader", + "display_name": "Atom Shader Builder", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/Atom/Bootstrap/gem.json b/Gems/Atom/Bootstrap/gem.json new file mode 100644 index 0000000000..8aa5cade6e --- /dev/null +++ b/Gems/Atom/Bootstrap/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "Atom_Bootstrap", + "display_name": "Atom Bootstrap", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/Atom/Component/DebugCamera/gem.json b/Gems/Atom/Component/DebugCamera/gem.json new file mode 100644 index 0000000000..06d39d1fc0 --- /dev/null +++ b/Gems/Atom/Component/DebugCamera/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "Atom_Component_DebugCamera", + "display_name": "Atom Debug Camera Component", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.materialtype index 214fc02660..9a2edc9fca 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/ReflectionProbe/ReflectionProbeVisualization.materialtype @@ -469,7 +469,7 @@ "max": 1.0, "connection": { "type": "ShaderInput", - "id": "m_depthFactor" + "id": "m_heightmapScale" } }, { @@ -479,18 +479,7 @@ "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_depthMap" - } - }, - { - "id": "invert", - "displayName": "Invert", - "description": "Invert to depthmap if the texture is heightmap", - "type": "Bool", - "defaultValue": true, - "connection": { - "type": "ShaderInput", - "id": "m_depthInverted" + "id": "m_heightmap" } }, { diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype index 954e01c592..ff0c4c59da 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype @@ -1,5 +1,5 @@ { - "description": "Material Type with properties used to define Enhanced PBR material shading model.", + "description": "Material Type with properties used to define Enhanced PBR, a metallic-roughness Physically-Based Rendering (PBR) material shading model, with advanced features like subsurface scattering, transmission, and anisotropy.", "propertyLayout": { "version": 3, "groups": [ @@ -13,11 +13,6 @@ "displayName": "Metallic", "description": "Properties for configuring whether the surface is metallic or not." }, - { - "id": "anisotropy", - "displayName": "Anisotropic Material Response", - "description": "How much is this material response anisotropic." - }, { "id": "roughness", "displayName": "Roughness", @@ -28,25 +23,25 @@ "displayName": "Specular Reflectance f0", "description": "The constant f0 represents the specular reflectance at normal incidence (Fresnel 0 Angle). Used to adjust reflectance of non-metal surfaces." }, - { - "id": "clearCoat", - "displayName": "Clear Coat", - "description": "Properties for configuring gloss clear coat" - }, { "id": "normal", "displayName": "Normal", "description": "Properties related to configuring surface normal." }, { - "id": "opacity", - "displayName": "Opacity", - "description": "Properties for configuring the materials transparency." + "id": "detailLayerGroup", + "displayName": "Detail Layer", + "description": "Properties for Fine Details Layer." }, { - "id": "uv", - "displayName": "UVs", - "description": "Properties for configuring UV transforms." + "id": "detailUV", + "displayName": "Detail Layer UV", + "description": "Properties for modifying detail layer UV." + }, + { + "id": "anisotropy", + "displayName": "Anisotropic Material Response", + "description": "How much is this material response anisotropic." }, { "id": "occlusion", @@ -58,25 +53,30 @@ "displayName": "Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, - { - "id": "parallax", - "displayName": "Parallax Mapping", - "description": "Properties for parallax effect produced by depthmap." - }, { "id": "subsurfaceScattering", "displayName": "Subsurface Scattering", "description": "Properties for configuring subsurface scattering effects." }, { - "id": "detailLayerGroup", - "displayName": "Detail Layer", - "description": "Properties for Fine Details Layer." + "id": "clearCoat", + "displayName": "Clear Coat", + "description": "Properties for configuring gloss clear coat" + }, + { + "id": "parallax", + "displayName": "Displacement", + "description": "Properties for parallax effect produced by a height map." }, { - "id": "detailUV", - "displayName": "Detail Layer UV", - "description": "Properties for modifying detail layer UV." + "id": "opacity", + "displayName": "Opacity", + "description": "Properties for configuring the materials transparency." + }, + { + "id": "uv", + "displayName": "UVs", + "description": "Properties for configuring UV transforms." }, { // Note: this property group is used in the DiffuseGlobalIllumination pass and not by the main forward shader @@ -86,7 +86,7 @@ }, { "id": "general", - "displayName": "General", + "displayName": "General Settings", "description": "General settings." } ], @@ -197,7 +197,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { @@ -205,10 +205,17 @@ "id": "m_baseColorMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", - "description": "Base color texture map UV set", + "description": "Base color map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -220,7 +227,7 @@ { "id": "textureBlendMode", "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture Map.", + "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], "defaultValue": "Multiply", @@ -228,13 +235,6 @@ "type": "ShaderOption", "id": "o_baseColorTextureBlendMode" } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map.", - "type": "Bool", - "defaultValue": true } ], "metallic": [ @@ -253,7 +253,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "", "type": "Image", "connection": { @@ -261,10 +261,17 @@ "id": "m_metallicMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture, or just default to the Factor value.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", - "description": "Metallic texture map UV set", + "description": "Metallic map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -272,43 +279,30 @@ "type": "ShaderInput", "id": "m_metallicMapUvIndex" } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", - "type": "Bool", - "defaultValue": true } ], "roughness": [ - { - "id": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the values", - "type": "Float", - "defaultValue": 1.0, - "min": 0.0, - "max": 1.0, - "connection": { - "type": "ShaderInput", - "id": "m_roughnessFactor" - } - }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface roughness.", + "displayName": "Texture", + "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_roughnessMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture, or just default to the Factor value.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -321,7 +315,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "lowerBound", "displayName": "Lower Bound", - "description": "The roughness value that corresponds to black in the texture map.", + "description": "The roughness value that corresponds to black in the texture.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -335,7 +329,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "upperBound", "displayName": "Upper Bound", - "description": "The roughness value that corresponds to white in the texture map.", + "description": "The roughness value that corresponds to white in the texture.", "type": "Float", "defaultValue": 1.0, "min": 0.0, @@ -346,11 +340,18 @@ } }, { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", - "type": "Bool", - "defaultValue": true + // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. + "id": "factor", + "displayName": "Factor", + "description": "Controls the roughness value", + "type": "Float", + "defaultValue": 1.0, + "min": 0.0, + "max": 1.0, + "connection": { + "type": "ShaderInput", + "id": "m_roughnessFactor" + } } ], "anisotropy": [ @@ -408,18 +409,25 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface reflectance.", + "displayName": "Texture", + "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_specularF0Map" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture, or just default to the Factor value.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", - "description": "Specular reflection texture map UV set", + "description": "Specular reflection map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -428,13 +436,7 @@ "id": "m_specularF0MapUvIndex" } }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", - "type": "Bool", - "defaultValue": true - }, + // Consider moving this to the "general" group to be consistent with StandardMultilayerPBR { "id": "enableMultiScatterCompensation", "displayName": "Multiscattering Compensation", @@ -452,11 +454,7 @@ "displayName": "Enable", "description": "Enable clear coat", "type": "Bool", - "defaultValue": false, - "connection": { - "type": "ShaderOption", - "id": "o_clearCoat_feature_enabled" - } + "defaultValue": false }, { "id": "factor", @@ -474,17 +472,24 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Strength factor texture map", + "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_clearCoatInfluenceMap" } }, + { + "id": "useInfluenceMap", + "displayName": " Use Texture", + "description": "Whether to use the texture, or just default to the Factor value.", + "type": "Bool", + "defaultValue": true + }, { "id": "influenceMapUv", "displayName": " UV", - "description": "Strength factor texture map UV set", + "description": "Strength factor map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -493,13 +498,6 @@ "id": "m_clearCoatInfluenceMapUvIndex" } }, - { - "id": "useInfluenceMap", - "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", - "type": "Bool", - "defaultValue": true - }, { "id": "roughness", "displayName": "Roughness", @@ -516,17 +514,24 @@ { "id": "roughnessMap", "displayName": " Roughness Map", - "description": "Roughness texture map", + "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_clearCoatRoughnessMap" } }, + { + "id": "useRoughnessMap", + "displayName": " Use Texture", + "description": "Whether to use the texture, or just default to the roughness value.", + "type": "Bool", + "defaultValue": true + }, { "id": "roughnessMapUv", "displayName": " UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -535,13 +540,6 @@ "id": "m_clearCoatRoughnessMapUvIndex" } }, - { - "id": "useRoughnessMap", - "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the roughness value.", - "type": "Bool", - "defaultValue": true - }, { "id": "normalStrength", "displayName": "Normal Strength", @@ -565,10 +563,17 @@ "id": "m_clearCoatNormalMap" } }, + { + "id": "useNormalMap", + "displayName": " Use Texture", + "description": "Whether to use the normal map", + "type": "Bool", + "defaultValue": true + }, { "id": "normalMapUv", - "displayName": "UV", - "description": "Normal texture map UV set", + "displayName": " UV", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -576,43 +581,30 @@ "type": "ShaderInput", "id": "m_clearCoatNormalMapUvIndex" } - }, - { - "id": "useNormalMap", - "displayName": "Use Texture", - "description": "Whether to use the normal map", - "type": "Bool", - "defaultValue": true } ], "normal": [ - { - "id": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the values", - "type": "Float", - "defaultValue": 1.0, - "min": 0.0, - "softMax": 2.0, - "connection": { - "type": "ShaderInput", - "id": "m_normalFactor" - } - }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface normal direction.", + "displayName": "Texture", + "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_normalMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture, or just rely on vertex normals.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -621,13 +613,6 @@ "id": "m_normalMapUvIndex" } }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", - "type": "Bool", - "defaultValue": true - }, { "id": "flipX", "displayName": "Flip X Channel", @@ -649,13 +634,26 @@ "type": "ShaderInput", "id": "m_flipNormalY" } + }, + { + "id": "factor", + "displayName": "Factor", + "description": "Strength factor for scaling the values", + "type": "Float", + "defaultValue": 1.0, + "min": 0.0, + "softMax": 2.0, + "connection": { + "type": "ShaderInput", + "id": "m_normalFactor" + } } ], "opacity": [ { "id": "mode", "displayName": "Opacity Mode", - "description": "Opacity mode for this texture.", + "description": "Indicates the general approach how transparency is to be applied.", "type": "Enum", "enumValues": [ "Opaque", "Cutout", "Blended", "TintedTransparent" ], "defaultValue": "Opaque", @@ -667,7 +665,7 @@ { "id": "alphaSource", "displayName": "Alpha Source", - "description": "Source texture of alpha value.", + "description": "Indicates whether to get the opacity texture from the Base Color map (Packed) or from a separate greyscale texture (Split).", "type": "Enum", "enumValues": [ "Packed", "Split", "None" ], "defaultValue": "Packed", @@ -678,8 +676,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface opacity.", + "displayName": "Texture", + "description": "Texture for defining surface opacity.", "type": "Image", "connection": { "type": "ShaderInput", @@ -689,7 +687,7 @@ { "id": "textureMapUv", "displayName": "UV", - "description": "Opacity texture map UV set", + "description": "Opacity map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -725,7 +723,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -784,7 +782,7 @@ { "id": "diffuseTextureMap", "displayName": "Diffuse AO", - "description": "Texture map for defining occlusion area for diffuse ambient lighting.", + "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -794,14 +792,14 @@ { "id": "diffuseUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Diffuse AO texture map.", + "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { "id": "diffuseTextureMapUv", "displayName": " UV", - "description": "Diffuse AO texture map UV set.", + "description": "Diffuse AO map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -826,7 +824,7 @@ { "id": "specularTextureMap", "displayName": "Specular Cavity", - "description": "Texture map for defining occlusion area for specular lighting.", + "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -836,14 +834,14 @@ { "id": "specularUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Specular Cavity texture map.", + "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { "id": "specularTextureMapUv", "displayName": " UV", - "description": "Specular Cavity texture map UV set.", + "description": "Specular Cavity map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -872,18 +870,14 @@ "displayName": "Enable", "description": "Enable the emissive group", "type": "Bool", - "defaultValue": false, - "connection": { - "type": "ShaderOption", - "id": "o_emissiveEnabled" - } + "defaultValue": false }, { "id": "unit", "displayName": "Units", "description": "The photometric units of the Intensity property.", "type": "Enum", - "enumValues": [ "Ev100" ], + "enumValues": ["Ev100"], "defaultValue": "Ev100" }, { @@ -910,59 +904,59 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining emissive area.", + "displayName": "Texture", + "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_emissiveMap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", - "description": "Emissive texture map UV set", + "description": "Emissive map UV set", "type": "Enum", - "enumValues": [ "UV0", "UV1" ], - "defaultValue": "UV0", + "enumIsUv": true, + "defaultValue": "Tiled", "connection": { "type": "ShaderInput", "id": "m_emissiveMapUvIndex" } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map.", - "type": "Bool", - "defaultValue": true } ], "parallax": [ - { - "id": "enable", - "displayName": "Enable", - "description": "Whether to enable the parallax feature.", - "type": "Bool", - "defaultValue": false - }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Depthmap to create parallax effect.", + "displayName": "Height Map", + "description": "Displacement height map to create parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_depthMap" + "id": "m_heightmap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the height map.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", - "description": "Depth texture map UV set", + "description": "Height map UV set", "type": "Enum", - "enumValues": [ "UV0", "UV1" ], - "defaultValue": "UV0", + "enumIsUv": true, + "defaultValue": "Tiled", "connection": { "type": "ShaderInput", "id": "m_parallaxUvIndex" @@ -970,15 +964,15 @@ }, { "id": "factor", - "displayName": "Heightmap Scale", - "description": "The total height of the heightmap in local model units.", + "displayName": "Height Map Scale", + "description": "The total height of the height map in local model units.", "type": "Float", - "defaultValue": 0.0, + "defaultValue": 0.05, "min": 0.0, "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_depthFactor" + "id": "m_heightmapScale" } }, { @@ -991,18 +985,7 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_depthOffset" - } - }, - { - "id": "invert", - "displayName": "Invert", - "description": "Invert to depthmap if the texture is heightmap", - "type": "Bool", - "defaultValue": true, - "connection": { - "type": "ShaderInput", - "id": "m_depthInverted" + "id": "m_heightmapOffset" } }, { @@ -1011,7 +994,7 @@ "description": "Select the algorithm to use for parallax mapping.", "type": "Enum", "enumValues": [ "Basic", "Steep", "POM", "Relief", "ContactRefinement" ], - "defaultValue": "Basic", + "defaultValue": "POM", "connection": { "type": "ShaderOption", "id": "o_parallax_algorithm" @@ -1043,7 +1026,7 @@ { "id": "showClipping", "displayName": "Show Clipping", - "description": "Highlight areas where the heightmap is clipped by the mesh surface.", + "description": "Highlight areas where the height map is clipped by the mesh surface.", "type": "Bool", "defaultValue": false, "connection": { @@ -1055,7 +1038,7 @@ "subsurfaceScattering": [ { "id": "enableSubsurfaceScattering", - "displayName": "Enable Subsurface Scattering", + "displayName": "Subsurface Scattering", "description": "Enable subsurface scattering feature, this will disable metallic and parallax mapping property due to incompatibility", "type": "Bool", "defaultValue": false, @@ -1069,7 +1052,7 @@ "displayName": " Factor", "description": "Strength factor for scaling percentage of subsurface scattering effect applied", "type": "float", - "defaultValue": 0.0, + "defaultValue": 1.0, "min": 0.0, "max": 1.0, "connection": { @@ -1080,32 +1063,32 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Use texture map to control the strength of subsurface scattering", + "description": "Texture for controlling the strength of subsurface scattering", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_subsurfaceScatteringInfluenceMap" } }, + { + "id": "useInfluenceMap", + "displayName": " Use Influence Map", + "description": "Whether to use the influence map.", + "type": "Bool", + "defaultValue": true + }, { "id": "influenceMapUv", "displayName": " UV", "description": "Influence map UV set", "type": "Enum", - "enumValues": [ "UV0", "UV1" ], - "defaultValue": "UV0", + "enumIsUv": true, + "defaultValue": "Tiled", "connection": { "type": "ShaderInput", "id": "m_subsurfaceScatteringInfluenceMapUvIndex" } }, - { - "id": "useInfluenceMap", - "displayName": " Use Influence Map", - "description": "Whether to use the texture map as influence mask.", - "type": "Bool", - "defaultValue": true - }, { "id": "scatterColor", "displayName": " Scatter color", @@ -1136,11 +1119,16 @@ } }, { - "id": "enableTransmission", - "displayName": "Enable Transmission", - "description": "Enable transmission feature", - "type": "Bool", - "defaultValue": false + "id": "transmissionMode", + "displayName": "Transmission", + "description": "Algorithm used for calculating transmission", + "type": "Enum", + "enumValues": [ "None", "ThickObject", "ThinObject" ], + "defaultValue": "None", + "connection": { + "type": "ShaderOption", + "id": "o_transmission_mode" + } }, { "id": "thickness", @@ -1154,51 +1142,39 @@ { "id": "thicknessMap", "displayName": " Thickness Map", - "description": "Use a greyscale texture for per pixel thickness", + "description": "Texture for controlling per pixel thickness", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_transmissionThicknessMap" } }, + { + "id": "useThicknessMap", + "displayName": " Use Thickness Map", + "description": "Whether to use the thickness map", + "type": "Bool", + "defaultValue": true + }, { "id": "thicknessMapUv", "displayName": " UV", "description": "Thickness map UV set", "type": "Enum", - "enumValues": [ "UV0", "UV1" ], - "defaultValue": "UV0", + "enumIsUv": true, + "defaultValue": "Tiled", "connection": { "type": "ShaderInput", "id": "m_transmissionThicknessMapUvIndex" } }, - { - "id": "useThicknessMap", - "displayName": " Use Thickness Map", - "description": "Whether to use the thickness map", - "type": "Bool", - "defaultValue": true - }, { "id": "transmissionTint", "displayName": " Transmission Tint", - "description": "Color of the volume light travelling through", + "description": "Color of the volume light traveling through", "type": "Color", "defaultValue": [ 1.0, 0.8, 0.6 ] }, - { - "id": "transmissionMode", - "displayName": " Mode", - "description": "Algorithm used for calculating transmission", - "type": "Enum", - "enumValues": [ "None", "ThickObject", "ThinObject" ], - "defaultValue": "None", - "connection": { - "type": "ShaderOption", - "id": "o_transmission_mode" - } - }, { "id": "transmissionPower", "displayName": " Power", @@ -1270,7 +1246,7 @@ { "id": "enableDetailMaskTexture", "displayName": " Use Texture", - "description": "Enable detail mask texture", + "description": "Enable detail blend mask", "type": "Bool", "defaultValue": true }, @@ -1287,9 +1263,9 @@ } }, { - "id": "detailMapsMapUv", + "id": "textureMapUv", "displayName": "Detail Map UVs", - "description": "Which UV set to use for detail map texture sampling", + "description": "Which UV set to use for detail map sampling", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1307,8 +1283,8 @@ }, { "id": "baseColorDetailMap", - "displayName": " Texture Map", - "description": "Detailed Base Color Texture map", + "displayName": " Texture", + "description": "Detailed Base Color Texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -1331,7 +1307,7 @@ { "id": "enableNormals", "displayName": "Enable Normal", - "description": "Enable detail normal texture to be used for fine detail normal such as scratches and small dents", + "description": "Enable detail normal map to be used for fine detail normal such as scratches and small dents", "type": "Bool", "defaultValue": false }, @@ -1350,8 +1326,8 @@ }, { "id": "normalDetailMap", - "displayName": " Texture Map", - "description": "Detailed Normal Texture map", + "displayName": " Texture", + "description": "Detailed Normal map", "type": "Image", "connection": { "type": "ShaderInput", @@ -1388,7 +1364,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -1506,7 +1482,7 @@ { "file": "Shaders/Depth/DepthPassTransparentMax.shader", "tag": "DepthPassTransparentMax" - } + } ], "functors": [ { @@ -1549,26 +1525,9 @@ "lightUnitProperty": "emissive.unit", "shaderInput": "m_emissiveIntensity", "ev100Index": 0, - "nitIndex": 1, - "ev100MinMax": [ -10, 20 ], - "nitMinMax": [ 0.001, 100000.0 ] - } - }, - { - // Enable/Disable shader based on different option. - "type": "ShaderEnable", - "args": { - "opacityMode": "opacity.mode", - "parallaxEnable": "parallax.enable", - "parallaxPdoEnable": "parallax.pdo", - "pbrShaderNoEdsIndex": 0, - "pbrShaderWithEdsIndex": 1, - "shadowShaderNoPSIndex": 2, - "shadowShaderWithPSIndex": 3, - "depthShaderNoPSIndex": 4, - "depthShaderWithPSIndex": 5, - "depthShaderTransparentMin": 8, - "depthShaderTransparentMax": 9 + "nitIndex" : 1, + "ev100MinMax": [-10, 20], + "nitMinMax": [0.001, 100000.0] } }, { @@ -1590,120 +1549,40 @@ "tintThickenssShaderInput": "m_transmissionTintThickness" } }, - { - // Reads material properties to determine whether a specific texture map should be sampled at runtime, and sets a shader option accordingly. - // @param textureProperty - which material property contains the texture asset reference (or maybe null) - // @param useTextureProperty - a boolean flag that toggles whether the texture should be sampled (if it's not null) - // @param shaderTags - which shader in the 'shaders' list above is configured by this functor - // @param shaderOption - the name of a shader option in the AZSL file that controls sampling of this texture + { "type": "UseTexture", "args": { "textureProperty": "baseColor.textureMap", - "dependentProperties": ["baseColor.textureMapUv"], "useTextureProperty": "baseColor.useTexture", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], + "dependentProperties": ["baseColor.textureMapUv", "baseColor.textureBlendMode"], "shaderOption": "o_baseColor_useTexture" } }, { - // See the comment above for details. "type": "UseTexture", "args": { "textureProperty": "metallic.textureMap", - "dependentProperties": ["metallic.textureMapUv"], "useTextureProperty": "metallic.useTexture", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], + "dependentProperties": ["metallic.textureMapUv"], "shaderOption": "o_metallic_useTexture" } }, { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "roughness.textureMap", - "dependentProperties": ["roughness.textureMapUv"], - "useTextureProperty": "roughness.useTexture", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_roughness_useTexture" - } - }, - { - // See the comment above for details. "type": "UseTexture", "args": { "textureProperty": "specularF0.textureMap", - "dependentProperties": ["specularF0.textureMapUv"], "useTextureProperty": "specularF0.useTexture", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], + "dependentProperties": ["specularF0.textureMapUv"], "shaderOption": "o_specularF0_useTexture" } }, { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "clearCoat.influenceMap", - "dependentProperties": ["clearCoat.influenceMapUv"], - "useTextureProperty": "clearCoat.useInfluenceMap", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_clearCoat_factor_useTexture" - } - }, - { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "clearCoat.roughnessMap", - "dependentProperties": ["clearCoat.roughnessMapUv"], - "useTextureProperty": "clearCoat.useRoughnessMap", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_clearCoat_roughness_useTexture" - } - }, - { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "clearCoat.normalMap", - "dependentProperties": ["clearCoat.normalMapUv"], - "useTextureProperty": "clearCoat.useNormalMap", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_clearCoat_normal_useTexture" - } - }, - { - // See the comment above for details. "type": "UseTexture", "args": { "textureProperty": "normal.textureMap", - "dependentProperties": ["normal.textureMapUv"], "useTextureProperty": "normal.useTexture", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_normal_useTexture" + "dependentProperties": ["normal.textureMapUv", "normal.factor", "normal.flipX", "normal.flipY"], + "shaderOption": "o_normal_useTexture" } }, { @@ -1725,341 +1604,39 @@ } }, { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "emissive.textureMap", - "dependentProperties": ["emissive.textureMapUv"], - "useTextureProperty": "emissive.useTexture", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_emissive_useTexture" - } - }, - { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "subsurfaceScattering.influenceMap", - "dependentProperties": ["subsurfaceScattering.influenceMapUv"], - "useTextureProperty": "subsurfaceScattering.useInfluenceMap", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_subsurfaceScattering_useTexture" - } - }, - { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "subsurfaceScattering.thicknessMap", - "dependentProperties": ["subsurfaceScattering.thicknessMapUv"], - "useTextureProperty": "subsurfaceScattering.useThicknessMap", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], - "shaderOption": "o_transmission_useTexture" - } - }, - { - // Controls visibility for properties in the editor. - // @param actions - a list of actions that are executed in order. visibility will be set when triggerProperty hits the triggerValue. - // @param affectedProperties - the properties that are affected by actions. - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "emissive.enable", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "emissive.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "emissive.color", - "emissive.intensity", - "emissive.useTexture", - "emissive.unit" - ] - } - }, - { - // Controls visibility for properties in the editor. - // @param actions - a list of actions that are executed in order. visibility will be set when triggerProperty hits the triggerValue. - // @param affectedProperties - the properties that are affected by actions. - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "emissive.useTexture", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "emissive.useTexture", - "triggerValue": false, - "visibility": "Disabled" - }, - { - "triggerProperty": "emissive.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "emissive.textureMap", - "emissive.textureMapUv" - ] - } - }, - { - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "clearCoat.useInfluenceMap", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "clearCoat.useInfluenceMap", - "triggerValue": false, - "visibility": "Disabled" - }, - { - "triggerProperty": "clearCoat.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "clearCoat.influenceMap", - "clearCoat.influenceMapUv" - ] - } - }, - { - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "clearCoat.useRoughnessMap", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "clearCoat.useRoughnessMap", - "triggerValue": false, - "visibility": "Disabled" - }, - { - "triggerProperty": "clearCoat.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "clearCoat.roughnessMap", - "clearCoat.roughnessMapUv" - ] - } - }, - { - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "clearCoat.useNormalMap", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "clearCoat.useNormalMap", - "triggerValue": false, - "visibility": "Disabled" - }, - { - "triggerProperty": "clearCoat.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "clearCoat.normalMap", - "clearCoat.normalMapUv" - ] - } - - }, - { - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "subsurfaceScattering.useInfluenceMap", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "subsurfaceScattering.useInfluenceMap", - "triggerValue": false, - "visibility": "Disabled" - }, - { - "triggerProperty": "subsurfaceScattering.enableSubsurfaceScattering", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "subsurfaceScattering.influenceMap", - "subsurfaceScattering.influenceMapUv" - ] - } - }, - { - "type": "UpdatePropertyVisibility", + "type": "Lua", "args": { - "actions": [ - { - "triggerProperty": "subsurfaceScattering.useThicknessMap", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "subsurfaceScattering.useThicknessMap", - "triggerValue": false, - "visibility": "Disabled" - }, - { - "triggerProperty": "subsurfaceScattering.enableTransmission", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "subsurfaceScattering.thicknessMap", - "subsurfaceScattering.thicknessMapUv" - ] + "file": "StandardPBR_ClearCoatState.lua" } }, { - "type": "UpdatePropertyVisibility", + "type": "Lua", "args": { - "actions": [ - { - "triggerProperty": "clearCoat.enable", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "clearCoat.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "clearCoat.factor", - "clearCoat.useInfluenceMap", - "clearCoat.roughness", - "clearCoat.useRoughnessMap", - "clearCoat.useNormalMap" - ] + "file": "StandardPBR_ClearCoatEnableFeature.lua" } }, { - "type": "UpdatePropertyVisibility", + "type": "Lua", "args": { - "actions": [ - { - "triggerProperty": "subsurfaceScattering.transmissionMode", - "triggerValue": "ThickObject", - "visibility": "Enabled" - }, - { - "triggerProperty": "subsurfaceScattering.transmissionMode", - "triggerValue": "None", - "visibility": "Hidden" - }, - { - "triggerProperty": "subsurfaceScattering.transmissionMode", - "triggerValue": "ThinObject", - "visibility": "Hidden" - }, - { - "triggerProperty": "subsurfaceScattering.enableTransmission", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "subsurfaceScattering.transmissionPower", - "subsurfaceScattering.transmissionDistortion", - "subsurfaceScattering.transmissionAttenuation" - ] + "file": "StandardPBR_EmissiveState.lua" } }, { - "type": "UpdatePropertyVisibility", + "type": "Lua", "args": { - "actions": [ - { - "triggerProperty": "subsurfaceScattering.enableSubsurfaceScattering", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "subsurfaceScattering.enableSubsurfaceScattering", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "subsurfaceScattering.subsurfaceScatterFactor", - "subsurfaceScattering.useInfluenceMap", - "subsurfaceScattering.scatterColor", - "subsurfaceScattering.scatterDistance", - "subsurfaceScattering.quality" - ] + "file": "StandardPBR_ParallaxState.lua" } }, { - "type": "UpdatePropertyVisibility", + "type": "Lua", "args": { - "actions": [ - { - "triggerProperty": "subsurfaceScattering.enableTransmission", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "subsurfaceScattering.enableTransmission", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "subsurfaceScattering.thickness", - "subsurfaceScattering.useThicknessMap", - "subsurfaceScattering.transmissionTint", - "subsurfaceScattering.transmissionMode", - "subsurfaceScattering.transmissionScale" - ] + "file": "StandardPBR_Roughness.lua" } }, { "type": "Lua", "args": { - "file": "StandardPBR_ParallaxState.lua" + "file": "EnhancedPBR_SubsurfaceState.lua" } }, { @@ -2069,33 +1646,21 @@ } }, { - "type": "OverrideDrawList", - "args": { - "triggerProperty": "opacity.mode", - "triggerValue": "Blended", - "shaderIndex": 1, - "drawList": "transparent" - } - }, - { - "type": "OverrideDrawList", + "type": "Lua", "args": { - "triggerProperty": "opacity.mode", - "triggerValue": "TintedTransparent", - "shaderIndex": 1, - "drawList": "transparent" + "file": "StandardPBR_HandleOpacityMode.lua" } }, { "type": "Lua", "args": { - "file": "StandardPBR_HandleOpacityMode.lua" + "file": "MaterialInputs/DetailMapsCommonFunctor.lua" } }, { "type": "Lua", "args": { - "file": "MaterialInputs/DetailMapsCommonFunctor.lua" + "file": "StandardPBR_ShaderEnable.lua" } } ], @@ -2104,3 +1669,4 @@ "UV1": "Unwrapped" } } + diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli index 34af9229c2..b6d6439268 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli @@ -108,7 +108,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial // Callback function for ParallaxMapping.azsli DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) { - return SampleDepthOrHeightMap(MaterialSrg::m_depthInverted, MaterialSrg::m_depthMap, MaterialSrg::m_sampler, uv, uv_ddx, uv_ddy); + return SampleDepthFromHeightmap(MaterialSrg::m_heightmap, MaterialSrg::m_sampler, uv, uv_ddx, uv_ddy); } COMMON_OPTIONS_PARALLAX() @@ -116,7 +116,7 @@ COMMON_OPTIONS_PARALLAX() bool ShouldHandleParallax() { // Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scattering is enabled. - return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap; + return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useHeightmap; } bool ShouldHandleParallaxInDepthShaders() diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl index db99ee7e24..d70e3b899a 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl @@ -77,15 +77,14 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) if(ShouldHandleParallaxInDepthShaders()) { - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset, + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_heightmapScale, MaterialSrg::m_heightmapOffset, ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth); } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl index a91e88afad..a8b4075d51 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl @@ -117,19 +117,12 @@ VSOutput EnhancedPbr_ForwardPassVS(VSInput IN) PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float depth) { // ------- Tangents & Bitangets ------- + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - - if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering && MaterialSrg::m_parallaxUvIndex != 0) - || (o_normal_useTexture && MaterialSrg::m_normalMapUvIndex != 0) - || (o_clearCoat_enabled && o_clearCoat_normal_useTexture && MaterialSrg::m_clearCoatNormalMapUvIndex != 0) - || (o_detail_normal_useTexture && MaterialSrg::m_detail_allMapsUvIndex != 0)) + if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering) || o_normal_useTexture || (o_clearCoat_enabled && o_clearCoat_normal_useTexture) || o_detail_normal_useTexture) { - // Generate the tangent/bitangent for UV[1+] - const int startIndex = 1; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, startIndex); + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } // ------- Depth & Parallax ------- @@ -148,7 +141,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset, + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_heightmapScale, MaterialSrg::m_heightmapOffset, ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth, IN.m_position.w, displacementIsClipped); @@ -260,7 +253,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // Convert the angle from [0..1] = [0 .. 180 degrees] to radians [0 .. PI] const float anisotropyAngle = MaterialSrg::m_anisotropicAngle * PI; const float anisotropyFactor = MaterialSrg::m_anisotropicFactor; - surface.anisotropy.Init(surface.normal, tangents[0], bitangents[0], anisotropyAngle, anisotropyFactor, surface.roughnessA); + surface.anisotropy.Init(surface.normal, IN.m_tangent, IN.m_bitangent, anisotropyAngle, anisotropyFactor, surface.roughnessA); } // ------- Lighting Data ------- @@ -274,16 +267,16 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // Directional light shadow coordinates lightingData.shadowCoords = IN.m_shadowCoords; - // ------- Occlusion ------- - - lightingData.diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_diffuseOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_diffuseOcclusionMapUvIndex], MaterialSrg::m_diffuseOcclusionFactor, o_diffuseOcclusion_useTexture); - lightingData.specularOcclusion = GetOcclusionInput(MaterialSrg::m_specularOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_specularOcclusionMapUvIndex], MaterialSrg::m_specularOcclusionFactor, o_specularOcclusion_useTexture); - // ------- Emissive ------- float2 emissiveUv = IN.m_uv[MaterialSrg::m_emissiveMapUvIndex]; lightingData.emissiveLighting = GetEmissiveInput(MaterialSrg::m_emissiveMap, MaterialSrg::m_sampler, emissiveUv, MaterialSrg::m_emissiveIntensity, MaterialSrg::m_emissiveColor.rgb, o_emissiveEnabled, o_emissive_useTexture); + // ------- Occlusion ------- + + lightingData.diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_diffuseOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_diffuseOcclusionMapUvIndex], MaterialSrg::m_diffuseOcclusionFactor, o_diffuseOcclusion_useTexture); + lightingData.specularOcclusion = GetOcclusionInput(MaterialSrg::m_specularOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_specularOcclusionMapUvIndex], MaterialSrg::m_specularOcclusionFactor, o_specularOcclusion_useTexture); + // ------- Clearcoat ------- // [GFX TODO][ATOM-14603]: Clean up the double uses of these clear coat flags diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl index 80aedcd6f4..6d3d4f2ea5 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl @@ -80,15 +80,15 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) { static const float ShadowMapDepthBias = 0.000001; - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset, + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_heightmapScale, MaterialSrg::m_heightmapOffset, ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_SubsurfaceState.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_SubsurfaceState.lua similarity index 100% rename from Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_SubsurfaceState.lua rename to Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_SubsurfaceState.lua diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/MaterialInputs/ParallaxInput.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/MaterialInputs/ParallaxInput.azsli index ffd7c18045..84d4bfcc02 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/MaterialInputs/ParallaxInput.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/MaterialInputs/ParallaxInput.azsli @@ -22,15 +22,14 @@ // You can optionally provide a prefix for the set of inputs which corresponds to a prefix string supplied by the .materialtype file. This is common for multi-layered material types. #define COMMON_SRG_INPUTS_PARALLAX(prefix) \ -Texture2D prefix##m_depthMap; \ -float prefix##m_depthFactor; \ -float prefix##m_depthOffset; \ -bool prefix##m_depthInverted; +Texture2D prefix##m_heightmap; \ +float prefix##m_heightmapScale; \ +float prefix##m_heightmapOffset; #define COMMON_OPTIONS_PARALLAX(prefix) \ -option bool prefix##o_useDepthMap; +option bool prefix##o_useHeightmap; -void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float depthFactor, float depthOffset, +void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float heightmapScale, float heightmapOffset, float4x4 objectWorldMatrix, float3x3 uvMatrix, float3x3 uvMatrixInverse, inout float2 uv, inout float3 worldPosition, inout float depthNDC, inout float depthCS, out bool isClipped) { @@ -48,8 +47,8 @@ void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float dep dirToCamera = ViewSrg::m_worldPosition.xyz - worldPosition; } - ParallaxOffset tangentOffset = GetParallaxOffset( depthFactor, - depthOffset, + ParallaxOffset tangentOffset = GetParallaxOffset( heightmapScale, + -heightmapOffset, uv, dirToCamera, tangent, @@ -62,7 +61,7 @@ void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float dep if(o_parallax_enablePixelDepthOffset) { - PixelDepthOffset pdo = CalcPixelDepthOffset(depthFactor, + PixelDepthOffset pdo = CalcPixelDepthOffset(heightmapScale, tangentOffset.m_offsetTS, worldPosition, tangent, @@ -81,19 +80,19 @@ void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float dep } } -void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float depthFactor, float depthOffset, +void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float heightmapScale, float heightmapOffset, float4x4 objectWorldMatrix, float3x3 uvMatrix, float3x3 uvMatrixInverse, inout float2 uv, inout float3 worldPosition, inout float depthNDC, inout float depthCS) { bool isClipped; - GetParallaxInput(normal, tangent, bitangent, depthFactor, depthOffset, objectWorldMatrix, uvMatrix, uvMatrixInverse, uv, worldPosition, depthNDC, depthCS, isClipped); + GetParallaxInput(normal, tangent, bitangent, heightmapScale, heightmapOffset, objectWorldMatrix, uvMatrix, uvMatrixInverse, uv, worldPosition, depthNDC, depthCS, isClipped); } -void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float depthFactor, float depthOffset, +void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float heightmapScale, float heightmapOffset, float4x4 objectWorldMatrix, float3x3 uvMatrix, float3x3 uvMatrixInverse, inout float2 uv, inout float3 worldPosition, inout float depthNDC) { float depthCS; - GetParallaxInput(normal, tangent, bitangent, depthFactor, depthOffset, objectWorldMatrix, uvMatrix, uvMatrixInverse, uv, worldPosition, depthNDC, depthCS); + GetParallaxInput(normal, tangent, bitangent, heightmapScale, heightmapOffset, objectWorldMatrix, uvMatrix, uvMatrixInverse, uv, worldPosition, depthNDC, depthCS); } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl index 456d7cbabe..4fe1f2b364 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl @@ -180,17 +180,12 @@ PbrLightingOutput SkinPS_Common(VSOutput IN) float3x3 uvMatrix = CreateIdentity3x3(); // ------- Tangents & Bitangets ------- + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - - if ( (o_normal_useTexture && MaterialSrg::m_normalMapUvIndex != 0) || - (o_detail_normal_useTexture && MaterialSrg::m_detail_allMapsUvIndex != 0)) + if (o_normal_useTexture || o_detail_normal_useTexture) { - // Generate the tangent/bitangent for UV[1+] - const int startIndex = 1; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, startIndex); + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } Surface surface; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype index 101a03b907..dfe2fad60f 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype @@ -23,6 +23,16 @@ "displayName": "Normal", "description": "Properties related to configuring surface normal." }, + { + "id": "detailLayerGroup", + "displayName": "Detail Layer", + "description": "Properties for Fine Details Layer." + }, + { + "id": "detailUV", + "displayName": "Detail Layer UV", + "description": "Properties for modifying detail layer UV." + }, { "id": "occlusion", "displayName": "Occlusion", @@ -38,19 +48,9 @@ "displayName": "Wrinkle Layers", "description": "Properties for wrinkle maps to support morph animation, using vertex color blend weights." }, - { - "id": "detailLayerGroup", - "displayName": "Detail Layer", - "description": "Properties for Fine Details Layer." - }, - { - "id": "detailUV", - "displayName": "Detail Layer UV", - "description": "Properties for modifying detail layer UV." - }, { "id": "general", - "displayName": "General", + "displayName": "General Settings", "description": "General settings." } ], @@ -150,7 +150,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { @@ -161,14 +161,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Base color texture map UV set", + "description": "Base color map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Unwrapped", @@ -180,7 +180,7 @@ { "id": "textureBlendMode", "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture Map.", + "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], "defaultValue": "Multiply", @@ -193,8 +193,8 @@ "roughness": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface roughness.", + "displayName": "Texture", + "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", @@ -204,14 +204,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Unwrapped", @@ -224,7 +224,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "lowerBound", "displayName": "Lower Bound", - "description": "The roughness value that corresponds to black in the texture map.", + "description": "The roughness value that corresponds to black in the texture.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -238,7 +238,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "upperBound", "displayName": "Upper Bound", - "description": "The roughness value that corresponds to white in the texture map.", + "description": "The roughness value that corresponds to white in the texture.", "type": "Float", "defaultValue": 1.0, "min": 0.0, @@ -279,18 +279,25 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface reflectance.", + "displayName": "Texture", + "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_specularF0Map" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the texture, or just default to the Factor value.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", - "description": "Specular reflection texture map UV set", + "description": "Specular reflection map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Unwrapped", @@ -299,13 +306,7 @@ "id": "m_specularF0MapUvIndex" } }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", - "type": "Bool", - "defaultValue": true - }, + // Consider moving this to the "general" group to be consistent with StandardMultilayerPBR { "id": "enableMultiScatterCompensation", "displayName": "Multiscattering Compensation", @@ -320,8 +321,8 @@ "normal": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface normal direction.", + "displayName": "Texture", + "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", @@ -331,14 +332,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", + "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Unwrapped", @@ -387,7 +388,7 @@ { "id": "diffuseTextureMap", "displayName": "Diffuse AO", - "description": "Texture map for defining occlusion area for diffuse ambient lighting.", + "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -397,14 +398,14 @@ { "id": "diffuseUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Diffuse AO texture map.", + "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { "id": "diffuseTextureMapUv", "displayName": " UV", - "description": "Diffuse AO texture map UV set.", + "description": "Diffuse AO map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -429,7 +430,7 @@ { "id": "specularTextureMap", "displayName": "Specular Cavity", - "description": "Texture map for defining occlusion area for specular lighting.", + "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -439,14 +440,14 @@ { "id": "specularUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Specular Cavity texture map.", + "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { "id": "specularTextureMapUv", "displayName": " UV", - "description": "Specular Cavity texture map UV set.", + "description": "Specular Cavity map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -497,7 +498,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Use texture map to control the strength of subsurface scattering", + "description": "Texture for controlling the strength of subsurface scattering", "type": "Image", "connection": { "type": "ShaderInput", @@ -507,7 +508,7 @@ { "id": "useInfluenceMap", "displayName": " Use Influence Map", - "description": "Whether to use the texture map as influence mask.", + "description": "Whether to use the influence map.", "type": "Bool", "defaultValue": true }, @@ -576,7 +577,7 @@ { "id": "thicknessMap", "displayName": " Thickness Map", - "description": "Use a greyscale texture for per pixel thickness", + "description": "Texture for controlling per pixel thickness", "type": "Image", "connection": { "type": "ShaderInput", @@ -605,7 +606,7 @@ { "id": "transmissionTint", "displayName": " Transmission Tint", - "description": "Color of the volume light travelling through", + "description": "Color of the volume light traveling through", "type": "Color", "defaultValue": [ 1.0, 0.8, 0.6 ] }, @@ -616,7 +617,7 @@ "type": "float", "defaultValue": 6.0, "min": 0.0, - "softMax": 20.0 + "softMax": 20.0 }, { "id": "transmissionDistortion", @@ -791,7 +792,7 @@ { "id": "enableDetailMaskTexture", "displayName": " Use Texture", - "description": "Enable detail mask texture", + "description": "Enable detail blend mask", "type": "Bool", "defaultValue": true }, @@ -810,7 +811,7 @@ { "id": "textureMapUv", "displayName": "Detail Map UVs", - "description": "Which UV set to use for detail map texture sampling", + "description": "Which UV set to use for detail map sampling", "type": "Enum", "enumIsUv": true, "defaultValue": "Unwrapped", @@ -828,8 +829,8 @@ }, { "id": "baseColorDetailMap", - "displayName": " Texture Map", - "description": "Detailed Base Color Texture map", + "displayName": " Texture", + "description": "Detailed Base Color Texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -852,7 +853,7 @@ { "id": "enableNormals", "displayName": "Enable Normal", - "description": "Enable detail normal texture to be used for fine detail normal such as scratches and small dents", + "description": "Enable detail normal map to be used for fine detail normal such as scratches and small dents", "type": "Bool", "defaultValue": false }, @@ -871,8 +872,8 @@ }, { "id": "normalDetailMap", - "displayName": " Texture Map", - "description": "Detailed Normal Texture map", + "displayName": " Texture", + "description": "Detailed Normal map", "type": "Image", "connection": { "type": "ShaderInput", @@ -909,7 +910,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -1011,18 +1012,18 @@ "type": "HandleSubsurfaceScatteringParameters", "args": { "mode": "subsurfaceScattering.transmissionMode", - "scale" : "subsurfaceScattering.transmissionScale", - "power" : "subsurfaceScattering.transmissionPower", - "distortion" : "subsurfaceScattering.transmissionDistortion", - "attenuation" : "subsurfaceScattering.transmissionAttenuation", - "tintColor" : "subsurfaceScattering.transmissionTint", - "thickness" : "subsurfaceScattering.thickness", + "scale": "subsurfaceScattering.transmissionScale", + "power": "subsurfaceScattering.transmissionPower", + "distortion": "subsurfaceScattering.transmissionDistortion", + "attenuation": "subsurfaceScattering.transmissionAttenuation", + "tintColor": "subsurfaceScattering.transmissionTint", + "thickness": "subsurfaceScattering.thickness", "enabled": "subsurfaceScattering.enableSubsurfaceScattering", - "scatterDistanceColor" : "subsurfaceScattering.scatterColor", - "scatterDistanceIntensity" : "subsurfaceScattering.scatterDistance", - "scatterDistanceShaderInput" : "m_scatterDistance", - "parametersShaderInput" : "m_transmissionParams", - "tintThickenssShaderInput" : "m_transmissionTintThickness" + "scatterDistanceColor": "subsurfaceScattering.scatterColor", + "scatterDistanceIntensity": "subsurfaceScattering.scatterDistance", + "scatterDistanceShaderInput": "m_scatterDistance", + "parametersShaderInput": "m_transmissionParams", + "tintThickenssShaderInput": "m_transmissionTintThickness" } }, { @@ -1038,8 +1039,8 @@ "type": "UseTexture", "args": { "textureProperty": "specularF0.textureMap", - "dependentProperties": ["specularF0.textureMapUv"], "useTextureProperty": "specularF0.useTexture", + "dependentProperties": ["specularF0.textureMapUv"], "shaderOption": "o_specularF0_useTexture" } }, @@ -1079,7 +1080,7 @@ { "type": "Lua", "args": { - "file": "StandardPBR_SubsurfaceState.lua" + "file": "EnhancedPBR_SubsurfaceState.lua" } }, { diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype index a2854e8902..c07eac3d47 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype @@ -3,11 +3,6 @@ "propertyLayout": { "version": 3, "groups": [ - { - "id": "general", - "displayName": "General", - "description": "General settings." - }, { "id": "blend", "displayName": "Blend Settings", @@ -29,6 +24,11 @@ "displayName": "Irradiance", "description": "Properties for configuring the irradiance used in global illumination." }, + { + "id": "general", + "displayName": "General Settings", + "description": "General settings." + }, //############################################################################################## // Layer 1 Groups //############################################################################################## @@ -57,11 +57,6 @@ "displayName": "Layer 1: Normal", "description": "Properties related to configuring surface normal." }, - { - "id": "layer1_clearCoat", - "displayName": "Layer 1: Clear Coat", - "description": "Properties for configuring gloss clear coat" - }, { "id": "layer1_occlusion", "displayName": "Layer 1: Occlusion", @@ -72,10 +67,15 @@ "displayName": "Layer 1: Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, + { + "id": "layer1_clearCoat", + "displayName": "Layer 1: Clear Coat", + "description": "Properties for configuring gloss clear coat" + }, { "id": "layer1_parallax", - "displayName": "Layer 1: Parallax Mapping", - "description": "Properties for parallax effect produced by depthmap." + "displayName": "Layer 1: Displacement", + "description": "Properties for surface displacement, which can be used for displacement-based blending and/or a parallax effect." }, { "id": "layer1_uv", @@ -110,11 +110,6 @@ "displayName": "Layer 2: Normal", "description": "Properties related to configuring surface normal." }, - { - "id": "layer2_clearCoat", - "displayName": "Layer 2: Clear Coat", - "description": "Properties for configuring gloss clear coat" - }, { "id": "layer2_occlusion", "displayName": "Layer 2: Occlusion", @@ -125,10 +120,15 @@ "displayName": "Layer 2: Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, + { + "id": "layer2_clearCoat", + "displayName": "Layer 2: Clear Coat", + "description": "Properties for configuring gloss clear coat" + }, { "id": "layer2_parallax", - "displayName": "Layer 2: Parallax Mapping", - "description": "Properties for parallax effect produced by depthmap." + "displayName": "Layer 2: Displacement", + "description": "Properties for surface displacement, which can be used for displacement-based blending and/or a parallax effect." }, { "id": "layer2_uv", @@ -163,11 +163,6 @@ "displayName": "Layer 3: Normal", "description": "Properties related to configuring surface normal." }, - { - "id": "layer3_clearCoat", - "displayName": "Layer 3: Clear Coat", - "description": "Properties for configuring gloss clear coat" - }, { "id": "layer3_occlusion", "displayName": "Layer 3: Occlusion", @@ -178,10 +173,15 @@ "displayName": "Layer 3: Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, + { + "id": "layer3_clearCoat", + "displayName": "Layer 3: Clear Coat", + "description": "Properties for configuring gloss clear coat" + }, { "id": "layer3_parallax", - "displayName": "Layer 3: Parallax Mapping", - "description": "Properties for parallax effect produced by depthmap." + "displayName": "Layer 3: Displacement", + "description": "Properties for surface displacement, which can be used for displacement-based blending and/or a parallax effect." }, { "id": "layer3_uv", @@ -194,18 +194,6 @@ // General Properties //############################################################################################## "general": [ - { - "id": "debugDrawMode", - "displayName": "Debug Draw Mode", - "description": "Enables various debug view features.", - "type": "Enum", - "enumValues": [ "None", "BlendSource", "DepthMaps" ], - "defaultValue": "None", - "connection": { - "type": "ShaderOption", - "id": "o_debugDrawMode" - } - }, { "id": "applySpecularAA", "displayName": "Apply Specular AA", @@ -322,16 +310,16 @@ "displayName": "Blend Source", "description": "The source to use for defining the blend mask. Note VertexColors mode will still use the texture as a fallback if the mesh does not have a COLOR0 stream.", "type": "Enum", - "enumValues": ["TextureMap", "VertexColors"], - "defaultValue": "TextureMap", + "enumValues": ["BlendMaskTexture", "BlendMaskVertexColors", "Displacement", "Displacement_With_BlendMaskTexture", "Displacement_With_BlendMaskVertexColors"], + "defaultValue": "BlendMaskTexture", "connection": { "type": "ShaderOption", - "id": "o_blendSource" + "id": "o_layerBlendSource" } }, { "id": "textureMap", - "displayName": "Blend Mask", + "displayName": "Blend Mask Texture", "description": "RGB image where each channel is the blend mask for one of the three available layers.", "type": "Image", "defaultValue": "Textures/DefaultBlendMask_layers.png", @@ -351,15 +339,44 @@ "type": "ShaderInput", "id": "m_blendMaskUvIndex" } + }, + { + "id": "displacementBlendDistance", + "displayName": "Blend Distance", + "description": "Adjusts how smoothly to transition between layers when displacement blending is enabled.", + "type": "Float", + "defaultValue": 0.0, + "min": 0.0, + "softMax": 0.1, + "step": 0.001, + "connection": { + "type": "ShaderInput", + "id": "m_displacementBlendDistance" + } + }, + { + "id": "debugDrawMode", + "displayName": "Debug Draw Mode", + "description": "Enables various debug view features.", + "type": "Enum", + "enumValues": [ "None", "BlendMask", "Displacement", "FinalBlendWeights" ], + "defaultValue": "None", + "connection": { + "type": "ShaderOption", + "id": "o_debugDrawMode" + } } ], "parallax": [ { + // Note parallax is enabled by default so that as soon as a user hooks up displacement settings they will see some parallax applied. + // The functor that controls parallax will set o_parallax_feature_enabled=false when all the individual layers have no displacement, so + // a default value of true here will not have any initial impact on performance. "id": "enable", "displayName": "Enable", "description": "Whether to enable the parallax feature for this material.", "type": "Bool", - "defaultValue": false + "defaultValue": true }, { "id": "parallaxUv", @@ -391,7 +408,7 @@ "description": "Quality of parallax mapping.", "type": "Enum", "enumValues": [ "Low", "Medium", "High", "Ultra" ], - "defaultValue": "Medium", + "defaultValue": "Low", "connection": { "type": "ShaderOption", "id": "o_parallax_quality" @@ -411,7 +428,7 @@ { "id": "showClipping", "displayName": "Show Clipping", - "description": "Highlight areas where the heightmap is clipped by the mesh surface.", + "description": "Highlight areas where the height map is clipped by the mesh surface.", "type": "Bool", "defaultValue": false, "connection": { @@ -427,7 +444,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -533,7 +550,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { @@ -544,14 +561,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Base color texture map UV set", + "description": "Base color map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -563,7 +580,7 @@ { "id": "textureBlendMode", "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture Map.", + "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], "defaultValue": "Multiply", @@ -589,7 +606,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "", "type": "Image", "connection": { @@ -600,14 +617,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Metallic texture map UV set", + "description": "Metallic map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -620,8 +637,8 @@ "layer1_roughness": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface roughness.", + "displayName": "Texture", + "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", @@ -631,14 +648,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -651,7 +668,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "lowerBound", "displayName": "Lower Bound", - "description": "The roughness value that corresponds to black in the texture map.", + "description": "The roughness value that corresponds to black in the texture.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -665,7 +682,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "upperBound", "displayName": "Upper Bound", - "description": "The roughness value that corresponds to white in the texture map.", + "description": "The roughness value that corresponds to white in the texture.", "type": "Float", "defaultValue": 1.0, "min": 0.0, @@ -706,8 +723,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface reflectance.", + "displayName": "Texture", + "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", @@ -717,14 +734,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Specular reflection texture map UV set", + "description": "Specular reflection map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -737,8 +754,8 @@ "layer1_normal": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface normal direction.", + "displayName": "Texture", + "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", @@ -748,14 +765,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", + "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -824,7 +841,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Strength factor texture map", + "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -834,14 +851,14 @@ { "id": "useInfluenceMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "influenceMapUv", "displayName": " UV", - "description": "Strength factor texture map UV set", + "description": "Strength factor map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -866,7 +883,7 @@ { "id": "roughnessMap", "displayName": " Roughness Map", - "description": "Roughness texture map", + "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", @@ -876,14 +893,14 @@ { "id": "useRoughnessMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the roughness value.", + "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { "id": "roughnessMapUv", "displayName": " UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -925,7 +942,7 @@ { "id": "normalMapUv", "displayName": " UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -939,7 +956,7 @@ { "id": "diffuseTextureMap", "displayName": "Diffuse AO", - "description": "Texture map for defining occlusion area for diffuse ambient lighting.", + "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -949,14 +966,14 @@ { "id": "diffuseUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Diffuse AO texture map.", + "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { "id": "diffuseTextureMapUv", "displayName": " UV", - "description": "Diffuse AO texture map UV set.", + "description": "Diffuse AO map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -981,7 +998,7 @@ { "id": "specularTextureMap", "displayName": "Specular Cavity", - "description": "Texture map for defining occlusion area for specular lighting.", + "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -991,14 +1008,14 @@ { "id": "specularUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Specular Cavity texture map.", + "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { "id": "specularTextureMapUv", "displayName": " UV", - "description": "Specular Cavity texture map UV set.", + "description": "Specular Cavity map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1061,8 +1078,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining emissive area.", + "displayName": "Texture", + "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1072,14 +1089,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Emissive texture map UV set", + "description": "Emissive map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1090,34 +1107,34 @@ } ], "layer1_parallax": [ - { - "id": "enable", - "displayName": "Enable", - "description": "Whether to enable the parallax feature.", - "type": "Bool", - "defaultValue": false - }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Depthmap to create parallax effect.", + "displayName": "Height Map", + "description": "Displacement height map, which can be used for layer blending and/or a parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer1_m_depthMap" + "id": "m_layer1_m_heightmap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the height map.", + "type": "Bool", + "defaultValue": true + }, { "id": "factor", - "displayName": "Heightmap Scale", - "description": "The total height of the heightmap in local model units.", + "displayName": "Scale", + "description": "The total height of the height map in local model units.", "type": "Float", - "defaultValue": 0.0, + "defaultValue": 0.05, "min": 0.0, "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_depthFactor" + "id": "m_layer1_m_heightmapScale" } }, { @@ -1130,18 +1147,7 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_layer1_m_depthOffset" - } - }, - { - "id": "invert", - "displayName": "Invert", - "description": "Invert to depthmap if the texture is heightmap", - "type": "Bool", - "defaultValue": true, - "connection": { - "type": "ShaderInput", - "id": "m_layer1_m_depthInverted" + "id": "m_layer1_m_heightmapOffset" } } ], @@ -1152,7 +1158,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -1239,7 +1245,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { @@ -1250,14 +1256,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Base color texture map UV set", + "description": "Base color map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1269,7 +1275,7 @@ { "id": "textureBlendMode", "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture Map.", + "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], "defaultValue": "Multiply", @@ -1295,7 +1301,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "", "type": "Image", "connection": { @@ -1306,14 +1312,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Metallic texture map UV set", + "description": "Metallic map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1326,8 +1332,8 @@ "layer2_roughness": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface roughness.", + "displayName": "Texture", + "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1337,14 +1343,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1357,7 +1363,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "lowerBound", "displayName": "Lower Bound", - "description": "The roughness value that corresponds to black in the texture map.", + "description": "The roughness value that corresponds to black in the texture.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -1371,7 +1377,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "upperBound", "displayName": "Upper Bound", - "description": "The roughness value that corresponds to white in the texture map.", + "description": "The roughness value that corresponds to white in the texture.", "type": "Float", "defaultValue": 1.0, "min": 0.0, @@ -1412,8 +1418,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface reflectance.", + "displayName": "Texture", + "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1423,14 +1429,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Specular reflection texture map UV set", + "description": "Specular reflection map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1443,8 +1449,8 @@ "layer2_normal": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface normal direction.", + "displayName": "Texture", + "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1454,14 +1460,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", + "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1530,7 +1536,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Strength factor texture map", + "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -1540,14 +1546,14 @@ { "id": "useInfluenceMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "influenceMapUv", "displayName": " UV", - "description": "Strength factor texture map UV set", + "description": "Strength factor map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1572,7 +1578,7 @@ { "id": "roughnessMap", "displayName": " Roughness Map", - "description": "Roughness texture map", + "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", @@ -1582,14 +1588,14 @@ { "id": "useRoughnessMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the roughness value.", + "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { "id": "roughnessMapUv", "displayName": " UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1631,7 +1637,7 @@ { "id": "normalMapUv", "displayName": " UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1645,7 +1651,7 @@ { "id": "diffuseTextureMap", "displayName": "Diffuse AO", - "description": "Texture map for defining occlusion area for diffuse ambient lighting.", + "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1655,14 +1661,14 @@ { "id": "diffuseUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Diffuse AO texture map.", + "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { "id": "diffuseTextureMapUv", "displayName": " UV", - "description": "Diffuse AO texture map UV set.", + "description": "Diffuse AO map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1687,7 +1693,7 @@ { "id": "specularTextureMap", "displayName": "Specular Cavity", - "description": "Texture map for defining occlusion area for specular lighting.", + "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1697,14 +1703,14 @@ { "id": "specularUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Specular Cavity texture map.", + "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { "id": "specularTextureMapUv", "displayName": " UV", - "description": "Specular Cavity texture map UV set.", + "description": "Specular Cavity map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1767,8 +1773,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining emissive area.", + "displayName": "Texture", + "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1778,14 +1784,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Emissive texture map UV set", + "description": "Emissive map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1796,34 +1802,34 @@ } ], "layer2_parallax": [ - { - "id": "enable", - "displayName": "Enable", - "description": "Whether to enable the parallax feature.", - "type": "Bool", - "defaultValue": false - }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Depthmap to create parallax effect.", + "displayName": "Height Map", + "description": "Displacement height map, which can be used for layer blending and/or a parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer2_m_depthMap" + "id": "m_layer2_m_heightmap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the height map.", + "type": "Bool", + "defaultValue": true + }, { "id": "factor", - "displayName": "Heightmap Scale", - "description": "The total height of the heightmap in local model units.", + "displayName": "Scale", + "description": "The total height of the height map in local model units.", "type": "Float", - "defaultValue": 0.0, + "defaultValue": 0.05, "min": 0.0, "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_depthFactor" + "id": "m_layer2_m_heightmapScale" } }, { @@ -1836,18 +1842,7 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_layer2_m_depthOffset" - } - }, - { - "id": "invert", - "displayName": "Invert", - "description": "Invert to depthmap if the texture is heightmap", - "type": "Bool", - "defaultValue": true, - "connection": { - "type": "ShaderInput", - "id": "m_layer2_m_depthInverted" + "id": "m_layer2_m_heightmapOffset" } } ], @@ -1858,7 +1853,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -1945,7 +1940,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { @@ -1956,14 +1951,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Base color texture map UV set", + "description": "Base color map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1975,7 +1970,7 @@ { "id": "textureBlendMode", "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture Map.", + "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], "defaultValue": "Multiply", @@ -2001,7 +1996,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "", "type": "Image", "connection": { @@ -2012,14 +2007,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Metallic texture map UV set", + "description": "Metallic map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2032,8 +2027,8 @@ "layer3_roughness": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface roughness.", + "displayName": "Texture", + "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2043,14 +2038,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2063,7 +2058,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "lowerBound", "displayName": "Lower Bound", - "description": "The roughness value that corresponds to black in the texture map.", + "description": "The roughness value that corresponds to black in the texture.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -2077,7 +2072,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "upperBound", "displayName": "Upper Bound", - "description": "The roughness value that corresponds to white in the texture map.", + "description": "The roughness value that corresponds to white in the texture.", "type": "Float", "defaultValue": 1.0, "min": 0.0, @@ -2118,8 +2113,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface reflectance.", + "displayName": "Texture", + "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2129,14 +2124,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Specular reflection texture map UV set", + "description": "Specular reflection map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2149,8 +2144,8 @@ "layer3_normal": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface normal direction.", + "displayName": "Texture", + "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2160,14 +2155,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", + "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2236,7 +2231,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Strength factor texture map", + "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -2246,14 +2241,14 @@ { "id": "useInfluenceMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "influenceMapUv", "displayName": " UV", - "description": "Strength factor texture map UV set", + "description": "Strength factor map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2278,7 +2273,7 @@ { "id": "roughnessMap", "displayName": " Roughness Map", - "description": "Roughness texture map", + "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", @@ -2288,14 +2283,14 @@ { "id": "useRoughnessMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the roughness value.", + "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { "id": "roughnessMapUv", "displayName": " UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2337,7 +2332,7 @@ { "id": "normalMapUv", "displayName": " UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2351,7 +2346,7 @@ { "id": "diffuseTextureMap", "displayName": "Diffuse AO", - "description": "Texture map for defining occlusion area for diffuse ambient lighting.", + "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2361,14 +2356,14 @@ { "id": "diffuseUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Diffuse AO texture map.", + "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { "id": "diffuseTextureMapUv", "displayName": " UV", - "description": "Diffuse AO texture map UV set.", + "description": "Diffuse AO map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2393,7 +2388,7 @@ { "id": "specularTextureMap", "displayName": "Specular Cavity", - "description": "Texture map for defining occlusion area for specular lighting.", + "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2403,14 +2398,14 @@ { "id": "specularUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Specular Cavity texture map.", + "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { "id": "specularTextureMapUv", "displayName": " UV", - "description": "Specular Cavity texture map UV set.", + "description": "Specular Cavity map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2473,8 +2468,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining emissive area.", + "displayName": "Texture", + "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2484,14 +2479,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Emissive texture map UV set", + "description": "Emissive map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2502,34 +2497,34 @@ } ], "layer3_parallax": [ - { - "id": "enable", - "displayName": "Enable", - "description": "Whether to enable the parallax feature.", - "type": "Bool", - "defaultValue": false - }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Depthmap to create parallax effect.", + "displayName": "Height Map", + "description": "Displacement height map, which can be used for layer blending and/or a parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_layer3_m_depthMap" + "id": "m_layer3_m_heightmap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the height map.", + "type": "Bool", + "defaultValue": true + }, { "id": "factor", - "displayName": "Heightmap Scale", - "description": "The total height of the heightmap in local model units.", + "displayName": "Scale", + "description": "The total height of the height map in local model units.", "type": "Float", - "defaultValue": 0.0, + "defaultValue": 0.05, "min": 0.0, "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_depthFactor" + "id": "m_layer3_m_heightmapScale" } }, { @@ -2542,18 +2537,7 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_layer3_m_depthOffset" - } - }, - { - "id": "invert", - "displayName": "Invert", - "description": "Invert to depthmap if the texture is heightmap", - "type": "Bool", - "defaultValue": true, - "connection": { - "type": "ShaderInput", - "id": "m_layer3_m_depthInverted" + "id": "m_layer3_m_heightmapOffset" } } ], @@ -2564,7 +2548,7 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", @@ -2699,7 +2683,7 @@ { "type": "Lua", "args": { - "file": "StandardMultilayerPBR_Parallax.lua" + "file": "StandardMultilayerPBR_Displacement.lua" } }, //############################################################################################## @@ -2816,12 +2800,12 @@ } }, { - "type": "Lua", + "type": "UseTexture", "args": { - "file": "StandardMultilayerPBR_ParallaxPerLayer.lua", - "propertyNamePrefix": "layer1_", - "srgNamePrefix": "m_layer1_", - "optionsNamePrefix": "o_layer1_" + "textureProperty": "layer1_parallax.textureMap", + "useTextureProperty": "layer1_parallax.useTexture", + "dependentProperties": ["layer1_parallax.factor"], + "shaderOption": "o_layer1_o_useHeightmap" } }, { @@ -2953,12 +2937,12 @@ } }, { - "type": "Lua", + "type": "UseTexture", "args": { - "file": "StandardMultilayerPBR_ParallaxPerLayer.lua", - "propertyNamePrefix": "layer2_", - "srgNamePrefix": "m_layer2_", - "optionsNamePrefix": "o_layer2_" + "textureProperty": "layer2_parallax.textureMap", + "useTextureProperty": "layer2_parallax.useTexture", + "dependentProperties": ["layer2_parallax.factor"], + "shaderOption": "o_layer2_o_useHeightmap" } }, { @@ -3090,14 +3074,14 @@ } }, { - "type": "Lua", + "type": "UseTexture", "args": { - "file": "StandardMultilayerPBR_ParallaxPerLayer.lua", - "propertyNamePrefix": "layer3_", - "srgNamePrefix": "m_layer3_", - "optionsNamePrefix": "o_layer3_" + "textureProperty": "layer3_parallax.textureMap", + "useTextureProperty": "layer3_parallax.useTexture", + "dependentProperties": ["layer3_parallax.factor"], + "shaderOption": "o_layer3_o_useHeightmap" } - }, + }, { // Maps 2D scale, offset, and rotate properties into a float3x3 transform matrix. "type": "Transform2D", 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 d2314efefe..1750da5020 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli @@ -15,6 +15,7 @@ #include #include #include +#include #include "MaterialInputs/BaseColorInput.azsli" #include "MaterialInputs/RoughnessInput.azsli" @@ -42,9 +43,19 @@ COMMON_SRG_INPUTS_PARALLAX(prefix) ShaderResourceGroup MaterialSrg : SRG_PerMaterial { - Texture2D m_blendMaskTexture; + Texture2D m_blendMaskTexture; uint m_blendMaskUvIndex; + // When parallax mapping is used, these limit the heightmap intersection search range to the narrowest band possible, to give the best quality result. + // These are also support other calculations related to displacement-based blending, even when parallax is not used. + float m_displacementMin; // The lowest displacement value possible from all layers combined (negative values are below the surface) + float m_displacementMax; // The highest displacement value possible from all layers combined (negative values are below the surface) + + // When displacement-based blending is used, this is the height range where the surface properties of different layers will be blended together. + // We use an absolute value rather than a relative factor because disconnecting this property from the influence of other properties makes per-layer + // displacement adjustments feel more natural if they don't impact the blend distance. + float m_displacementBlendDistance; + // Auto-generate material SRG fields for common inputs for each layer DEFINE_LAYER_SRG_INPUTS(m_layer1_) DEFINE_LAYER_SRG_INPUTS(m_layer2_) @@ -61,10 +72,6 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial uint m_parallaxUvIndex; - // These are used to limit the heightmap intersection search range to the narrowest band possible, to give the best quality result. - float m_displacementMin; // The lowest displacement value possible from all layers combined - float m_displacementMax; // The highest displacement value possible from all layers combined - float3x3 m_uvMatrix; float4 m_pad4; // [GFX TODO][ATOM-14595] This is a workaround for a data stomping bug. Remove once it's fixed. float3x3 m_uvMatrixInverse; @@ -98,11 +105,11 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial option bool o_layer2_enabled; option bool o_layer3_enabled; -enum class DebugDrawMode { None, BlendSource, DepthMaps }; +enum class DebugDrawMode { None, BlendMask, Displacement, FinalBlendWeights }; option DebugDrawMode o_debugDrawMode; -enum class BlendMaskSource { TextureMap, VertexColors, Fallback }; -option BlendMaskSource o_blendSource; +enum class LayerBlendSource { BlendMaskTexture, BlendMaskVertexColors, Displacement, Displacement_With_BlendMaskTexture, Displacement_With_BlendMaskVertexColors, Fallback }; +option LayerBlendSource o_layerBlendSource; // Indicates whether the vertex input struct's "m_optional_blendMask" is bound. If false, it is not safe to read from m_optional_blendMask. // This option gets set automatically by the system at runtime; there is a soft naming convention that associates it with m_optional_blendMask. @@ -111,53 +118,83 @@ option BlendMaskSource o_blendSource; option bool o_blendMask_isBound; // ------ Blend Utilities ---------------------------------------- - // This is mainly used to pass extra data to the GetDepth callback function during the parallax depth search. // But since we have it, we use it in some other functions as well rather than passing it around. static float3 s_blendMaskFromVertexStream; -//! Returns the BlendMaskSource that will actually be used when rendering (not necessarily the same BlendMaskSource specified by the user) -BlendMaskSource GetFinalBlendMaskSource() +// TODO: Consider storing the result of GetFinalLayerBlendSource() in a static similar to s_blendMaskFromVertexStream. That might give better performance when variants aren't used. + +//! Returns the LayerBlendSource that will actually be used when rendering (not necessarily the same LayerBlendSource specified by the user) +LayerBlendSource GetFinalLayerBlendSource() { - if(o_blendSource == BlendMaskSource::TextureMap) + if(o_layerBlendSource == LayerBlendSource::BlendMaskTexture) + { + return o_layerBlendSource; + } + else if(o_layerBlendSource == LayerBlendSource::BlendMaskVertexColors) + { + if(o_blendMask_isBound) + { + return o_layerBlendSource; + } + else + { + return LayerBlendSource::Fallback; + } + } + else if(o_layerBlendSource == LayerBlendSource::Displacement) + { + return o_layerBlendSource; + } + else if(o_layerBlendSource == LayerBlendSource::Displacement_With_BlendMaskTexture) { - return BlendMaskSource::TextureMap; + return o_layerBlendSource; } - else if(o_blendSource == BlendMaskSource::VertexColors) + else if(o_layerBlendSource == LayerBlendSource::Displacement_With_BlendMaskVertexColors) { if(o_blendMask_isBound) { - return BlendMaskSource::VertexColors; + return o_layerBlendSource; } else { - return BlendMaskSource::TextureMap; + return LayerBlendSource::Displacement; } } else { - return BlendMaskSource::Fallback; + return LayerBlendSource::Fallback; } } -//! Return the raw blend source values directly from the blend mask or vertex colors, depending on the available data and configuration. +//! Return the applicable blend mask values from the blend mask texture or vertex colors, and filters out any that don't apply. //! layer1 is an implicit base layer -//! layer2 is weighted by r -//! layer3 is weighted by g +//! layer2 mask is in the r channel +//! layer3 mask is in the g channel //! b is reserved for perhaps a dedicated puddle layer -float3 GetBlendSourceValues(float2 uv) +//! @param blendSource indicates where to get the blend mask from +//! @param blendMaskUv for sampling a blend mask texture, if that's the blend source +//! @param blendMaskVertexColors the vertex color values to use for the blend mask, if that's the blend source +//! @return the blend mask values, or 0 if there is no blend mask +float3 GetApplicableBlendMaskValues(LayerBlendSource blendSource, float2 blendMaskUv, float3 blendMaskVertexColors) { float3 blendSourceValues = float3(0,0,0); if(o_layer2_enabled || o_layer3_enabled) { - switch(GetFinalBlendMaskSource()) + switch(blendSource) { - case BlendMaskSource::TextureMap: - blendSourceValues = MaterialSrg::m_blendMaskTexture.Sample(MaterialSrg::m_sampler, uv).rgb; + case LayerBlendSource::Displacement: + // In this case the blend mask has no effect, returning (1,1,1) disables any impact of the mask. + blendSourceValues = float3(1,1,1); + break; + case LayerBlendSource::BlendMaskTexture: + case LayerBlendSource::Displacement_With_BlendMaskTexture: + blendSourceValues = MaterialSrg::m_blendMaskTexture.Sample(MaterialSrg::m_sampler, blendMaskUv).rgb; break; - case BlendMaskSource::VertexColors: - blendSourceValues = s_blendMaskFromVertexStream; + case LayerBlendSource::BlendMaskVertexColors: + case LayerBlendSource::Displacement_With_BlendMaskVertexColors: + blendSourceValues = blendMaskVertexColors; break; } @@ -175,29 +212,96 @@ float3 GetBlendSourceValues(float2 uv) return blendSourceValues; } -//! Return the final blend mask values to be used for rendering, based on the available data and configuration. +//! When dealing with masks for displacement-based blending, we sometimes need to push the value below the min displacement to make it disappear. +float GetSubMinDisplacement() +{ + return MaterialSrg::m_displacementMin - 0.001; + //return MaterialSrg::m_displacementMin - max(MaterialSrg::m_displacementBlendDistance, 0.001); +} + +float3 ApplyBlendMaskToDepthValues(float3 blendMaskValues, float3 layerDepthValues, float zeroMaskDisplacement); + +//! Return the final blend weights to be used for rendering, based on the available data and configuration. +//! @param blendSource - indicates where to get the blend mask from +//! @param blendMaskValues - blend mask values as returned by GetApplicableBlendMaskValues() +//! @param layerDepthValues - the depth values for each layer, used if the blend source includes displacement. See GetLayerDepthValues(). +//! Note the blendMaskValues will not be applied here, those should have already been applied to layerDepthValues. +//! @param layerDepthBlendDistance - controls how smoothly to blend layers 2 and 3 with the base layer, when the blend source includes displacement. +//! When layers are close together their weights will be blended together, otherwise the highest layer will have the full weight. //! @return The blend weights for each layer. -//! Even though layer1 not explicitly specified in the blend source data, it is explicitly included with the returned values. +//! Even though layer1 not explicitly specified in the blend mask data, it is explicitly included with the returned values. //! layer1 = r //! layer2 = g //! layer3 = b -float3 GetBlendWeights(float2 uv) +float3 GetBlendWeights(LayerBlendSource blendSource, float3 blendMaskValues, float3 layerDepthValues, float layerDepthBlendDistance) { float3 blendWeights; if(o_layer2_enabled || o_layer3_enabled) { - float3 blendSourceValues = GetBlendSourceValues(uv); - - // Calculate blend weights such that multiplying and adding them with layer data is equivalent - // to lerping between each layer. - // final = lerp(final, layer1, blendWeights.r) - // final = lerp(final, layer2, blendWeights.g) - // final = lerp(final, layer3, blendWeights.b) + if(LayerBlendSource::Displacement == blendSource || + LayerBlendSource::Displacement_With_BlendMaskTexture == blendSource || + LayerBlendSource::Displacement_With_BlendMaskVertexColors == blendSource) + { + // Calculate the blend weights based on displacement values... + + // The inputs are depth values, but we change them to height values to make the code a bit more intuitive. + float3 layerHeightValues = -layerDepthValues; + + float highestPoint = layerHeightValues.x; + if(o_layer2_enabled) + { + highestPoint = max(highestPoint, layerHeightValues.y); + } + if(o_layer3_enabled) + { + highestPoint = max(highestPoint, layerHeightValues.z); + } + + if(layerDepthBlendDistance > 0.0001) + { + float lowestVisiblePoint = highestPoint - layerDepthBlendDistance; + blendWeights = smoothstep(lowestVisiblePoint, highestPoint, layerHeightValues); + + if(!o_layer2_enabled) + { + blendWeights.y = 0.0; + } + + if(!o_layer3_enabled) + { + blendWeights.z = 0.0; + } + } + else + { + blendWeights = float3(layerHeightValues.x >= highestPoint ? 1.0 : 0.0, + layerHeightValues.y >= highestPoint && o_layer2_enabled ? 1.0 : 0.0, + layerHeightValues.z >= highestPoint && o_layer3_enabled ? 1.0 : 0.0); + } + + // Calculate blend weights such that multiplying and adding them with layer data is equivalent + // to lerping between each layer. + // final = lerp(final, layer1, blendWeights.r) + // final = lerp(final, layer2, blendWeights.g) + // final = lerp(final, layer3, blendWeights.b) + + blendWeights.y = (1 - blendWeights.z) * blendWeights.y; + blendWeights.x = 1 - blendWeights.y - blendWeights.z; + } + else + { + // Calculate blend weights such that multiplying and adding them with layer data is equivalent + // to lerping between each layer. + // final = lerp(final, layer1, blendWeights.r) + // final = lerp(final, layer2, blendWeights.g) + // final = lerp(final, layer3, blendWeights.b) + + blendWeights.b = blendMaskValues.g; + blendWeights.g = (1.0 - blendMaskValues.g) * blendMaskValues.r; + blendWeights.r = (1.0 - blendMaskValues.g) * (1.0 - blendMaskValues.r); + } - blendWeights.b = blendSourceValues.g; - blendWeights.g = (1.0 - blendSourceValues.g) * blendSourceValues.r; - blendWeights.r = (1.0 - blendSourceValues.g) * (1.0 - blendSourceValues.r); } else { @@ -207,6 +311,38 @@ float3 GetBlendWeights(float2 uv) return blendWeights; } +float3 GetLayerDepthValues(float2 uv, float2 uv_ddx, float2 uv_ddy); + +//! Return the final blend weights to be used for rendering, based on the available data and configuration. +//! Note this will sample the displacement maps in the case of LayerBlendSource::Displacement. If you have already +//! the layer depth values, use the GetBlendWeights() overload that takes layerDepthValues instead. +float3 GetBlendWeights(LayerBlendSource blendSource, float2 uv, float3 blendMaskVertexColors) +{ + float3 layerDepthValues = float3(0,0,0); + + float3 blendMaskValues = GetApplicableBlendMaskValues(blendSource, uv, blendMaskVertexColors); + + if(blendSource == LayerBlendSource::Displacement || + blendSource == LayerBlendSource::Displacement_With_BlendMaskTexture || + blendSource == LayerBlendSource::Displacement_With_BlendMaskVertexColors) + { + bool useBlendMask = + LayerBlendSource::Displacement_With_BlendMaskTexture == blendSource || + LayerBlendSource::Displacement_With_BlendMaskVertexColors == blendSource; + + layerDepthValues = GetLayerDepthValues(uv, ddx_fine(uv), ddy_fine(uv)); + + if(useBlendMask) + { + // Unlike the GetDepth() callback used for parallax, we don't just shift the values toward GetSubMinDisplacement(), + // we shift extra to ensure that completely masked-out layers are not blended onto upper layers. + layerDepthValues = ApplyBlendMaskToDepthValues(blendMaskValues, layerDepthValues, GetSubMinDisplacement() - MaterialSrg::m_displacementBlendDistance); + } + } + + return GetBlendWeights(blendSource, blendMaskValues, layerDepthValues, MaterialSrg::m_displacementBlendDistance); +} + float BlendLayers(float layer1, float layer2, float layer3, float3 blendWeights) { return dot(float3(layer1, layer2, layer3), blendWeights); @@ -236,55 +372,127 @@ bool ShouldHandleParallaxInDepthShaders() return ShouldHandleParallax() && o_parallax_enablePixelDepthOffset; } -// Callback function for ParallaxMapping.azsli -DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) +//! Returns the depth values for each layer. +float3 GetLayerDepthValues(float2 uv, float2 uv_ddx, float2 uv_ddy) { float3 layerDepthValues = float3(0,0,0); - - if(o_layer1_o_useDepthMap) + + // layer1 { - float2 layerUv = uv; - if(MaterialSrg::m_parallaxUvIndex == 0) + if(o_layer1_o_useHeightmap) { - layerUv = mul(MaterialSrg::m_layer1_m_uvMatrix, float3(uv, 1.0)).xy; + float2 layerUv = uv; + if(MaterialSrg::m_parallaxUvIndex == 0) + { + layerUv = mul(MaterialSrg::m_layer1_m_uvMatrix, float3(uv, 1.0)).xy; + } + + layerDepthValues.r = SampleDepthFromHeightmap(MaterialSrg::m_layer1_m_heightmap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; + layerDepthValues.r *= MaterialSrg::m_layer1_m_heightmapScale; } - layerDepthValues.r = SampleDepthOrHeightMap(MaterialSrg::m_layer1_m_depthInverted, MaterialSrg::m_layer1_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; - layerDepthValues.r *= MaterialSrg::m_layer1_m_depthFactor; - layerDepthValues.r -= MaterialSrg::m_layer1_m_depthOffset; + layerDepthValues.r -= MaterialSrg::m_layer1_m_heightmapOffset; } - if(o_layer2_enabled && o_layer2_o_useDepthMap) + if(o_layer2_enabled) { - float2 layerUv = uv; - if(MaterialSrg::m_parallaxUvIndex == 0) + if(o_layer2_o_useHeightmap) { - layerUv = mul(MaterialSrg::m_layer2_m_uvMatrix, float3(uv, 1.0)).xy; + float2 layerUv = uv; + if(MaterialSrg::m_parallaxUvIndex == 0) + { + layerUv = mul(MaterialSrg::m_layer2_m_uvMatrix, float3(uv, 1.0)).xy; + } + + layerDepthValues.g = SampleDepthFromHeightmap(MaterialSrg::m_layer2_m_heightmap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; + layerDepthValues.g *= MaterialSrg::m_layer2_m_heightmapScale; } - layerDepthValues.g = SampleDepthOrHeightMap(MaterialSrg::m_layer2_m_depthInverted, MaterialSrg::m_layer2_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; - layerDepthValues.g *= MaterialSrg::m_layer2_m_depthFactor; - layerDepthValues.g -= MaterialSrg::m_layer2_m_depthOffset; + layerDepthValues.g -= MaterialSrg::m_layer2_m_heightmapOffset; + } - if(o_layer3_enabled && o_layer3_o_useDepthMap) + if(o_layer3_enabled) { - float2 layerUv = uv; - if(MaterialSrg::m_parallaxUvIndex == 0) + if(o_layer3_o_useHeightmap) { - layerUv = mul(MaterialSrg::m_layer3_m_uvMatrix, float3(uv, 1.0)).xy; + float2 layerUv = uv; + if(MaterialSrg::m_parallaxUvIndex == 0) + { + layerUv = mul(MaterialSrg::m_layer3_m_uvMatrix, float3(uv, 1.0)).xy; + } + + layerDepthValues.b = SampleDepthFromHeightmap(MaterialSrg::m_layer3_m_heightmap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; + layerDepthValues.b *= MaterialSrg::m_layer3_m_heightmapScale; } - layerDepthValues.b = SampleDepthOrHeightMap(MaterialSrg::m_layer3_m_depthInverted, MaterialSrg::m_layer3_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; - layerDepthValues.b *= MaterialSrg::m_layer3_m_depthFactor; - layerDepthValues.b -= MaterialSrg::m_layer3_m_depthOffset; + layerDepthValues.b -= MaterialSrg::m_layer3_m_heightmapOffset; + + } + + return layerDepthValues; +} + + +//! Uses a layer blend mask to further displace each layer's surface so that it disappears beyond the other surfaces. +//! Note the blend mask does not apply to the first layer, it is the implicit base layer. Layers 2 and 3 are masked by the r and g channels of the mask. +//! @param blendMaskValues layer mask values as returned by GetApplicableBlendMaskValues() +//! @param layerDepthValues layer depth values as returned by GetLayerDepthValues() +//! @param zeroMaskDisplacement the target displacement value that corresponds to a mask value of 0 +//! @return new layer depth values that have been adjusted according to the layerMaskValues +float3 ApplyBlendMaskToDepthValues(float3 blendMaskValues, float3 layerDepthValues, float zeroMaskDisplacement) +{ + if(o_layer2_enabled || o_layer3_enabled) + { + // We add to the depth value rather than lerp toward m_displacementMin to avoid squashing the topology, but instead lower it out of sight. + + if(o_layer2_enabled) + { + float dropoffRange = MaterialSrg::m_layer2_m_heightmapOffset - zeroMaskDisplacement; + layerDepthValues.g += dropoffRange * (1-blendMaskValues.r); + } + + if(o_layer3_enabled) + { + float dropoffRange = MaterialSrg::m_layer3_m_heightmapOffset - zeroMaskDisplacement; + layerDepthValues.b += dropoffRange * (1-blendMaskValues.g); + } } + + return layerDepthValues; +} + +//! Callback function for ParallaxMapping.azsli +DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) +{ + LayerBlendSource blendSource = GetFinalLayerBlendSource(); + + float3 layerDepthValues = GetLayerDepthValues(uv, uv_ddx, uv_ddy); - // Note, when the blend source is BlendMaskSource::VertexColors, parallax will not be able to blend correctly between layers. It will end up using the same blend mask values - // for every UV position when searching for the intersection. This leads to smearing artifacts at the transition point, but these won't be so noticeable as long as + // Note, when the blend source uses the blend mask from the vertex colors, parallax will not be able to blend correctly between layers. It will end up using the same blend mask values + // for every UV position when searching for the intersection. This leads to smearing artifacts at the transition point, but these won't be as noticeable if // you have a small depth factor relative to the size of the blend transition. - float3 blendWeights = GetBlendWeights(uv); + float3 blendMaskValues = GetApplicableBlendMaskValues(blendSource, uv, s_blendMaskFromVertexStream); + + bool useBlendMask = + LayerBlendSource::Displacement_With_BlendMaskTexture == blendSource || + LayerBlendSource::Displacement_With_BlendMaskVertexColors == blendSource; + + if(useBlendMask) + { + // Regarding GetSubMinDisplacement(), when a mask of 0 pushes the surface all the way to the bottom, we want that + // to go a little below the min so it will disappear if there is something else right at the min. + + layerDepthValues = ApplyBlendMaskToDepthValues(blendMaskValues, layerDepthValues, GetSubMinDisplacement()); + } + + // When blending the depth together, we don't use MaterialSrg::m_displacementBlendDistance. The intention is that m_displacementBlendDistance + // is for transitioning the appearance of the surface itself, but we still want a distinct change in the heightmap. If someday we want to + // support smoothly blending the depth as well, there is a bit more work to do to get it to play nice with the blend mask code in GetLayerDepthValues(). + float layerDepthBlendDistance = 0.0f; + float3 blendWeightValues = GetBlendWeights(blendSource, blendMaskValues, layerDepthValues, layerDepthBlendDistance); + + float depth = BlendLayers(layerDepthValues.r, layerDepthValues.g, layerDepthValues.b, blendWeightValues); - float depth = BlendLayers(layerDepthValues.r, layerDepthValues.g, layerDepthValues.b, blendWeights); return DepthResultAbsolute(depth); } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl index 178deca8a2..cf4d53e805 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl @@ -84,7 +84,7 @@ VSDepthOutput MainVS(VSInput IN) } else { - OUT.m_blendMask = float3(1,1,1); + OUT.m_blendMask = float3(0,0,0); } return OUT; @@ -103,11 +103,11 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) if(ShouldHandleParallaxInDepthShaders()) { - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); - + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); + s_blendMaskFromVertexStream = IN.m_blendMask; float depth; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua new file mode 100644 index 0000000000..d2bf8f28d3 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua @@ -0,0 +1,195 @@ +-------------------------------------------------------------------------------------- +-- +-- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +-- its licensors. +-- +-- For complete copyright and license terms please see the LICENSE at the root of this +-- distribution (the "License"). All use of this software is governed by the License, +-- or, if provided, by the license below or the license accompanying this file. Do not +-- remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- +-- +---------------------------------------------------------------------------------------------------- + +-- This functor handles general parallax properties that affect the entire material. + +function GetMaterialPropertyDependencies() + return { + "blend.blendSource", + "blend.enableLayer2", + "blend.enableLayer3", + "parallax.enable", + "layer1_parallax.textureMap", + "layer2_parallax.textureMap", + "layer3_parallax.textureMap", + "layer1_parallax.useTexture", + "layer2_parallax.useTexture", + "layer3_parallax.useTexture", + "layer1_parallax.factor", + "layer2_parallax.factor", + "layer3_parallax.factor", + "layer1_parallax.offset", + "layer2_parallax.offset", + "layer3_parallax.offset" + } +end + +function GetShaderOptionDependencies() + return {"o_parallax_feature_enabled"} +end + +-- These values must align with LayerBlendSource in StandardMultilayerPBR_Common.azsli. +LayerBlendSource_BlendMaskTexture = 0 +LayerBlendSource_BlendMaskVertexColors = 1 +LayerBlendSource_Displacement = 2 +LayerBlendSource_Displacement_With_BlendMaskTexture = 3 +LayerBlendSource_Displacement_With_BlendMaskVertexColors = 4 + +function BlendSourceUsesDisplacement(context) + local blendSource = context:GetMaterialPropertyValue_enum("blend.blendSource") + local blendSourceIncludesDisplacement = (blendSource == LayerBlendSource_Displacement or + blendSource == LayerBlendSource_Displacement_With_BlendMaskTexture or + blendSource == LayerBlendSource_Displacement_With_BlendMaskVertexColors) + return blendSourceIncludesDisplacement +end + +function IsParallaxNeededForLayer(context, layerNumber) + local enableLayer = true + if(layerNumber > 1) then -- layer 1 is always enabled, it is the implicit base layer + enableLayer = context:GetMaterialPropertyValue_bool("blend.enableLayer" .. layerNumber) + end + + if not enableLayer then + return false + end + + local parallaxGroupName = "layer" .. layerNumber .. "_parallax." + + local factor = context:GetMaterialPropertyValue_float(parallaxGroupName .. "factor") + local offset = context:GetMaterialPropertyValue_float(parallaxGroupName .. "offset") + + if factor == 0.0 and offset == 0.0 then + return false + end + + local hasTexture = nil ~= context:GetMaterialPropertyValue_Image(parallaxGroupName .. "textureMap") + local useTexture = context:GetMaterialPropertyValue_bool(parallaxGroupName .. "useTexture") + + if not hasTexture or not useTexture then + factorLayer = 0.0 + end + + if factor == 0.0 and offset == 0.0 then + return false + end + + return true +end + +-- Calculates the min and max displacement height values encompassing all enabled layers. +-- @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 function GetMergedHeightRange(heightMinMax, offset, factor) + top = offset + bottom = offset - factor + + if(heightMinMax[1] == nil) then + heightMinMax[1] = top + else + heightMinMax[1] = math.max(heightMinMax[1], top) + end + + if(heightMinMax[0] == nil) then + heightMinMax[0] = bottom + else + heightMinMax[0] = math.min(heightMinMax[0], bottom) + end + end + + local enableParallax = context:GetMaterialPropertyValue_bool("parallax.enable") + + if(enableParallax or BlendSourceUsesDisplacement(context)) then + local hasTextureLayer1 = nil ~= context:GetMaterialPropertyValue_Image("layer1_parallax.textureMap") + local hasTextureLayer2 = nil ~= context:GetMaterialPropertyValue_Image("layer2_parallax.textureMap") + local hasTextureLayer3 = nil ~= context:GetMaterialPropertyValue_Image("layer3_parallax.textureMap") + + local useTextureLayer1 = context:GetMaterialPropertyValue_bool("layer1_parallax.useTexture") + local useTextureLayer2 = context:GetMaterialPropertyValue_bool("layer2_parallax.useTexture") + local useTextureLayer3 = context:GetMaterialPropertyValue_bool("layer3_parallax.useTexture") + + local factorLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.factor") + local factorLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.factor") + local factorLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.factor") + + if not hasTextureLayer1 or not useTextureLayer1 then factorLayer1 = 0 end + if not hasTextureLayer2 or not useTextureLayer2 then factorLayer2 = 0 end + if not hasTextureLayer3 or not useTextureLayer3 then factorLayer3 = 0 end + + local offsetLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.offset") + local offsetLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.offset") + local offsetLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.offset") + + local enableLayer2 = context:GetMaterialPropertyValue_bool("blend.enableLayer2") + local enableLayer3 = context:GetMaterialPropertyValue_bool("blend.enableLayer3") + + GetMergedHeightRange(heightMinMax, offsetLayer1, factorLayer1) + if(enableLayer2) then GetMergedHeightRange(heightMinMax, offsetLayer2, factorLayer2) end + if(enableLayer3) then GetMergedHeightRange(heightMinMax, offsetLayer3, factorLayer3) end + + else + heightMinMax = {0,0} + end + + return heightMinMax +end + +function Process(context) + local heightMinMax = CalcOverallHeightRange(context) + context:SetShaderConstant_float("m_displacementMin", heightMinMax[0]) + context:SetShaderConstant_float("m_displacementMax", heightMinMax[1]) + + local parallaxFeatureEnabled = context:GetMaterialPropertyValue_bool("parallax.enable") + if parallaxFeatureEnabled then + if not IsParallaxNeededForLayer(context, 1) and + not IsParallaxNeededForLayer(context, 2) and + not IsParallaxNeededForLayer(context, 3) then + parallaxFeatureEnabled = false + end + end + + context:SetShaderOptionValue_bool("o_parallax_feature_enabled", parallaxFeatureEnabled) +end + +function ProcessEditor(context) + local enableParallaxSettings = context:GetMaterialPropertyValue_bool("parallax.enable") + + local parallaxSettingVisibility = MaterialPropertyVisibility_Enabled + if(not enableParallaxSettings) then + parallaxSettingVisibility = MaterialPropertyVisibility_Hidden + end + + context:SetMaterialPropertyVisibility("parallax.parallaxUv", parallaxSettingVisibility) + context:SetMaterialPropertyVisibility("parallax.algorithm", parallaxSettingVisibility) + context:SetMaterialPropertyVisibility("parallax.quality", parallaxSettingVisibility) + context:SetMaterialPropertyVisibility("parallax.pdo", parallaxSettingVisibility) + context:SetMaterialPropertyVisibility("parallax.showClipping", parallaxSettingVisibility) + + if BlendSourceUsesDisplacement(context) then + context:SetMaterialPropertyVisibility("blend.displacementBlendDistance", MaterialPropertyVisibility_Enabled) + else + context:SetMaterialPropertyVisibility("blend.displacementBlendDistance", MaterialPropertyVisibility_Hidden) + end + + -- We set the displacementBlendDistance slider range to match the range of displacement, so the slider will feel good + -- regardless of how big the overall displacement is. Using a soft max allows the user to exceed the limit if desired, + -- but the main reason for the *soft* max is to avoid impacting the value of displacementBlendDistance which could + -- otherwise lead to edge cases. + local heightMinMax = CalcOverallHeightRange(context) + local totalDisplacementRange = heightMinMax[1] - heightMinMax[0] + context:SetMaterialPropertySoftMaxValue_float("blend.displacementBlendDistance", math.max(totalDisplacementRange, 0.001)) +end + diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl index 580e5206ab..19efc60a1e 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl @@ -116,7 +116,7 @@ VSOutput ForwardPassVS(VSInput IN) } else { - OUT.m_blendMask = float3(1,1,1); + OUT.m_blendMask = float3(0,0,0); } // Shadow coords will be calculated in the pixel shader in this case @@ -310,38 +310,45 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float s_blendMaskFromVertexStream = IN.m_blendMask; - // ------- Tangents & Bitangets ------- + LayerBlendSource blendSource = GetFinalLayerBlendSource(); - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - - if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering && MaterialSrg::m_parallaxUvIndex != 0) - || (o_layer1_o_normal_useTexture && MaterialSrg::m_layer1_m_normalMapUvIndex != 0) - || (o_layer2_o_normal_useTexture && MaterialSrg::m_layer2_m_normalMapUvIndex != 0) - || (o_layer3_o_normal_useTexture && MaterialSrg::m_layer3_m_normalMapUvIndex != 0) - || (o_layer1_o_clearCoat_normal_useTexture && MaterialSrg::m_layer1_m_clearCoatNormalMapUvIndex != 0) - || (o_layer2_o_clearCoat_normal_useTexture && MaterialSrg::m_layer2_m_clearCoatNormalMapUvIndex != 0) - || (o_layer3_o_clearCoat_normal_useTexture && MaterialSrg::m_layer3_m_clearCoatNormalMapUvIndex != 0) + // ------- Tangents & Bitangets ------- + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering) + || o_layer1_o_normal_useTexture + || o_layer2_o_normal_useTexture + || o_layer3_o_normal_useTexture + || o_layer1_o_clearCoat_normal_useTexture + || o_layer2_o_clearCoat_normal_useTexture + || o_layer3_o_clearCoat_normal_useTexture ) { - // Generate the tangent/bitangent for UV[1+] - const int startIndex = 1; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, startIndex); + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } // ------- Debug Modes ------- - if(o_debugDrawMode == DebugDrawMode::BlendSource) + if(o_debugDrawMode == DebugDrawMode::BlendMask) + { + float3 blendMaskValues = GetApplicableBlendMaskValues(blendSource, IN.m_uv[MaterialSrg::m_blendMaskUvIndex], IN.m_blendMask); + return DebugOutput(blendMaskValues); + } + + if(o_debugDrawMode == DebugDrawMode::Displacement) { - float3 blendSource = GetBlendSourceValues(IN.m_uv[MaterialSrg::m_blendMaskUvIndex]); - return DebugOutput(blendSource); + float startDepth = -MaterialSrg::m_displacementMax; + float stopDepth = -MaterialSrg::m_displacementMin; + float depth = GetNormalizedDepth(startDepth, stopDepth, IN.m_uv[MaterialSrg::m_parallaxUvIndex], float2(0,0), float2(0,0)); + float height = 1 - saturate(depth); + return DebugOutput(float3(height,height,height)); } - if(o_debugDrawMode == DebugDrawMode::DepthMaps) + if(o_debugDrawMode == DebugDrawMode::FinalBlendWeights) { - float depth = GetNormalizedDepth(-MaterialSrg::m_displacementMax, -MaterialSrg::m_displacementMin, IN.m_uv[MaterialSrg::m_parallaxUvIndex], float2(0,0), float2(0,0)); - return DebugOutput(float3(depth,depth,depth)); + float3 blendWeights = GetBlendWeights(blendSource, IN.m_uv[MaterialSrg::m_blendMaskUvIndex], IN.m_blendMask); + return DebugOutput(blendWeights); } // ------- Parallax ------- @@ -373,21 +380,27 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // ------- Calculate Layer Blend Mask Values ------- // Now that any parallax has been calculated, we calculate the blend factors for any layers that are impacted by the parallax. - float3 blendWeights = GetBlendWeights(IN.m_uv[MaterialSrg::m_blendMaskUvIndex]); + float3 blendWeights = GetBlendWeights(blendSource, IN.m_uv[MaterialSrg::m_blendMaskUvIndex], IN.m_blendMask); // ------- Layer 1 (base layer) ----------- ProcessedMaterialInputs lightingInputLayer1; + if(blendWeights.r > 0) { StandardMaterialInputs inputs; FILL_STANDARD_MATERIAL_INPUTS(inputs, MaterialSrg::m_layer1_, o_layer1_, blendWeights.r) lightingInputLayer1 = ProcessStandardMaterialInputs(inputs); } + else + { + lightingInputLayer1.InitializeToZero(); + blendWeights.r = 0; + } // ----------- Layer 2 ----------- ProcessedMaterialInputs lightingInputLayer2; - if(o_layer2_enabled) + if(o_layer2_enabled && blendWeights.g > 0) { StandardMaterialInputs inputs; FILL_STANDARD_MATERIAL_INPUTS(inputs, MaterialSrg::m_layer2_, o_layer2_, blendWeights.g) @@ -396,12 +409,13 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float else { lightingInputLayer2.InitializeToZero(); + blendWeights.g = 0; } // ----------- Layer 3 ----------- ProcessedMaterialInputs lightingInputLayer3; - if(o_layer3_enabled) + if(o_layer3_enabled && blendWeights.b > 0) { StandardMaterialInputs inputs; FILL_STANDARD_MATERIAL_INPUTS(inputs, MaterialSrg::m_layer3_, o_layer3_, blendWeights.b) @@ -410,6 +424,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float else { lightingInputLayer3.InitializeToZero(); + blendWeights.b = 0; } // ------- Combine all layers --------- @@ -420,12 +435,16 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // ------- Combine Normals --------- - float3 normalTS = lightingInputLayer1.m_normalTS; - if(o_layer2_enabled) + float3 normalTS = float3(0,0,1); + if(blendWeights.r > 0) + { + normalTS = lightingInputLayer1.m_normalTS; + } + if(o_layer2_enabled && blendWeights.g > 0) { normalTS = ReorientTangentSpaceNormal(normalTS, lightingInputLayer2.m_normalTS); } - if(o_layer3_enabled) + if(o_layer3_enabled && blendWeights.b > 0) { normalTS = ReorientTangentSpaceNormal(normalTS, lightingInputLayer3.m_normalTS); } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Parallax.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Parallax.lua deleted file mode 100644 index 8880a4b842..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Parallax.lua +++ /dev/null @@ -1,97 +0,0 @@ --------------------------------------------------------------------------------------- --- --- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or --- its licensors. --- --- For complete copyright and license terms please see the LICENSE at the root of this --- distribution (the "License"). All use of this software is governed by the License, --- or, if provided, by the license below or the license accompanying this file. Do not --- remove or modify any license notices. This file is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- --- ----------------------------------------------------------------------------------------------------- - --- This functor handles general parallax properties that affect the entire material. - -function GetMaterialPropertyDependencies() - return { - "parallax.enable", - "layer1_parallax.enable", - "layer2_parallax.enable", - "layer3_parallax.enable", - "layer1_parallax.factor", - "layer2_parallax.factor", - "layer3_parallax.factor", - "layer1_parallax.offset", - "layer2_parallax.offset", - "layer3_parallax.offset" - } -end - -function GetShaderOptionDependencies() - return {"o_parallax_feature_enabled"} -end - -function MergeRange(heightMinMax, offset, factor) - top = offset - bottom = offset - factor - - if(heightMinMax[1] == nil) then - heightMinMax[1] = top - else - heightMinMax[1] = math.max(heightMinMax[1], top) - end - - if(heightMinMax[0] == nil) then - heightMinMax[0] = bottom - else - heightMinMax[0] = math.min(heightMinMax[0], bottom) - end -end - -function Process(context) - local enableParallax = context:GetMaterialPropertyValue_bool("parallax.enable") - local enable1 = context:GetMaterialPropertyValue_bool("layer1_parallax.enable") - local enable2 = context:GetMaterialPropertyValue_bool("layer2_parallax.enable") - local enable3 = context:GetMaterialPropertyValue_bool("layer3_parallax.enable") - enableParallax = enableParallax and (enable1 or enable2 or enable3) - context:SetShaderOptionValue_bool("o_parallax_feature_enabled", enableParallax) - - if(enableParallax) then - local factorLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.factor") - local factorLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.factor") - local factorLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.factor") - - local offsetLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.offset") - local offsetLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.offset") - local offsetLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.offset") - - local heightMinMax = {nil, nil} - if(enable1) then MergeRange(heightMinMax, offsetLayer1, factorLayer1) end - if(enable2) then MergeRange(heightMinMax, offsetLayer2, factorLayer2) end - if(enable3) then MergeRange(heightMinMax, offsetLayer3, factorLayer3) end - - if(heightMinMax[1] - heightMinMax[0] < 0.0001) then - context:SetShaderOptionValue_bool("o_parallax_feature_enabled", false) - else - context:SetShaderConstant_float("m_displacementMin", heightMinMax[0]) - context:SetShaderConstant_float("m_displacementMax", heightMinMax[1]) - end - end -end - -function ProcessEditor(context) - local enable = context:GetMaterialPropertyValue_bool("parallax.enable") - - local visibility = MaterialPropertyVisibility_Enabled - if(not enable) then - visibility = MaterialPropertyVisibility_Hidden - end - - context:SetMaterialPropertyVisibility("parallax.parallaxUv", visibility) - context:SetMaterialPropertyVisibility("parallax.algorithm", visibility) - context:SetMaterialPropertyVisibility("parallax.quality", visibility) - context:SetMaterialPropertyVisibility("parallax.pdo", visibility) - context:SetMaterialPropertyVisibility("parallax.showClipping", visibility) -end diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua deleted file mode 100644 index bd56292229..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua +++ /dev/null @@ -1,49 +0,0 @@ --------------------------------------------------------------------------------------- --- --- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or --- its licensors. --- --- For complete copyright and license terms please see the LICENSE at the root of this --- distribution (the "License"). All use of this software is governed by the License, --- or, if provided, by the license below or the license accompanying this file. Do not --- remove or modify any license notices. This file is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- --- ----------------------------------------------------------------------------------------------------- - --- This functor handles parallax properties that are specific to a single layer. - -function GetMaterialPropertyDependencies() - return {"parallax.enable", "parallax.textureMap"} -end - -function GetShaderOptionDependencies() - return {"o_useDepthMap"} -end - -function Process(context) - local enable = context:GetMaterialPropertyValue_bool("parallax.enable") - local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") - context:SetShaderOptionValue_bool("o_useDepthMap", enable and textureMap ~= nil) -end - -function ProcessEditor(context) - local enable = context:GetMaterialPropertyValue_bool("parallax.enable") - - if enable then - context:SetMaterialPropertyVisibility("parallax.textureMap", MaterialPropertyVisibility_Enabled) - else - context:SetMaterialPropertyVisibility("parallax.textureMap", MaterialPropertyVisibility_Hidden) - end - - local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") - local visibility = MaterialPropertyVisibility_Enabled - if(not enable or textureMap == nil) then - visibility = MaterialPropertyVisibility_Hidden - end - - context:SetMaterialPropertyVisibility("parallax.factor", visibility) - context:SetMaterialPropertyVisibility("parallax.offset", visibility) - context:SetMaterialPropertyVisibility("parallax.invert", visibility) -end diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl index 02ccd78735..6c80dc874b 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl @@ -83,7 +83,7 @@ VertexOutput MainVS(VertexInput IN) } else { - OUT.m_blendMask = float3(1,1,1); + OUT.m_blendMask = float3(0,0,0); } return OUT; @@ -102,10 +102,10 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) if(ShouldHandleParallaxInDepthShaders()) { - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); s_blendMaskFromVertexStream = IN.m_blendMask; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype index 2b0d09bc5c..2d94f66edf 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype @@ -23,26 +23,11 @@ "displayName": "Specular Reflectance f0", "description": "The constant f0 represents the specular reflectance at normal incidence (Fresnel 0 Angle). Used to adjust reflectance of non-metal surfaces." }, - { - "id": "clearCoat", - "displayName": "Clear Coat", - "description": "Properties for configuring gloss clear coat" - }, { "id": "normal", "displayName": "Normal", "description": "Properties related to configuring surface normal." }, - { - "id": "opacity", - "displayName": "Opacity", - "description": "Properties for configuring the materials transparency." - }, - { - "id": "uv", - "displayName": "UVs", - "description": "Properties for configuring UV transforms." - }, { "id": "occlusion", "displayName": "Occlusion", @@ -53,15 +38,25 @@ "displayName": "Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, + { + "id": "clearCoat", + "displayName": "Clear Coat", + "description": "Properties for configuring gloss clear coat" + }, { "id": "parallax", - "displayName": "Parallax Mapping", - "description": "Properties for parallax effect produced by depthmap." + "displayName": "Displacement", + "description": "Properties for parallax effect produced by a height map." }, { - "id": "subsurfaceScattering", - "displayName": "Subsurface Scattering", - "description": "Properties for configuring subsurface scattering effects." + "id": "opacity", + "displayName": "Opacity", + "description": "Properties for configuring the materials transparency." + }, + { + "id": "uv", + "displayName": "UVs", + "description": "Properties for configuring UV transforms." }, { // Note: this property group is used in the DiffuseGlobalIllumination pass, it is not read by the StandardPBR shader @@ -71,7 +66,7 @@ }, { "id": "general", - "displayName": "General", + "displayName": "General Settings", "description": "General settings." } ], @@ -182,7 +177,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { @@ -193,14 +188,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Base color texture map UV set", + "description": "Base color map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -212,7 +207,7 @@ { "id": "textureBlendMode", "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture Map.", + "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], "defaultValue": "Multiply", @@ -238,7 +233,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "", "type": "Image", "connection": { @@ -249,14 +244,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Metallic texture map UV set", + "description": "Metallic map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -269,8 +264,8 @@ "roughness": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface roughness.", + "displayName": "Texture", + "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", @@ -280,14 +275,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -300,7 +295,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "lowerBound", "displayName": "Lower Bound", - "description": "The roughness value that corresponds to black in the texture map.", + "description": "The roughness value that corresponds to black in the texture.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -314,7 +309,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "upperBound", "displayName": "Upper Bound", - "description": "The roughness value that corresponds to white in the texture map.", + "description": "The roughness value that corresponds to white in the texture.", "type": "Float", "defaultValue": 1.0, "min": 0.0, @@ -355,8 +350,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface reflectance.", + "displayName": "Texture", + "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", @@ -366,14 +361,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Specular reflection texture map UV set", + "description": "Specular reflection map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -418,7 +413,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Strength factor texture map", + "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -428,14 +423,14 @@ { "id": "useInfluenceMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "influenceMapUv", "displayName": " UV", - "description": "Strength factor texture map UV set", + "description": "Strength factor map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -460,7 +455,7 @@ { "id": "roughnessMap", "displayName": " Roughness Map", - "description": "Roughness texture map", + "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", @@ -470,14 +465,14 @@ { "id": "useRoughnessMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the roughness value.", + "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { "id": "roughnessMapUv", "displayName": " UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -519,7 +514,7 @@ { "id": "normalMapUv", "displayName": " UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -532,8 +527,8 @@ "normal": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface normal direction.", + "displayName": "Texture", + "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", @@ -543,14 +538,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", + "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -599,9 +594,9 @@ { "id": "mode", "displayName": "Opacity Mode", - "description": "Opacity mode for this texture.", + "description": "Indicates the general approach how transparency is to be applied.", "type": "Enum", - "enumValues": [ "Opaque", "Cutout", "Blended" ], + "enumValues": [ "Opaque", "Cutout", "Blended", "TintedTransparent" ], "defaultValue": "Opaque", "connection": { "type": "ShaderOption", @@ -611,7 +606,7 @@ { "id": "alphaSource", "displayName": "Alpha Source", - "description": "Source texture of alpha value.", + "description": "Indicates whether to get the opacity texture from the Base Color map (Packed) or from a separate greyscale texture (Split).", "type": "Enum", "enumValues": [ "Packed", "Split", "None" ], "defaultValue": "Packed", @@ -622,8 +617,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface opacity.", + "displayName": "Texture", + "description": "Texture for defining surface opacity.", "type": "Image", "connection": { "type": "ShaderInput", @@ -633,7 +628,7 @@ { "id": "textureMapUv", "displayName": "UV", - "description": "Opacity texture map UV set", + "description": "Opacity map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -669,12 +664,12 @@ "description": "Center point for scaling and rotation transformations.", "type": "vector2", "vectorLabels": [ "U", "V" ], - "defaultValue": [ 0.0, 0.0 ] + "defaultValue": [ 0.5, 0.5 ] }, { "id": "tileU", "displayName": "Tile U", - "description": "Scales texture coordinates in V.", + "description": "Scales texture coordinates in U.", "type": "float", "defaultValue": 1.0, "step": 0.1 @@ -728,7 +723,7 @@ { "id": "diffuseTextureMap", "displayName": "Diffuse AO", - "description": "Texture map for defining occlusion area for diffuse ambient lighting.", + "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -738,14 +733,14 @@ { "id": "diffuseUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Diffuse AO texture map.", + "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { "id": "diffuseTextureMapUv", "displayName": " UV", - "description": "Diffuse AO texture map UV set.", + "description": "Diffuse AO map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -770,7 +765,7 @@ { "id": "specularTextureMap", "displayName": "Specular Cavity", - "description": "Texture map for defining occlusion area for specular lighting.", + "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -780,14 +775,14 @@ { "id": "specularUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Specular Cavity texture map.", + "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { "id": "specularTextureMapUv", "displayName": " UV", - "description": "Specular Cavity texture map UV set.", + "description": "Specular Cavity map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -850,8 +845,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining emissive area.", + "displayName": "Texture", + "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", @@ -861,14 +856,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Emissive texture map UV set", + "description": "Emissive map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -879,27 +874,27 @@ } ], "parallax": [ - { - "id": "enable", - "displayName": "Enable", - "description": "Whether to enable the parallax feature.", - "type": "Bool", - "defaultValue": false - }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Depthmap to create parallax effect.", + "displayName": "Height Map", + "description": "Displacement height map to create parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", - "id": "m_depthMap" + "id": "m_heightmap" } }, + { + "id": "useTexture", + "displayName": "Use Texture", + "description": "Whether to use the height map.", + "type": "Bool", + "defaultValue": true + }, { "id": "textureMapUv", "displayName": "UV", - "description": "Depth texture map UV set", + "description": "Height map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -910,15 +905,15 @@ }, { "id": "factor", - "displayName": "Heightmap Scale", - "description": "The total height of the heightmap in local model units.", + "displayName": "Height Map Scale", + "description": "The total height of the height map in local model units.", "type": "Float", - "defaultValue": 0.0, + "defaultValue": 0.05, "min": 0.0, "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_depthFactor" + "id": "m_heightmapScale" } }, { @@ -931,18 +926,7 @@ "softMax": 0.1, "connection": { "type": "ShaderInput", - "id": "m_depthOffset" - } - }, - { - "id": "invert", - "displayName": "Invert", - "description": "Invert to depthmap if the texture is heightmap", - "type": "Bool", - "defaultValue": true, - "connection": { - "type": "ShaderInput", - "id": "m_depthInverted" + "id": "m_heightmapOffset" } }, { @@ -951,7 +935,7 @@ "description": "Select the algorithm to use for parallax mapping.", "type": "Enum", "enumValues": [ "Basic", "Steep", "POM", "Relief", "ContactRefinement" ], - "defaultValue": "Basic", + "defaultValue": "POM", "connection": { "type": "ShaderOption", "id": "o_parallax_algorithm" @@ -983,7 +967,7 @@ { "id": "showClipping", "displayName": "Show Clipping", - "description": "Highlight areas where the heightmap is clipped by the mesh surface.", + "description": "Highlight areas where the height map is clipped by the mesh surface.", "type": "Bool", "defaultValue": false, "connection": { @@ -992,185 +976,8 @@ } } ], - "subsurfaceScattering": [ - { - "id": "enableSubsurfaceScattering", - "displayName": "Subsurface Scattering", - "description": "Enable subsurface scattering feature, this will disable metallic and parallax mapping property due to incompatibility", - "type": "Bool", - "defaultValue": false, - "connection": { - "type": "ShaderOption", - "id": "o_enableSubsurfaceScattering" - } - }, - { - "id": "subsurfaceScatterFactor", - "displayName": " Factor", - "description": "Strength factor for scaling percentage of subsurface scattering effect applied", - "type": "float", - "defaultValue": 1.0, - "min": 0.0, - "max": 1.0, - "connection": { - "type": "ShaderInput", - "id": "m_subsurfaceScatteringFactor" - } - }, - { - "id": "influenceMap", - "displayName": " Influence Map", - "description": "Use texture map to control the strength of subsurface scattering", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_subsurfaceScatteringInfluenceMap" - } - }, - { - "id": "useInfluenceMap", - "displayName": " Use Influence Map", - "description": "Whether to use the texture map as influence mask.", - "type": "Bool", - "defaultValue": true - }, - { - "id": "influenceMapUv", - "displayName": " UV", - "description": "Influence map UV set", - "type": "Enum", - "enumIsUv": true, - "defaultValue": "Tiled", - "connection": { - "type": "ShaderInput", - "id": "m_subsurfaceScatteringInfluenceMapUvIndex" - } - }, - { - "id": "scatterColor", - "displayName": " Scatter color", - "description": "Color of volume light traveled through", - "type": "Color", - "defaultValue": [ 1.0, 0.27, 0.13 ] - }, - { - "id": "scatterDistance", - "displayName": " Scatter distance", - "description": "How far light traveled inside the volume", - "type": "float", - "defaultValue": 8, - "min": 0.0, - "softMax": 20.0 - }, - { - "id": "quality", - "displayName": " Quality", - "description": "How much percent of sample will be used for each pixel, more samples improve quality and reduce artifacts, especially when the scatter distance is relatively large, but slow down computation time, 1.0 = full set 200 samples per pixel", - "type": "float", - "defaultValue": 0.4, - "min": 0.2, - "max": 1.0, - "connection": { - "type": "ShaderInput", - "id": "m_subsurfaceScatteringQuality" - } - }, - { - "id": "transmissionMode", - "displayName": "Transmission", - "description": "Algorithm used for calculating transmission", - "type": "Enum", - "enumValues": [ "None", "ThickObject", "ThinObject" ], - "defaultValue": "None", - "connection": { - "type": "ShaderOption", - "id": "o_transmission_mode" - } - }, - { - "id": "thickness", - "displayName": " Thickness", - "description": "Normalized global thickness, the maxima between this value (multiplied by thickness map if enabled) and thickness from shadow map (if applicable) will be used as final thickness of pixel", - "type": "float", - "defaultValue": 0.5, - "min": 0.0, - "max": 1.0 - }, - { - "id": "thicknessMap", - "displayName": " Thickness Map", - "description": "Use a greyscale texture for per pixel thickness", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_transmissionThicknessMap" - } - }, - { - "id": "useThicknessMap", - "displayName": " Use Thickness Map", - "description": "Whether to use the thickness map", - "type": "Bool", - "defaultValue": true - }, - { - "id": "thicknessMapUv", - "displayName": " UV", - "description": "Thickness map UV set", - "type": "Enum", - "enumIsUv": true, - "defaultValue": "Tiled", - "connection": { - "type": "ShaderInput", - "id": "m_transmissionThicknessMapUvIndex" - } - }, - { - "id": "transmissionTint", - "displayName": " Transmission Tint", - "description": "Color of the volume light travelling through", - "type": "Color", - "defaultValue": [ 1.0, 0.8, 0.6 ] - }, - { - "id": "transmissionPower", - "displayName": " Power", - "description": "How much transmitted light scatter radially ", - "type": "float", - "defaultValue": 6.0, - "min": 0.0, - "softMax": 20.0 - }, - { - "id": "transmissionDistortion", - "displayName": " Distortion", - "description": "How much light direction distorted towards surface normal", - "type": "float", - "defaultValue": 0.1, - "min": 0.0, - "max": 1.0 - }, - { - "id": "transmissionAttenuation", - "displayName": " Attenuation", - "description": "How fast transmitted light fade with thickness", - "type": "float", - "defaultValue": 4.0, - "min": 0.0, - "softMax": 20.0 - }, - { - "id": "transmissionScale", - "displayName": " Scale", - "description": "Strength of transmission", - "type": "float", - "defaultValue": 3.0, - "min": 0.0, - "softMax": 20.0 - } - ], "irradiance": [ - // Note: this property group is used in the DiffuseGlobalIllumination pass, it is not read by the StandardPBR shader + // Note: this property group is used in the DiffuseGlobalIllumination pass and not by the main forward shader { "id": "color", "displayName": "Color", @@ -1272,25 +1079,6 @@ "nitMinMax": [0.001, 100000.0] } }, - { - // Preprocess & build parameter set for subsurface scattering and translucency - "type": "HandleSubsurfaceScatteringParameters", - "args": { - "mode": "subsurfaceScattering.transmissionMode", - "scale" : "subsurfaceScattering.transmissionScale", - "power" : "subsurfaceScattering.transmissionPower", - "distortion" : "subsurfaceScattering.transmissionDistortion", - "attenuation" : "subsurfaceScattering.transmissionAttenuation", - "tintColor" : "subsurfaceScattering.transmissionTint", - "thickness" : "subsurfaceScattering.thickness", - "enabled": "subsurfaceScattering.enableSubsurfaceScattering", - "scatterDistanceColor" : "subsurfaceScattering.scatterColor", - "scatterDistanceIntensity" : "subsurfaceScattering.scatterDistance", - "scatterDistanceShaderInput" : "m_scatterDistance", - "parametersShaderInput" : "m_transmissionParams", - "tintThickenssShaderInput" : "m_transmissionTintThickness" - } - }, { "type": "UseTexture", "args": { @@ -1375,27 +1163,12 @@ "file": "StandardPBR_Roughness.lua" } }, - { - "type": "Lua", - "args": { - "file": "StandardPBR_SubsurfaceState.lua" - } - }, { "type": "Lua", "args": { "file": "StandardPBR_HandleOpacityDoubleSided.lua" } }, - { - "type": "OverrideDrawList", - "args": { - "triggerProperty": "opacity.mode", - "triggerValue": "Blended", - "shaderIndex": 1, - "drawList": "transparent" - } - }, { "type": "Lua", "args": { diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Common.azsli index 5723a6cd1e..f339642a4d 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Common.azsli @@ -73,31 +73,12 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial MagFilter = Linear; MipFilter = Linear; }; - - // Parameters for subsurface scattering - float m_subsurfaceScatteringFactor; - float m_subsurfaceScatteringQuality; - float3 m_scatterDistance; - Texture2D m_subsurfaceScatteringInfluenceMap; - uint m_subsurfaceScatteringInfluenceMapUvIndex; - - // Parameters for transmission - - // Elements of m_transmissionParams: - // Thick object mode: (attenuation coefficient, power, distortion, scale) - // Thin object mode: (float3 scatter distance, scale) - float4 m_transmissionParams; - - // (float3 TintColor, thickness) - float4 m_transmissionTintThickness; - Texture2D m_transmissionThicknessMap; - uint m_transmissionThicknessMapUvIndex; } // Callback function for ParallaxMapping.azsli DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) { - return SampleDepthOrHeightMap(MaterialSrg::m_depthInverted, MaterialSrg::m_depthMap, MaterialSrg::m_sampler, uv, uv_ddx, uv_ddy); + return SampleDepthFromHeightmap(MaterialSrg::m_heightmap, MaterialSrg::m_sampler, uv, uv_ddx, uv_ddy); } @@ -106,7 +87,7 @@ COMMON_OPTIONS_PARALLAX() bool ShouldHandleParallax() { // Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scattering is enabled. - return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap; + return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useHeightmap; } bool ShouldHandleParallaxInDepthShaders() diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl index 28708c12d5..cc2b4ce659 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl @@ -78,15 +78,15 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) if(ShouldHandleParallaxInDepthShaders()) { - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset, + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_heightmapScale, MaterialSrg::m_heightmapOffset, ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth); } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl index bf1ec96b79..b221ee0a33 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl @@ -47,13 +47,6 @@ COMMON_OPTIONS_EMISSIVE() // Alpha #include "MaterialInputs/AlphaInput.azsli" -// Subsurface -#include "MaterialInputs/SubsurfaceInput.azsli" - -// Transmission -#include "MaterialInputs/TransmissionInput.azsli" - - // ---------- Vertex Shader ---------- struct VSInput @@ -110,19 +103,12 @@ VSOutput StandardPbr_ForwardPassVS(VSInput IN) PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float depthNDC) { // ------- Tangents & Bitangets ------- + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - - if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering && MaterialSrg::m_parallaxUvIndex != 0) - || (o_normal_useTexture && MaterialSrg::m_normalMapUvIndex != 0) - || (o_clearCoat_enabled && o_clearCoat_normal_useTexture && MaterialSrg::m_clearCoatNormalMapUvIndex != 0) - ) + if (ShouldHandleParallax() || o_normal_useTexture || (o_clearCoat_enabled && o_clearCoat_normal_useTexture)) { - // Generate the tangent/bitangent for UV[1+] - const int startIndex = 1; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, startIndex); + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } // ------- Depth & Parallax ------- @@ -131,13 +117,12 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float bool displacementIsClipped = false; - // Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scatteirng is enabled if(ShouldHandleParallax()) { float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset, + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_heightmapScale, MaterialSrg::m_heightmapOffset, ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depthNDC, IN.m_position.w, displacementIsClipped); @@ -181,12 +166,8 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // ------- Metallic ------- - float metallic = 0; - if(!o_enableSubsurfaceScattering) // If subsurface scattering is enabled skip texture lookup for metallic, as this quantity won't be used anyway - { - float2 metallicUv = IN.m_uv[MaterialSrg::m_metallicMapUvIndex]; - metallic = GetMetallicInput(MaterialSrg::m_metallicMap, MaterialSrg::m_sampler, metallicUv, MaterialSrg::m_metallicFactor, o_metallic_useTexture); - } + float2 metallicUv = IN.m_uv[MaterialSrg::m_metallicMapUvIndex]; + float metallic = GetMetallicInput(MaterialSrg::m_metallicMap, MaterialSrg::m_sampler, metallicUv, MaterialSrg::m_metallicFactor, o_metallic_useTexture); // ------- Specular ------- @@ -202,11 +183,6 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float MaterialSrg::m_roughnessLowerBound, MaterialSrg::m_roughnessUpperBound, o_roughness_useTexture); surface.CalculateRoughnessA(); - // ------- Subsurface ------- - - float surfaceScatteringFactor = 0.0f; - surface.transmission.InitializeToZero(); - // ------- Lighting Data ------- LightingData lightingData; @@ -278,7 +254,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float ApplyIBL(surface, lightingData); // Finalize Lighting - lightingData.FinalizeLighting(surface.transmission.tint); + lightingData.FinalizeLighting(); if (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent) @@ -301,12 +277,23 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float lightingOutput.m_diffuseColor.rgb *= lightingOutput.m_diffuseColor.w; // pre-multiply diffuse lightingOutput.m_diffuseColor.rgb += lightingOutput.m_specularColor.rgb; // add specular } - else + else if (o_opacity_mode == OpacityMode::TintedTransparent) { - // Pack factor and quality, drawback: because of precision limit of float16 cannot represent exact 1, maximum representable value is 0.9961 - uint factorAndQuality = dot(round(float2(saturate(surfaceScatteringFactor), MaterialSrg::m_subsurfaceScatteringQuality) * 255), float2(256, 1)); - lightingOutput.m_diffuseColor.w = factorAndQuality * (o_enableSubsurfaceScattering ? 1.0 : -1.0); - lightingOutput.m_scatterDistance = MaterialSrg::m_scatterDistance; + // See OpacityMode::Blended above for the basic method. TintedTransparent adds onto the above concept by supporting + // colored alpha. This is currently a very basic calculation that uses the baseColor as a multiplier with strength + // determined by the alpha. We'll modify this later to be more physically accurate and allow surface depth, + // absorption, and interior color to be specified. + // + // The technique uses dual source blending to allow two separate sources to be part of the blending equation + // even though ultimately only a single render target is being written to. m_diffuseColor is render target 0 and + // m_specularColor render target 1, and the blend mode is (dest * source1color) + (source * 1.0). + // + // This means that m_specularColor.rgb (source 1) is multiplied against the destination, then + // m_diffuseColor.rgb (source) is added to that, and the final result is stored in render target 0. + + lightingOutput.m_diffuseColor.rgb *= lightingOutput.m_diffuseColor.w; // pre-multiply diffuse + lightingOutput.m_diffuseColor.rgb += lightingOutput.m_specularColor.rgb; // add specular + lightingOutput.m_specularColor.rgb = baseColor * (1.0 - lightingOutput.m_diffuseColor.w); } return lightingOutput; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua index 541b1ac1ce..20d3ee47ae 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua @@ -60,10 +60,13 @@ function Process(context) if(opacityMode == OpacityMode_Blended) then ConfigureAlphaBlending(context:GetShader(ForwardPassIndex)) + context:GetShader(ForwardPassIndex):SetDrawListTagOverride("transparent") elseif(opacityMode == OpacityMode_TintedTransparent) then ConfigureDualSourceBlending(context:GetShader(ForwardPassIndex)) + context:GetShader(ForwardPassIndex):SetDrawListTagOverride("transparent") else ResetAlphaBlending(context:GetShader(ForwardPassIndex)) + context:GetShader(ForwardPassIndex):SetDrawListTagOverride("") -- reset to default draw list end end diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua index e6689da327..771726aea7 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua @@ -13,35 +13,39 @@ ---------------------------------------------------------------------------------------------------- function GetMaterialPropertyDependencies() - return {"parallax.enable", "parallax.textureMap"} + return {"parallax.textureMap", "parallax.useTexture"} end function GetShaderOptionDependencies() - return {"o_parallax_feature_enabled", "o_useDepthMap"} + return {"o_parallax_feature_enabled", "o_useHeightmap"} end function Process(context) - local enable = context:GetMaterialPropertyValue_bool("parallax.enable") local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") + local useTexture = context:GetMaterialPropertyValue_bool("parallax.useTexture") + local enable = textureMap ~= nil and useTexture context:SetShaderOptionValue_bool("o_parallax_feature_enabled", enable) - context:SetShaderOptionValue_bool("o_useDepthMap", enable and textureMap ~= nil) + context:SetShaderOptionValue_bool("o_useHeightmap", enable) end function ProcessEditor(context) - local enable = context:GetMaterialPropertyValue_bool("parallax.enable") + local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") - if enable then - context:SetMaterialPropertyVisibility("parallax.textureMap", MaterialPropertyVisibility_Enabled) + if textureMap ~= nil then + context:SetMaterialPropertyVisibility("parallax.useTexture", MaterialPropertyVisibility_Enabled) else - context:SetMaterialPropertyVisibility("parallax.textureMap", MaterialPropertyVisibility_Hidden) + context:SetMaterialPropertyVisibility("parallax.useTexture", MaterialPropertyVisibility_Hidden) end - local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") + local useTexture = context:GetMaterialPropertyValue_bool("parallax.useTexture") + local visibility = MaterialPropertyVisibility_Enabled - if(not enable or textureMap == nil) then + if(textureMap == nil) then visibility = MaterialPropertyVisibility_Hidden + elseif not useTexture then + visibility = MaterialPropertyVisibility_Disabled end - + context:SetMaterialPropertyVisibility("parallax.factor", visibility) context:SetMaterialPropertyVisibility("parallax.offset", visibility) context:SetMaterialPropertyVisibility("parallax.showClipping", visibility) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ShaderEnable.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ShaderEnable.lua index e502eb38f8..b245fde3df 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ShaderEnable.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ShaderEnable.lua @@ -13,7 +13,7 @@ ---------------------------------------------------------------------------------------------------- function GetMaterialPropertyDependencies() - return {"opacity.mode", "parallax.enable", "parallax.pdo"} + return {"opacity.mode", "parallax.textureMap", "parallax.useTexture", "parallax.pdo"} end OpacityMode_Opaque = 0 @@ -21,41 +21,61 @@ OpacityMode_Cutout = 1 OpacityMode_Blended = 2 OpacityMode_TintedTransparent = 3 +function TryGetShaderByTag(context, shaderTag) + if context:HasShaderWithTag(shaderTag) then + return context:GetShaderByTag(shaderTag) + else + return nil + end +end + +function TrySetShaderEnabled(shader, enabled) + if shader then + shader:SetEnabled(enabled) + end +end + function Process(context) local opacityMode = context:GetMaterialPropertyValue_enum("opacity.mode") - local parallaxEnabled = context:GetMaterialPropertyValue_bool("parallax.enable") + local displacementMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") + local useDisplacementMap = context:GetMaterialPropertyValue_bool("parallax.useTexture") + local parallaxEnabled = displacementMap ~= nil and useDisplacementMap local parallaxPdoEnabled = context:GetMaterialPropertyValue_bool("parallax.pdo") local depthPass = context:GetShaderByTag("DepthPass") local shadowMap = context:GetShaderByTag("Shadowmap") local forwardPassEDS = context:GetShaderByTag("ForwardPass_EDS") - local lowEndForwardEDS = context:GetShaderByTag("LowEndForward_EDS") local depthPassWithPS = context:GetShaderByTag("DepthPass_WithPS") local shadowMapWithPS = context:GetShaderByTag("Shadowmap_WithPS") local forwardPass = context:GetShaderByTag("ForwardPass") - local lowEndForward = context:GetShaderByTag("LowEndForward") + + -- Use TryGetShaderByTag because these shaders only exist in StandardPBR but this script is also used for EnhancedPBR + local lowEndForwardEDS = TryGetShaderByTag(context, "LowEndForward_EDS") + local lowEndForward = TryGetShaderByTag(context, "LowEndForward") if parallaxEnabled and parallaxPdoEnabled then depthPass:SetEnabled(false) shadowMap:SetEnabled(false) forwardPassEDS:SetEnabled(false) - lowEndForwardEDS:SetEnabled(false) depthPassWithPS:SetEnabled(true) shadowMapWithPS:SetEnabled(true) forwardPass:SetEnabled(true) - lowEndForward:SetEnabled(true) + + TrySetShaderEnabled(lowEndForwardEDS, false) + TrySetShaderEnabled(lowEndForward, true) else depthPass:SetEnabled(opacityMode == OpacityMode_Opaque) shadowMap:SetEnabled(opacityMode == OpacityMode_Opaque) forwardPassEDS:SetEnabled((opacityMode == OpacityMode_Opaque) or (opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent)) - lowEndForwardEDS:SetEnabled((opacityMode == OpacityMode_Opaque) or (opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent)) depthPassWithPS:SetEnabled(opacityMode == OpacityMode_Cutout) shadowMapWithPS:SetEnabled(opacityMode == OpacityMode_Cutout) forwardPass:SetEnabled(opacityMode == OpacityMode_Cutout) - lowEndForward:SetEnabled(opacityMode == OpacityMode_Cutout) + + TrySetShaderEnabled(lowEndForwardEDS, (opacityMode == OpacityMode_Opaque) or (opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent)) + TrySetShaderEnabled(lowEndForward, opacityMode == OpacityMode_Cutout) end context:GetShaderByTag("DepthPassTransparentMin"):SetEnabled((opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent)) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl index 7f24b29700..8b6fee849e 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl @@ -81,15 +81,14 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) { static const float ShadowMapDepthBias = 0.000001; - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset, + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_heightmapScale, MaterialSrg::m_heightmapOffset, ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth); diff --git a/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass index b19569fb9d..38a4524aa2 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass @@ -243,6 +243,13 @@ "Attachment": "LightListRemapped" } }, + { + "LocalSlot": "InputLinearDepth", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "DepthLinear" + } + }, { "LocalSlot": "DepthStencil", "AttachmentRef": { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass index ee943f6d39..af7408b48c 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass @@ -251,6 +251,13 @@ "Attachment": "LightListRemapped" } }, + { + "LocalSlot": "InputLinearDepth", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "DepthLinear" + } + }, { "LocalSlot": "DepthStencil", "AttachmentRef": { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/Transparent.pass b/Gems/Atom/Feature/Common/Assets/Passes/Transparent.pass index 415aa2fec0..3bb184a1f8 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/Transparent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/Transparent.pass @@ -67,6 +67,12 @@ "ShaderInputName": "m_lightListRemapped", "ScopeAttachmentUsage": "Shader" }, + { + "Name": "InputLinearDepth", + "SlotType": "Input", + "ShaderInputName": "m_linearDepthTexture", + "ScopeAttachmentUsage": "Shader" + }, // Input/Outputs { "Name": "DepthStencil", diff --git a/Gems/Atom/Feature/Common/Assets/Passes/TransparentParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/TransparentParent.pass index 32517484f3..b278f2bcb4 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/TransparentParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/TransparentParent.pass @@ -32,6 +32,10 @@ "Name": "LightListRemapped", "SlotType": "Input" }, + { + "Name": "InputLinearDepth", + "SlotType": "Input" + }, // Input/Outputs... { "Name": "DepthStencil", @@ -91,6 +95,13 @@ "Attachment": "LightListRemapped" } }, + { + "LocalSlot": "InputLinearDepth", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "InputLinearDepth" + } + }, // Input/Outputs... { "LocalSlot": "DepthStencil", diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli index 37cae0acb1..e37aa55c09 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli @@ -54,6 +54,7 @@ class LightingData void Init(float3 positionWS, float3 normal, float roughnessLinear); void CalculateMultiscatterCompensation(float3 specularF0, bool enabled); + void FinalizeLighting(); void FinalizeLighting(float3 transmissionTint); }; @@ -80,10 +81,15 @@ void LightingData::CalculateMultiscatterCompensation(float3 specularF0, bool ena multiScatterCompensation = GetMultiScatterCompensation(specularF0, brdf, enabled); } -void LightingData::FinalizeLighting(float3 transmissionTint) +void LightingData::FinalizeLighting() { specularLighting *= specularOcclusion; specularLighting += emissiveLighting; +} + +void LightingData::FinalizeLighting(float3 transmissionTint) +{ + FinalizeLighting(); // Transmitted light if(o_transmission_mode != TransmissionMode::None) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/StandardLighting.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/StandardLighting.azsli index 45aabeede4..f690163a58 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/StandardLighting.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/StandardLighting.azsli @@ -75,7 +75,6 @@ struct PbrLightingOutput float4 m_albedo; float4 m_specularF0; float4 m_normal; - float3 m_scatterDistance; }; diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli index 1a74a68e96..bc4e41d2c2 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli @@ -20,7 +20,7 @@ class Surface { ClearCoatSurfaceData clearCoat; - TransmissionSurfaceData transmission; + TransmissionSurfaceData transmission; // This is not actually used for Standard PBR, but must be present for common lighting code to compile // ------- BasePbrSurfaceData ------- diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli index 14cff21739..d9367f9d03 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli @@ -35,4 +35,5 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_tileLightData; StructuredBuffer m_lightListRemapped; + Texture2D m_linearDepthTexture; } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ParallaxMapping.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ParallaxMapping.azsli index 8b1efc8eea..ff2a37d29b 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ParallaxMapping.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ParallaxMapping.azsli @@ -58,7 +58,7 @@ DepthResult DepthResultAbsolute(float depth) //! The client shader must define this function. //! This allows the client shader to implement special depth map sampling, for example procedurally generating or blending depth maps. -//! In simple cases though, the implementation of GetDepth() can simply call SampleDepthOrHeightMap(). +//! In simple cases though, the implementation of GetDepth() can simply call SampleDepthFromHeightmap(). //! @param uv the UV coordinates to use for sampling //! @param uv_ddx will be set to ddx_fine(uv) //! @param uv_ddy will be set to ddy_fine(uv) @@ -66,13 +66,12 @@ DepthResult DepthResultAbsolute(float depth) DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy); //! Convenience function that can be used to implement GetDepth(). -//! @param isHeightmap indicates whether to sample the map is a height map rather than a depth map. //! @return see struct DepthResult. In this case it will always contain a Code::Normalized result. -DepthResult SampleDepthOrHeightMap(bool isHeightmap, Texture2D map, sampler mapSampler, float2 uv, float2 uv_ddx, float2 uv_ddy) +DepthResult SampleDepthFromHeightmap(Texture2D map, sampler mapSampler, float2 uv, float2 uv_ddx, float2 uv_ddy) { DepthResult result; result.m_resultCode = DepthResultCode_Normalized; - result.m_depth = abs((isHeightmap * 1.0) - map.SampleGrad(mapSampler, uv, uv_ddx, uv_ddy).r); + result.m_depth = 1.0 - map.SampleGrad(mapSampler, uv, uv_ddx, uv_ddy).r; return result; } @@ -169,20 +168,20 @@ ParallaxOffset AdvancedParallaxMapping(float depthFactor, float depthOffset, flo float2 ddx_uv = ddx_fine(uv); float2 ddy_uv = ddy_fine(uv); - float depthSearchStart = -depthOffset; + float depthSearchStart = depthOffset; float depthSearchEnd = depthSearchStart + depthFactor; float inverseDepthFactor = 1.0 / depthFactor; // This is the relative position at which we begin searching for intersection. // It is adjusted according to the depthOffset, raising or lowering the whole surface by depthOffset units. - float3 parallaxOffset = dirToCameraTS.xyz * dirToCameraZInverse * depthOffset; + float3 parallaxOffset = -dirToCameraTS.xyz * dirToCameraZInverse * depthOffset; // Get an initial heightmap sample to start the intersection search, starting at our initial parallaxOffset position. float currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv); float prevSample; - // Note that when depthOffset < 0, we could actually narrow the search so that instead of going through the entire [depthSearchStart,depthSearchEnd] range + // Note that when depthOffset > 0, we could actually narrow the search so that instead of going through the entire [depthSearchStart,depthSearchEnd] range // of the heightmap, we could go through the range [0,depthSearchEnd]. This would give more accurate results and fewer artifacts // in case where the magnitude of depthOffset is significant. But for the sake of simplicity we currently search the whole range in all cases. @@ -271,7 +270,7 @@ ParallaxOffset AdvancedParallaxMapping(float depthFactor, float depthOffset, flo } // Even though we do a bunch of clamping above when calling GetClampedDepth(), there are still cases where the parallax offset - // can be noticeably above the surface and still needs to be clamped here. The main case is when depthFactor==0 and depthOffset>1. + // can be noticeably above the surface and still needs to be clamped here. The main case is when depthFactor==0 and depthOffset<1. if(parallaxOffset.z > 0.0) { parallaxOffset = float3(0,0,0); @@ -371,13 +370,13 @@ ParallaxOffset CalculateParallaxOffset(float depthFactor, float depthOffset, flo // @param dirToCameraTS - normalized direction to the camera, in tangent space. // @param dirToLightTS - normalized direction to a light source, in tangent space, for self-shadowing (if enabled via o_parallax_shadow). ParallaxOffset GetParallaxOffset( float depthFactor, - float depthOffset, - float2 uv, - float3 dirToCameraWS, - float3 tangentWS, - float3 bitangentWS, - float3 normalWS, - float3x3 uvMatrix) + float depthOffset, + float2 uv, + float3 dirToCameraWS, + float3 tangentWS, + float3 bitangentWS, + float3 normalWS, + float3x3 uvMatrix) { // Tangent space eye vector float3 dirToCameraTS = normalize(WorldSpaceToTangent(dirToCameraWS, normalWS, tangentWS, bitangentWS)); diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialSrg.azsli new file mode 100644 index 0000000000..40faba24f9 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialSrg.azsli @@ -0,0 +1,48 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +ShaderResourceGroup RayTracingMaterialSrg : SRG_RayTracingMaterial +{ + Sampler LinearSampler + { + AddressU = Wrap; + AddressV = Wrap; + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + MaxAnisotropy = 16; + }; + + // material info structured buffer + struct MaterialInfo + { + float4 m_baseColor; + float m_metallicFactor; + float m_roughnessFactor; + uint m_textureFlags; + uint m_textureStartIndex; + }; + + // hit shaders can retrieve the MaterialInfo for a mesh hit using: RayTracingMaterialSrg::m_materialInfo[InstanceIndex()] + StructuredBuffer m_materialInfo; + + // texture flag bits indicating if optional textures are present + #define TEXTURE_FLAG_BASECOLOR 1 + #define TEXTURE_FLAG_NORMAL 2 + #define TEXTURE_FLAG_METALLIC 4 + #define TEXTURE_FLAG_ROUGHNESS 8 + + // unbounded array of Material textures + Texture2D m_materialTextures[]; +} \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialUtils.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialUtils.azsli new file mode 100644 index 0000000000..d6dd77f4fa --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialUtils.azsli @@ -0,0 +1,69 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +struct TextureData +{ + float4 m_baseColor; + float3 m_normal; + float m_metallic; + float m_roughness; +}; + +TextureData GetHitTextureData(RayTracingMaterialSrg::MaterialInfo materialInfo, float2 uv) +{ + TextureData textureData = (TextureData)0; + + uint textureIndex = materialInfo.m_textureStartIndex; + + // base color + if (materialInfo.m_textureFlags & TEXTURE_FLAG_BASECOLOR) + { + textureData.m_baseColor = RayTracingMaterialSrg::m_materialTextures[textureIndex++].SampleLevel(RayTracingMaterialSrg::LinearSampler, uv, 0); + } + else + { + textureData.m_baseColor = materialInfo.m_baseColor; + } + + // normal + if (materialInfo.m_textureFlags & TEXTURE_FLAG_NORMAL) + { + textureData.m_normal = RayTracingMaterialSrg::m_materialTextures[textureIndex++].SampleLevel(RayTracingMaterialSrg::LinearSampler, uv, 0); + } + else + { + textureData.m_normal = float3(0.0f, 0.0f, 1.0f); + } + + // metallic + if (materialInfo.m_textureFlags & TEXTURE_FLAG_METALLIC) + { + textureData.m_metallic = RayTracingMaterialSrg::m_materialTextures[textureIndex++].SampleLevel(RayTracingMaterialSrg::LinearSampler, uv, 0); + } + else + { + textureData.m_metallic = materialInfo.m_metallicFactor; + } + + // roughness + if (materialInfo.m_textureFlags & TEXTURE_FLAG_ROUGHNESS) + { + textureData.m_roughness = RayTracingMaterialSrg::m_materialTextures[textureIndex++].SampleLevel(RayTracingMaterialSrg::LinearSampler, uv, 0); + } + else + { + textureData.m_roughness = materialInfo.m_roughnessFactor; + } + + return textureData; +} + \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli index fdf7ba92de..b8c97ef421 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli @@ -136,18 +136,35 @@ ShaderResourceGroup RayTracingSceneSrg : SRG_RayTracingScene uint m_indexOffset; uint m_positionOffset; uint m_normalOffset; + uint m_tangentOffset; + uint m_bitangentOffset; + uint m_uvOffset; + float m_padding0[2]; + float4 m_irradianceColor; float3x3 m_worldInvTranspose; + float m_padding1; + + uint m_bufferFlags; + uint m_bufferStartIndex; }; - + + // hit shaders can retrieve the MeshInfo for a mesh hit using: RayTracingSceneSrg::m_meshInfo[InstanceIndex()] StructuredBuffer m_meshInfo; - // unbounded array of Index, VertexPosition, and VertexNormal buffers - // each mesh has three entries in this array starting at its InstanceIndex() * BUFFER_COUNT_PER_MESH - #define BUFFER_COUNT_PER_MESH 3 - #define MESH_INDEX_BUFFER_OFFSET 0 - #define MESH_POSITION_BUFFER_OFFSET 1 - #define MESH_NORMAL_BUFFER_OFFSET 2 - + // buffer array index offsets for buffers that are always present for each mesh + #define MESH_INDEX_BUFFER_OFFSET 0 + #define MESH_POSITION_BUFFER_OFFSET 1 + #define MESH_NORMAL_BUFFER_OFFSET 2 + #define MESH_TANGENT_BUFFER_OFFSET 3 + #define MESH_BITANGENT_BUFFER_OFFSET 4 + + // buffer flag bits indicating if optional buffers are present + #define MESH_BUFFER_FLAG_UV 1 + + // Unbounded array of mesh stream buffers: + // - Index, Position, Normal, Tangent, and Bitangent stream buffers are always present + // - Optional stream buffers such as UV are indicated in the MeshInfo.m_bufferFlags field + // - Buffers for a particular mesh start at MeshInfo.m_bufferStartIndex ByteAddressBuffer m_meshBuffers[]; } \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneUtils.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneUtils.azsli new file mode 100644 index 0000000000..f858b9a11a --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneUtils.azsli @@ -0,0 +1,126 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +// returns the normalized camera view ray into the scene for this raytracing dispatch thread +float3 GetViewRayDirection(float4x4 viewProjectionInverseMatrix) +{ + float2 pixel = ((float2)DispatchRaysIndex().xy + float2(0.5f, 0.5f)) / (float2)DispatchRaysDimensions(); + float2 ndc = pixel * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f); + return normalize(mul(viewProjectionInverseMatrix, float4(ndc, 0.0f, 1.0f)).xyz); +} + +// returns the vertex indices for the primitive hit by the ray +// Note: usable only in a raytracing Hit shader +uint3 GetHitIndices(RayTracingSceneSrg::MeshInfo meshInfo) +{ + // compute the array index of the index buffer for this mesh in the m_meshBuffers unbounded array + uint meshIndexBufferArrayIndex = meshInfo.m_bufferStartIndex + MESH_INDEX_BUFFER_OFFSET; + + // compute the offset into the index buffer for this primitve of the mesh + uint offsetBytes = meshInfo.m_indexOffset + (PrimitiveIndex() * 12); + + // load the indices for this primitive from the index buffer + return RayTracingSceneSrg::m_meshBuffers[meshIndexBufferArrayIndex].Load3(offsetBytes); +} + +// returns the interpolated vertex data for the primitive hit by the ray +// Note: usable only in a raytracing hit shader +struct VertexData +{ + float3 m_position; + float3 m_normal; + float3 m_tangent; + float3 m_bitangent; + float2 m_uv; +}; + +VertexData GetHitInterpolatedVertexData(RayTracingSceneSrg::MeshInfo meshInfo, float2 builtInBarycentrics) +{ + // retrieve the poly indices + uint3 indices = GetHitIndices(meshInfo); + + // compute barycentrics + float3 barycentrics = float3((1.0f - builtInBarycentrics.x - builtInBarycentrics.y), builtInBarycentrics.x, builtInBarycentrics.y); + + // compute the vertex data using barycentric interpolation + VertexData vertexData = (VertexData)0; + for (uint i = 0; i < 3; ++i) + { + // position + { + // array index of the position buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexPositionArrayIndex = meshInfo.m_bufferStartIndex + MESH_POSITION_BUFFER_OFFSET; + + // offset into the position buffer for this vertex + uint positionOffset = meshInfo.m_positionOffset + (indices[i] * 12); + + // load the position data + vertexData.m_position += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexPositionArrayIndex].Load3(positionOffset)) * barycentrics[i]; + } + + // normal + { + // array index of the normal buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexNormalArrayIndex = meshInfo.m_bufferStartIndex + MESH_NORMAL_BUFFER_OFFSET; + + // offset into the normal buffer for this vertex + uint normalOffset = meshInfo.m_normalOffset + (indices[i] * 12); + + // load the normal data + vertexData.m_normal += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexNormalArrayIndex].Load3(normalOffset)) * barycentrics[i]; + } + + // tangent + { + // array index of the tangent buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexTangentArrayIndex = meshInfo.m_bufferStartIndex + MESH_TANGENT_BUFFER_OFFSET; + + // offset into the tangent buffer for this vertex + uint tangentOffset = meshInfo.m_tangentOffset + (indices[i] * 12); + + // load the tangent data + vertexData.m_tangent += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexTangentArrayIndex].Load3(tangentOffset)) * barycentrics[i]; + } + + // bitangent + { + // array index of the bitangent buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexBitangentArrayIndex = meshInfo.m_bufferStartIndex + MESH_BITANGENT_BUFFER_OFFSET; + + // offset into the bitangent buffer for this vertex + uint bitangentOffset = meshInfo.m_bitangentOffset + (indices[i] * 12); + + // load the bitangent data + vertexData.m_bitangent += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexBitangentArrayIndex].Load3(bitangentOffset)) * barycentrics[i]; + } + + // optional streams begin after MESH_BITANGENT_BUFFER_OFFSET + uint optionalBufferOffset = MESH_BITANGENT_BUFFER_OFFSET + 1; + + // UV + if (meshInfo.m_bufferFlags & MESH_BUFFER_FLAG_UV) + { + // array index of the UV buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexUVArrayIndex = meshInfo.m_bufferStartIndex + optionalBufferOffset++; + + // offset into the UV buffer for this vertex + uint uvOffset = meshInfo.m_uvOffset + (indices[i] * 8); + + // load the UV data + vertexData.m_uv += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexUVArrayIndex].Load2(uvOffset)) * barycentrics[i]; + } + } + + vertexData.m_normal = normalize(vertexData.m_normal); + + return vertexData; +} \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/SrgSemantics.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/SrgSemantics.azsli index c134a9d293..6d1b05d797 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/SrgSemantics.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/SrgSemantics.azsli @@ -64,3 +64,8 @@ ShaderResourceGroupSemantic SRG_RayTracingScene { FrequencyId = 1; }; + +ShaderResourceGroupSemantic SRG_RayTracingMaterial +{ + FrequencyId = 2; +}; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader index 804b534277..746ae357ee 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_dx12_0.azshadervariant index b3bee56bfd..841c5defe3 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_null_0.azshadervariant index d9b2f17848..39eacb4ade 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_vulkan_0.azshadervariant index cf39160d3f..597b239218 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader index c4a83a26dc..0f42cd04d6 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_dx12_0.azshadervariant index fb5b740f04..49bc94767a 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant index b7bdabeb4e..d9d30c99f7 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant index 1eeeffa8be..e2f2a13ca3 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingcommon_raytracingglobalsrg.azsrg b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingcommon_raytracingglobalsrg.azsrg index c9fa0f4e1c..35d7050294 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingcommon_raytracingglobalsrg.azsrg +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingcommon_raytracingglobalsrg.azsrg @@ -44,8 +44,14 @@ "field": "element", "typeName": "ShaderResourceGroupLayout", "typeId": "{1F92C651-9B83-4379-AB5C-5201F1B2C278}", - "version": 6, + "version": 7, "Objects": [ + { + "field": "m_name", + "typeName": "Name", + "typeId": "{3D2B920C-9EFD-40D5-AAE0-DF131C3D4931}", + "value": "" + }, { "field": "m_staticSamplers", "typeName": "AZStd::vector", @@ -3338,8 +3344,14 @@ "field": "element", "typeName": "ShaderResourceGroupLayout", "typeId": "{1F92C651-9B83-4379-AB5C-5201F1B2C278}", - "version": 6, + "version": 7, "Objects": [ + { + "field": "m_name", + "typeName": "Name", + "typeId": "{3D2B920C-9EFD-40D5-AAE0-DF131C3D4931}", + "value": "" + }, { "field": "m_staticSamplers", "typeName": "AZStd::vector", @@ -6632,8 +6644,14 @@ "field": "element", "typeName": "ShaderResourceGroupLayout", "typeId": "{1F92C651-9B83-4379-AB5C-5201F1B2C278}", - "version": 6, + "version": 7, "Objects": [ + { + "field": "m_name", + "typeName": "Name", + "typeId": "{3D2B920C-9EFD-40D5-AAE0-DF131C3D4931}", + "value": "" + }, { "field": "m_staticSamplers", "typeName": "AZStd::vector", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss.azshader index 4f0cea1bfe..77a0f94f97 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant index 839efa7278..c144350c7f 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_null_0.azshadervariant index 5f08825487..c369381fe9 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_vulkan_0.azshadervariant index a5b0e842ef..13fd579d9b 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/AreaTex.dds.assetinfo b/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/AreaTex.dds.assetinfo index 53dbfb2623..72a7948174 100644 --- a/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/AreaTex.dds.assetinfo +++ b/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/AreaTex.dds.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/SearchTex.dds.assetinfo b/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/SearchTex.dds.assetinfo index 3ae9447621..86d698d24e 100644 --- a/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/SearchTex.dds.assetinfo +++ b/Gems/Atom/Feature/Common/Assets/Textures/PostProcessing/SearchTex.dds.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr deleted file mode 100644 index 0fcbcc4746..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bc9981393c88c6d30a0a5a6837e6f6246a9f550042b3c4be39dd34e479b90569 -size 16793931 diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr.assetinfo b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr.assetinfo deleted file mode 100644 index da00149fb0..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr.assetinfo +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleBrdf_lut.dds b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleBrdf_lut.dds deleted file mode 100644 index f2b2ce550d..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleBrdf_lut.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:440bcb1579d4ad667c040bda914ed3121980526a94f23879a0c482c799fd5132 -size 1310848 diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleDiffuseHDR_cm.dds b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleDiffuseHDR_cm.dds deleted file mode 100644 index 9585c25dd1..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleDiffuseHDR_cm.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cf4b22481726214c062ac27fb2d9d8a49e760a76d8fff03330ff9c5f9275a8da -size 1966208 diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleSpecularHDR_cm.dds b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleSpecularHDR_cm.dds deleted file mode 100644 index a35fac45fb..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleSpecularHDR_cm.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8b4c99faffc34988c268613948f2004f40e9bd51de915461a4cb74edc5e8bae6 -size 134217920 diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/example_iblskyboxcm.dds b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/example_iblskyboxcm.dds deleted file mode 100644 index a89dfdbd3d..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/example_iblskyboxcm.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f7edea42ded8143764654f12c19e0c9b74c74afb21f435ebb59c0a4d203892a3 -size 536871104 diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/papermill_license.txt b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/papermill_license.txt deleted file mode 100644 index 83cfe08ab9..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/papermill_license.txt +++ /dev/null @@ -1,10 +0,0 @@ -The papermill 'Image base lighting' (IBL) images are modified from the following: - -http://www.hdrlabs.com/sibl/archive.html -'Papermill Ruins E' - -All sIBL-sets on this page, including the images within, are licensed under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 License. - -Creative Commons License: http://creativecommons.org/licenses/by-nc-sa/3.0/us/ - -Remember: Do what you want with them, but always mention where you got them from... \ No newline at end of file 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 f1d8fa81be..359c0b9b20 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 @@ -23,6 +23,7 @@ set(FILES Materials/Types/EnhancedPBR_ForwardPass_EDS.shader Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl Materials/Types/EnhancedPBR_Shadowmap_WithPS.shader + Materials/Types/EnhancedPBR_SubsurfaceState.lua Materials/Types/Skin.azsl Materials/Types/Skin.materialtype Materials/Types/Skin.shader @@ -33,11 +34,11 @@ set(FILES Materials/Types/StandardMultilayerPBR_Common.azsli Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.shader + Materials/Types/StandardMultilayerPBR_Displacement.lua Materials/Types/StandardMultilayerPBR_ForwardPass.azsl Materials/Types/StandardMultilayerPBR_ForwardPass.shader Materials/Types/StandardMultilayerPBR_ForwardPass_EDS.shader - Materials/Types/StandardMultilayerPBR_Parallax.lua - Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua + Materials/Types/StandardMultilayerPBR_LayerEnable.lua Materials/Types/StandardMultilayerPBR_ShaderEnable.lua Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.shader @@ -61,7 +62,6 @@ set(FILES Materials/Types/StandardPBR_ShaderEnable.lua Materials/Types/StandardPBR_Shadowmap_WithPS.azsl Materials/Types/StandardPBR_Shadowmap_WithPS.shader - Materials/Types/StandardPBR_SubsurfaceState.lua Materials/Types/MaterialInputs/AlphaInput.azsli Materials/Types/MaterialInputs/BaseColorInput.azsli Materials/Types/MaterialInputs/ClearCoatInput.azsli diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h index 15b416597d..73ce175d99 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h @@ -14,6 +14,9 @@ #include #include +#include +#include +#include namespace AZ { @@ -23,6 +26,57 @@ namespace AZ using DiffuseProbeGridHandle = AZStd::shared_ptr; + enum class DiffuseProbeGridMode : uint8_t + { + RealTime, + Baked, + AutoSelect + }; + + enum class DiffuseProbeGridTextureNotificationType + { + None, + Ready, + Error + }; + + struct DiffuseProbeGridTexture + { + const AZStd::shared_ptr> m_data; + RHI::Format m_format; + RHI::Size m_size; + }; + + static const char* DiffuseProbeGridIrradianceFileName = "Irradiance_lutrgba16.dds"; + static const char* DiffuseProbeGridDistanceFileName = "Distance_lutrg32f.dds"; + static const char* DiffuseProbeGridRelocationFileName = "Relocation_lutrgba16f.dds"; + static const char* DiffuseProbeGridClassificationFileName = "Classification_lutr32f.dds"; + + using DiffuseProbeGridBakeTexturesCallback = AZStd::function; + + struct DiffuseProbeGridBakedTextures + { + // irradiance and distance images can be used directly + Data::Instance m_irradianceImage; + AZStd::string m_irradianceImageRelativePath; + + Data::Instance m_distanceImage; + AZStd::string m_distanceImageRelativePath; + + // relocation and classification images need to be recreated as RW textures + RHI::ImageDescriptor m_relocationImageDescriptor; + AZStd::array_view m_relocationImageData; + AZStd::string m_relocationImageRelativePath; + + RHI::ImageDescriptor m_classificationImageDescriptor; + AZStd::array_view m_classificationImageData; + AZStd::string m_classificationImageRelativePath; + }; + // DiffuseProbeGridFeatureProcessorInterface provides an interface to the feature processor for code outside of Atom class DiffuseProbeGridFeatureProcessorInterface : public RPI::FeatureProcessor @@ -44,6 +98,29 @@ namespace AZ virtual void Enable(const DiffuseProbeGridHandle& probeGrid, bool enable) = 0; virtual void SetGIShadows(const DiffuseProbeGridHandle& probeGrid, bool giShadows) = 0; virtual void SetUseDiffuseIbl(const DiffuseProbeGridHandle& probeGrid, bool useDiffuseIbl) = 0; + virtual void SetMode(const DiffuseProbeGridHandle& probeGrid, DiffuseProbeGridMode mode) = 0; + virtual void SetBakedTextures(const DiffuseProbeGridHandle& probeGrid, const DiffuseProbeGridBakedTextures& bakedTextures) = 0; + + virtual void BakeTextures( + const DiffuseProbeGridHandle& probeGrid, + DiffuseProbeGridBakeTexturesCallback callback, + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) = 0; + + // check for and retrieve a new baked texture asset (does not apply to hot-reloaded assets, only initial bakes) + virtual bool CheckTextureAssetNotification( + const AZStd::string& relativePath, + Data::Asset& outTextureAsset, + DiffuseProbeGridTextureNotificationType& outNotificationType) = 0; + + virtual bool AreBakedTexturesReferenced( + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) = 0; + }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.h index 101387205f..be761d8e7b 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.h @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #pragma once @@ -26,14 +26,15 @@ namespace AZ , public AzToolsFramework::EditorEntityVisibilityNotificationBus::Handler { public: - using BaseClass = AzToolsFramework::Components::EditorComponentAdapter; - AZ_RTTI((EditorRenderComponentAdapter, "{AAF38BE4-EA2F-408B-9C44-63C7FBAC6B33}", TController, TRuntimeComponent, TConfiguration), BaseClass); + AZ_RTTI( + (EditorRenderComponentAdapter, "{AAF38BE4-EA2F-408B-9C44-63C7FBAC6B33}", TController, TRuntimeComponent, TConfiguration), + BaseClass); static void Reflect(AZ::ReflectContext* context); EditorRenderComponentAdapter() = default; - EditorRenderComponentAdapter(const TConfiguration& config); + explicit EditorRenderComponentAdapter(const TConfiguration& config); // AzToolsFramework::Components::EditorComponentAdapter overrides void Activate() override; @@ -50,7 +51,8 @@ namespace AZ // Convert pre-existing EditorCompnentAdapter based serialized data to EditorRenderComponentAdapter template - static bool ConvertToEditorRenderComponentAdapter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); + static bool ConvertToEditorRenderComponentAdapter( + AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.inl b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.inl index e3f7ccd44d..633ec0e0ca 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.inl +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/EditorRenderComponentAdapter.inl @@ -1,14 +1,14 @@ /* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ #include #include @@ -19,11 +19,12 @@ namespace AZ { template template - bool EditorRenderComponentAdapter::ConvertToEditorRenderComponentAdapter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) + bool EditorRenderComponentAdapter::ConvertToEditorRenderComponentAdapter( + AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) { if (classElement.GetVersion() < TVersion) { - // Get the and remove the EditorComponentAdapter base class data that was previpously serialized + // Get the and remove the EditorComponentAdapter base class data that was previously serialized AzToolsFramework::Components::EditorComponentAdapter oldBaseClassData; if (!classElement.FindSubElementAndGetData(AZ_CRC("BaseClass1", 0xd4925735), oldBaseClassData)) @@ -41,8 +42,8 @@ namespace AZ // Replace the old base class data with EditorRenderComponentAdapter EditorRenderComponentAdapter newBaseClassData; - AZ::SerializeContext::DataElementNode& newBaseClassElement = classElement.GetSubElement( - classElement.AddElementWithData(context, "BaseClass1", newBaseClassData)); + AZ::SerializeContext::DataElementNode& newBaseClassElement = + classElement.GetSubElement(classElement.AddElementWithData(context, "BaseClass1", newBaseClassData)); // Overwrite EditorRenderComponentAdapter base class data with retrieved EditorComponentAdapter base class data if (!newBaseClassElement.RemoveElementByName(AZ_CRC("BaseClass1", 0xd4925735))) @@ -62,25 +63,24 @@ namespace AZ { BaseClass::Reflect(context); - if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + if (auto serializeContext = azrtti_cast(context)) { - serializeContext->Class() - ->Version(0) - ; + serializeContext->Class()->Version(0); if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { - editContext->Class( - "EditorRenderComponentAdapter", "") + // clang-format off + editContext->Class("EditorRenderComponentAdapter", "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ; + ->Attribute(AZ::Edit::Attributes::AutoExpand, true); + // clang-format on } } } template - EditorRenderComponentAdapter::EditorRenderComponentAdapter(const TConfiguration& config) + EditorRenderComponentAdapter::EditorRenderComponentAdapter( + const TConfiguration& config) : BaseClass(config) { } @@ -103,7 +103,8 @@ namespace AZ bool EditorRenderComponentAdapter::IsVisible() const { bool visible = true; - AzToolsFramework::EditorEntityInfoRequestBus::EventResult(visible, this->GetEntityId(), &AzToolsFramework::EditorEntityInfoRequestBus::Events::IsVisible); + AzToolsFramework::EditorEntityInfoRequestBus::EventResult( + visible, this->GetEntityId(), &AzToolsFramework::EditorEntityInfoRequestBus::Events::IsVisible); return visible; } @@ -114,15 +115,16 @@ namespace AZ } template - void EditorRenderComponentAdapter::OnEntityVisibilityChanged([[maybe_unused]] bool visibility) + void EditorRenderComponentAdapter::OnEntityVisibilityChanged( + [[maybe_unused]] bool visibility) { this->m_controller.Deactivate(); if (this->ShouldActivateController()) { - this->m_controller.Activate(this->GetEntityId()); + AzFramework::Components::ComponentActivateHelper::Activate( + this->m_controller, AZ::EntityComponentIdPair(this->GetEntityId(), this->GetId())); } } - } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index 089a6168b1..00c55cbade 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include @@ -91,6 +89,8 @@ #include #include +#include +#include #include #include #include @@ -114,7 +114,6 @@ namespace AZ ProjectedShadowFeatureProcessor::Reflect(context); SkyBoxFeatureProcessor::Reflect(context); UseTextureFunctor::Reflect(context); - PropertyVisibilityFunctor::Reflect(context); DrawListFunctor::Reflect(context); SubsurfaceTransmissionParameterFunctor::Reflect(context); Transform2DFunctor::Reflect(context); @@ -126,12 +125,12 @@ namespace AZ DisplayMapperPassData::Reflect(context); ConvertEmissiveUnitFunctor::Reflect(context); LookupTableAsset::Reflect(context); - ShaderEnableFunctor::Reflect(context); ReflectionProbeFeatureProcessor::Reflect(context); DecalTextureArrayFeatureProcessor::Reflect(context); SMAAFeatureProcessor::Reflect(context); PostProcessFeatureProcessor::Reflect(context); ImGuiPassData::Reflect(context); + RayTracingPassData::Reflect(context); LightingPreset::Reflect(context); ModelPreset::Reflect(context); @@ -275,6 +274,9 @@ namespace AZ passSystem->AddPassCreator(Name("ReflectionScreenSpaceBlurChildPass"), &Render::ReflectionScreenSpaceBlurChildPass::Create); passSystem->AddPassCreator(Name("ReflectionCopyFrameBufferPass"), &Render::ReflectionCopyFrameBufferPass::Create); + // Add RayTracing pas + passSystem->AddPassCreator(Name("RayTracingPass"), &Render::RayTracingPass::Create); + // setup handler for load pass template mappings m_loadTemplatesHandler = RPI::PassSystemInterface::OnReadyLoadTemplatesEvent::Handler([this]() { this->LoadPassTemplateMappings(); }); RPI::PassSystemInterface::Get()->ConnectEvent(m_loadTemplatesHandler); diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp index 55fa633e5d..7c97af4f79 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp @@ -279,7 +279,7 @@ namespace AZ if (handle.IsValid()) { Quaternion orientation = world.GetRotation(); - Vector3 scale = world.GetScale() * nonUniformScale; + Vector3 scale = world.GetUniformScale() * nonUniformScale; SetDecalHalfSize(handle, scale); SetDecalPosition(handle, world.GetTranslation()); diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp index febb0b16c5..e783f3b531 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp @@ -285,7 +285,7 @@ namespace AZ { if (handle.IsValid()) { - SetDecalHalfSize(handle, nonUniformScale * world.GetScale()); + SetDecalHalfSize(handle, nonUniformScale * world.GetUniformScale()); SetDecalPosition(handle, world.GetTranslation()); SetDecalOrientation(handle, world.GetRotation()); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp index 5be1303434..a55d8fc78c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,11 @@ namespace AZ { namespace Render { + DiffuseProbeGrid::DiffuseProbeGrid() + : m_textureReadback(this) + { + } + DiffuseProbeGrid::~DiffuseProbeGrid() { m_scene->GetCullingScene()->UnregisterCullable(m_cullable); @@ -166,6 +172,84 @@ namespace AZ m_updateRenderObjectSrg = true; } + void DiffuseProbeGrid::SetMode(DiffuseProbeGridMode mode) + { + // handle auto-select + if (mode == DiffuseProbeGridMode::AutoSelect) + { + RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); + m_mode = (device->GetFeatures().m_rayTracing) ? DiffuseProbeGridMode::RealTime : DiffuseProbeGridMode::Baked; + } + else + { + m_mode = mode; + } + + m_updateTextures = true; + } + + void DiffuseProbeGrid::SetBakedTextures(const DiffuseProbeGridBakedTextures& bakedTextures) + { + AZ_Assert(bakedTextures.m_irradianceImage.get(), "Invalid Irradiance image passed to SetBakedTextures"); + AZ_Assert(bakedTextures.m_distanceImage.get(), "Invalid Distance image passed to SetBakedTextures"); + AZ_Assert(bakedTextures.m_relocationImageData.size() > 0, "Invalid Relocation image data passed to SetBakedTextures"); + AZ_Assert(bakedTextures.m_classificationImageData.size() > 0, "Invalid Classification image data passed to SetBakedTextures"); + + m_bakedIrradianceImage = bakedTextures.m_irradianceImage; + m_bakedDistanceImage = bakedTextures.m_distanceImage; + + m_bakedIrradianceRelativePath = bakedTextures.m_irradianceImageRelativePath; + m_bakedDistanceRelativePath = bakedTextures.m_distanceImageRelativePath; + m_bakedRelocationRelativePath = bakedTextures.m_relocationImageRelativePath; + m_bakedClassificationRelativePath = bakedTextures.m_classificationImageRelativePath; + + m_bakedRelocationImageData.resize(bakedTextures.m_relocationImageData.size()); + memcpy(m_bakedRelocationImageData.data(), bakedTextures.m_relocationImageData.data(), bakedTextures.m_relocationImageData.size()); + + m_bakedClassificationImageData.resize(bakedTextures.m_classificationImageData.size()); + memcpy(m_bakedClassificationImageData.data(), bakedTextures.m_classificationImageData.data(), bakedTextures.m_classificationImageData.size()); + + // create the relocation and distance RW textures now, these are needed for shader compatibility + // (image data is copied in UpdateTextures) + { + m_bakedRelocationImage = RHI::Factory::Get().CreateImage(); + RHI::ImageInitRequest initRequest; + initRequest.m_image = m_bakedRelocationImage.get(); + initRequest.m_descriptor = RHI::ImageDescriptor::Create2D( + RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, + bakedTextures.m_relocationImageDescriptor.m_size.m_width, + bakedTextures.m_relocationImageDescriptor.m_size.m_height, + bakedTextures.m_relocationImageDescriptor.m_format); + + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(initRequest); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize Relocation image"); + } + + { + m_bakedClassificationImage = RHI::Factory::Get().CreateImage(); + RHI::ImageInitRequest initRequest; + initRequest.m_image = m_bakedClassificationImage.get(); + initRequest.m_descriptor = RHI::ImageDescriptor::Create2D( + RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, + bakedTextures.m_classificationImageDescriptor.m_size.m_width, + bakedTextures.m_classificationImageDescriptor.m_size.m_height, + bakedTextures.m_classificationImageDescriptor.m_format); + + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(initRequest); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize Classification image"); + } + + m_updateTextures = true; + } + + bool DiffuseProbeGrid::HasValidBakedTextures() const + { + return m_bakedIrradianceImage.get() && + m_bakedDistanceImage.get() && + m_bakedRelocationImage.get() && + m_bakedClassificationImage.get(); + } + uint32_t DiffuseProbeGrid::GetTotalProbeCount() const { return m_probeCountX * m_probeCountY * m_probeCountZ; @@ -188,83 +272,117 @@ namespace AZ RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); - // advance to the next image in the frame image array - m_currentImageIndex = (m_currentImageIndex + 1) % ImageFrameCount; + uint32_t probeCountX; + uint32_t probeCountY; + GetTexture2DProbeCount(probeCountX, probeCountY); - // probe raytrace + if (m_mode == DiffuseProbeGridMode::RealTime) { - uint32_t width = m_numRaysPerProbe; - uint32_t height = GetTotalProbeCount(); + // advance to the next image in the frame image array + m_currentImageIndex = (m_currentImageIndex + 1) % ImageFrameCount; - m_rayTraceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + // probe raytrace + { + uint32_t width = m_numRaysPerProbe; + uint32_t height = GetTotalProbeCount(); - RHI::ImageInitRequest request; - request.m_image = m_rayTraceImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::RayTraceImageFormat); - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRayTraceImage image"); - } + m_rayTraceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); - uint32_t probeCountX; - uint32_t probeCountY; - GetTexture2DProbeCount(probeCountX, probeCountY); + RHI::ImageInitRequest request; + request.m_image = m_rayTraceImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::RayTraceImageFormat); + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRayTraceImage image"); + } - // probe irradiance - { - uint32_t width = probeCountX * (DefaultNumIrradianceTexels + 2); - uint32_t height = probeCountY * (DefaultNumIrradianceTexels + 2); - - m_irradianceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); - - RHI::ImageInitRequest request; - request.m_image = m_irradianceImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::IrradianceImageFormat); - RHI::ClearValue clearValue = RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f); - request.m_optimizedClearValue = &clearValue; - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeIrradianceImage image"); - } + // probe irradiance + { + uint32_t width = probeCountX * (DefaultNumIrradianceTexels + 2); + uint32_t height = probeCountY * (DefaultNumIrradianceTexels + 2); + + m_irradianceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + + RHI::ImageInitRequest request; + request.m_image = m_irradianceImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::IrradianceImageFormat); + RHI::ClearValue clearValue = RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f); + request.m_optimizedClearValue = &clearValue; + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeIrradianceImage image"); + } - // probe distance - { - uint32_t width = probeCountX * (DefaultNumDistanceTexels + 2); - uint32_t height = probeCountY * (DefaultNumDistanceTexels + 2); + // probe distance + { + uint32_t width = probeCountX * (DefaultNumDistanceTexels + 2); + uint32_t height = probeCountY * (DefaultNumDistanceTexels + 2); - m_distanceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + m_distanceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); - RHI::ImageInitRequest request; - request.m_image = m_distanceImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::DistanceImageFormat); - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeDistanceImage image"); - } + RHI::ImageInitRequest request; + request.m_image = m_distanceImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::DistanceImageFormat); + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeDistanceImage image"); + } - // probe relocation - { - uint32_t width = probeCountX; - uint32_t height = probeCountY; + // probe relocation + { + uint32_t width = probeCountX; + uint32_t height = probeCountY; - m_relocationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + m_relocationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); - RHI::ImageInitRequest request; - request.m_image = m_relocationImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::RelocationImageFormat); - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRelocationImage image"); - } + RHI::ImageInitRequest request; + request.m_image = m_relocationImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::RelocationImageFormat); + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRelocationImage image"); + } - // probe classification + // probe classification + { + uint32_t width = probeCountX; + uint32_t height = probeCountY; + + m_classificationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + + RHI::ImageInitRequest request; + request.m_image = m_classificationImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::ClassificationImageFormat); + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeClassificationImage image"); + } + } + else if (m_mode == DiffuseProbeGridMode::Baked && HasValidBakedTextures()) { - uint32_t width = probeCountX; - uint32_t height = probeCountY; + // copy the baked relocation and classification texture data to the RW textures + // (these need to be RW for shader compatibility) + RHI::ImageSubresourceRange range{ 0, 0, 0 ,0 }; + RHI::ImageSubresourceLayoutPlaced layout; - m_classificationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + // relocation + { + m_bakedRelocationImage->GetSubresourceLayouts(range, &layout, nullptr); + + RHI::ImageUpdateRequest updateRequest; + updateRequest.m_image = m_bakedRelocationImage.get(); + updateRequest.m_sourceSubresourceLayout = layout; + updateRequest.m_sourceData = m_bakedRelocationImageData.data(); + updateRequest.m_imageSubresourcePixelOffset = RHI::Origin(0, 0, 0); + m_renderData->m_imagePool->UpdateImageContents(updateRequest); + } - RHI::ImageInitRequest request; - request.m_image = m_classificationImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::ClassificationImageFormat); - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeClassificationImage image"); + // classification + { + m_bakedClassificationImage->GetSubresourceLayouts(range, &layout, nullptr); + + RHI::ImageUpdateRequest updateRequest; + updateRequest.m_image = m_bakedClassificationImage.get(); + updateRequest.m_sourceSubresourceLayout = layout; + updateRequest.m_sourceData = m_bakedClassificationImageData.data(); + updateRequest.m_imageSubresourcePixelOffset = RHI::Origin(0, 0, 0); + m_renderData->m_imagePool->UpdateImageContents(updateRequest); + } } m_updateTextures = false; @@ -639,16 +757,16 @@ namespace AZ m_renderObjectSrg->SetConstant(constantIndex, m_ambientMultiplier); imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeIrradiance")); - m_renderObjectSrg->SetImageView(imageIndex, m_irradianceImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeIrradianceImageViewDescriptor).get()); + m_renderObjectSrg->SetImageView(imageIndex, GetIrradianceImage()->GetImageView(m_renderData->m_probeIrradianceImageViewDescriptor).get()); imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeDistance")); - m_renderObjectSrg->SetImageView(imageIndex, m_distanceImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeDistanceImageViewDescriptor).get()); + m_renderObjectSrg->SetImageView(imageIndex, GetDistanceImage()->GetImageView(m_renderData->m_probeDistanceImageViewDescriptor).get()); imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeOffsets")); - m_renderObjectSrg->SetImageView(imageIndex, m_relocationImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeRelocationImageViewDescriptor).get()); + m_renderObjectSrg->SetImageView(imageIndex, GetRelocationImage()->GetImageView(m_renderData->m_probeRelocationImageViewDescriptor).get()); imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeStates")); - m_renderObjectSrg->SetImageView(imageIndex, m_classificationImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeClassificationImageViewDescriptor).get()); + m_renderObjectSrg->SetImageView(imageIndex, GetClassificationImage()->GetImageView(m_renderData->m_probeClassificationImageViewDescriptor).get()); SetGridConstants(m_renderObjectSrg); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h index e1ca2123a5..ff6ad719cf 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace AZ { @@ -26,11 +27,12 @@ namespace AZ struct DiffuseProbeGridRenderData { + // [GFX TODO][ATOM-15650] Change DiffuseProbeGrid Classification texture to R8_UINT static const RHI::Format RayTraceImageFormat = RHI::Format::R32G32B32A32_FLOAT; static const RHI::Format IrradianceImageFormat = RHI::Format::R16G16B16A16_UNORM; static const RHI::Format DistanceImageFormat = RHI::Format::R32G32_FLOAT; static const RHI::Format RelocationImageFormat = RHI::Format::R16G16B16A16_FLOAT; - static const RHI::Format ClassificationImageFormat = RHI::Format::R8_UINT; + static const RHI::Format ClassificationImageFormat = RHI::Format::R32_FLOAT; // image pool RHI::Ptr m_imagePool; @@ -61,7 +63,7 @@ namespace AZ class DiffuseProbeGrid final { public: - DiffuseProbeGrid() = default; + DiffuseProbeGrid(); ~DiffuseProbeGrid(); void Init(RPI::Scene* scene, DiffuseProbeGridRenderData* diffuseProbeGridRenderData); @@ -96,6 +98,9 @@ namespace AZ bool GetUseDiffuseIbl() const { return m_useDiffuseIbl; } void SetUseDiffuseIbl(bool useDiffuseIbl) { m_useDiffuseIbl = useDiffuseIbl; } + DiffuseProbeGridMode GetMode() const { return m_mode; } + void SetMode(DiffuseProbeGridMode mode); + uint32_t GetNumRaysPerProbe() const { return m_numRaysPerProbe; } uint32_t GetRemainingRelocationIterations() const { return aznumeric_cast(m_remainingRelocationIterations); } @@ -133,11 +138,16 @@ namespace AZ void UpdateRenderObjectSrg(); // textures - const RHI::Ptr& GetRayTraceImage() { return m_rayTraceImage[m_currentImageIndex]; } - const RHI::Ptr& GetIrradianceImage() { return m_irradianceImage[m_currentImageIndex]; } - const RHI::Ptr& GetDistanceImage() { return m_distanceImage[m_currentImageIndex]; } - const RHI::Ptr& GetRelocationImage() { return m_relocationImage[m_currentImageIndex]; } - const RHI::Ptr& GetClassificationImage() { return m_classificationImage[m_currentImageIndex]; } + const RHI::Ptr GetRayTraceImage() { return m_rayTraceImage[m_currentImageIndex]; } + const RHI::Ptr GetIrradianceImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_irradianceImage[m_currentImageIndex] : m_bakedIrradianceImage->GetRHIImage(); } + const RHI::Ptr GetDistanceImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_distanceImage[m_currentImageIndex] : m_bakedDistanceImage->GetRHIImage(); } + const RHI::Ptr GetRelocationImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_relocationImage[m_currentImageIndex] : m_bakedRelocationImage; } + const RHI::Ptr GetClassificationImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_classificationImage[m_currentImageIndex] : m_bakedClassificationImage; } + + const AZStd::string& GetBakedIrradianceRelativePath() const { return m_bakedIrradianceRelativePath; } + const AZStd::string& GetBakedDistanceRelativePath() const { return m_bakedDistanceRelativePath; } + const AZStd::string& GetBakedRelocationRelativePath() const { return m_bakedRelocationRelativePath; } + const AZStd::string& GetBakedClassificationRelativePath() const { return m_bakedClassificationRelativePath; } // attachment Ids const RHI::AttachmentId GetRayTraceImageAttachmentId() const { return m_rayTraceImageAttachmentId; } @@ -152,6 +162,12 @@ namespace AZ bool GetIrradianceClearRequired() const { return m_irradianceClearRequired; } void ResetIrradianceClearRequired() { m_irradianceClearRequired = false; } + // texture readback + DiffuseProbeGridTextureReadback& GetTextureReadback() { return m_textureReadback; } + + void SetBakedTextures(const DiffuseProbeGridBakedTextures& bakedTextures); + bool HasValidBakedTextures() const; + static constexpr uint32_t DefaultNumIrradianceTexels = 6; static constexpr uint32_t DefaultNumDistanceTexels = 14; static constexpr int32_t DefaultNumRelocationIterations = 100; @@ -221,7 +237,10 @@ namespace AZ // culling RPI::Cullable m_cullable; - // textures + // grid mode (RealTime or Baked) + DiffuseProbeGridMode m_mode = DiffuseProbeGridMode::RealTime; + + // real-time textures static const uint32_t MaxTextureDimension = 8192; static const uint32_t ImageFrameCount = 3; RHI::Ptr m_rayTraceImage[ImageFrameCount]; @@ -233,6 +252,25 @@ namespace AZ bool m_updateTextures = false; bool m_irradianceClearRequired = true; + // baked textures + Data::Instance m_bakedIrradianceImage; + Data::Instance m_bakedDistanceImage; + RHI::Ptr m_bakedRelocationImage; + RHI::Ptr m_bakedClassificationImage; + + // baked texture relative paths + AZStd::string m_bakedIrradianceRelativePath; + AZStd::string m_bakedDistanceRelativePath; + AZStd::string m_bakedRelocationRelativePath; + AZStd::string m_bakedClassificationRelativePath; + + // baked texture data (only needed for the relocation and classification textures) + AZStd::vector m_bakedRelocationImageData; + AZStd::vector m_bakedClassificationImageData; + + // texture readback + DiffuseProbeGridTextureReadback m_textureReadback; + // Srgs Data::Instance m_rayTraceSrg; Data::Instance m_blendIrradianceSrg; diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp index 3df13556d3..2a06dacf3f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp @@ -87,7 +87,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -111,7 +111,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe raytrace image { @@ -150,7 +150,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -167,7 +167,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItem for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetBlendDistanceSrg()->GetRHIShaderResourceGroup(); commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp index 4e05b8ef31..4818018ea3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp @@ -87,7 +87,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -111,7 +111,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe raytrace image { @@ -150,7 +150,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -167,7 +167,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItem for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetBlendIrradianceSrg()->GetRHIShaderResourceGroup(); commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp index 59549de331..8821de8a9d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp @@ -100,7 +100,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -124,7 +124,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe irradiance image { @@ -153,7 +153,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see line ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -173,7 +173,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItems for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { uint32_t probeCountX; uint32_t probeCountY; diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp index db85914cee..65f1c2dd5a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp @@ -91,7 +91,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -115,7 +115,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe raytrace image { @@ -143,7 +143,7 @@ namespace AZ { RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -159,7 +159,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItems for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetClassificationSrg()->GetRHIShaderResourceGroup(); commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp index 1aaa06c797..060d51d1d0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp @@ -45,6 +45,7 @@ namespace AZ RHI::RHISystemInterface* rhiSystem = RHI::RHISystemInterface::Get(); m_diffuseProbeGrids.reserve(InitialProbeGridAllocationSize); + m_realTimeDiffuseProbeGrids.reserve(InitialProbeGridAllocationSize); RHI::BufferPoolDescriptor desc; desc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device; @@ -61,7 +62,7 @@ namespace AZ // image pool { RHI::ImagePoolDescriptor imagePoolDesc; - imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite; + imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead; m_probeGridRenderData.m_imagePool = RHI::Factory::Get().CreateImagePool(); [[maybe_unused]] RHI::ResultCode result = m_probeGridRenderData.m_imagePool->Init(*rhiSystem->GetDevice(), imagePoolDesc); @@ -123,6 +124,32 @@ namespace AZ m_needUpdatePipelineStates = false; } + // check pending textures and connect bus for notifications + for (auto& notificationEntry : m_notifyTextureAssets) + { + if (notificationEntry.m_assetId.IsValid()) + { + // asset already has an assetId + continue; + } + + // query for the assetId + AZ::Data::AssetId assetId; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetId, + &AZ::Data::AssetCatalogRequests::GetAssetIdByPath, + notificationEntry.m_relativePath.c_str(), + azrtti_typeid(), + false); + + if (assetId.IsValid()) + { + notificationEntry.m_assetId = assetId; + notificationEntry.m_asset.Create(assetId, true); + Data::AssetBus::MultiHandler::BusConnect(assetId); + } + } + // if the volumes changed we need to re-sort the probe list if (m_probeGridSortRequired) { @@ -139,6 +166,7 @@ namespace AZ }; AZStd::sort(m_diffuseProbeGrids.begin(), m_diffuseProbeGrids.end(), sortFn); + AZStd::sort(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), sortFn); m_probeGridSortRequired = false; } @@ -160,6 +188,9 @@ namespace AZ diffuseProbeGrid->SetExtents(extents); diffuseProbeGrid->SetProbeSpacing(probeSpacing); m_diffuseProbeGrids.push_back(diffuseProbeGrid); + + UpdateRealTimeList(diffuseProbeGrid); + m_probeGridSortRequired = true; return diffuseProbeGrid; @@ -169,6 +200,7 @@ namespace AZ { AZ_Assert(probeGrid.get(), "RemoveProbeGrid called with an invalid handle"); + // remove from main list auto itEntry = AZStd::find_if(m_diffuseProbeGrids.begin(), m_diffuseProbeGrids.end(), [&](AZStd::shared_ptr const& entry) { return (entry == probeGrid); @@ -176,6 +208,18 @@ namespace AZ AZ_Assert(itEntry != m_diffuseProbeGrids.end(), "RemoveProbeGrid called with a probe grid that is not in the probe list"); m_diffuseProbeGrids.erase(itEntry); + + // remove from side list of real-time grids + itEntry = AZStd::find_if(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), [&](AZStd::shared_ptr const& entry) + { + return (entry == probeGrid); + }); + + if (itEntry != m_realTimeDiffuseProbeGrids.end()) + { + m_realTimeDiffuseProbeGrids.erase(itEntry); + } + probeGrid = nullptr; } @@ -247,6 +291,134 @@ namespace AZ probeGrid->SetUseDiffuseIbl(useDiffuseIbl); } + void DiffuseProbeGridFeatureProcessor::BakeTextures( + const DiffuseProbeGridHandle& probeGrid, + DiffuseProbeGridBakeTexturesCallback callback, + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) + { + AZ_Assert(probeGrid.get(), "BakeTextures called with an invalid handle"); + + AddNotificationEntry(irradianceTextureRelativePath); + AddNotificationEntry(distanceTextureRelativePath); + AddNotificationEntry(relocationTextureRelativePath); + AddNotificationEntry(classificationTextureRelativePath); + + probeGrid->GetTextureReadback().BeginTextureReadback(callback); + } + + void DiffuseProbeGridFeatureProcessor::UpdateRealTimeList(const DiffuseProbeGridHandle& diffuseProbeGrid) + { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::RealTime) + { + // add to side list of real-time grids + auto itEntry = AZStd::find_if(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), [&](AZStd::shared_ptr const& entry) + { + return (entry == diffuseProbeGrid); + }); + + if (itEntry == m_realTimeDiffuseProbeGrids.end()) + { + m_realTimeDiffuseProbeGrids.push_back(diffuseProbeGrid); + } + } + else + { + // remove from side list of real-time grids + auto itEntry = AZStd::find_if(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), [&](AZStd::shared_ptr const& entry) + { + return (entry == diffuseProbeGrid); + }); + + if (itEntry != m_realTimeDiffuseProbeGrids.end()) + { + m_realTimeDiffuseProbeGrids.erase(itEntry); + } + } + } + + void DiffuseProbeGridFeatureProcessor::AddNotificationEntry(const AZStd::string& relativePath) + { + AZStd::string assetPath = relativePath + ".streamingimage"; + + // check to see if this is an existing asset + AZ::Data::AssetId assetId; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetId, + &AZ::Data::AssetCatalogRequests::GetAssetIdByPath, + assetPath.c_str(), + azrtti_typeid(), + false); + + // We only track notifications for new texture assets, meaning assets that are created the first time a DiffuseProbeGrid is baked. + // On subsequent bakes the existing assets are automatically reloaded by the RPI since they are already known by the asset system. + if (!assetId.IsValid()) + { + m_notifyTextureAssets.push_back({ assetPath, assetId }); + } + } + + bool DiffuseProbeGridFeatureProcessor::CheckTextureAssetNotification( + const AZStd::string& relativePath, + Data::Asset& outTextureAsset, + DiffuseProbeGridTextureNotificationType& outNotificationType) + { + for (NotifyTextureAssetVector::iterator itNotification = m_notifyTextureAssets.begin(); itNotification != m_notifyTextureAssets.end(); ++itNotification) + { + if (itNotification->m_relativePath == relativePath) + { + outNotificationType = itNotification->m_notificationType; + if (outNotificationType != DiffuseProbeGridTextureNotificationType::None) + { + outTextureAsset = itNotification->m_asset; + m_notifyTextureAssets.erase(itNotification); + } + + return true; + } + } + + return false; + } + + bool DiffuseProbeGridFeatureProcessor::AreBakedTexturesReferenced( + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) + { + for (auto& diffuseProbeGrid : m_diffuseProbeGrids) + { + if ((diffuseProbeGrid->GetBakedIrradianceRelativePath() == irradianceTextureRelativePath) || + (diffuseProbeGrid->GetBakedDistanceRelativePath() == distanceTextureRelativePath) || + (diffuseProbeGrid->GetBakedRelocationRelativePath() == relocationTextureRelativePath) || + (diffuseProbeGrid->GetBakedClassificationRelativePath() == classificationTextureRelativePath)) + { + return true; + } + } + + return false; + } + + void DiffuseProbeGridFeatureProcessor::SetMode(const DiffuseProbeGridHandle& probeGrid, DiffuseProbeGridMode mode) + { + AZ_Assert(probeGrid.get(), "SetMode called with an invalid handle"); + probeGrid->SetMode(mode); + + UpdateRealTimeList(probeGrid); + + m_probeGridSortRequired = true; + } + + void DiffuseProbeGridFeatureProcessor::SetBakedTextures(const DiffuseProbeGridHandle& probeGrid, const DiffuseProbeGridBakedTextures& bakedTextures) + { + AZ_Assert(probeGrid.get(), "SetBakedTextures called with an invalid handle"); + probeGrid->SetBakedTextures(bakedTextures); + } + void DiffuseProbeGridFeatureProcessor::CreateBoxMesh() { // vertex positions @@ -418,5 +590,34 @@ namespace AZ } } + void DiffuseProbeGridFeatureProcessor::HandleAssetNotification(Data::Asset asset, DiffuseProbeGridTextureNotificationType notificationType) + { + for (NotifyTextureAssetVector::iterator itNotification = m_notifyTextureAssets.begin(); itNotification != m_notifyTextureAssets.end(); ++itNotification) + { + if (itNotification->m_assetId == asset.GetId()) + { + // store the texture asset + itNotification->m_asset = Data::static_pointer_cast(asset); + itNotification->m_notificationType = notificationType; + + // stop notifications on this asset + Data::AssetBus::MultiHandler::BusDisconnect(itNotification->m_assetId); + + break; + } + } + } + + void DiffuseProbeGridFeatureProcessor::OnAssetReady(Data::Asset asset) + { + HandleAssetNotification(asset, DiffuseProbeGridTextureNotificationType::Ready); + } + + void DiffuseProbeGridFeatureProcessor::OnAssetError(Data::Asset asset) + { + AZ_Error("ReflectionProbeFeatureProcessor", false, "Failed to load cubemap [%s]", asset.GetHint().c_str()); + + HandleAssetNotification(asset, DiffuseProbeGridTextureNotificationType::Error); + } } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h index ad36f8aafa..19e9bf1b1d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h @@ -22,6 +22,7 @@ namespace AZ //! This class manages DiffuseProbeGrids which generate diffuse global illumination class DiffuseProbeGridFeatureProcessor final : public DiffuseProbeGridFeatureProcessorInterface + , private Data::AssetBus::MultiHandler { public: AZ_RTTI(AZ::Render::DiffuseProbeGridFeatureProcessor, "{BCD232F9-1EBF-4D0D-A5F4-84AEC933A93C}", DiffuseProbeGridFeatureProcessorInterface); @@ -46,6 +47,27 @@ namespace AZ void Enable(const DiffuseProbeGridHandle& probeGrid, bool enable) override; void SetGIShadows(const DiffuseProbeGridHandle& probeGrid, bool giShadows) override; void SetUseDiffuseIbl(const DiffuseProbeGridHandle& probeGrid, bool useDiffuseIbl) override; + void SetMode(const DiffuseProbeGridHandle& probeGrid, DiffuseProbeGridMode mode) override; + void SetBakedTextures(const DiffuseProbeGridHandle& probeGrid, const DiffuseProbeGridBakedTextures& bakedTextures) override; + + void BakeTextures( + const DiffuseProbeGridHandle& probeGrid, + DiffuseProbeGridBakeTexturesCallback callback, + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) override; + + bool CheckTextureAssetNotification( + const AZStd::string& relativePath, + Data::Asset& outTextureAsset, + DiffuseProbeGridTextureNotificationType& outNotificationType) override; + + bool AreBakedTexturesReferenced( + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) override; // FeatureProcessor overrides void Activate() override; @@ -56,12 +78,28 @@ namespace AZ using DiffuseProbeGridVector = AZStd::vector>; DiffuseProbeGridVector& GetProbeGrids() { return m_diffuseProbeGrids; } + // retrieve the side list of probe grids that are using real-time (raytraced) mode + DiffuseProbeGridVector& GetRealTimeProbeGrids() { return m_realTimeDiffuseProbeGrids; } + private: AZ_DISABLE_COPY_MOVE(DiffuseProbeGridFeatureProcessor); // create the box vertex and index streams, which are used to render the probe volumes void CreateBoxMesh(); + // AssetBus::MultiHandler overrides... + void OnAssetReady(Data::Asset asset) override; + void OnAssetError(Data::Asset asset) override; + + // updates the real-time list for a specific probe grid + void UpdateRealTimeList(const DiffuseProbeGridHandle& diffuseProbeGrid); + + // adds a notification entry for a new asset + void AddNotificationEntry(const AZStd::string& relativePath); + + // notifies and removes the notification entry + void HandleAssetNotification(Data::Asset asset, DiffuseProbeGridTextureNotificationType notificationType); + // RPI::SceneNotificationBus::Handler overrides void OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) override; void OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) override; @@ -70,10 +108,13 @@ namespace AZ void UpdatePipelineStates(); void UpdatePasses(); - // list of diffuse probe grids + // list of all diffuse probe grids const size_t InitialProbeGridAllocationSize = 64; DiffuseProbeGridVector m_diffuseProbeGrids; + // side list of diffuse probe grids that are in real-time mode (subset of m_diffuseProbeGrids) + DiffuseProbeGridVector m_realTimeDiffuseProbeGrids; + // position structure for the box vertices struct Position { @@ -102,6 +143,17 @@ namespace AZ // indicates the the diffuse probe grid render pipeline state needs to be updated bool m_needUpdatePipelineStates = false; + + // list of texture assets that we need to check during Simulate() to see if they are ready + struct NotifyTextureAssetEntry + { + AZStd::string m_relativePath; + AZ::Data::AssetId m_assetId; + Data::Asset m_asset; + DiffuseProbeGridTextureNotificationType m_notificationType = DiffuseProbeGridTextureNotificationType::None; + }; + typedef AZStd::vector NotifyTextureAssetVector; + NotifyTextureAssetVector m_notifyTextureAssets; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp index b2b5f60660..65d71b8272 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp @@ -136,7 +136,7 @@ namespace AZ } DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -153,9 +153,9 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor(); - frameGraph.SetEstimatedItemCount(aznumeric_cast(diffuseProbeGridFeatureProcessor->GetProbeGrids().size())); + frameGraph.SetEstimatedItemCount(aznumeric_cast(diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().size())); - for (const auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (const auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // TLAS { @@ -260,7 +260,7 @@ namespace AZ rayTracingFeatureProcessor->GetMeshInfoBuffer() && rayTracingFeatureProcessor->GetSubMeshCount()) { - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader // inputs (see line ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -308,7 +308,7 @@ namespace AZ m_rayTracingShaderTable) { // submit the DispatchRaysItem for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroups[] = { diffuseProbeGrid->GetRayTraceSrg()->GetRHIShaderResourceGroup(), diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp index 2bd6595b71..86a26f002d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp @@ -91,7 +91,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -108,7 +108,7 @@ namespace AZ // create the Relocation Srgs for each DiffuseProbeGrid, and check to see if any grids need relocation bool needRelocation = false; - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { uint32_t rayTracingDataRevision = rayTracingFeatureProcessor->GetRevision(); if (rayTracingDataRevision != m_rayTracingDataRevision) @@ -139,7 +139,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe raytrace image { @@ -167,7 +167,7 @@ namespace AZ { RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -187,7 +187,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItems for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetRelocationSrg()->GetRHIShaderResourceGroup(); commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp index af6fce6f6a..4f9221a65f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp @@ -79,6 +79,12 @@ namespace AZ params.m_scissorState = scissor; Base::FrameBeginInternal(params); + + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) + { + // process attachment readback + diffuseProbeGrid->GetTextureReadback().FrameBegin(params); + } } void DiffuseProbeGridRenderPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) @@ -88,8 +94,21 @@ namespace AZ for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked && + !diffuseProbeGrid->HasValidBakedTextures()) + { + continue; + } + // probe irradiance image { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked) + { + // import the irradiance image now, since it is baked and therefore was not imported during the raytracing pass + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetIrradianceImageAttachmentId(), diffuseProbeGrid->GetIrradianceImage()); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeIrradianceImage"); + } + RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = diffuseProbeGrid->GetIrradianceImageAttachmentId(); desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeIrradianceImageViewDescriptor; @@ -100,6 +119,13 @@ namespace AZ // probe distance image { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked) + { + // import the distance image now, since it is baked and therefore was not imported during the raytracing pass + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetDistanceImageAttachmentId(), diffuseProbeGrid->GetDistanceImage()); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeDistanceImage"); + } + RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = diffuseProbeGrid->GetDistanceImageAttachmentId(); desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeDistanceImageViewDescriptor; @@ -110,6 +136,13 @@ namespace AZ // probe relocation image { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked) + { + // import the relocation image now, since it is baked and therefore was not imported during the raytracing pass + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetRelocationImageAttachmentId(), diffuseProbeGrid->GetRelocationImage()); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeRelocationImage"); + } + RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = diffuseProbeGrid->GetRelocationImageAttachmentId(); desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeRelocationImageViewDescriptor; @@ -120,6 +153,13 @@ namespace AZ // probe classification image { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked) + { + // import the classification image now, since it is baked and therefore was not imported during the raytracing pass + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetClassificationImageAttachmentId(), diffuseProbeGrid->GetClassificationImage()); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeClassificationImage"); + } + RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = diffuseProbeGrid->GetClassificationImageAttachmentId(); desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeClassificationImageViewDescriptor; @@ -127,6 +167,8 @@ namespace AZ frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite); } + + diffuseProbeGrid->GetTextureReadback().Update(GetName()); } Base::SetupFrameGraphDependencies(frameGraph); @@ -139,6 +181,12 @@ namespace AZ for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked && + !diffuseProbeGrid->HasValidBakedTextures()) + { + continue; + } + // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() of ShaderResourceGroupData.cpp) diffuseProbeGrid->UpdateRenderObjectSrg(); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp new file mode 100644 index 0000000000..bc619cc277 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp @@ -0,0 +1,134 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +namespace AZ +{ + namespace Render + { + DiffuseProbeGridTextureReadback::DiffuseProbeGridTextureReadback(DiffuseProbeGrid* diffuseProbeGrid) + : m_diffuseProbeGrid(diffuseProbeGrid) + { + } + + void DiffuseProbeGridTextureReadback::BeginTextureReadback(DiffuseProbeGridBakeTexturesCallback callback) + { + AZ_Assert(m_readbackState == DiffuseProbeGridReadbackState::Idle, "DiffuseProbeGridTextureReadback is already processing a readback request"); + + m_callback = callback; + m_readbackState = DiffuseProbeGridReadbackState::Irradiance; + } + + void DiffuseProbeGridTextureReadback::Update(const AZ::Name& passName) + { + if (m_readbackState == DiffuseProbeGridReadbackState::Idle || m_readbackState == DiffuseProbeGridReadbackState::Complete) + { + return; + } + + if (m_attachmentReadback.get() && m_attachmentReadback->GetReadbackState() > RPI::AttachmentReadback::ReadbackState::Idle) + { + // still processing previous request + return; + } + + AZStd::string readbackName = AZStd::string::format("DiffuseProbeGridReadback_%s", passName.GetCStr()); + RHI::ImageDescriptor descriptor; + RHI::AttachmentId attachmentId; + RPI::AttachmentReadback::CallbackFunction callbackFunction; + + switch (m_readbackState) + { + case DiffuseProbeGridReadbackState::Irradiance: + descriptor = m_diffuseProbeGrid->GetIrradianceImage()->GetDescriptor(); + attachmentId = m_diffuseProbeGrid->GetIrradianceImageAttachmentId(); + callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + m_irradianceReadbackResult = readbackResult; + m_readbackState = DiffuseProbeGridReadbackState::Distance; + }; + break; + case DiffuseProbeGridReadbackState::Distance: + descriptor = m_diffuseProbeGrid->GetDistanceImage()->GetDescriptor(); + attachmentId = m_diffuseProbeGrid->GetDistanceImageAttachmentId(); + callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + m_distanceReadbackResult = readbackResult; + m_readbackState = DiffuseProbeGridReadbackState::Relocation; + }; + break; + case DiffuseProbeGridReadbackState::Relocation: + descriptor = m_diffuseProbeGrid->GetRelocationImage()->GetDescriptor(); + attachmentId = m_diffuseProbeGrid->GetRelocationImageAttachmentId(); + callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + m_relocationReadbackResult = readbackResult; + m_readbackState = DiffuseProbeGridReadbackState::Classification; + }; + break; + case DiffuseProbeGridReadbackState::Classification: + descriptor = m_diffuseProbeGrid->GetClassificationImage()->GetDescriptor(); + attachmentId = m_diffuseProbeGrid->GetClassificationImageAttachmentId(); + callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + m_classificationReadbackResult = readbackResult; + m_readbackState = DiffuseProbeGridReadbackState::Complete; + }; + break; + default: + AZ_Assert(false, "Unknown readback state"); + } + + m_attachmentReadback = AZStd::make_shared(AZ::RHI::ScopeId{ "DiffuseProbeGridTextureReadBack" }); + m_attachmentReadback->SetCallback(callbackFunction); + + AZ::RPI::PassAttachment passAttachment; + passAttachment.m_descriptor = descriptor; + passAttachment.m_path = attachmentId; + passAttachment.m_name = readbackName; + passAttachment.m_lifetime = RHI::AttachmentLifetimeType::Imported; + + m_attachmentReadback->ReadPassAttachment(&passAttachment, AZ::Name(readbackName)); + } + + void DiffuseProbeGridTextureReadback::FrameBegin(AZ::RPI::Pass::FramePrepareParams& params) + { + if (m_readbackState == DiffuseProbeGridReadbackState::Idle) + { + return; + } + + if (!m_attachmentReadback.get()) + { + return; + } + + if (m_readbackState == DiffuseProbeGridReadbackState::Complete) + { + // readback of all textures is complete, invoke callback and return to Idle state + m_callback( + { m_irradianceReadbackResult.m_dataBuffer, m_irradianceReadbackResult.m_imageDescriptor.m_format, m_irradianceReadbackResult.m_imageDescriptor.m_size }, + { m_distanceReadbackResult.m_dataBuffer, m_distanceReadbackResult.m_imageDescriptor.m_format, m_distanceReadbackResult.m_imageDescriptor.m_size }, + { m_relocationReadbackResult.m_dataBuffer, m_relocationReadbackResult.m_imageDescriptor.m_format, m_relocationReadbackResult.m_imageDescriptor.m_size }, + { m_classificationReadbackResult.m_dataBuffer, m_classificationReadbackResult.m_imageDescriptor.m_format, m_classificationReadbackResult.m_imageDescriptor.m_size }); + + m_readbackState = DiffuseProbeGridReadbackState::Idle; + m_attachmentReadback.reset(); + return; + } + + m_attachmentReadback->FrameBegin(params); + } + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h new file mode 100644 index 0000000000..1becd6fb3e --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h @@ -0,0 +1,60 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class DiffuseProbeGrid; + + enum class DiffuseProbeGridReadbackState + { + Idle, + Irradiance, + Distance, + Relocation, + Classification, + Complete + }; + + //! This class contains functionality necessary to read back the DiffuseProbeGrid textures, which + //! allows them to be saved as assets to run the DiffuseProbeGrid in non-realtime mode. + class DiffuseProbeGridTextureReadback final + { + public: + DiffuseProbeGridTextureReadback(DiffuseProbeGrid* diffuseProbeGrid); + ~DiffuseProbeGridTextureReadback() = default; + + void BeginTextureReadback(DiffuseProbeGridBakeTexturesCallback callback); + void Update(const AZ::Name& passName); + void FrameBegin(AZ::RPI::Pass::FramePrepareParams& params); + + private: + + DiffuseProbeGrid* m_diffuseProbeGrid = nullptr; + DiffuseProbeGridReadbackState m_readbackState = DiffuseProbeGridReadbackState::Idle; + AZStd::shared_ptr m_attachmentReadback; + DiffuseProbeGridBakeTexturesCallback m_callback; + + AZ::RPI::AttachmentReadback::ReadbackResult m_irradianceReadbackResult; + AZ::RPI::AttachmentReadback::ReadbackResult m_distanceReadbackResult; + AZ::RPI::AttachmentReadback::ReadbackResult m_relocationReadbackResult; + AZ::RPI::AttachmentReadback::ReadbackResult m_classificationReadbackResult; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/EditorCommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/EditorCommonSystemComponent.cpp index 32a29cf4d3..2373d0cb00 100644 --- a/Gems/Atom/Feature/Common/Code/Source/EditorCommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/EditorCommonSystemComponent.cpp @@ -12,11 +12,9 @@ #include #include -#include #include #include #include -#include #include #include @@ -58,11 +56,9 @@ namespace AZ } AZ::Render::UseTextureFunctorSourceData::Reflect(context); - AZ::Render::PropertyVisibilityFunctorSourceData::Reflect(context); AZ::Render::DrawListFunctorSourceData::Reflect(context); AZ::Render::Transform2DFunctorSourceData::Reflect(context); AZ::Render::ConvertEmissiveUnitFunctorSourceData::Reflect(context); - AZ::Render::ShaderEnableFunctorSourceData::Reflect(context); AZ::Render::SubsurfaceTransmissionParameterFunctorSourceData::Reflect(context); AZ::Render::EditorLightingPreset::Reflect(context); @@ -104,11 +100,9 @@ namespace AZ } materialFunctorRegistration->RegisterMaterialFunctor("UseTexture", azrtti_typeid()); - materialFunctorRegistration->RegisterMaterialFunctor("UpdatePropertyVisibility", azrtti_typeid()); materialFunctorRegistration->RegisterMaterialFunctor("OverrideDrawList", azrtti_typeid()); materialFunctorRegistration->RegisterMaterialFunctor("Transform2D", azrtti_typeid()); materialFunctorRegistration->RegisterMaterialFunctor("ConvertEmissiveUnit", azrtti_typeid()); - materialFunctorRegistration->RegisterMaterialFunctor("ShaderEnable", azrtti_typeid()); materialFunctorRegistration->RegisterMaterialFunctor("HandleSubsurfaceScatteringParameters", azrtti_typeid()); materialFunctorRegistration->RegisterMaterialFunctor("Lua", azrtti_typeid()); diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index 90499be8b8..0d9e46a418 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -16,11 +16,14 @@ #include #include #include +#include #include #include #include +#include +#include #include #include @@ -55,6 +58,43 @@ namespace AZ FrameCaptureOutputResult PngFrameCaptureOutput( const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) { + AZStd::shared_ptr> buffer = readbackResult.m_dataBuffer; + + // convert bgra to rgba by swapping channels + const int numChannels = AZ::RHI::GetFormatComponentCount(readbackResult.m_imageDescriptor.m_format); + if (readbackResult.m_imageDescriptor.m_format == RHI::Format::B8G8R8A8_UNORM) + { + buffer = AZStd::make_shared>(readbackResult.m_dataBuffer->size()); + AZStd::copy(readbackResult.m_dataBuffer->begin(), readbackResult.m_dataBuffer->end(), buffer->begin()); + + AZ::JobCompletion jobCompletion; + const int numThreads = 8; + const int numPixelsPerThread = buffer->size() / numChannels / numThreads; + for (int i = 0; i < numThreads; ++i) + { + int startPixel = i * numPixelsPerThread; + + AZ::Job* job = AZ::CreateJobFunction( + [&, startPixel, numPixelsPerThread]() + { + for (int pixelOffset = 0; pixelOffset < numPixelsPerThread; ++pixelOffset) + { + if (startPixel * numChannels + numChannels < buffer->size()) + { + AZStd::swap( + buffer->data()[(startPixel + pixelOffset) * numChannels], + buffer->data()[(startPixel + pixelOffset) * numChannels + 2] + ); + } + } + }, true, nullptr); + + job->SetDependent(&jobCompletion); + job->Start(); + } + jobCompletion.StartAndWaitForCompletion(); + } + using namespace OIIO; AZStd::unique_ptr out = ImageOutput::create(outputFilePath.c_str()); if (out) @@ -62,13 +102,13 @@ namespace AZ ImageSpec spec( readbackResult.m_imageDescriptor.m_size.m_width, readbackResult.m_imageDescriptor.m_size.m_height, - AZ::RHI::GetFormatComponentCount(readbackResult.m_imageDescriptor.m_format) + numChannels ); spec.attribute("png:compressionLevel", r_pngCompressionLevel); if (out->open(outputFilePath.c_str(), spec)) { - out->write_image(TypeDesc::UINT8, readbackResult.m_dataBuffer->data()); + out->write_image(TypeDesc::UINT8, buffer->data()); out->close(); return FrameCaptureOutputResult{FrameCaptureResult::Success, AZStd::nullopt}; } @@ -250,11 +290,7 @@ namespace AZ bool FrameCaptureSystemComponent::CaptureScreenshot(const AZStd::string& filePath) { - AzFramework::NativeWindowHandle windowHandle = nullptr; - AzFramework::WindowSystemRequestBus::BroadcastResult( - windowHandle, - &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle); - + AzFramework::NativeWindowHandle windowHandle = AZ::RPI::ViewportContextRequests::Get()->GetDefaultViewportContext()->GetWindowHandle(); if (windowHandle) { return CaptureScreenshotForWindow(filePath, windowHandle); @@ -460,13 +496,23 @@ namespace AZ #if defined(OPEN_IMAGE_IO_ENABLED) else if (extension == "png") { - AZStd::string folderPath; - AzFramework::StringFunc::Path::GetFolderPath(m_outputFilePath.c_str(), folderPath); - AZ::IO::SystemFile::CreateDir(folderPath.c_str()); + if (readbackResult.m_imageDescriptor.m_format == RHI::Format::R8G8B8A8_UNORM || + readbackResult.m_imageDescriptor.m_format == RHI::Format::B8G8R8A8_UNORM) + { + AZStd::string folderPath; + AzFramework::StringFunc::Path::GetFolderPath(m_outputFilePath.c_str(), folderPath); + AZ::IO::SystemFile::CreateDir(folderPath.c_str()); - const auto frameCaptureResult = PngFrameCaptureOutput(m_outputFilePath, readbackResult); - m_result = frameCaptureResult.m_result; - m_latestCaptureInfo = frameCaptureResult.m_errorMessage.value_or(""); + const auto frameCaptureResult = PngFrameCaptureOutput(m_outputFilePath, readbackResult); + m_result = frameCaptureResult.m_result; + m_latestCaptureInfo = frameCaptureResult.m_errorMessage.value_or(""); + } + else + { + m_latestCaptureInfo = AZStd::string::format( + "Can't save image with format %s to a png file", RHI::ToString(readbackResult.m_imageDescriptor.m_format)); + m_result = FrameCaptureResult::UnsupportedFormat; + } } #endif else diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctor.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctor.cpp deleted file mode 100644 index da8730f1ed..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctor.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include "PropertyVisibilityFunctor.h" - -namespace AZ -{ - namespace Render - { - void PropertyVisibilityFunctor::Reflect(ReflectContext* context) - { - if (auto* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(1) - ->Field("triggerProperty", &Action::m_triggerPropertyIndex) - ->Field("triggerValue", &Action::m_triggerValue) - ->Field("visibility", &Action::m_visibility) - ; - serializeContext->Class() - ->Version(1) - ->Field("actions", &PropertyVisibilityFunctor::m_actions) - ->Field("affectedProperties", &PropertyVisibilityFunctor::m_affectedProperties) - ; - } - } - - void PropertyVisibilityFunctor::Process(EditorContext& context) - { - bool visibilityApplied = false; - RPI::MaterialPropertyVisibility lastAppliedVisibility; - - for (const auto& action : m_actions) - { - bool willSetVisibility = false; - if (action.m_triggerValue.Is() || action.m_triggerValue.Is() || action.m_triggerValue.Is()) - { - willSetVisibility = action.m_triggerValue == context.GetMaterialPropertyValue(action.m_triggerPropertyIndex); - } - else if (action.m_triggerValue.Is()) - { - willSetVisibility = AZ::IsClose(action.m_triggerValue.GetValue(), - context.GetMaterialPropertyValue(action.m_triggerPropertyIndex), - std::numeric_limits::epsilon()); - } - else // for types Vector2, Vector3, Vector4, Color, Image - { - AZ_Error("PropertyVisibilityFunctor", false, "Unsupported property data type as an enable property."); - } - - if (willSetVisibility) - { - visibilityApplied = true; - lastAppliedVisibility = action.m_visibility; - } - } - - if (visibilityApplied) - { - for (const auto& propertyIndex : m_affectedProperties) - { - context.SetMaterialPropertyVisibility(propertyIndex, lastAppliedVisibility); - } - } - } - - } // namespace Render -} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctor.h b/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctor.h deleted file mode 100644 index d1a7772fc8..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctor.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#include -#include - -namespace AZ -{ - namespace Render - { - //! Materials can use this functor to control when and how to set the visibility of a group of properties. - class PropertyVisibilityFunctor final - : public RPI::MaterialFunctor - { - friend class PropertyVisibilityFunctorSourceData; - public: - AZ_RTTI(AZ::Render::PropertyVisibilityFunctor, "{2582B36F-FA7C-450F-B46A-39AAE18356A0}", RPI::MaterialFunctor); - - static void Reflect(ReflectContext* context); - - void Process(EditorContext& context) override; - - private: - struct Action - { - AZ_TYPE_INFO(AZ::Render::PropertyVisibilityFunctor::Action, "{5DF4D981-9D0C-4040-A6C5-52E1D0BD876B}"); - - RPI::MaterialPropertyIndex m_triggerPropertyIndex; //! The control property for affected properties. - RPI::MaterialPropertyValue m_triggerValue; //! The trigger value of the control property. - RPI::MaterialPropertyVisibility m_visibility; //! The visibility of affected properties when the trigger value is hit. - }; - // Material property inputs... - AZStd::vector m_actions; //! The actions that describes when and what to do with visibilities. - AZStd::vector m_affectedProperties; //! The properties that are affected by actions. - }; - - } // namespace Render -} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctorSourceData.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctorSourceData.cpp deleted file mode 100644 index 3baf27de60..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctorSourceData.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include "PropertyVisibilityFunctorSourceData.h" -#include -#include - -#include - -namespace AZ -{ - namespace Render - { - void PropertyVisibilityFunctorSourceData::Reflect(ReflectContext* context) - { - if (auto* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(1) - ->Field("triggerProperty", &ActionSourceData::m_triggerPropertyName) - ->Field("triggerValue", &ActionSourceData::m_triggerValue) - ->Field("visibility", &ActionSourceData::m_visibility) - ; - serializeContext->Class() - ->Version(2) - ->Field("actions", &PropertyVisibilityFunctorSourceData::m_actions) - ->Field("affectedProperties", &PropertyVisibilityFunctorSourceData::m_affectedPropertyNames) - ; - } - } - - RPI::MaterialFunctorSourceData::FunctorResult PropertyVisibilityFunctorSourceData::CreateFunctor(const EditorContext& context) const - { - using namespace RPI; - - RPI::Ptr functor = aznew PropertyVisibilityFunctor; - - functor->m_actions.reserve(m_actions.size()); - - for (const auto& actionSource : m_actions) - { - functor->m_actions.emplace_back(); - PropertyVisibilityFunctor::Action& action = functor->m_actions.back(); - action.m_triggerPropertyIndex = context.FindMaterialPropertyIndex(AZ::Name{ actionSource.m_triggerPropertyName }); - if (action.m_triggerPropertyIndex.IsNull()) - { - return Failure(); - } - AddMaterialPropertyDependency(functor, action.m_triggerPropertyIndex); - - if (!actionSource.m_triggerValue.Resolve(*context.GetMaterialPropertiesLayout(), Name{ actionSource.m_triggerPropertyName })) - { - // Error is reported in Resolve(). - return Failure(); - } - - const MaterialPropertyDescriptor* propertyDescriptor = context.GetMaterialPropertiesLayout()->GetPropertyDescriptor(action.m_triggerPropertyIndex); - // Enum type should resolve further to a unit32_t from the string source. - if (propertyDescriptor->GetDataType() == RPI::MaterialPropertyDataType::Enum) - { - if (!RPI::MaterialUtils::ResolveMaterialPropertyEnumValue( - propertyDescriptor, - Name(actionSource.m_triggerValue.GetValue().GetValue()), - action.m_triggerValue)) - { - return Failure(); - } - } - else - { - action.m_triggerValue = actionSource.m_triggerValue.GetValue(); - } - - action.m_visibility = actionSource.m_visibility; - } - - functor->m_affectedProperties.reserve(m_affectedPropertyNames.size()); - for (const AZStd::string& name : m_affectedPropertyNames) - { - RPI::MaterialPropertyIndex index = context.FindMaterialPropertyIndex(AZ::Name{ name }); - if (index.IsNull()) - { - return Failure(); - } - functor->m_affectedProperties.push_back(index); - } - - return Success(RPI::Ptr(functor)); - } - } // namespace Render -} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctorSourceData.h b/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctorSourceData.h deleted file mode 100644 index 6b212f78a0..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/PropertyVisibilityFunctorSourceData.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#include "PropertyVisibilityFunctor.h" -#include -#include - -namespace AZ -{ - namespace Render - { - //! Builds a PropertyVisibilityFunctor. - //! Materials can use this functor to control whether a specific property group will be enabled. - class PropertyVisibilityFunctorSourceData final - : public RPI::MaterialFunctorSourceData - { - public: - AZ_RTTI(AZ::Render::PropertyVisibilityFunctorSourceData, "{B44E6929-8FFF-405F-9056-B9B811F97676}", RPI::MaterialFunctorSourceData); - - static void Reflect(ReflectContext* context); - - FunctorResult CreateFunctor(const EditorContext& context) const override; - private: - struct ActionSourceData - { - AZ_TYPE_INFO(AZ::Render::PropertyVisibilityFunctorSourceData::ActionSourceData, "{70E01DA6-0B42-4CCB-AAD0-51980DB43F62}"); - AZStd::string m_triggerPropertyName; //! The control property for affected properties. - RPI::MaterialPropertyValueSourceData m_triggerValue; //! The trigger value of the control property. - RPI::MaterialPropertyVisibility m_visibility; //! The visibility of affected properties when the trigger value is hit. - }; - // Material property inputs... - AZStd::vector m_actions; //! The actions that describes when and what to do with visibilities. - AZStd::vector m_affectedPropertyNames; //! The properties that are affected by actions. - }; - - } // namespace Render -} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctor.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctor.cpp deleted file mode 100644 index dd602cc4ba..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctor.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include "./ShaderEnableFunctor.h" -#include -#include -#include - -namespace AZ -{ - namespace Render - { - void ShaderEnableFunctor::Reflect(ReflectContext* context) - { - if (auto* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(4) - ->Field("opacityModeIndex", &ShaderEnableFunctor::m_opacityModeIndex) - ->Field("parallaxEnableIndex", &ShaderEnableFunctor::m_parallaxEnableIndex) - ->Field("parallaxPdoEnableIndex", &ShaderEnableFunctor::m_parallaxPdoEnableIndex) - ->Field("shadowShaderNoPSIndex", &ShaderEnableFunctor::m_shadowShaderNoPSIndex) - ->Field("shadowShaderWithPSIndex", &ShaderEnableFunctor::m_shadowShaderWithPSIndex) - ->Field("depthShaderNoPSIndex", &ShaderEnableFunctor::m_depthShaderNoPSIndex) - ->Field("depthShaderWithPSIndex", &ShaderEnableFunctor::m_depthShaderWithPSIndex) - ->Field("pbrShaderNoEdsIndex", &ShaderEnableFunctor::m_pbrShaderNoEdsIndex) - ->Field("pbrShaderWithEdsIndex", &ShaderEnableFunctor::m_pbrShaderWithEdsIndex) - ->Field("depthShaderTransparentMin", &ShaderEnableFunctor::m_depthShaderTransparentMin) - ->Field("depthShaderTransparentMax", &ShaderEnableFunctor::m_depthShaderTransparentMax) - ; - } - } - - void ShaderEnableFunctor::Process(RuntimeContext& context) - { - unsigned int opacityMode = context.GetMaterialPropertyValue(m_opacityModeIndex); - bool parallaxEnabled = context.GetMaterialPropertyValue(m_parallaxEnableIndex); - bool parallaxPdoEnabled = context.GetMaterialPropertyValue(m_parallaxPdoEnableIndex); - - if (parallaxEnabled && parallaxPdoEnabled) - { - context.SetShaderEnabled(m_depthShaderNoPSIndex, false); - context.SetShaderEnabled(m_shadowShaderNoPSIndex, false); - context.SetShaderEnabled(m_pbrShaderWithEdsIndex, false); - - context.SetShaderEnabled(m_depthShaderWithPSIndex, true); - context.SetShaderEnabled(m_shadowShaderWithPSIndex, true); - context.SetShaderEnabled(m_pbrShaderNoEdsIndex, true); - } - else - { - context.SetShaderEnabled(m_depthShaderNoPSIndex, opacityMode == OpacityMode::Opaque ); - context.SetShaderEnabled(m_shadowShaderNoPSIndex, opacityMode == OpacityMode::Opaque); - context.SetShaderEnabled(m_pbrShaderWithEdsIndex, opacityMode == OpacityMode::Opaque || opacityMode == OpacityMode::Blended || opacityMode == OpacityMode::TintedTransparent); - - context.SetShaderEnabled(m_depthShaderWithPSIndex, opacityMode == OpacityMode::Cutout); - context.SetShaderEnabled(m_shadowShaderWithPSIndex, opacityMode == OpacityMode::Cutout); - context.SetShaderEnabled(m_pbrShaderNoEdsIndex, opacityMode == OpacityMode::Cutout); - } - - context.SetShaderEnabled(m_depthShaderTransparentMin, opacityMode == OpacityMode::Blended || opacityMode == OpacityMode::TintedTransparent); - context.SetShaderEnabled(m_depthShaderTransparentMax, opacityMode == OpacityMode::Blended || opacityMode == OpacityMode::TintedTransparent); - } - } -} diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctor.h b/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctor.h deleted file mode 100644 index ac5f31ba30..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctor.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#include -#include -#include - -namespace AZ -{ - namespace Render - { - enum OpacityMode - { - Opaque = 0, - Cutout, - Blended, - TintedTransparent, - }; - - //! Select shadow and depth shader based on opacity mode and parallax state - //! Opaque: Enable shader without PS - //! Cutout or Parallax enable: Enable shader with PS - //! Blended: Disable both - //! TintedTransparent: Disable both - class ShaderEnableFunctor final - : public RPI::MaterialFunctor - { - friend class ShaderEnableFunctorSourceData; - public: - AZ_RTTI(ShaderEnableFunctor, "{2079A693-FE4F-46A7-95C0-09D88AC156D0}", RPI::MaterialFunctor); - - static void Reflect(ReflectContext* context); - - void Process(RuntimeContext& context) override; - - private: - RPI::MaterialPropertyIndex m_opacityModeIndex; - RPI::MaterialPropertyIndex m_parallaxEnableIndex; - RPI::MaterialPropertyIndex m_parallaxPdoEnableIndex; - - uint32_t m_shadowShaderNoPSIndex = -1; - uint32_t m_shadowShaderWithPSIndex = -1; - uint32_t m_depthShaderNoPSIndex = -1; - uint32_t m_depthShaderWithPSIndex = -1; - uint32_t m_pbrShaderWithEdsIndex = -1; - uint32_t m_pbrShaderNoEdsIndex = -1; - // The following are used by the light culling system to produce min/max depth bounds - uint32_t m_depthShaderTransparentMin = -1; - uint32_t m_depthShaderTransparentMax = -1; - }; - } -} diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctorSourceData.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctorSourceData.cpp deleted file mode 100644 index 3c16666f82..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctorSourceData.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include "./ShaderEnableFunctorSourceData.h" -#include -#include - -namespace AZ -{ - namespace Render - { - void ShaderEnableFunctorSourceData::Reflect(ReflectContext* context) - { - if (auto* serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(5) - ->Field("opacityMode", &ShaderEnableFunctorSourceData::m_opacityMode) - ->Field("parallaxEnable", &ShaderEnableFunctorSourceData::m_parallaxEnable) - ->Field("parallaxPdoEnable", &ShaderEnableFunctorSourceData::m_parallaxPdoEnable) - ->Field("shadowShaderNoPSIndex", &ShaderEnableFunctorSourceData::m_shadowShaderNoPSIndex) - ->Field("shadowShaderWithPSIndex", &ShaderEnableFunctorSourceData::m_shadowShaderWithPSIndex) - ->Field("depthShaderNoPSIndex", &ShaderEnableFunctorSourceData::m_depthShaderNoPSIndex) - ->Field("depthShaderWithPSIndex", &ShaderEnableFunctorSourceData::m_depthShaderWithPSIndex) - ->Field("pbrShaderNoEdsIndex", &ShaderEnableFunctorSourceData::m_pbrShaderNoEdsIndex) - ->Field("pbrShaderWithEdsIndex", &ShaderEnableFunctorSourceData::m_pbrShaderWithEdsIndex) - ->Field("depthShaderTransparentMin", &ShaderEnableFunctorSourceData::m_depthShaderTransparentMin) - ->Field("depthShaderTransparentMax", &ShaderEnableFunctorSourceData::m_depthShaderTransparentMax) - ; - } - } - - RPI::MaterialFunctorSourceData::FunctorResult ShaderEnableFunctorSourceData::CreateFunctor(const RuntimeContext& context) const - { - RPI::Ptr functor = aznew ShaderEnableFunctor; - - functor->m_opacityModeIndex = context.FindMaterialPropertyIndex(Name{ m_opacityMode }); - if (functor->m_opacityModeIndex.IsNull()) - { - return Failure(); - } - AddMaterialPropertyDependency(functor, functor->m_opacityModeIndex); - - functor->m_parallaxEnableIndex = context.FindMaterialPropertyIndex(Name{ m_parallaxEnable }); - if (functor->m_parallaxEnableIndex.IsNull()) - { - return Failure(); - } - AddMaterialPropertyDependency(functor, functor->m_parallaxEnableIndex); - - functor->m_parallaxPdoEnableIndex = context.FindMaterialPropertyIndex(Name{ m_parallaxPdoEnable }); - if (functor->m_parallaxPdoEnableIndex.IsNull()) - { - return Failure(); - } - AddMaterialPropertyDependency(functor, functor->m_parallaxPdoEnableIndex); - - if (!context.CheckShaderIndexValid(m_shadowShaderWithPSIndex)) - { - return Failure(); - } - functor->m_shadowShaderWithPSIndex = m_shadowShaderWithPSIndex; - - if (!context.CheckShaderIndexValid(m_shadowShaderNoPSIndex)) - { - return Failure(); - } - functor->m_shadowShaderNoPSIndex = m_shadowShaderNoPSIndex; - - if (!context.CheckShaderIndexValid(m_depthShaderWithPSIndex)) - { - return Failure(); - } - functor->m_depthShaderWithPSIndex = m_depthShaderWithPSIndex; - - if (!context.CheckShaderIndexValid(m_depthShaderNoPSIndex)) - { - return Failure(); - } - functor->m_depthShaderNoPSIndex = m_depthShaderNoPSIndex; - - if (!context.CheckShaderIndexValid(m_pbrShaderNoEdsIndex)) - { - return Failure(); - } - functor->m_pbrShaderNoEdsIndex = m_pbrShaderNoEdsIndex; - - if (!context.CheckShaderIndexValid(m_pbrShaderWithEdsIndex)) - { - return Failure(); - } - functor->m_pbrShaderWithEdsIndex = m_pbrShaderWithEdsIndex; - if (!context.CheckShaderIndexValid(m_depthShaderTransparentMin)) - { - return Failure(); - } - functor->m_depthShaderTransparentMin = m_depthShaderTransparentMin; - if (!context.CheckShaderIndexValid(m_depthShaderTransparentMax)) - { - return Failure(); - } - functor->m_depthShaderTransparentMax = m_depthShaderTransparentMax; - - return Success(RPI::Ptr(functor)); - } - } -} diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctorSourceData.h b/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctorSourceData.h deleted file mode 100644 index 2d00a4a015..0000000000 --- a/Gems/Atom/Feature/Common/Code/Source/Material/ShaderEnableFunctorSourceData.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#include "./ShaderEnableFunctor.h" -#include - -namespace AZ -{ - namespace Render - { - class ShaderEnableFunctor; - - //! Builds a ShaderEnableFunctor - class ShaderEnableFunctorSourceData final - : public RPI::MaterialFunctorSourceData - { - public: - AZ_RTTI(ShaderEnableFunctorSourceData, "{63775ECB-5C3E-44D3-B175-4537BF76C3A7}", RPI::MaterialFunctorSourceData); - - static void Reflect(ReflectContext* context); - - FunctorResult CreateFunctor(const RuntimeContext& context) const override; - - private: - - AZStd::string m_opacityMode; - AZStd::string m_parallaxEnable; - AZStd::string m_parallaxPdoEnable; - - uint32_t m_shadowShaderNoPSIndex = -1; - uint32_t m_shadowShaderWithPSIndex = -1; - uint32_t m_depthShaderNoPSIndex = -1; - uint32_t m_depthShaderWithPSIndex = -1; - uint32_t m_pbrShaderWithEdsIndex = -1; - uint32_t m_pbrShaderNoEdsIndex = -1; - // The following are used by the light culling system to produce min/max depth bounds - uint32_t m_depthShaderTransparentMin = -1; - uint32_t m_depthShaderTransparentMax = -1; - }; - } -} diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 4059d65cbb..4aaa0be132 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -710,27 +710,53 @@ namespace AZ uint32_t rayTracingLod = aznumeric_cast(modelLods.size() - 1); const Data::Instance& modelLod = modelLods[rayTracingLod]; - // setup a stream layout and shader input contract for the position and normal streams + // setup a stream layout and shader input contract for the vertex streams static const char* PositionSemantic = "POSITION"; static const char* NormalSemantic = "NORMAL"; - static const RHI::Format StreamFormat = RHI::Format::R32G32B32_FLOAT; + static const char* TangentSemantic = "TANGENT"; + static const char* BitangentSemantic = "BITANGENT"; + static const char* UVSemantic = "UV"; + static const RHI::Format PositionStreamFormat = RHI::Format::R32G32B32_FLOAT; + static const RHI::Format NormalStreamFormat = RHI::Format::R32G32B32_FLOAT; + static const RHI::Format TangentStreamFormat = RHI::Format::R32G32B32_FLOAT; + static const RHI::Format BitangentStreamFormat = RHI::Format::R32G32B32_FLOAT; + static const RHI::Format UVStreamFormat = RHI::Format::R32G32_FLOAT; RHI::InputStreamLayoutBuilder layoutBuilder; - layoutBuilder.AddBuffer()->Channel(PositionSemantic, StreamFormat); - layoutBuilder.AddBuffer()->Channel(NormalSemantic, StreamFormat); + layoutBuilder.AddBuffer()->Channel(PositionSemantic, PositionStreamFormat); + layoutBuilder.AddBuffer()->Channel(NormalSemantic, NormalStreamFormat); + layoutBuilder.AddBuffer()->Channel(UVSemantic, UVStreamFormat); + layoutBuilder.AddBuffer()->Channel(TangentSemantic, TangentStreamFormat); + layoutBuilder.AddBuffer()->Channel(BitangentSemantic, BitangentStreamFormat); RHI::InputStreamLayout inputStreamLayout = layoutBuilder.End(); RPI::ShaderInputContract::StreamChannelInfo positionStreamChannelInfo; positionStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(PositionSemantic)); - positionStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(StreamFormat); + positionStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(PositionStreamFormat); RPI::ShaderInputContract::StreamChannelInfo normalStreamChannelInfo; normalStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(NormalSemantic)); - normalStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(StreamFormat); + normalStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(NormalStreamFormat); + + RPI::ShaderInputContract::StreamChannelInfo tangentStreamChannelInfo; + tangentStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(TangentSemantic)); + tangentStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(TangentStreamFormat); + + RPI::ShaderInputContract::StreamChannelInfo bitangentStreamChannelInfo; + bitangentStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(BitangentSemantic)); + bitangentStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(BitangentStreamFormat); + + RPI::ShaderInputContract::StreamChannelInfo uvStreamChannelInfo; + uvStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(UVSemantic)); + uvStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(UVStreamFormat); + uvStreamChannelInfo.m_isOptional = true; RPI::ShaderInputContract shaderInputContract; shaderInputContract.m_streamChannels.emplace_back(positionStreamChannelInfo); shaderInputContract.m_streamChannels.emplace_back(normalStreamChannelInfo); + shaderInputContract.m_streamChannels.emplace_back(tangentStreamChannelInfo); + shaderInputContract.m_streamChannels.emplace_back(bitangentStreamChannelInfo); + shaderInputContract.m_streamChannels.emplace_back(uvStreamChannelInfo); // setup the raytracing data for each sub-mesh const size_t meshCount = modelLod->GetMeshes().size(); @@ -739,16 +765,45 @@ namespace AZ { const RPI::ModelLod::Mesh& mesh = modelLod->GetMeshes()[meshIndex]; + // retrieve the material + Data::Instance material = mesh.m_material; + + const MaterialAssignmentId materialAssignmentId(rayTracingLod, material ? material->GetAssetId() : AZ::Data::AssetId()); + const MaterialAssignment& materialAssignment = GetMaterialAssignmentFromMapWithFallback(m_materialAssignments, materialAssignmentId); + if (materialAssignment.m_materialInstance.get()) + { + material = materialAssignment.m_materialInstance; + } + // retrieve vertex/index buffers RPI::ModelLod::StreamBufferViewList streamBufferViews; - [[maybe_unused]] bool result = modelLod->GetStreamsForMesh(inputStreamLayout, streamBufferViews, shaderInputContract, meshIndex); + [[maybe_unused]] bool result = modelLod->GetStreamsForMesh( + inputStreamLayout, + streamBufferViews, + nullptr, + shaderInputContract, + meshIndex, + materialAssignment.m_matModUvOverrides, + material->GetAsset()->GetMaterialTypeAsset()->GetUvNameMap()); AZ_Assert(result, "Failed to retrieve mesh stream buffer views"); // note that the element count is the size of the entire buffer, even though this mesh may only // occupy a portion of the vertex buffer. This is necessary since we are accessing it using // a ByteAddressBuffer in the raytracing shaders and passing the byte offset to the shader in a constant buffer. - uint32_t vertexBufferByteCount = const_cast(streamBufferViews[0].GetBuffer())->GetDescriptor().m_byteCount; - RHI::BufferViewDescriptor vertexBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, vertexBufferByteCount); + uint32_t positionBufferByteCount = const_cast(streamBufferViews[0].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor positionBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, positionBufferByteCount); + + uint32_t normalBufferByteCount = const_cast(streamBufferViews[1].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor normalBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, normalBufferByteCount); + + uint32_t tangentBufferByteCount = const_cast(streamBufferViews[2].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor tangentBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, tangentBufferByteCount); + + uint32_t bitangentBufferByteCount = const_cast(streamBufferViews[3].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor bitangentBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, bitangentBufferByteCount); + + uint32_t uvBufferByteCount = const_cast(streamBufferViews[4].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor uvBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, uvBufferByteCount); const RHI::IndexBufferView& indexBufferView = mesh.m_indexBufferView; uint32_t indexElementSize = indexBufferView.GetIndexFormat() == RHI::IndexFormat::Uint16 ? 2 : 4; @@ -759,41 +814,124 @@ namespace AZ indexBufferDescriptor.m_elementSize = indexElementSize; indexBufferDescriptor.m_elementFormat = indexBufferView.GetIndexFormat() == RHI::IndexFormat::Uint16 ? RHI::Format::R16_UINT : RHI::Format::R32_UINT; - // retrieve the material - Data::Instance material = mesh.m_material; + // set the SubMesh data to pass to the RayTracingFeatureProcessor, starting with vertex/index data + RayTracingFeatureProcessor::SubMesh subMesh; + subMesh.m_positionFormat = PositionStreamFormat; + subMesh.m_positionVertexBufferView = streamBufferViews[0]; + subMesh.m_positionShaderBufferView = const_cast(streamBufferViews[0].GetBuffer())->GetBufferView(positionBufferDescriptor); - const MaterialAssignmentId materialAssignmentId(rayTracingLod, material ? material->GetAssetId() : AZ::Data::AssetId()); - const MaterialAssignment& materialAssignment = GetMaterialAssignmentFromMapWithFallback(m_materialAssignments, materialAssignmentId); - if (materialAssignment.m_materialInstance.get()) + subMesh.m_normalFormat = NormalStreamFormat; + subMesh.m_normalVertexBufferView = streamBufferViews[1]; + subMesh.m_normalShaderBufferView = const_cast(streamBufferViews[1].GetBuffer())->GetBufferView(normalBufferDescriptor); + + subMesh.m_tangentFormat = TangentStreamFormat; + subMesh.m_tangentVertexBufferView = streamBufferViews[2]; + subMesh.m_tangentShaderBufferView = const_cast(streamBufferViews[2].GetBuffer())->GetBufferView(tangentBufferDescriptor); + + subMesh.m_bitangentFormat = BitangentStreamFormat; + subMesh.m_bitangentVertexBufferView = streamBufferViews[3]; + subMesh.m_bitangentShaderBufferView = const_cast(streamBufferViews[3].GetBuffer())->GetBufferView(bitangentBufferDescriptor); + + if (uvBufferByteCount > 0) { - material = materialAssignment.m_materialInstance; + subMesh.m_bufferFlags |= RayTracingSubMeshBufferFlags::UV; + subMesh.m_uvFormat = UVStreamFormat; + subMesh.m_uvVertexBufferView = streamBufferViews[4]; + subMesh.m_uvShaderBufferView = const_cast(streamBufferViews[4].GetBuffer())->GetBufferView(uvBufferDescriptor); } - AZ::Color irradianceColor(1.0f, 1.0f, 1.0f, 1.0f); + subMesh.m_indexBufferView = mesh.m_indexBufferView; + subMesh.m_indexShaderBufferView = const_cast(mesh.m_indexBufferView.GetBuffer())->GetBufferView(indexBufferDescriptor); + + // add material data if (material) { + // irradiance color RPI::MaterialPropertyIndex propertyIndex = material->FindPropertyIndex(AZ::Name("irradiance.color")); if (propertyIndex.IsValid()) { - irradianceColor = material->GetPropertyValue(propertyIndex); + subMesh.m_irradianceColor = material->GetPropertyValue(propertyIndex); } propertyIndex = material->FindPropertyIndex(AZ::Name("irradiance.factor")); if (propertyIndex.IsValid()) { - irradianceColor *= material->GetPropertyValue(propertyIndex); + subMesh.m_irradianceColor *= material->GetPropertyValue(propertyIndex); + } + + // base color + propertyIndex = material->FindPropertyIndex(AZ::Name("baseColor.color")); + if (propertyIndex.IsValid()) + { + subMesh.m_baseColor = material->GetPropertyValue(propertyIndex); + } + + propertyIndex = material->FindPropertyIndex(AZ::Name("baseColor.factor")); + if (propertyIndex.IsValid()) + { + subMesh.m_baseColor *= material->GetPropertyValue(propertyIndex); + } + + // metallic + propertyIndex = material->FindPropertyIndex(AZ::Name("metallic.factor")); + if (propertyIndex.IsValid()) + { + subMesh.m_metallicFactor = material->GetPropertyValue(propertyIndex); + } + + // roughness + propertyIndex = material->FindPropertyIndex(AZ::Name("roughness.factor")); + if (propertyIndex.IsValid()) + { + subMesh.m_roughnessFactor = material->GetPropertyValue(propertyIndex); + } + + // textures + propertyIndex = material->FindPropertyIndex(AZ::Name("baseColor.textureMap")); + if (propertyIndex.IsValid()) + { + Data::Instance image = material->GetPropertyValue>(propertyIndex); + if (image.get()) + { + subMesh.m_textureFlags |= RayTracingSubMeshTextureFlags::BaseColor; + subMesh.m_baseColorImageView = image->GetImageView(); + } + } + + propertyIndex = material->FindPropertyIndex(AZ::Name("normal.textureMap")); + if (propertyIndex.IsValid()) + { + Data::Instance image = material->GetPropertyValue>(propertyIndex); + if (image.get()) + { + subMesh.m_textureFlags |= RayTracingSubMeshTextureFlags::Normal; + subMesh.m_normalImageView = image->GetImageView(); + } + } + + propertyIndex = material->FindPropertyIndex(AZ::Name("metallic.textureMap")); + if (propertyIndex.IsValid()) + { + Data::Instance image = material->GetPropertyValue>(propertyIndex); + if (image.get()) + { + subMesh.m_textureFlags |= RayTracingSubMeshTextureFlags::Metallic; + subMesh.m_metallicImageView = image->GetImageView(); + } + } + + propertyIndex = material->FindPropertyIndex(AZ::Name("roughness.textureMap")); + if (propertyIndex.IsValid()) + { + Data::Instance image = material->GetPropertyValue>(propertyIndex); + if (image.get()) + { + subMesh.m_textureFlags |= RayTracingSubMeshTextureFlags::Roughness; + subMesh.m_roughnessImageView = image->GetImageView(); + } } } - RayTracingFeatureProcessor::SubMesh subMesh; - subMesh.m_vertexFormat = StreamFormat; - subMesh.m_positionVertexBufferView = streamBufferViews[0]; - subMesh.m_positionShaderBufferView = const_cast(streamBufferViews[0].GetBuffer())->GetBufferView(vertexBufferDescriptor); - subMesh.m_normalVertexBufferView = streamBufferViews[1]; - subMesh.m_normalShaderBufferView = const_cast(streamBufferViews[1].GetBuffer())->GetBufferView(vertexBufferDescriptor); - subMesh.m_indexBufferView = mesh.m_indexBufferView; - subMesh.m_indexShaderBufferView = const_cast(mesh.m_indexBufferView.GetBuffer())->GetBufferView(indexBufferDescriptor); - subMesh.m_irradianceColor = irradianceColor; subMeshes.push_back(subMesh); } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp index 2bb2fa2ac2..92cd41b4e8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp @@ -115,11 +115,11 @@ namespace AZ } } - // update and compile the RayTracingSceneSrg + // update and compile the RayTracingSceneSrg and RayTracingMaterialSrg // Note: the timing of this update is very important, it needs to be updated after the TLAS is allocated so it can // be set on the RayTracingSceneSrg for this frame, and the ray tracing mesh data in the RayTracingSceneSrg must // exactly match the TLAS. Any mismatch in this data may result in a TDR. - rayTracingFeatureProcessor->UpdateRayTracingSceneSrg(); + rayTracingFeatureProcessor->UpdateRayTracingSrgs(); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp index 7c13daea3b..10c7c2d378 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -71,6 +71,13 @@ namespace AZ AZ_Assert(rayTracingSceneSrgAsset.IsReady(), "Failed to load RayTracingSceneSrg asset"); m_rayTracingSceneSrg = RPI::ShaderResourceGroup::Create(rayTracingSceneSrgAsset); + + // load the RayTracingMaterialSrg asset + Data::Asset rayTracingMaterialSrgAsset = + RPI::AssetUtils::LoadAssetByProductPath("shaderlib/atom/features/raytracing/raytracingmaterialsrg_raytracingmaterialsrg.azsrg", RPI::AssetUtils::TraceLevel::Error); + AZ_Assert(rayTracingMaterialSrgAsset.IsReady(), "Failed to load RayTracingMaterialSrg asset"); + + m_rayTracingMaterialSrg = RPI::ShaderResourceGroup::Create(rayTracingMaterialSrgAsset); } void RayTracingFeatureProcessor::SetMesh(const ObjectId objectId, const SubMeshVector& subMeshes) @@ -104,7 +111,7 @@ namespace AZ RHI::RayTracingBlasDescriptor blasDescriptor; blasDescriptor.Build() ->Geometry() - ->VertexFormat(subMesh.m_vertexFormat) + ->VertexFormat(subMesh.m_positionFormat) ->VertexBuffer(subMesh.m_positionVertexBufferView) ->IndexBuffer(subMesh.m_indexBufferView) ; @@ -124,6 +131,7 @@ namespace AZ m_subMeshCount += aznumeric_cast(subMeshes.size()); m_meshInfoBufferNeedsUpdate = true; + m_materialInfoBufferNeedsUpdate = true; } void RayTracingFeatureProcessor::RemoveMesh(const ObjectId objectId) @@ -142,6 +150,7 @@ namespace AZ } m_meshInfoBufferNeedsUpdate = true; + m_materialInfoBufferNeedsUpdate = true; } void RayTracingFeatureProcessor::SetMeshTransform(const ObjectId objectId, const AZ::Transform transform, const AZ::Vector3 nonUniformScale) @@ -162,14 +171,14 @@ namespace AZ m_meshInfoBufferNeedsUpdate = true; } - void RayTracingFeatureProcessor::UpdateRayTracingSceneSrg() + void RayTracingFeatureProcessor::UpdateRayTracingSrgs() { if (!m_tlas->GetTlasBuffer()) { return; } - if (m_rayTracingSceneSrg->IsQueuedForCompile()) + if (m_rayTracingSceneSrg->IsQueuedForCompile() || m_rayTracingMaterialSrg->IsQueuedForCompile()) { //[GFX TODO][ATOM-14792] AtomSampleViewer: Reset scene and feature processors before switching to sample return; @@ -178,7 +187,148 @@ namespace AZ // update the mesh info buffer with the latest ray tracing enabled meshes UpdateMeshInfoBuffer(); + // update the material info buffer with the latest ray tracing enabled meshes + UpdateMaterialInfoBuffer(); + // update the RayTracingSceneSrg + UpdateRayTracingSceneSrg(); + + // update the RayTracingMaterialSrg + UpdateRayTracingMaterialSrg(); + } + + void RayTracingFeatureProcessor::UpdateMeshInfoBuffer() + { + if (m_meshInfoBufferNeedsUpdate && (m_subMeshCount > 0)) + { + TransformServiceFeatureProcessor* transformFeatureProcessor = GetParentScene()->GetFeatureProcessor(); + + AZStd::vector meshInfos; + meshInfos.reserve(m_subMeshCount); + + uint32_t newMeshByteCount = m_subMeshCount * sizeof(MeshInfo); + + if (m_meshInfoBuffer == nullptr) + { + AZStd::string uuidString = AZ::Uuid::CreateRandom().ToString(); + + // allocate the MeshInfo structured buffer + RPI::CommonBufferDescriptor desc; + desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; + desc.m_bufferName = AZStd::string::format("RayTracingMeshInfo_%s", uuidString.c_str()); + desc.m_byteCount = newMeshByteCount; + desc.m_elementSize = sizeof(MeshInfo); + m_meshInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); + } + else if (m_meshInfoBuffer->GetBufferSize() < newMeshByteCount) + { + // resize for the new sub-mesh count + m_meshInfoBuffer->Resize(newMeshByteCount); + } + + // keep track of the start index of the buffers for each mesh, this is put into the MeshInfo + // entry for each mesh so it knows where to find the start of its buffers in the unbounded array + uint32_t bufferStartIndex = 0; + + for (const auto& mesh : m_meshes) + { + AZ::Transform meshTransform = transformFeatureProcessor->GetTransformForId(TransformServiceFeatureProcessorInterface::ObjectId(mesh.first)); + AZ::Transform noScaleTransform = meshTransform; + noScaleTransform.ExtractUniformScale(); + AZ::Matrix3x3 rotationMatrix = Matrix3x3::CreateFromTransform(noScaleTransform); + rotationMatrix = rotationMatrix.GetInverseFull().GetTranspose(); + + const RayTracingFeatureProcessor::SubMeshVector& subMeshes = mesh.second.m_subMeshes; + for (const auto& subMesh : subMeshes) + { + MeshInfo meshInfo; + meshInfo.m_indexOffset = subMesh.m_indexBufferView.GetByteOffset(); + meshInfo.m_positionOffset = subMesh.m_positionVertexBufferView.GetByteOffset(); + meshInfo.m_normalOffset = subMesh.m_normalVertexBufferView.GetByteOffset(); + meshInfo.m_tangentOffset = subMesh.m_tangentVertexBufferView.GetByteOffset(); + meshInfo.m_bitangentOffset = subMesh.m_bitangentVertexBufferView.GetByteOffset(); + + if (RHI::CheckBitsAll(subMesh.m_bufferFlags, RayTracingSubMeshBufferFlags::UV)) + { + meshInfo.m_uvOffset = subMesh.m_uvVertexBufferView.GetByteOffset(); + } + + subMesh.m_irradianceColor.StoreToFloat4(meshInfo.m_irradianceColor.data()); + rotationMatrix.StoreToRowMajorFloat9(meshInfo.m_worldInvTranspose.data()); + meshInfo.m_bufferFlags = subMesh.m_bufferFlags; + meshInfo.m_bufferStartIndex = bufferStartIndex; + + // add the count of buffers present in this subMesh to the start index for the next subMesh + // note that the Index, Position, Normal, Tangent, and Bitangent buffers are always counted since they are guaranteed + static const uint32_t RayTracingSubMeshFixedStreamCount = 5; + bufferStartIndex += (RayTracingSubMeshFixedStreamCount + RHI::CountBitsSet(aznumeric_cast(meshInfo.m_bufferFlags))); + + meshInfos.emplace_back(meshInfo); + } + } + + m_meshInfoBuffer->UpdateData(meshInfos.data(), newMeshByteCount); + m_meshInfoBufferNeedsUpdate = false; + } + } + + void RayTracingFeatureProcessor::UpdateMaterialInfoBuffer() + { + if (m_materialInfoBufferNeedsUpdate && (m_subMeshCount > 0)) + { + AZStd::vector materialInfos; + materialInfos.reserve(m_subMeshCount); + + uint32_t newMaterialByteCount = m_subMeshCount * sizeof(MaterialInfo); + + if (m_materialInfoBuffer == nullptr) + { + AZStd::string uuidString = AZ::Uuid::CreateRandom().ToString(); + + // allocate the MaterialInfo structured buffer + RPI::CommonBufferDescriptor desc; + desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; + desc.m_bufferName = AZStd::string::format("RayTracingMaterialInfo_%s", uuidString.c_str()); + desc.m_byteCount = newMaterialByteCount; + desc.m_elementSize = sizeof(MaterialInfo); + m_materialInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); + } + else if (m_materialInfoBuffer->GetBufferSize() < newMaterialByteCount) + { + // resize for the new sub-mesh count + m_materialInfoBuffer->Resize(newMaterialByteCount); + } + + // keep track of the start index of the textures for each mesh, this is put into the MaterialInfo + // entry for each mesh so it knows where to find the start of its textures in the unbounded array + uint32_t textureStartIndex = 0; + + for (const auto& mesh : m_meshes) + { + const RayTracingFeatureProcessor::SubMeshVector& subMeshes = mesh.second.m_subMeshes; + for (const auto& subMesh : subMeshes) + { + MaterialInfo materialInfo; + subMesh.m_baseColor.StoreToFloat4(materialInfo.m_baseColor.data()); + materialInfo.m_metallicFactor = subMesh.m_metallicFactor; + materialInfo.m_roughnessFactor = subMesh.m_roughnessFactor; + materialInfo.m_textureFlags = subMesh.m_textureFlags; + materialInfo.m_textureStartIndex = textureStartIndex; + + // add the count of textures present in this subMesh to the start index for the next subMesh + textureStartIndex += RHI::CountBitsSet(aznumeric_cast(materialInfo.m_textureFlags)); + + materialInfos.emplace_back(materialInfo); + } + } + + m_materialInfoBuffer->UpdateData(materialInfos.data(), newMaterialByteCount); + m_materialInfoBufferNeedsUpdate = false; + } + } + + void RayTracingFeatureProcessor::UpdateRayTracingSceneSrg() + { const RHI::ShaderResourceGroupLayout* srgLayout = m_rayTracingSceneSrg->GetLayout(); RHI::ShaderInputImageIndex imageIndex; RHI::ShaderInputBufferIndex bufferIndex; @@ -272,11 +422,18 @@ namespace AZ const SubMeshVector& subMeshes = mesh.second.m_subMeshes; for (const auto& subMesh : subMeshes) { - // add the index, position, and normal buffers for this sub-mesh to the mesh buffer list, this will - // go into the shader as an unbounded array in the Srg + // add the stream buffers for this sub-mesh to the mesh buffer list, + // this is sent to the shader as an unbounded array in the Srg meshBuffers.push_back(subMesh.m_indexShaderBufferView.get()); meshBuffers.push_back(subMesh.m_positionShaderBufferView.get()); meshBuffers.push_back(subMesh.m_normalShaderBufferView.get()); + meshBuffers.push_back(subMesh.m_tangentShaderBufferView.get()); + meshBuffers.push_back(subMesh.m_bitangentShaderBufferView.get()); + + if (RHI::CheckBitsAll(subMesh.m_bufferFlags, RayTracingSubMeshBufferFlags::UV)) + { + meshBuffers.push_back(subMesh.m_uvShaderBufferView.get()); + } } } @@ -287,58 +444,53 @@ namespace AZ m_rayTracingSceneSrg->Compile(); } - void RayTracingFeatureProcessor::UpdateMeshInfoBuffer() + void RayTracingFeatureProcessor::UpdateRayTracingMaterialSrg() { - if (m_meshInfoBufferNeedsUpdate && (m_subMeshCount > 0)) - { - TransformServiceFeatureProcessor* transformFeatureProcessor = GetParentScene()->GetFeatureProcessor(); - - AZStd::vector meshInfos; - meshInfos.reserve(m_subMeshCount); + const RHI::ShaderResourceGroupLayout* srgLayout = m_rayTracingMaterialSrg->GetLayout(); + RHI::ShaderInputImageIndex imageIndex; + RHI::ShaderInputBufferIndex bufferIndex; + RHI::ShaderInputConstantIndex constantIndex; - uint32_t newMeshByteCount = m_subMeshCount * sizeof(MeshInfo); - - if (m_meshInfoBuffer == nullptr) - { - // allocate the MeshInfo structured buffer - RPI::CommonBufferDescriptor desc; - desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc.m_bufferName = "RayTracingMeshInfo"; - desc.m_byteCount = newMeshByteCount; - desc.m_elementSize = sizeof(MeshInfo); - m_meshInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); - } - else if (m_meshInfoBuffer->GetBufferSize() < newMeshByteCount) - { - // resize for the new sub-mesh count - m_meshInfoBuffer->Resize(newMeshByteCount); - } + bufferIndex = srgLayout->FindShaderInputBufferIndex(AZ::Name("m_materialInfo")); + m_rayTracingMaterialSrg->SetBufferView(bufferIndex, m_materialInfoBuffer->GetBufferView()); + if (m_subMeshCount) + { + AZStd::vector materialTextures; for (const auto& mesh : m_meshes) { - AZ::Transform meshTransform = transformFeatureProcessor->GetTransformForId(TransformServiceFeatureProcessorInterface::ObjectId(mesh.first)); - AZ::Transform noScaleTransform = meshTransform; - noScaleTransform.ExtractUniformScale(); - AZ::Matrix3x3 rotationMatrix = Matrix3x3::CreateFromTransform(noScaleTransform); - rotationMatrix = rotationMatrix.GetInverseFull().GetTranspose(); - - const RayTracingFeatureProcessor::SubMeshVector& subMeshes = mesh.second.m_subMeshes; + const SubMeshVector& subMeshes = mesh.second.m_subMeshes; for (const auto& subMesh : subMeshes) { - MeshInfo meshInfo; - meshInfo.m_indexOffset = subMesh.m_indexBufferView.GetByteOffset(); - meshInfo.m_positionOffset = subMesh.m_positionVertexBufferView.GetByteOffset(); - meshInfo.m_normalOffset = subMesh.m_normalVertexBufferView.GetByteOffset(); - subMesh.m_irradianceColor.StoreToFloat4(meshInfo.m_irradianceColor.data()); - rotationMatrix.StoreToRowMajorFloat9(meshInfo.m_worldInvTranspose.data()); - - meshInfos.emplace_back(meshInfo); + // add the baseColor, normal, metallic, and roughness images for this sub-mesh to the material texture list, + // this is sent to the shader as an unbounded array in the Srg + if (RHI::CheckBitsAll(subMesh.m_textureFlags, RayTracingSubMeshTextureFlags::BaseColor)) + { + materialTextures.push_back(subMesh.m_baseColorImageView.get()); + } + + if (RHI::CheckBitsAll(subMesh.m_textureFlags, RayTracingSubMeshTextureFlags::Normal)) + { + materialTextures.push_back(subMesh.m_normalImageView.get()); + } + + if (RHI::CheckBitsAll(subMesh.m_textureFlags, RayTracingSubMeshTextureFlags::Metallic)) + { + materialTextures.push_back(subMesh.m_metallicImageView.get()); + } + + if (RHI::CheckBitsAll(subMesh.m_textureFlags, RayTracingSubMeshTextureFlags::Roughness)) + { + materialTextures.push_back(subMesh.m_roughnessImageView.get()); + } } } - m_meshInfoBuffer->UpdateData(meshInfos.data(), newMeshByteCount); - m_meshInfoBufferNeedsUpdate = false; + RHI::ShaderInputImageUnboundedArrayIndex textureUnboundedArrayIndex = srgLayout->FindShaderInputImageUnboundedArrayIndex(AZ::Name("m_materialTextures")); + m_rayTracingMaterialSrg->SetImageViewUnboundedArray(textureUnboundedArrayIndex, materialTextures); } + + m_rayTracingMaterialSrg->Compile(); } } } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h index f317f1c096..90a9383ae6 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,28 @@ namespace AZ { namespace Render { + static const uint32_t RayTracingGlobalSrgBindingSlot = 0; + static const uint32_t RayTracingSceneSrgBindingSlot = 1; + static const uint32_t RayTracingMaterialSrgBindingSlot = 2; + + enum class RayTracingSubMeshBufferFlags : uint32_t + { + None = 0, + + UV = AZ_BIT(0) + }; + AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::Render::RayTracingSubMeshBufferFlags); + + enum class RayTracingSubMeshTextureFlags : uint32_t + { + None = 0, + BaseColor = AZ_BIT(0), + Normal = AZ_BIT(1), + Metallic = AZ_BIT(2), + Roughness = AZ_BIT(3) + }; + AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::Render::RayTracingSubMeshTextureFlags); + //! This feature processor manages ray tracing data for a Scene class RayTracingFeatureProcessor : public RPI::FeatureProcessor @@ -42,20 +65,53 @@ namespace AZ //! Contains data for a single sub-mesh struct SubMesh { - // vertex/index buffer data - RHI::Format m_vertexFormat = RHI::Format::Unknown; + // vertex streams + RHI::Format m_positionFormat = RHI::Format::Unknown; RHI::StreamBufferView m_positionVertexBufferView; RHI::Ptr m_positionShaderBufferView; + + RHI::Format m_normalFormat = RHI::Format::Unknown; RHI::StreamBufferView m_normalVertexBufferView; RHI::Ptr m_normalShaderBufferView; + + RHI::Format m_tangentFormat = RHI::Format::Unknown; + RHI::StreamBufferView m_tangentVertexBufferView; + RHI::Ptr m_tangentShaderBufferView; + + RHI::Format m_bitangentFormat = RHI::Format::Unknown; + RHI::StreamBufferView m_bitangentVertexBufferView; + RHI::Ptr m_bitangentShaderBufferView; + + RHI::Format m_uvFormat = RHI::Format::Unknown; + RHI::StreamBufferView m_uvVertexBufferView; + RHI::Ptr m_uvShaderBufferView; + + // index buffer RHI::IndexBufferView m_indexBufferView; RHI::Ptr m_indexShaderBufferView; + // vertex buffer usage flags + RayTracingSubMeshBufferFlags m_bufferFlags = RayTracingSubMeshBufferFlags::None; + // color of the bounced light from this sub-mesh - AZ::Color m_irradianceColor; + AZ::Color m_irradianceColor = AZ::Color(1.0f); // ray tracing Blas RHI::Ptr m_blas; + + // material data + AZ::Color m_baseColor = AZ::Color(0.0f); + float m_metallicFactor = 0.0f; + float m_roughnessFactor = 0.0f; + + // material texture usage flags + RayTracingSubMeshTextureFlags m_textureFlags = RayTracingSubMeshTextureFlags::None; + + // material textures + RHI::Ptr m_baseColorImageView; + RHI::Ptr m_normalImageView; + RHI::Ptr m_metallicImageView; + RHI::Ptr m_roughnessImageView; }; using SubMeshVector = AZStd::vector; @@ -98,6 +154,9 @@ namespace AZ //! Retrieves the RayTracingSceneSrg Data::Instance GetRayTracingSceneSrg() const { return m_rayTracingSceneSrg; } + //! Retrieves the RayTracingMaterialSrg + Data::Instance GetRayTracingMaterialSrg() const { return m_rayTracingMaterialSrg; } + //! Retrieves the RayTracingTlas const RHI::Ptr& GetTlas() const { return m_tlas; } RHI::Ptr& GetTlas() { return m_tlas; } @@ -118,14 +177,20 @@ namespace AZ //! Retrieves the GPU buffer containing information for all ray tracing meshes. const Data::Instance GetMeshInfoBuffer() const { return m_meshInfoBuffer; } - //! Updates the RayTracingSceneSrg, called after the TLAS is allocated in the RayTracingAccelerationStructurePass - void UpdateRayTracingSceneSrg(); + //! Retrieves the GPU buffer containing information for all ray tracing materials. + const Data::Instance GetMaterialInfoBuffer() const { return m_materialInfoBuffer; } + + //! Updates the RayTracingSceneSrg and RayTracingMaterialSrg, called after the TLAS is allocated in the RayTracingAccelerationStructurePass + void UpdateRayTracingSrgs(); private: AZ_DISABLE_COPY_MOVE(RayTracingFeatureProcessor); void UpdateMeshInfoBuffer(); + void UpdateMaterialInfoBuffer(); + void UpdateRayTracingSceneSrg(); + void UpdateRayTracingMaterialSrg(); // flag indicating if RayTracing is enabled, currently based on device support bool m_rayTracingEnabled = false; @@ -143,6 +208,9 @@ namespace AZ // ray tracing scene Srg Data::Instance m_rayTracingSceneSrg; + // ray tracing material Srg + Data::Instance m_rayTracingMaterialSrg; + // current revision number of ray tracing data uint32_t m_revision = 0; @@ -158,18 +226,43 @@ namespace AZ // structure for data in the m_meshInfoBuffer, shaders that use the buffer must match this type struct MeshInfo { - uint32_t m_indexOffset; - uint32_t m_positionOffset; - uint32_t m_normalOffset; + uint32_t m_indexOffset; + uint32_t m_positionOffset; + uint32_t m_normalOffset; + uint32_t m_tangentOffset; + uint32_t m_bitangentOffset; + uint32_t m_uvOffset; + float m_padding0[2]; + AZStd::array m_irradianceColor; // float4 AZStd::array m_worldInvTranspose; // float3x3 + float m_padding1; + + RayTracingSubMeshBufferFlags m_bufferFlags = RayTracingSubMeshBufferFlags::None; + uint32_t m_bufferStartIndex = 0; }; // buffer containing a MeshInfo for each sub-mesh Data::Instance m_meshInfoBuffer; - // flag indicating we need to update the mesh info GPU buffer + // structure for data in the m_materialInfoBuffer, shaders that use the buffer must match this type + struct MaterialInfo + { + AZStd::array m_baseColor; // float4 + float m_metallicFactor = 0.0f; + float m_roughnessFactor = 0.0f; + RayTracingSubMeshTextureFlags m_textureFlags = RayTracingSubMeshTextureFlags::None; + uint32_t m_textureStartIndex = 0; + }; + + // buffer containing a MaterialInfo for each sub-mesh + Data::Instance m_materialInfoBuffer; + + // flag indicating we need to update the meshInfo buffer bool m_meshInfoBufferNeedsUpdate = false; + + // flag indicating we need to update the materialInfo buffer + bool m_materialInfoBufferNeedsUpdate = false; }; } } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp new file mode 100644 index 0000000000..988870cc0e --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp @@ -0,0 +1,362 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + RPI::Ptr RayTracingPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew RayTracingPass(descriptor); + return pass; + } + + RayTracingPass::RayTracingPass(const RPI::PassDescriptor& descriptor) + : RenderPass(descriptor) + , m_passDescriptor(descriptor) + { + RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); + if (device->GetFeatures().m_rayTracing == false) + { + // raytracing is not supported on this platform + SetEnabled(false); + return; + } + + Init(); + } + + RayTracingPass::~RayTracingPass() + { + RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect(); + } + + void RayTracingPass::Init() + { + RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); + + m_passData = RPI::PassUtils::GetPassData(m_passDescriptor); + if (m_passData == nullptr) + { + AZ_Error("PassSystem", false, "RayTracingPass [%s]: Invalid RayTracingPassData", GetPathName().GetCStr()); + return; + } + + // ray generation shader + m_rayGenerationShader = LoadShader(m_passData->m_rayGenerationShaderAssetReference); + if (m_rayGenerationShader == nullptr) + { + AZ_Error("PassSystem", false, "RayTracingPass [%s]: Failed to load RayGeneration shader [%s]", GetPathName().GetCStr(), m_passData->m_rayGenerationShaderAssetReference.m_filePath.data()); + return; + } + + auto shaderVariant = m_rayGenerationShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId); + RHI::PipelineStateDescriptorForRayTracing rayGenerationShaderDescriptor; + shaderVariant.ConfigurePipelineState(rayGenerationShaderDescriptor); + + // closest hit shader + m_closestHitShader = LoadShader(m_passData->m_closestHitShaderAssetReference); + if (m_closestHitShader == nullptr) + { + AZ_Error("PassSystem", false, "RayTracingPass [%s]: Failed to load ClosestHit shader [%s]", GetPathName().GetCStr(), m_passData->m_closestHitShaderAssetReference.m_filePath.data()); + return; + } + + shaderVariant = m_closestHitShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId); + RHI::PipelineStateDescriptorForRayTracing closestHitShaderDescriptor; + shaderVariant.ConfigurePipelineState(closestHitShaderDescriptor); + + // miss shader + m_missShader = LoadShader(m_passData->m_missShaderAssetReference); + if (m_missShader == nullptr) + { + AZ_Error("PassSystem", false, "RayTracingPass [%s]: Failed to load Miss shader [%s]", GetPathName().GetCStr(), m_passData->m_missShaderAssetReference.m_filePath.data()); + return; + } + + shaderVariant = m_missShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId); + RHI::PipelineStateDescriptorForRayTracing missShaderDescriptor; + shaderVariant.ConfigurePipelineState(missShaderDescriptor); + + // retrieve global pipeline state + m_globalPipelineState = m_rayGenerationShader->AcquirePipelineState(rayGenerationShaderDescriptor); + AZ_Assert(m_globalPipelineState, "Failed to acquire ray tracing global pipeline state"); + + // create global srg + Data::Asset globalSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RayTracingGlobalSrgBindingSlot); + AZ_Error("PassSystem", globalSrgAsset.GetId().IsValid(), "RayTracingPass [%s] Failed to find RayTracingGlobalSrg asset", GetPathName().GetCStr()); + AZ_Error("PassSystem", globalSrgAsset.IsReady(), "RayTracingPass [%s] asset is not loaded for shader", GetPathName().GetCStr()); + + m_shaderResourceGroup = RPI::ShaderResourceGroup::Create(globalSrgAsset); + AZ_Assert(m_shaderResourceGroup, "RayTracingPass [%s]: Failed to create RayTracingGlobalSrg", GetPathName().GetCStr()); + RPI::PassUtils::BindDataMappingsToSrg(m_passDescriptor, m_shaderResourceGroup.get()); + + // check to see if the shader requires the View and RayTracingMaterial Srgs + Data::Asset viewSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RPI::SrgBindingSlot::View); + m_requiresViewSrg = viewSrgAsset.GetId().IsValid(); + + Data::Asset rayTracingMaterialSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RayTracingMaterialSrgBindingSlot); + m_requiresRayTracingMaterialSrg = rayTracingMaterialSrgAsset.GetId().IsValid(); + + // build the ray tracing pipeline state descriptor + RHI::RayTracingPipelineStateDescriptor descriptor; + descriptor.Build() + ->PipelineState(m_globalPipelineState.get()) + ->MaxPayloadSize(m_passData->m_maxPayloadSize) + ->MaxAttributeSize(m_passData->m_maxAttributeSize) + ->MaxRecursionDepth(m_passData->m_maxRecursionDepth) + ->ShaderLibrary(rayGenerationShaderDescriptor) + ->RayGenerationShaderName(AZ::Name(m_passData->m_rayGenerationShaderName.c_str())) + ->ShaderLibrary(missShaderDescriptor) + ->MissShaderName(AZ::Name(m_passData->m_missShaderName.c_str())) + ->ShaderLibrary(closestHitShaderDescriptor) + ->ClosestHitShaderName(AZ::Name(m_passData->m_closestHitShaderName.c_str())) + ->HitGroup(AZ::Name("HitGroup")) + ->ClosestHitShaderName(AZ::Name(m_passData->m_closestHitShaderName.c_str())); + + // create the ray tracing pipeline state object + m_rayTracingPipelineState = RHI::Factory::Get().CreateRayTracingPipelineState(); + m_rayTracingPipelineState->Init(*device.get(), &descriptor); + + // make sure the shader table rebuilds if we're hotreloading + m_rayTracingRevision = 0; + + RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect(); + RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_passData->m_rayGenerationShaderAssetReference.m_assetId); + RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_passData->m_closestHitShaderAssetReference.m_assetId); + RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_passData->m_missShaderAssetReference.m_assetId); + } + + Data::Instance RayTracingPass::LoadShader(const RPI::AssetReference& shaderAssetReference) + { + Data::Asset shaderAsset; + if (shaderAssetReference.m_assetId.IsValid()) + { + shaderAsset = RPI::FindShaderAsset(shaderAssetReference.m_assetId, shaderAssetReference.m_filePath); + } + + if (!shaderAsset.GetId().IsValid()) + { + AZ_Error("PassSystem", false, "RayTracingPass [%s]: Failed to load shader asset [%s]", GetPathName().GetCStr(), shaderAssetReference.m_filePath.data()); + return nullptr; + } + + return RPI::Shader::FindOrCreate(shaderAsset); + } + + void RayTracingPass::FrameBeginInternal(FramePrepareParams params) + { + RPI::Scene* scene = m_pipeline->GetScene(); + RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor(); + if (!rayTracingFeatureProcessor) + { + return; + } + + if (!m_rayTracingShaderTable) + { + RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); + RHI::RayTracingBufferPools& rayTracingBufferPools = rayTracingFeatureProcessor->GetBufferPools(); + + m_rayTracingShaderTable = RHI::Factory::Get().CreateRayTracingShaderTable(); + m_rayTracingShaderTable->Init(*device.get(), rayTracingBufferPools); + } + + RPI::RenderPass::FrameBeginInternal(params); + } + + void RayTracingPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) + { + RPI::Scene* scene = m_pipeline->GetScene(); + RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor(); + AZ_Assert(rayTracingFeatureProcessor, "RayTracingPass requires the RayTracingFeatureProcessor"); + + RPI::RenderPass::SetupFrameGraphDependencies(frameGraph); + frameGraph.SetEstimatedItemCount(1); + + // TLAS + { + const RHI::Ptr& rayTracingTlasBuffer = rayTracingFeatureProcessor->GetTlas()->GetTlasBuffer(); + if (rayTracingTlasBuffer) + { + AZ::RHI::AttachmentId tlasAttachmentId = rayTracingFeatureProcessor->GetTlasAttachmentId(); + if (frameGraph.GetAttachmentDatabase().IsAttachmentValid(tlasAttachmentId) == false) + { + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportBuffer(tlasAttachmentId, rayTracingTlasBuffer); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import ray tracing TLAS buffer with error %d", result); + } + + uint32_t tlasBufferByteCount = aznumeric_cast(rayTracingFeatureProcessor->GetTlas()->GetTlasBuffer()->GetDescriptor().m_byteCount); + RHI::BufferViewDescriptor tlasBufferViewDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, tlasBufferByteCount); + + RHI::BufferScopeAttachmentDescriptor desc; + desc.m_attachmentId = tlasAttachmentId; + desc.m_bufferViewDescriptor = tlasBufferViewDescriptor; + desc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Load; + + frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite); + } + } + } + + void RayTracingPass::CompileResources(const RHI::FrameGraphCompileContext& context) + { + RPI::Scene* scene = m_pipeline->GetScene(); + RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor(); + AZ_Assert(rayTracingFeatureProcessor, "RayTracingPass requires the RayTracingFeatureProcessor"); + + if (m_shaderResourceGroup != nullptr) + { + BindPassSrg(context, m_shaderResourceGroup); + m_shaderResourceGroup->Compile(); + } + + uint32_t rayTracingRevision = rayTracingFeatureProcessor->GetRevision(); + if (m_rayTracingRevision != rayTracingRevision) + { + // scene changed, need to rebuild the shader table + m_rayTracingRevision = rayTracingRevision; + + AZStd::shared_ptr descriptor = AZStd::make_shared(); + + if (rayTracingFeatureProcessor->GetSubMeshCount()) + { + // build the ray tracing shader table descriptor + RHI::RayTracingShaderTableDescriptor* descriptorBuild = descriptor->Build(AZ::Name("RayTracingShaderTable"), m_rayTracingPipelineState) + ->RayGenerationRecord(AZ::Name(m_passData->m_rayGenerationShaderName.c_str())) + ->MissRecord(AZ::Name(m_passData->m_missShaderName.c_str())); + + // add a hit group for each mesh to the shader table + for (uint32_t i = 0; i < rayTracingFeatureProcessor->GetSubMeshCount(); ++i) + { + descriptorBuild->HitGroupRecord(AZ::Name("HitGroup")); + } + } + + m_rayTracingShaderTable->Build(descriptor); + } + } + + void RayTracingPass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) + { + RPI::Scene* scene = m_pipeline->GetScene(); + RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor(); + AZ_Assert(rayTracingFeatureProcessor, "RayTracingPass requires the RayTracingFeatureProcessor"); + + if (!rayTracingFeatureProcessor || + !rayTracingFeatureProcessor->GetTlas()->GetTlasBuffer() || + !rayTracingFeatureProcessor->GetSubMeshCount() || + !m_rayTracingShaderTable) + { + return; + } + + RHI::DispatchRaysItem dispatchRaysItem; + + // calculate thread counts if this is a full screen raytracing pass + if (m_passData->m_makeFullscreenPass) + { + RPI::PassAttachment* outputAttachment = nullptr; + + if (GetOutputCount() > 0) + { + outputAttachment = GetOutputBinding(0).m_attachment.get(); + } + else if (GetInputOutputCount() > 0) + { + outputAttachment = GetInputOutputBinding(0).m_attachment.get(); + } + + AZ_Assert(outputAttachment != nullptr, "[RayTracingPass '%s']: A fullscreen RayTracing pass must have a valid output or input/output.", GetPathName().GetCStr()); + AZ_Assert(outputAttachment->GetAttachmentType() == RHI::AttachmentType::Image, "[RayTracingPass '%s']: The output of a fullscreen RayTracing pass must be an image.", GetPathName().GetCStr()); + + RHI::Size imageSize = outputAttachment->m_descriptor.m_image.m_size; + + dispatchRaysItem.m_width = imageSize.m_width; + dispatchRaysItem.m_height = imageSize.m_height; + dispatchRaysItem.m_depth = imageSize.m_depth; + } + else + { + dispatchRaysItem.m_width = m_passData->m_threadCountX; + dispatchRaysItem.m_height = m_passData->m_threadCountY; + dispatchRaysItem.m_depth = m_passData->m_threadCountZ; + } + + // bind RayTracingGlobal, RayTracingScene, and View Srgs + // [GFX TODO][ATOM-15610] Add RenderPass::SetSrgsForRayTracingDispatch + AZStd::vector shaderResourceGroups = + { + m_shaderResourceGroup->GetRHIShaderResourceGroup(), + rayTracingFeatureProcessor->GetRayTracingSceneSrg()->GetRHIShaderResourceGroup() + }; + + if (m_requiresViewSrg) + { + const AZStd::vector& views = m_pipeline->GetViews(m_passData->m_pipelineViewTag); + if (views.size() > 0) + { + shaderResourceGroups.push_back(views[0]->GetRHIShaderResourceGroup()); + } + } + + if (m_requiresRayTracingMaterialSrg) + { + shaderResourceGroups.push_back(rayTracingFeatureProcessor->GetRayTracingMaterialSrg()->GetRHIShaderResourceGroup()); + } + + dispatchRaysItem.m_shaderResourceGroupCount = aznumeric_cast(shaderResourceGroups.size()); + dispatchRaysItem.m_shaderResourceGroups = shaderResourceGroups.data(); + dispatchRaysItem.m_rayTracingPipelineState = m_rayTracingPipelineState.get(); + dispatchRaysItem.m_rayTracingShaderTable = m_rayTracingShaderTable.get(); + dispatchRaysItem.m_globalPipelineState = m_globalPipelineState.get(); + + // submit the DispatchRays item + context.GetCommandList()->Submit(dispatchRaysItem); + } + + void RayTracingPass::OnShaderReinitialized([[maybe_unused]] const RPI::Shader& shader) + { + Init(); + } + + void RayTracingPass::OnShaderAssetReinitialized([[maybe_unused]] const Data::Asset& shaderAsset) + { + Init(); + } + + void RayTracingPass::OnShaderVariantReinitialized([[maybe_unused]] const RPI::Shader& shader, [[maybe_unused]] const RPI::ShaderVariantId& shaderVariantId, [[maybe_unused]] RPI::ShaderVariantStableId shaderVariantStableId) + { + Init(); + } + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h new file mode 100644 index 0000000000..6ad082e894 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h @@ -0,0 +1,82 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + struct RayTracingPassData; + + //! This pass executes a raytracing shader as specified in the PassData. + class RayTracingPass + : public RPI::RenderPass + , private RPI::ShaderReloadNotificationBus::MultiHandler + { + AZ_RPI_PASS(RayTracingPass); + + public: + AZ_RTTI(RayTracingPass, "{7A68A36E-956A-4258-93FE-38686042C4D9}", RPI::RenderPass); + AZ_CLASS_ALLOCATOR(RayTracingPass, SystemAllocator, 0); + virtual ~RayTracingPass(); + + //! Creates a RayTracingPass + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + protected: + RayTracingPass(const RPI::PassDescriptor& descriptor); + + // Pass overrides + void FrameBeginInternal(FramePrepareParams params) override; + + // Scope producer functions + void SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) override; + void CompileResources(const RHI::FrameGraphCompileContext& context) override; + void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override; + + // ShaderReloadNotificationBus::Handler overrides + void OnShaderReinitialized(const RPI::Shader& shader) override; + void OnShaderAssetReinitialized(const Data::Asset& shaderAsset) override; + void OnShaderVariantReinitialized(const RPI::Shader& shader, const RPI::ShaderVariantId& shaderVariantId, RPI::ShaderVariantStableId shaderVariantStableId) override; + + // load the raytracing shaders and setup pipeline states + void Init(); + + // helper for loading a shader from a shader asset reference + Data::Instance LoadShader(const RPI::AssetReference& shaderAssetReference); + + // pass data + RPI::PassDescriptor m_passDescriptor; + const RayTracingPassData* m_passData = nullptr; + + // revision number of the ray tracing TLAS when the shader table was built + uint32_t m_rayTracingRevision = 0; + + // raytracing shaders, pipeline states, and shader table + Data::Instance m_rayGenerationShader; + Data::Instance m_missShader; + Data::Instance m_closestHitShader; + RHI::Ptr m_rayTracingPipelineState; + RHI::ConstPtr m_globalPipelineState; + RHI::Ptr m_rayTracingShaderTable; + bool m_requiresViewSrg = false; + bool m_requiresRayTracingMaterialSrg = false; + }; + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPassData.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPassData.h new file mode 100644 index 0000000000..bc15a8372c --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPassData.h @@ -0,0 +1,73 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + //! Custom data for the RayTracingPass, specified in the PassRequest. + struct RayTracingPassData + : public RPI::RenderPassData + { + AZ_RTTI(RayTracingPassData, "{26C2E2FD-D30A-4142-82A3-0167BC94B3EE}", RPI::RenderPassData); + AZ_CLASS_ALLOCATOR(RayTracingPassData, SystemAllocator, 0); + + RayTracingPassData() = default; + virtual ~RayTracingPassData() = default; + + static void Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("RayGenerationShaderAsset", &RayTracingPassData::m_rayGenerationShaderAssetReference) + ->Field("RayGenerationShaderName", &RayTracingPassData::m_rayGenerationShaderName) + ->Field("ClosestHitShaderAsset", &RayTracingPassData::m_closestHitShaderAssetReference) + ->Field("ClosestHitShaderName", &RayTracingPassData::m_closestHitShaderName) + ->Field("MissShaderAsset", &RayTracingPassData::m_missShaderAssetReference) + ->Field("MissShaderName", &RayTracingPassData::m_missShaderName) + ->Field("MaxPayloadSize", &RayTracingPassData::m_maxPayloadSize) + ->Field("MaxAttributeSize", &RayTracingPassData::m_maxAttributeSize) + ->Field("MaxRecursionDepth", &RayTracingPassData::m_maxRecursionDepth) + ->Field("Thread Count X", &RayTracingPassData::m_threadCountX) + ->Field("Thread Count Y", &RayTracingPassData::m_threadCountY) + ->Field("Thread Count Z", &RayTracingPassData::m_threadCountZ) + ->Field("Make Fullscreen Pass", &RayTracingPassData::m_makeFullscreenPass) + ; + } + } + + RPI::AssetReference m_rayGenerationShaderAssetReference; + AZStd::string m_rayGenerationShaderName; + RPI::AssetReference m_closestHitShaderAssetReference; + AZStd::string m_closestHitShaderName; + RPI::AssetReference m_missShaderAssetReference; + AZStd::string m_missShaderName; + + uint32_t m_maxPayloadSize = 64; + uint32_t m_maxAttributeSize = 32; + uint32_t m_maxRecursionDepth = 1; + + uint32_t m_threadCountX = 1; + uint32_t m_threadCountY = 1; + uint32_t m_threadCountZ = 1; + + bool m_makeFullscreenPass = false; + }; + } // namespace RPI +} // namespace AZ + diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp index 3497855c07..3e9e316a5a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp @@ -209,7 +209,7 @@ namespace AZ void ReflectionProbe::SetTransform(const AZ::Transform& transform) { // retrieve previous scale and revert the scale on the inner/outer extents - AZ::Vector3 previousScale = m_transform.GetScale(); + float previousScale = m_transform.GetUniformScale(); m_outerExtents /= previousScale; m_innerExtents /= previousScale; @@ -218,12 +218,12 @@ namespace AZ // avoid scaling the visualization sphere AZ::Transform visualizationTransform = m_transform; - visualizationTransform.ExtractScale(); + visualizationTransform.ExtractUniformScale(); m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, visualizationTransform); // update the inner/outer extents with the new scale - m_outerExtents *= m_transform.GetScale(); - m_innerExtents *= m_transform.GetScale(); + 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); @@ -232,14 +232,14 @@ namespace AZ void ReflectionProbe::SetOuterExtents(const AZ::Vector3& outerExtents) { - m_outerExtents = outerExtents * m_transform.GetScale(); + m_outerExtents = outerExtents * m_transform.GetUniformScale(); m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f); m_updateSrg = true; } void ReflectionProbe::SetInnerExtents(const AZ::Vector3& innerExtents) { - m_innerExtents = innerExtents * m_transform.GetScale(); + m_innerExtents = innerExtents * m_transform.GetUniformScale(); m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f); m_updateSrg = true; } diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_editor_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_editor_files.cmake index 3a749a4b67..4e7cc9dab3 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_editor_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_editor_files.cmake @@ -24,16 +24,12 @@ set(FILES Source/Material/ConvertEmissiveUnitFunctorSourceData.h Source/Material/MaterialConverterSystemComponent.cpp Source/Material/MaterialConverterSystemComponent.h - Source/Material/ShaderEnableFunctorSourceData.cpp - Source/Material/ShaderEnableFunctorSourceData.h Source/Material/SubsurfaceTransmissionParameterFunctorSourceData.cpp Source/Material/SubsurfaceTransmissionParameterFunctorSourceData.h Source/Material/Transform2DFunctorSourceData.cpp Source/Material/Transform2DFunctorSourceData.h Source/Material/UseTextureFunctorSourceData.cpp Source/Material/UseTextureFunctorSourceData.h - Source/Material/PropertyVisibilityFunctorSourceData.cpp - Source/Material/PropertyVisibilityFunctorSourceData.h Source/Material/DrawListFunctorSourceData.cpp Source/Material/DrawListFunctorSourceData.h ) 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 8926b0c19f..76b71e5fae 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -135,6 +135,8 @@ set(FILES Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp Source/DiffuseProbeGrid/DiffuseProbeGrid.h + Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp + Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp Source/DisplayMapper/AcesOutputTransformPass.cpp @@ -153,16 +155,12 @@ set(FILES Source/LookupTable/LookupTableAsset.cpp Source/Material/ConvertEmissiveUnitFunctor.cpp Source/Material/ConvertEmissiveUnitFunctor.h - Source/Material/ShaderEnableFunctor.cpp - Source/Material/ShaderEnableFunctor.h Source/Material/SubsurfaceTransmissionParameterFunctor.cpp Source/Material/SubsurfaceTransmissionParameterFunctor.h Source/Material/Transform2DFunctor.cpp Source/Material/Transform2DFunctor.h Source/Material/UseTextureFunctor.cpp Source/Material/UseTextureFunctor.h - Source/Material/PropertyVisibilityFunctor.cpp - Source/Material/PropertyVisibilityFunctor.h Source/Material/DrawListFunctor.cpp Source/Material/DrawListFunctor.h Source/Math/GaussianMathFilter.h @@ -252,6 +250,9 @@ set(FILES Source/RayTracing/RayTracingFeatureProcessor.cpp Source/RayTracing/RayTracingAccelerationStructurePass.cpp Source/RayTracing/RayTracingAccelerationStructurePass.h + Source/RayTracing/RayTracingPass.cpp + Source/RayTracing/RayTracingPass.h + Source/RayTracing/RayTracingPassData.h Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp Source/ReflectionProbe/ReflectionProbe.cpp Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp diff --git a/Gems/Atom/Feature/Common/gem.json b/Gems/Atom/Feature/Common/gem.json new file mode 100644 index 0000000000..6980863b4c --- /dev/null +++ b/Gems/Atom/Feature/Common/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "Atom_Feature_Common", + "display_name": "Atom Feature Common", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h index 2073c48d25..a5d8f53573 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h @@ -13,6 +13,8 @@ #include #include +#include +#include namespace AZ { @@ -30,9 +32,16 @@ namespace AZ static void Reflect(ReflectContext* context); + //! Returns true if either @m_azslcAdditionalFreeArguments or @m_dxcAdditionalFreeArguments contain + //! macro definitions, e.g. "-D MACRO" or "-D MACRO=VALUE" or "-DMACRO", "-DMACRO=VALUE". + //! It is used for validation to forbid macro definitions, because the idea is that this struct + //! is used inside GlobalBuildOptions which has a dedicated variable for macro definitions. + bool HasMacroDefinitionsInCommandLineArguments(); + //! Mix two instances of arguments, by or-ing bools, or by "if different, right hand side wins" void Merge(const ShaderCompilerArguments& right); + //! [GFX TODO] [ATOM-15472] Remove this function. //! Determine whether there is a rebuild-worthy difference in arguments for AZSLc bool HasDifferentAzslcArguments(const ShaderCompilerArguments& right) const; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h index 3e593794af..6f884eb358 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h @@ -110,6 +110,46 @@ namespace AZ AZStd::string BuildFileNameWithExtension(const AZStd::string& shaderSourceFile, const AZStd::string& tempFolder, const char* outputExtension); + + namespace CommandLineArgumentUtils + { + //! @param commandLineString: A string with command line arguments of the form: + //! "- -- --[=] ..." + //! Example: "--use-spaces --namespace=vk -W1" + //! Returns: A list with just the [-|--]: + //! ["-", "--", "--arg3"] + //! For the example shown above it will return this vector: + //! ["--use-spaces", "--namespace", "-W1] + AZStd::vector GetListOfArgumentNames(AZStd::string_view commandLineString); + + //! Takes a list of names of command line arguments and removes those arguments from @commandLineString. + //! The core functionality of this function is that it searches by name in @commandLineString and removes + //! name and value if the name is found. + //! @param listOfArguments: This is a list of strings, usually generated by the helper function + //! ShaderCompilerArguments::GetListOfArgumentNames() + //! @param commandLineString: A single string made of several command line arguments + //! @returns A new string based on @commandLineString but with the matching arguments and their values + //! removed from it. + AZStd::string RemoveArgumentsFromCommandLineString( + AZStd::array_view listOfArguments, AZStd::string_view commandLineString); + + //! @param commandLineString: " --arg1 -arg2 --arg3=foo --arg4=bar " + //! @returns "--arg1 -arg2 --arg3=foo --arg4=bar" + AZStd::string RemoveExtraSpaces(AZStd::string_view commandLineString); + + //! Accepts two arbitrary strings that contain typical command line arguments and returns + //! a new string that combines the arguments were the arguments on the @right have precedence. + //! Example: + //! @param left: "--arg1 -arg2 --arg3=foo" + //! @param right: "--arg3=bar --arg4" + //! @returns: "--arg1 -arg2 --arg3=bar --arg4" + AZStd::string MergeCommandLineArguments(AZStd::string_view left, AZStd::string_view right); + + //! @param commandLineString: A string that contains a series of command line arguments. + //! @returns: true if @commandLineString contains macro definitions, e.g: + //! "-D MACRO" or "-D MACRO=VALUE" or "-DMACRO", "-DMACRO=VALUE". + bool HasMacroDefinitions(AZStd::string_view commandLineString); + } } } diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp index 7304a351d4..09cd1c125e 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp @@ -12,6 +12,9 @@ #include #include +#include + +#include namespace AZ { @@ -49,6 +52,12 @@ namespace AZ } } + bool ShaderCompilerArguments::HasMacroDefinitionsInCommandLineArguments() + { + return CommandLineArgumentUtils::HasMacroDefinitions(m_azslcAdditionalFreeArguments) || + CommandLineArgumentUtils::HasMacroDefinitions(m_dxcAdditionalFreeArguments); + } + void ShaderCompilerArguments::Merge(const ShaderCompilerArguments& right) { if (right.m_azslcWarningLevel != LevelUnset) @@ -56,7 +65,7 @@ namespace AZ m_azslcWarningLevel = right.m_azslcWarningLevel; } m_azslcWarningAsError = m_azslcWarningAsError || right.m_azslcWarningAsError; - m_azslcAdditionalFreeArguments += " " + right.m_azslcAdditionalFreeArguments; + m_azslcAdditionalFreeArguments = CommandLineArgumentUtils::MergeCommandLineArguments(m_azslcAdditionalFreeArguments, right.m_azslcAdditionalFreeArguments); m_dxcDisableWarnings = m_dxcDisableWarnings || right.m_dxcDisableWarnings; m_dxcWarningAsError = m_dxcWarningAsError || right.m_dxcWarningAsError; m_dxcDisableOptimizations = m_dxcDisableOptimizations || right.m_dxcDisableOptimizations; @@ -65,13 +74,14 @@ namespace AZ { m_dxcOptimizationLevel = right.m_dxcOptimizationLevel; } - m_dxcAdditionalFreeArguments += " " + right.m_dxcAdditionalFreeArguments; + m_dxcAdditionalFreeArguments = CommandLineArgumentUtils::MergeCommandLineArguments(m_dxcAdditionalFreeArguments, right.m_dxcAdditionalFreeArguments); if (right.m_defaultMatrixOrder != MatrixOrder::Default) { m_defaultMatrixOrder = right.m_defaultMatrixOrder; } } + //! [GFX TODO] [ATOM-15472] Remove this function. bool ShaderCompilerArguments::HasDifferentAzslcArguments(const ShaderCompilerArguments& right) const { auto isSet = +[](uint8_t level) { return level != LevelUnset; }; @@ -154,7 +164,7 @@ namespace AZ arguments += " -Zi"; // Generate debug information arguments += " -Zss"; // Compute Shader Hash considering source information } - arguments += m_dxcAdditionalFreeArguments; + arguments += " " + m_dxcAdditionalFreeArguments; return arguments; } } diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp index 00b5dada69..dc0efb7e9a 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp @@ -494,5 +494,64 @@ namespace AZ AzFramework::StringFunc::Path::ReplaceExtension(outputFile, outputExtension); return outputFile; } + + namespace CommandLineArgumentUtils + { + AZStd::vector GetListOfArgumentNames(AZStd::string_view commandLineString) + { + AZStd::vector listOfTokens; + AzFramework::StringFunc::Tokenize(commandLineString, listOfTokens, " \t\n"); + AZStd::vector listOfArguments; + for (const AZStd::string& token : listOfTokens) + { + AZStd::vector splitArguments; + AzFramework::StringFunc::Tokenize(token, splitArguments, "="); + listOfArguments.push_back(splitArguments[0]); + } + return listOfArguments; + } + + AZStd::string RemoveArgumentsFromCommandLineString( + AZStd::array_view listOfArgumentsToRemove, AZStd::string_view commandLineString) + { + AZStd::string customizedArguments = commandLineString; + for (const AZStd::string& azslcArgumentName : listOfArgumentsToRemove) + { + AZStd::string regexStr = AZStd::string::format("%s(=\\S+)?", azslcArgumentName.c_str()); + AZStd::regex replaceRegex(regexStr, AZStd::regex::ECMAScript); + customizedArguments = AZStd::regex_replace(customizedArguments, replaceRegex, ""); + } + return customizedArguments; + } + + AZStd::string RemoveExtraSpaces(AZStd::string_view commandLineString) + { + AZStd::vector argumentList; + AzFramework::StringFunc::Tokenize(commandLineString, argumentList, " \t\n"); + AZStd::string cleanStringWithArguments; + AzFramework::StringFunc::Join(cleanStringWithArguments, argumentList.begin(), argumentList.end(), " "); + return cleanStringWithArguments; + } + + AZStd::string MergeCommandLineArguments(AZStd::string_view left, AZStd::string_view right) + { + auto listOfArgumentNamesFromRight = GetListOfArgumentNames(right); + auto leftWithRightArgumentsRemoved = RemoveArgumentsFromCommandLineString(listOfArgumentNamesFromRight, left); + AZStd::string combinedArguments = AZStd::string::format("%s %s", leftWithRightArgumentsRemoved.c_str(), right.data()); + return RemoveExtraSpaces(combinedArguments); + } + + bool HasMacroDefinitions(AZStd::string_view commandLineString) + { + const AZStd::regex macroRegex(R"((^-D\s*(\w+))|(\s+-D\s*(\w+)))", AZStd::regex::ECMAScript); + + AZStd::smatch match; + if (AZStd::regex_search(commandLineString.data(), match, macroRegex)) + { + return (match.size() >= 1); + } + return false; + } + } //namespace CommandLineArgumentUtils } // namespace RHI } // namespace AZ diff --git a/Gems/Atom/RHI/Code/Tests/UtilsTests.cpp b/Gems/Atom/RHI/Code/Tests/UtilsTests.cpp index 506edc0d1e..afeca6e617 100644 --- a/Gems/Atom/RHI/Code/Tests/UtilsTests.cpp +++ b/Gems/Atom/RHI/Code/Tests/UtilsTests.cpp @@ -60,7 +60,9 @@ namespace UnitTest TEST_F(UtilsTests, LoadFileString_Error_DoesNotExist) { + AZ_TEST_START_TRACE_SUPPRESSION; auto outcome = AZ::RHI::LoadFileString("FileDoesNotExist"); + AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; EXPECT_FALSE(outcome.IsSuccess()); EXPECT_TRUE(outcome.GetError().find("Could not open file") != AZStd::string::npos); EXPECT_TRUE(outcome.GetError().find("FileDoesNotExist") != AZStd::string::npos); @@ -68,7 +70,9 @@ namespace UnitTest TEST_F(UtilsTests, LoadFileBytes_Error_DoesNotExist) { + AZ_TEST_START_TRACE_SUPPRESSION; auto outcome = AZ::RHI::LoadFileBytes("FileDoesNotExist"); + AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; EXPECT_FALSE(outcome.IsSuccess()); EXPECT_TRUE(outcome.GetError().find("Could not open file") != AZStd::string::npos); EXPECT_TRUE(outcome.GetError().find("FileDoesNotExist") != AZStd::string::npos); diff --git a/Gems/Atom/RHI/DX12/gem.json b/Gems/Atom/RHI/DX12/gem.json new file mode 100644 index 0000000000..683ccfb43a --- /dev/null +++ b/Gems/Atom/RHI/DX12/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "Atom_RHI_DX12", + "display_name": "Atom RHI DX12", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp index 841c71126a..c4666578a7 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp @@ -251,7 +251,7 @@ namespace AZ ByProducts& byProducts) const { // Shader compiler executable - static const char* dxcRelativePath = "Builders/DirectXShaderCompilerAz/bin/dxc"; + static const char* dxcRelativePath = "Builders/DirectXShaderCompiler/bin/dxc"; // Output file AZStd::string shaderMSLOutputFile = RHI::BuildFileNameWithExtension(shaderSourceFile, tempFolder, "metal"); diff --git a/Gems/Atom/RHI/Metal/gem.json b/Gems/Atom/RHI/Metal/gem.json new file mode 100644 index 0000000000..3e1726e8fa --- /dev/null +++ b/Gems/Atom/RHI/Metal/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "Atom_RHI_Metal", + "display_name": "Atom RHI Metal", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/Atom/RHI/Null/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/Null/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp index 35ee5a7ec0..0be84dba15 100644 --- a/Gems/Atom/RHI/Null/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp +++ b/Gems/Atom/RHI/Null/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp @@ -104,7 +104,7 @@ namespace AZ { return WindowsAzslShaderHeader; } - else if (platform.m_identifier == "osx_gl") + else if (platform.m_identifier == "mac") { return MacAzslShaderHeader; } diff --git a/Gems/Atom/RHI/Null/gem.json b/Gems/Atom/RHI/Null/gem.json new file mode 100644 index 0000000000..4fa5f1e480 --- /dev/null +++ b/Gems/Atom/RHI/Null/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "Atom_RHI_Null", + "display_name": "Atom RHI Null", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/NullDescriptorManager.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/NullDescriptorManager.cpp index cdacd2578f..791fa006b0 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/NullDescriptorManager.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/NullDescriptorManager.cpp @@ -180,6 +180,7 @@ namespace AZ for (uint32_t imageIndex = static_cast(NullDescriptorManager::ImageTypes::General2D); imageIndex < static_cast(NullDescriptorManager::ImageTypes::Count); imageIndex++) { // different options for the images + imageCreateInfo.imageType = (imageIndex >= static_cast(NullDescriptorManager::ImageTypes::General3D)) ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D; imageCreateInfo.extent = { m_imageNullDescriptor.m_images[imageIndex].m_dimension, m_imageNullDescriptor.m_images[imageIndex].m_dimension, 1 }; imageCreateInfo.samples = m_imageNullDescriptor.m_images[imageIndex].m_sampleCountFlag; imageCreateInfo.format = m_imageNullDescriptor.m_images[imageIndex].m_format; diff --git a/Gems/Atom/RHI/Vulkan/gem.json b/Gems/Atom/RHI/Vulkan/gem.json new file mode 100644 index 0000000000..1f2fcd7f30 --- /dev/null +++ b/Gems/Atom/RHI/Vulkan/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "Atom_RHI_Vulkan", + "display_name": "Atom RHI Vulkan", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/Atom/RHI/gem.json b/Gems/Atom/RHI/gem.json new file mode 100644 index 0000000000..eb67e40a4a --- /dev/null +++ b/Gems/Atom/RHI/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "Atom_RHI", + "display_name": "Atom RHI", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli index 2381febed0..9b90c40ab9 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli @@ -225,3 +225,16 @@ float NextRandomFloatUniform(inout uint seed) seed = Xorshift(seed); return (float)seed / 4294967295.0f; } + +//! Returns the inverse of lerp, 't', such that value = lerp(a, b, t), or returns 0 when a == b. +float LerpInverse(float a, float b, float value) +{ + if(abs(a - b) <= EPSILON) + { + return 0.0; + } + else + { + return (value - a) / (b - a); + } +} diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli index 69381ca0fc..e12736dfec 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli @@ -16,7 +16,12 @@ ShaderResourceGroup DrawSrg : SRG_PerDraw { - float4 m_placeholder; // [GFX-TODO] [Atom-1727] Bug in AZSLc, empty SRGs cannot be shader variant fallbacks! // This SRG is unique per draw packet + uint m_uvStreamTangentBitmask; + + uint GetTangentAtUv(uint uvIndex) + { + return (m_uvStreamTangentBitmask >> (4 * uvIndex)) & 0xF; + } } diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli index 13e3c652db..62111b3e80 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli @@ -52,6 +52,12 @@ float3 GetTangentSpaceNormal_Unnormalized(float2 normalMapSample, float normalSt // The image build pipeline drops the B channel so we have to reconstruct it here. surfaceNormal.z = sqrt(1 - dot(surfaceNormal.xy, surfaceNormal.xy)); + // Don't allow z to be zero just in case normalStrength approaches 0, to avoid a 0-length normal. + // It doesn't make sense anyway to have a surface with a normal map completely tangential. + // This also addresses the possibility of z being NaN, in the case where x^2+y^2 > 1, so we don't need to call saturate in the sqrt operation above. + // (Note this edge case would be particularly evident in multilayer material types, where the normal map is masked out using normalStrength). + surfaceNormal.z = max(surfaceNormal.z, 0.01); + surfaceNormal.xy *= normalStrength; return surfaceNormal; @@ -190,12 +196,22 @@ void SurfaceGradientNormalMapping_GenerateTB(float2 uv, out float3 tangentWS, ou } //! Utility macro to nest SGBNM setup processes. -#define PrepareGeneratedTangent(normal, worldPos, isFrontFace, uvSets, uvSetCount, outTangents, outBitangents, startIndex) \ +//! We support two UV streams, but only a single stream of tangent/bitangent. +//! By default, the first UV stream is applied and the default tangent/bitangent are used. +//! If anything uses the second UV stream, and it is not a duplication of the first stream, +//! generated tangent/bitangent will be applied. +//! (As it implies, cases may occur where all/none of the UV steams use the default TB.) +//! What tangent/bitangent a UV stream uses is encoded in DrawSrg. +#define PrepareGeneratedTangent(normal, worldPos, isFrontFace, uvSets, uvSetCount, outTangents, outBitangents) \ { \ SurfaceGradientNormalMapping_Init(normal, worldPos, !isFrontFace); \ [unroll] \ - for (int i = startIndex; i < uvSetCount; ++i) \ + for (uint i = 0; i < uvSetCount; ++i) \ { \ + if (DrawSrg::GetTangentAtUv(i) == 0) \ + { \ + continue; \ + } \ SurfaceGradientNormalMapping_GenerateTB(uvSets[i], outTangents[i], outBitangents[i]); \ } \ } diff --git a/Gems/Atom/RPI/Code/CMakeLists.txt b/Gems/Atom/RPI/Code/CMakeLists.txt index f92213d9d7..d2b7fba071 100644 --- a/Gems/Atom/RPI/Code/CMakeLists.txt +++ b/Gems/Atom/RPI/Code/CMakeLists.txt @@ -69,6 +69,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) PRIVATE AZ::AtomCore AZ::AzToolsFramework + Gem::Atom_RHI.Edit Gem::Atom_RPI.Public ) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h index 9baf80bae0..c8b406f94b 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h @@ -32,7 +32,7 @@ namespace AZ //! In the source data, properties and UV names are loaded separately. //! However, treating UV names as a special property group can greatly simplify the editor code. //! See MaterialInspector::AddUvNamesGroup() for more details. - static constexpr const char UvGroupName[] = "UvNames"; + static constexpr const char UvGroupName[] = "uvSets"; class MaterialAsset; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h index e4b9b91e81..2dd0865688 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h @@ -87,6 +87,14 @@ namespace AZ //! Finalize and validate initialization. Any initialization functions should be called before EndInit is called. void EndInit(); + //! Set up the DynamicDrawContext for the input Scene. + //! This should be called after the last frame is done and before any draw calls. + void SetScene(Scene* scene); + + //! Set up the DynamicDrawContext for the input RenderPipeline. + //! This should be called after the last frame is done and before any draw calls. + void SetRenderPipeline(RenderPipeline* pipeline); + //! Return if this DynamicDrawContext is ready to add draw calls bool IsReady(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h index 0aa979d611..70cabca371 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h @@ -98,7 +98,7 @@ namespace AZ //! List of shader options set for this specific draw packet typedef AZStd::pair ShaderOptionPair; typedef AZStd::vector ShaderOptionVector; - ShaderOptionVector m_shaderOptions; + ShaderOptionVector m_shaderOptions; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h index 285cf80aca..5a1c571a26 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h @@ -14,6 +14,7 @@ #include #include +#include #include @@ -108,6 +109,7 @@ namespace AZ const MaterialUvNameMap& materialUvNameMap = {}) const; //! Fills a InputStreamLayout and StreamBufferViewList for the set of streams that satisfy a ShaderInputContract. + // @param uvStreamTangentBitmaskOut a mask processed during UV stream matching, and later to determine which tangent/bitangent stream to use. // @param contract the contract that defines the expected inputs for a shader, used to determine which streams are optional. // @param meshIndex the index of the mesh to search in. // @param materialModelUvMap a map of UV name overrides, which can be supplied to bind a specific mesh stream name to a different material shader stream name. @@ -115,6 +117,7 @@ namespace AZ bool GetStreamsForMesh( RHI::InputStreamLayout& layoutOut, ModelLod::StreamBufferViewList& streamBufferViewsOut, + UvStreamTangentBitmask* uvStreamTangentBitmaskOut, const ShaderInputContract& contract, size_t meshIndex, const MaterialModelUvOverrideMap& materialModelUvMap = {}, @@ -130,6 +133,8 @@ namespace AZ const ModelLodAsset::Mesh::StreamBufferInfo& streamBufferInfo, Mesh& meshInstance); + StreamInfoList::const_iterator FindFirstUvStreamFromMesh(size_t meshIndex) const; + StreamInfoList::const_iterator FindDefaultUvStream(size_t meshIndex, const MaterialUvNameMap& materialUvNameMap) const; // Finds a mesh vertex input stream that is the best match for a contracted stream channel. @@ -137,12 +142,16 @@ namespace AZ // @param materialModelUvMap a map of UV name overrides, which can be supplied to bind a specific mesh stream name to a different material shader stream name. // @param materialUvNameMap the UV name map that came from a MaterialTypeAsset, which defines the default set of material shader stream names. // @param defaultUv the default UV stream to use if a matching UV stream could not be found. Use FindDefaultUvStream() to populate this. + // @param firstUv the first UV stream from the mesh, which, by design, the tangent/bitangent stream belongs to. + // @param uvStreamTangentIndex a bitset indicating which tangent/bitangent stream (including generated ones) a UV stream will be using. StreamInfoList::const_iterator FindMatchingStream( size_t meshIndex, const MaterialModelUvOverrideMap& materialModelUvMap, const MaterialUvNameMap& materialUvNameMap, const ShaderInputContract::StreamChannelInfo& contractStreamChannel, - StreamInfoList::const_iterator defaultUv) const; + StreamInfoList::const_iterator defaultUv, + StreamInfoList::const_iterator firstUv, + UvStreamTangentBitmask* uvStreamTangentBitmaskOut) const; // Meshes may share index/stream buffers in an LOD or they may have // unique buffers. Often the asset builder will prioritize shared buffers diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h new file mode 100644 index 0000000000..aa599bc27c --- /dev/null +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h @@ -0,0 +1,72 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +#include + +namespace AZ +{ + namespace RPI + { + //! An encoded bitmask for tangent used by UV streams. + //! It contains the information about number of UV streams and which tangent/bitangent is used by each UV stream. + //! See m_mask for more details. + //! The mask will be passed through per draw SRG. + class UvStreamTangentBitmask + { + public: + //! Get the full mask including number of UVs and tangent/bitangent assignment to each UV. + uint32_t GetFullTangentBitmask() const; + + //! Get number of UVs that have tangent/bitangent assigned. + uint32_t GetUvStreamCount() const; + + //! Get tangent/bitangent assignment to the specified UV in the material. + //! @param uvIndex the index of the UV from the material, in default order as in the shader code. + uint32_t GetTangentAtUv(uint32_t uvIndex) const; + + //! Apply the tangent to the next UV, whose index is the same as GetUvStreamCount. + //! @param tangent the tangent/bitangent to be assigned. Ranged in [0, 0xF) + //! It comes from the model in order, e.g. 0 means the first available tangent stream from the model. + //! Specially, value 0xF(=UnassignedTangent) means generated tangent/bitangent will be used in shader. + //! If ranged out of definition, unassigned tangent will be applied. + void ApplyTangent(uint32_t tangent); + + //! Reset the bitmask to clear state. + void Reset(); + + //! The bit mask indicating generated tangent/bitangent will be used. + static constexpr uint32_t UnassignedTangent = 0b1111u; + + //! The variable name defined in the SRG shader code. + static constexpr const char* SrgName = "m_uvStreamTangentBitmask"; + private: + //! Mask composition: + //! The number of UV slots (highest 4 bits) + tangent mask (4 bits each) * 7 + //! e.g. 0x200000F0 means there are 2 UV streams, + //! the first UV stream uses 0th tangent stream (0x0), + //! the second UV stream uses the generated tangent stream (0xF). + uint32_t m_mask = 0; + + //! Bit size in the mask composition. + static constexpr uint32_t BitsPerTangent = 4; + static constexpr uint32_t BitsForUvIndex = 4; + + public: + //! Max UV slots available in this bit mask. + static constexpr uint32_t MaxUvSlots = (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex) / BitsPerTangent; + }; + } +} diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h index 4f24a20f35..8075c6fc5d 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h @@ -26,9 +26,9 @@ namespace AZ class ViewportContextManager; //! ViewportContext wraps a native window and represents a minimal viewport - //! in which a scene is rendered on-screen + //! in which a scene is rendered on-screen. //! ViewportContexts are registered on creation to allow consumers to listen to notifications - //! and manage the view stack for a given viewport + //! and manage the view stack for a given viewport. class ViewportContext : public SceneNotificationBus::Handler , public AzFramework::WindowNotificationBus::Handler @@ -61,11 +61,11 @@ namespace AZ //! Gets the current name of this ViewportContext. //! This name is used to tie this ViewportContext to its View stack, and ViewportContexts may be - //! renamed via AZ::Interface::Get()->RenameViewportContext. + //! renamed via AZ::RPI::ViewportContextRequests::Get()->RenameViewportContext(...). AZ::Name GetName() const; //! Gets the default view associated with this ViewportContext. - //! Alternatively, use AZ::Interface::Get()->GetCurrentView. + //! Alternatively, use AZ::RPI::ViewportContextRequests::Get()->GetCurrentView(). ViewPtr GetDefaultView(); ConstViewPtr GetDefaultView() const; @@ -99,6 +99,18 @@ namespace AZ //! Notifies consumers when the render scene has changed. void ConnectSceneChangedHandler(SceneChangedEvent::Handler& handler); + using PipelineChangedEvent = AZ::Event; + //! Notifies consumers when the current pipeline associated with our window has changed. + void ConnectCurrentPipelineChangedHandler(PipelineChangedEvent::Handler& handler); + + using ViewChangedEvent = AZ::Event; + //! Notifies consumers when the default view has changed. + void ConnectDefaultViewChangedHandler(ViewChangedEvent::Handler& handler); + + using ViewportIdEvent = AZ::Event; + //! Notifies consumers when this ViewportContext is about to be destroyed. + void ConnectAboutToBeDestroyedHandler(ViewportIdEvent::Handler& handler); + // ViewportRequestBus interface //! Gets the current camera's view matrix. const AZ::Matrix4x4& GetCameraViewMatrix() const override; @@ -123,12 +135,17 @@ namespace AZ WindowContextSharedPtr m_windowContext; ViewPtr m_defaultView; AzFramework::WindowSize m_viewportSize; + SizeChangedEvent m_sizeChangedEvent; MatrixChangedEvent m_viewMatrixChangedEvent; MatrixChangedEvent::Handler m_onViewMatrixChangedHandler; MatrixChangedEvent m_projectionMatrixChangedEvent; MatrixChangedEvent::Handler m_onProjectionMatrixChangedHandler; SceneChangedEvent m_sceneChangedEvent; + PipelineChangedEvent m_currentPipelineChangedEvent; + ViewChangedEvent m_defaultViewChangedEvent; + ViewportIdEvent m_aboutToBeDestroyedEvent; + ViewportContextManager* m_manager; RenderPipelinePtr m_currentPipeline; Name m_name; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Base.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Base.h index c376112290..52b75c4157 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Base.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Base.h @@ -59,7 +59,10 @@ namespace AZ static constexpr uint32_t Draw = 0; static constexpr uint32_t Object = 1; static constexpr uint32_t Material = 2; + static constexpr uint32_t SubPass = 3; static constexpr uint32_t Pass = 4; + static constexpr uint32_t View = 5; + static constexpr uint32_t Scene = 6; }; } } 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 396ba14810..f372f40981 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 @@ -288,6 +288,7 @@ namespace AZ AZStd::size_t GetShaderCount() const; LuaMaterialFunctorShaderItem GetShader(AZStd::size_t index); LuaMaterialFunctorShaderItem GetShaderByTag(const char* shaderTag); + bool HasShaderWithTag(const char* shaderTag); private: diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Shader/ShaderSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Shader/ShaderSourceData.cpp index aac81a6e26..376399ff84 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Shader/ShaderSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Shader/ShaderSourceData.cpp @@ -11,6 +11,8 @@ */ #include +#include +#include #include #include @@ -57,7 +59,7 @@ namespace AZ bool ShaderSourceData::IsRhiBackendDisabled(const AZ::Name& rhiName) const { - return AZStd::any_of(m_disabledRhiBackends.begin(), m_disabledRhiBackends.end(), [&](const AZStd::string& currentRhiName) + return AZStd::any_of(AZ_BEGIN_END(m_disabledRhiBackends), [&](const AZStd::string& currentRhiName) { return currentRhiName == rhiName.GetStringView(); }); @@ -72,19 +74,32 @@ namespace AZ static void GetListOfMacroDefinitionNames( const AZStd::string& stringWithArguments, AZStd::vector& macroDefinitionNames) { - static const AZStd::regex macroRegex("-D\\s*(\\w+)", AZStd::regex::ECMAScript); + const AZStd::regex macroRegex(R"(-D\s*(\w+))", AZStd::regex::ECMAScript); - AZStd::cmatch match; - if (AZStd::regex_search(stringWithArguments.c_str(), match, macroRegex)) + AZStd::string hayStack(stringWithArguments); + AZStd::smatch match; + while (AZStd::regex_search(hayStack.c_str(), match, macroRegex)) { // First pattern is always the entire string for (unsigned i = 1; i < match.size(); ++i) { if (match[i].matched) { - macroDefinitionNames.push_back(match[i].str().c_str()); + AZStd::string macroToAdd(match[i].str().c_str()); + const bool isPresent = AZStd::any_of(AZ_BEGIN_END(macroDefinitionNames), + [&](AZStd::string_view macroName) -> bool + { + return macroToAdd == macroName; + } + ); + if (isPresent) + { + continue; + } + macroDefinitionNames.push_back(macroToAdd); } } + hayStack = match.suffix(); } } @@ -103,19 +118,22 @@ namespace AZ static void GetListOfMacroDefinitions( const AZStd::string& stringWithArguments, AZStd::vector& macroDefinitions) { - static const AZStd::regex macroRegex("-D\\s*(\\w+(=\\w+)?)", AZStd::regex::ECMAScript); + const AZStd::regex macroRegex(R"(-D\s*(\w+)(=\w+)?)", AZStd::regex::ECMAScript); - AZStd::cmatch match; - if (AZStd::regex_search(stringWithArguments.c_str(), match, macroRegex)) + AZStd::string hayStack(stringWithArguments); + AZStd::smatch match; + while (AZStd::regex_search(hayStack.c_str(), match, macroRegex)) { - // First pattern is always the entire string - for (unsigned i = 1; i < match.size(); ++i) + if (match.size() > 1) { - if (match[i].matched) + AZStd::string macro(match[1].str().c_str()); + if (match.size() > 2) { - macroDefinitions.push_back(match[i].str().c_str()); + macro += match[2].str().c_str(); } + macroDefinitions.push_back(macro); } + hayStack = match.suffix(); } } @@ -126,62 +144,27 @@ namespace AZ return parsedMacroDefinitions; } - - // Helper. - // @arguments: A string with command line arguments for a console application of the form: - // "- -- --[=] ..." - // Example: "--use-spaces --namespace=vk" - // Returns: A list with just the [-|--]: - // ["-", "--", "--arg3"] - // For the example shown above it will return this vector: - // ["--use-spaces", "--namespace"] - AZStd::vector GetListOfArgumentNames(const AZStd::string& arguments) - { - AZStd::vector listOfTokens; - AzFramework::StringFunc::Tokenize(arguments, listOfTokens); - AZStd::vector listOfArguments; - for (const AZStd::string& token : listOfTokens) - { - AZStd::vector splitArguments; - AzFramework::StringFunc::Tokenize(token, splitArguments, "="); - listOfArguments.push_back(splitArguments[0]); - } - return listOfArguments; - } - AZStd::string ShaderSourceData::SupervariantInfo::GetCustomizedArgumentsForAzslc( const AZStd::string& initialAzslcCompilerArguments) const { - static const AZStd::regex macroRegex("-D\\s*(\\w+(=\\S+)?)", AZStd::regex::ECMAScript); + const AZStd::regex macroRegex(R"(-D\s*(\w+(=\S+)?))", AZStd::regex::ECMAScript); // We are only concerned with AZSLc arguments. Let's remove the C-Preprocessor macro definitions // from @minusArguments. const AZStd::string minusArguments = AZStd::regex_replace(m_minusArguments, macroRegex, ""); const AZStd::string plusArguments = AZStd::regex_replace(m_plusArguments, macroRegex, ""); AZStd::string azslcArgumentsToRemove = minusArguments + " " + plusArguments; - AZStd::vector azslcArgumentNamesToRemove = GetListOfArgumentNames(azslcArgumentsToRemove); + AZStd::vector azslcArgumentNamesToRemove = RHI::CommandLineArgumentUtils::GetListOfArgumentNames(azslcArgumentsToRemove); // At this moment @azslcArgumentsToRemove contains arguments for AZSLc that can be of the form: // - // --[=] // We need to remove those from @initialAzslcCompilerArguments. - AZStd::string customizedArguments = initialAzslcCompilerArguments; - for (const AZStd::string& azslcArgumentName : azslcArgumentNamesToRemove) - { - AZStd::string regexStr = AZStd::string::format("%s(=\\S+)?", azslcArgumentName.c_str()); - AZStd::regex replaceRegex(regexStr, AZStd::regex::ECMAScript); - customizedArguments = AZStd::regex_replace(customizedArguments, replaceRegex, ""); - } - + AZStd::string customizedArguments = RHI::CommandLineArgumentUtils::RemoveArgumentsFromCommandLineString( + azslcArgumentNamesToRemove, initialAzslcCompilerArguments); customizedArguments += " " + plusArguments; - // Will contain the results that will be joined by a space. - // This is used to get a clean string to return without excess spaces. - AZStd::vector argumentList; - AzFramework::StringFunc::Tokenize(customizedArguments, argumentList, " \t\n"); - customizedArguments.clear(); // Need to clear because Join appends. - AzFramework::StringFunc::Join(customizedArguments, argumentList.begin(), argumentList.end(), " "); - return customizedArguments; + return RHI::CommandLineArgumentUtils::RemoveExtraSpaces(customizedArguments); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp index b7ba7d4a32..4e4a7a5e71 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp @@ -211,6 +211,42 @@ namespace AZ m_rhiPipelineState = m_pipelineState->GetRHIPipelineState(); } + void DynamicDrawContext::SetScene(Scene* scene) + { + AZ_Assert(scene, "SetScene called with an invalid scene"); + if (!scene || m_scene == scene) + { + return; + } + m_scene = scene; + m_drawFilter = RHI::DrawFilterMaskDefaultValue; + // Reinitialize if it was initialized + if (m_initialized) + { + // Report warning if there were some draw data + AZ_Warning( + "DynamicDrawContext", m_cachedDrawItems.size() == 0, + "DynamicDrawContext::SetForScene should be called" + " when there is no cached draw data"); + // Clear some cached data + FrameEnd(); + m_cachedRhiPipelineStates.clear(); + // Reinitialize + EndInit(); + } + } + + void DynamicDrawContext::SetRenderPipeline(RenderPipeline* pipeline) + { + AZ_Assert(pipeline, "SetRenderPipeline called with an invalid pipeline"); + if (!pipeline) + { + return; + } + SetScene(pipeline->GetScene()); + m_drawFilter = pipeline->GetDrawFilterMask(); + } + bool DynamicDrawContext::IsReady() { return m_initialized; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/Query.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/Query.cpp index 227e096594..1d6e8cdc65 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/Query.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/Query.cpp @@ -96,12 +96,6 @@ namespace AZ return QueryResultCode::Fail; } - // Limit calling BeginQuery() to the first CommandList in the array. - if (context.GetCommandListIndex() != 0) - { - return QueryResultCode::Success; - } - const auto rhiQueryIndices = GetRhiQueryIndicesFromCurrentFrame(); if (!rhiQueryIndices) { @@ -124,12 +118,6 @@ namespace AZ return QueryResultCode::Fail; } - // Limit calling EndQuery() to the last CommandList in the array. - if (context.GetCommandListIndex() != context.GetCommandListCount() - 1) - { - return QueryResultCode::Success; - } - // Validate that the queries are recorded for the same scope. if (m_cachedScopeId != context.GetScopeId()) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp index d0a277304e..b1265b1228 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp @@ -169,7 +169,7 @@ namespace AZ const AZ::Data::Asset& drawSrgAsset = shader->GetAsset()->GetDrawSrgAsset(); // Set all unspecified shader options to default values, so that we get the most specialized variant possible. - // (because FindVariantStableId treats unspecified options as a request specificlly for a variant that doesn't specify those options) + // (because FindVariantStableId treats unspecified options as a request specifically for a variant that doesn't specify those options) // [GFX TODO][ATOM-3883] We should consider updating the FindVariantStableId algorithm to handle default values for us, and remove this step here. RPI::ShaderOptionGroup shaderOptions = *shaderItem.GetShaderOptions(); shaderOptions.SetUnspecifiedToDefaultValues(); @@ -198,21 +198,6 @@ namespace AZ const ShaderVariantId finalVariantId = shaderOptions.GetShaderVariantId(); const ShaderVariant& variant = r_forceRootShaderVariantUsage ? shader->GetRootVariant() : shader->GetVariant(finalVariantId); - Data::Instance drawSrg; - if (drawSrgAsset) - { - AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "create drawSrg"); - // If the DrawSrg exists we must create and bind it, otherwise the CommandList will fail validation for SRG being null - drawSrg = RPI::ShaderResourceGroup::Create(drawSrgAsset); - - if (!variant.IsFullyBaked() && drawSrgAsset->GetLayout()->HasShaderVariantKeyFallbackEntry()) - { - drawSrg->SetShaderVariantKeyFallbackValue(shaderOptions.GetShaderVariantKeyFallbackValue()); - } - - drawSrg->Compile(); - } - RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor; variant.ConfigurePipelineState(pipelineStateDescriptor); @@ -224,9 +209,12 @@ namespace AZ streamBufferViewsPerShader.push_back(); auto& streamBufferViews = streamBufferViewsPerShader.back(); + UvStreamTangentBitmask uvStreamTangentBitmask; + if (!m_modelLod->GetStreamsForMesh( pipelineStateDescriptor.m_inputStreamLayout, streamBufferViews, + &uvStreamTangentBitmask, variant.GetInputContract(), m_modelLodMeshIndex, m_materialModelUvMap, @@ -235,6 +223,32 @@ namespace AZ return false; } + Data::Instance drawSrg; + if (drawSrgAsset) + { + AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "create drawSrg"); + // If the DrawSrg exists we must create and bind it, otherwise the CommandList will fail validation for SRG being null + drawSrg = RPI::ShaderResourceGroup::Create(drawSrgAsset); + + if (!variant.IsFullyBaked() && drawSrgAsset->GetLayout()->HasShaderVariantKeyFallbackEntry()) + { + drawSrg->SetShaderVariantKeyFallbackValue(shaderOptions.GetShaderVariantKeyFallbackValue()); + } + + // Pass UvStreamTangentBitmask to the shader if the draw SRG has it. + { + AZ::Name shaderUvStreamTangentBitmask = AZ::Name(UvStreamTangentBitmask::SrgName); + auto index = drawSrg->FindShaderInputConstantIndex(shaderUvStreamTangentBitmask); + + if (index.IsValid()) + { + drawSrg->SetConstant(index, uvStreamTangentBitmask.GetFullTangentBitmask()); + } + } + + drawSrg->Compile(); + } + // Use the default draw list tag from the shader variant. RHI::DrawListTag drawListTag = shader->GetDrawListTag(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp index 8747dac663..c6a1a51f39 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp @@ -117,6 +117,17 @@ namespace AZ return RHI::ResultCode::Success; } + ModelLod::StreamInfoList::const_iterator ModelLod::FindFirstUvStreamFromMesh(size_t meshIndex) const + { + const Mesh& mesh = m_meshes[meshIndex]; + + auto firstUv = AZStd::find_if(mesh.m_streamInfo.begin(), mesh.m_streamInfo.end(), [](const StreamBufferInfo& info) { + return info.m_semantic.m_name.GetStringView().starts_with(RHI::ShaderSemantic::UvStreamSemantic); + }); + + return firstUv; + } + ModelLod::StreamInfoList::const_iterator ModelLod::FindDefaultUvStream(size_t meshIndex, const MaterialUvNameMap& materialUvNameMap) const { const Mesh& mesh = m_meshes[meshIndex]; @@ -160,7 +171,9 @@ namespace AZ const MaterialModelUvOverrideMap& materialModelUvMap, const MaterialUvNameMap& materialUvNameMap, const ShaderInputContract::StreamChannelInfo& contractStreamChannel, - StreamInfoList::const_iterator defaultUv) const + StreamInfoList::const_iterator defaultUv, + StreamInfoList::const_iterator firstUv, + UvStreamTangentBitmask* uvStreamTangentBitmaskOut) const { const Mesh& mesh = m_meshes[meshIndex]; auto iter = mesh.m_streamInfo.end(); @@ -184,8 +197,8 @@ namespace AZ // Cost of linear search UV names is low because the size is extremely limited. return uvNamePair.m_shaderInput == contractStreamChannel.m_semantic; }); - const bool IsUv = materialUvIter != materialUvNameMap.end(); - if (IsUv) + const bool isUv = materialUvIter != materialUvNameMap.end(); + if (isUv) { const AZ::Name& materialUvName = materialUvIter->m_uvName; auto modelUvMapIter = materialModelUvMap.find(materialUvIter->m_shaderInput); @@ -224,17 +237,23 @@ namespace AZ }); } - if (iter == mesh.m_streamInfo.end() && IsUv) + if (iter == mesh.m_streamInfo.end() && isUv) { iter = defaultUv; } + if (isUv && uvStreamTangentBitmaskOut) + { + uvStreamTangentBitmaskOut->ApplyTangent(iter == firstUv ? 0 : UvStreamTangentBitmask::UnassignedTangent); + } + return iter; } bool ModelLod::GetStreamsForMesh( RHI::InputStreamLayout& layoutOut, StreamBufferViewList& streamBufferViewsOut, + UvStreamTangentBitmask* uvStreamTangentBitmaskOut, const ShaderInputContract& contract, size_t meshIndex, const MaterialModelUvOverrideMap& materialModelUvMap, @@ -250,11 +269,17 @@ namespace AZ bool success = true; + // Searching for the first UV in the mesh, so it can be used to paired with tangent/bitangent stream + auto firstUv = FindFirstUvStreamFromMesh(meshIndex); auto defaultUv = FindDefaultUvStream(meshIndex, materialUvNameMap); + if (uvStreamTangentBitmaskOut) + { + uvStreamTangentBitmaskOut->Reset(); + } for (auto& contractStreamChannel : contract.m_streamChannels) { - auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv); + auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, uvStreamTangentBitmaskOut); if (iter == mesh.m_streamInfo.end()) { @@ -340,6 +365,7 @@ namespace AZ const Mesh& mesh = m_meshes[meshIndex]; auto defaultUv = FindDefaultUvStream(meshIndex, materialUvNameMap); + auto firstUv = FindFirstUvStreamFromMesh(meshIndex); for (auto& contractStreamChannel : contract.m_streamChannels) { @@ -350,7 +376,7 @@ namespace AZ AZ_Assert(contractStreamChannel.m_streamBoundIndicatorIndex.IsValid(), "m_streamBoundIndicatorIndex was invalid for an optional shader input stream"); - auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv); + auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, nullptr); ShaderOptionValue isStreamBound = (iter == mesh.m_streamInfo.end()) ? ShaderOptionValue{0} : ShaderOptionValue{1}; shaderOptions.SetValue(contractStreamChannel.m_streamBoundIndicatorIndex, isStreamBound); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/UvStreamTangentBitmask.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/UvStreamTangentBitmask.cpp new file mode 100644 index 0000000000..829207e406 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/UvStreamTangentBitmask.cpp @@ -0,0 +1,71 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +namespace AZ +{ + namespace RPI + { + uint32_t UvStreamTangentBitmask::GetFullTangentBitmask() const + { + return m_mask; + } + + uint32_t UvStreamTangentBitmask::GetUvStreamCount() const + { + return m_mask >> (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex); + } + + uint32_t UvStreamTangentBitmask::GetTangentAtUv(uint32_t uvIndex) const + { + return (m_mask >> (BitsPerTangent * uvIndex)) & 0b1111u; + } + + void UvStreamTangentBitmask::ApplyTangent(uint32_t tangentIndex) + { + uint32_t currentSlot = GetUvStreamCount(); + if (currentSlot >= MaxUvSlots) + { + AZ_Error("UV Stream", false, "Reaching the max of avaiblable stream slots."); + return; + } + + if (tangentIndex > UnassignedTangent) + { + AZ_Warning( + "UV Stream", false, + "Tangent index must use %d bits as defined in UvStreamTangentIndex::m_flag. Unassigned index will be applied.", + BitsPerTangent); + tangentIndex = UnassignedTangent; + } + + uint32_t clearMask = 0b1111u << (BitsPerTangent * currentSlot); + clearMask = ~clearMask; + + // Clear the writing bits in case + m_mask &= clearMask; + + // Write the bits to the slot + m_mask |= (tangentIndex << (BitsPerTangent * currentSlot)); + + // Increase the index + m_mask += (1u << (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex)); + } + + void UvStreamTangentBitmask::Reset() + { + m_mask = 0; + } + } +} diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp index 9c6a95e582..3a2a556429 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp @@ -522,8 +522,11 @@ namespace AZ } }; - ExecuteOnTimestampQuery(beginQuery); - ExecuteOnPipelineStatisticsQuery(beginQuery); + if (context.GetCommandListIndex() == 0) + { + ExecuteOnTimestampQuery(beginQuery); + ExecuteOnPipelineStatisticsQuery(beginQuery); + } } void RenderPass::EndScopeQuery(const RHI::FrameGraphExecuteContext& context) @@ -533,8 +536,23 @@ namespace AZ query->EndQuery(context); }; - ExecuteOnTimestampQuery(endQuery); - ExecuteOnPipelineStatisticsQuery(endQuery); + // This scopy query implmentation should be replaced by + // [ATOM-5407] [RHI][Core] - Add GPU timestamp and pipeline statistic support for scopes + + // For timestamp query, it's okay to execute across different command lists + if (context.GetCommandListIndex() == context.GetCommandListCount() - 1) + { + ExecuteOnTimestampQuery(endQuery); + } + // For all the other types of queries except timestamp, the query start and end has to be in the same command list + // Here only tracks the PipelineStatistics for the first command list due to that we don't know how many queries are + // needed when AddScopeQueryToFrameGraph is called. + // This implementation leads to an issue that we may not get accurate pipeline statistic data + // for passes which were executed with more than one command list + if (context.GetCommandListIndex() == 0) + { + ExecuteOnPipelineStatisticsQuery(endQuery); + } } void RenderPass::ReadbackScopeQueryResults() diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderVariantAsyncLoader.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderVariantAsyncLoader.cpp index 6579e6a98d..3fc2bbd197 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderVariantAsyncLoader.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderVariantAsyncLoader.cpp @@ -150,10 +150,7 @@ namespace AZ } } - if (!shaderVariantTreePendingRequests.empty() || !shaderVariantPendingRequests.empty()) - { - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1000)); - } + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1000)); } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp index 22287238e9..08f8ff0c5e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp @@ -51,6 +51,8 @@ namespace AZ ViewportContext::~ViewportContext() { + m_aboutToBeDestroyedEvent.Signal(m_id); + AzFramework::WindowNotificationBus::Handler::BusDisconnect(); AzFramework::ViewportRequestBus::Handler::BusDisconnect(); @@ -171,6 +173,21 @@ namespace AZ handler.Connect(m_sceneChangedEvent); } + void ViewportContext::ConnectCurrentPipelineChangedHandler(PipelineChangedEvent::Handler& handler) + { + handler.Connect(m_currentPipelineChangedEvent); + } + + void ViewportContext::ConnectDefaultViewChangedHandler(ViewChangedEvent::Handler& handler) + { + handler.Connect(m_defaultViewChangedEvent); + } + + void ViewportContext::ConnectAboutToBeDestroyedHandler(ViewportIdEvent::Handler& handler) + { + handler.Connect(m_aboutToBeDestroyedEvent); + } + const AZ::Matrix4x4& ViewportContext::GetCameraViewMatrix() const { return GetDefaultView()->GetWorldToViewMatrix(); @@ -214,6 +231,7 @@ namespace AZ m_defaultView = view; UpdatePipelineView(); + m_defaultViewChangedEvent.Signal(view); m_viewMatrixChangedEvent.Signal(view->GetWorldToViewMatrix()); m_projectionMatrixChangedEvent.Signal(view->GetViewToClipMatrix()); @@ -232,6 +250,7 @@ namespace AZ if (!m_currentPipeline) { m_currentPipeline = m_rootScene ? m_rootScene->FindRenderPipelineForWindow(m_windowContext->GetWindowHandle()) : nullptr; + m_currentPipelineChangedEvent.Signal(m_currentPipeline); } if (auto pipeline = GetCurrentPipeline()) 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 7db5f12560..d6421e1337 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp @@ -296,6 +296,7 @@ namespace AZ ->Method("GetShaderCount", &LuaMaterialFunctorRuntimeContext::GetShaderCount) ->Method("GetShader", &LuaMaterialFunctorRuntimeContext::GetShader) ->Method("GetShaderByTag", &LuaMaterialFunctorRuntimeContext::GetShaderByTag) + ->Method("HasShaderWithTag", &LuaMaterialFunctorRuntimeContext::HasShaderWithTag) ; } @@ -424,6 +425,11 @@ namespace AZ return LuaMaterialFunctorShaderItem{nullptr}; } } + + bool LuaMaterialFunctorRuntimeContext::HasShaderWithTag(const char* shaderTag) + { + return m_runtimeContextImpl->m_shaderCollection->HasShaderTag(AZ::Name{shaderTag}); + } void LuaMaterialFunctorEditorContext::LuaMaterialFunctorEditorContext::Reflect(BehaviorContext* behaviorContext) { diff --git a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp index 84e8a4ba65..6b9c503ec2 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp +++ b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp @@ -72,7 +72,15 @@ namespace UnitTest return false; } - bool AssetSystemStub::GetFullSourcePathFromRelativeProductPath([[maybe_unused]] const AZStd::string& relPath, [[maybe_unused]] AZStd::string& fullSourcePath) + bool AssetSystemStub::GenerateRelativeSourcePath( + [[maybe_unused]] const AZStd::string& sourcePath, [[maybe_unused]] AZStd::string& relativePath, + [[maybe_unused]] AZStd::string& watchFolder) + { + return false; + } + + bool AssetSystemStub::GetFullSourcePathFromRelativeProductPath( + [[maybe_unused]] const AZStd::string& relPath, [[maybe_unused]] AZStd::string& fullSourcePath) { return false; } diff --git a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h index c6f4ac891f..48609ed0cb 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h +++ b/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h @@ -63,6 +63,8 @@ namespace UnitTest const char* GetAbsoluteDevGameFolderPath() override; const char* GetAbsoluteDevRootFolderPath() override; bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& relativeProductPath) override; + bool GenerateRelativeSourcePath( + const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder) override; bool GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullSourcePath) override; bool GetAssetInfoById(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath) override; bool GetSourceInfoBySourceUUID(const AZ::Uuid& sourceUuid, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override; diff --git a/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp b/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp index 980a9ac320..1ec14c6169 100644 --- a/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -917,6 +918,43 @@ namespace UnitTest } } + TEST_F(ModelTests, UvStream) + { + AZ::RPI::UvStreamTangentBitmask uvStreamTangentBitmask; + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0u); + + uvStreamTangentBitmask.ApplyTangent(1u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(0u), 1u); + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x10000001); + EXPECT_EQ(uvStreamTangentBitmask.GetUvStreamCount(), 1u); + + uvStreamTangentBitmask.ApplyTangent(5u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(0u), 1u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(1u), 5u); + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x20000051); + EXPECT_EQ(uvStreamTangentBitmask.GetUvStreamCount(), 2u); + + uvStreamTangentBitmask.ApplyTangent(100u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(0u), 1u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(1u), 5u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(2u), AZ::RPI::UvStreamTangentBitmask::UnassignedTangent); + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x30000F51); + EXPECT_EQ(uvStreamTangentBitmask.GetUvStreamCount(), 3u); + + for (uint32_t i = 3; i < AZ::RPI::UvStreamTangentBitmask::MaxUvSlots; ++i) + { + uvStreamTangentBitmask.ApplyTangent(0u); + } + + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x70000F51); + + AZ_TEST_START_TRACE_SUPPRESSION; + uvStreamTangentBitmask.ApplyTangent(0u); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x70000F51); + } + // This class creates a Model with one LOD, whose mesh contains 2 planes. Plane 1 is in the XY plane at Z=-0.5, and // plane 2 is in the XY plane at Z=0.5. The two planes each have 9 quads which have been triangulated. It only has // a position and index buffer. diff --git a/Gems/Atom/RPI/Code/Tests/System/GpuQueryTests.cpp b/Gems/Atom/RPI/Code/Tests/System/GpuQueryTests.cpp index e55ec8012b..98ff6befdd 100644 --- a/Gems/Atom/RPI/Code/Tests/System/GpuQueryTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/System/GpuQueryTests.cpp @@ -159,7 +159,9 @@ namespace UnitTest const uint32_t ResultSize = sizeof(uint64_t); uint64_t mockData; - const RHI::FrameGraphExecuteContext::Descriptor desc = {}; + RHI::FrameGraphExecuteContext::Descriptor desc = {}; + uint64_t dummyCommandList; + desc.m_commandList = reinterpret_cast(&dummyCommandList); RHI::FrameGraphExecuteContext context(desc); RHI::Scope scope; @@ -209,7 +211,9 @@ namespace UnitTest const uint32_t ResultSize = sizeof(uint64_t) * 4u; uint64_t mockData; - const RHI::FrameGraphExecuteContext::Descriptor desc = {}; + RHI::FrameGraphExecuteContext::Descriptor desc = {}; + uint64_t dummyCommandList; + desc.m_commandList = reinterpret_cast(&dummyCommandList); RHI::FrameGraphExecuteContext context(desc); RHI::Scope scope; @@ -273,7 +277,9 @@ namespace UnitTest const uint32_t ResultSize = sizeof(uint64_t) * 2u; uint64_t mockData; - const RHI::FrameGraphExecuteContext::Descriptor desc = {}; + RHI::FrameGraphExecuteContext::Descriptor desc = {}; + uint64_t dummyCommandList; + desc.m_commandList = reinterpret_cast(&dummyCommandList); RHI::FrameGraphExecuteContext context(desc); RHI::Scope scope; diff --git a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake index 0d5c19758b..6242f3f140 100644 --- a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake +++ b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake @@ -57,6 +57,7 @@ set(FILES Include/Atom/RPI.Public/Model/ModelLod.h Include/Atom/RPI.Public/Model/ModelLodUtils.h Include/Atom/RPI.Public/Model/ModelSystem.h + Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h Include/Atom/RPI.Public/Pass/AttachmentReadback.h Include/Atom/RPI.Public/Pass/ComputePass.h Include/Atom/RPI.Public/Pass/CopyPass.h @@ -136,6 +137,7 @@ set(FILES Source/RPI.Public/Model/ModelLod.cpp Source/RPI.Public/Model/ModelLodUtils.cpp Source/RPI.Public/Model/ModelSystem.cpp + Source/RPI.Public/Model/UvStreamTangentBitmask.cpp Source/RPI.Public/Pass/AttachmentReadback.cpp Source/RPI.Public/Pass/ComputePass.cpp Source/RPI.Public/Pass/CopyPass.cpp diff --git a/Gems/Atom/RPI/gem.json b/Gems/Atom/RPI/gem.json new file mode 100644 index 0000000000..7e822611a9 --- /dev/null +++ b/Gems/Atom/RPI/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "Atom_RPI", + "display_name": "Atom API", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/Atom/TestData/TestData/Materials/ParallaxRock.material b/Gems/Atom/TestData/TestData/Materials/ParallaxRock.material index 4c3a925e52..c9276216eb 100644 --- a/Gems/Atom/TestData/TestData/Materials/ParallaxRock.material +++ b/Gems/Atom/TestData/TestData/Materials/ParallaxRock.material @@ -17,7 +17,6 @@ "textureMap": "TestData/Textures/cc0/Rock030_2K_Normal.jpg" }, "parallax": { - "enable": true, "algorithm": "POM", "factor": 0.03, "quality": "High", diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material index d9a4aabe2a..415bd36dcf 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material @@ -36,7 +36,6 @@ "diffuseTextureMap": "TestData/Textures/cc0/bark1_disp.jpg" }, "layer1_parallax": { - "enable": true, "factor": 0.02500000037252903, "textureMap": "TestData/Textures/cc0/bark1_disp.jpg" }, @@ -80,7 +79,6 @@ "specularTextureMap": "TestData/Textures/cc0/Tiles009_1K_Displacement.jpg" }, "layer2_parallax": { - "enable": true, "factor": 0.01600000075995922, "textureMap": "TestData/Textures/cc0/Lava004_1K_Displacement.jpg" }, @@ -88,6 +86,10 @@ "textureMap": "TestData/Textures/cc0/Lava004_1K_Roughness.jpg" }, "layer2_uv": { + "center": [ + 0.0, + 0.0 + ], "offsetU": 0.5, "offsetV": 0.25 }, @@ -118,7 +120,6 @@ "diffuseTextureMap": "TestData/Textures/cc0/PaintedMetal003_1K_Displacement.jpg" }, "layer3_parallax": { - "enable": true, "factor": 0.004999999888241291, "textureMap": "TestData/Textures/cc0/PaintedMetal003_1K_Displacement.jpg" }, @@ -131,11 +132,15 @@ "factor": 0.47474750876426699 }, "layer3_uv": { + "center": [ + 0.0, + 0.0 + ], "offsetU": 0.11999999731779099, "rotateDegrees": -57.599998474121097 }, "parallax": { - "enable": true + "quality": "Medium" }, "uv": { "center": [ diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material index 0bf4177db9..64adf317a9 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material @@ -15,7 +15,6 @@ "textureMap": "TestData/Textures/cc0/bark1_norm.jpg" }, "layer1_parallax": { - "enable": true, "factor": 0.03999999910593033, "textureMap": "TestData/Textures/cc0/bark1_disp.jpg" }, @@ -32,7 +31,6 @@ "textureMap": "TestData/Textures/cc0/Rock030_2K_Normal.jpg" }, "layer2_parallax": { - "enable": true, "factor": 0.05299999937415123, "offset": -0.024000000208616258, "textureMap": "TestData/Textures/cc0/Rock030_2K_Displacement.jpg" @@ -50,8 +48,8 @@ "textureMap": "TestData/Textures/cc0/Concrete019_1K_Color.jpg" }, "parallax": { - "enable": true, - "pdo": true + "pdo": true, + "quality": "Medium" } } } \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_DepthMaps.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendMask.material similarity index 81% rename from Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_DepthMaps.material rename to Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendMask.material index d41e116067..ffcbf3ce7e 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_DepthMaps.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendMask.material @@ -4,8 +4,8 @@ "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material", "propertyLayoutVersion": 3, "properties": { - "general": { - "debugDrawMode": "DepthMaps" + "blend": { + "debugDrawMode": "BlendMask" } } } diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendWeights.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendWeights.material new file mode 100644 index 0000000000..8d13ac781f --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendWeights.material @@ -0,0 +1,11 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "debugDrawMode": "FinalBlendWeights" + } + } +} diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendSource.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_Displacement.material similarity index 80% rename from Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendSource.material rename to Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_Displacement.material index cdd21212c9..7aff50cb56 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendSource.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_Displacement.material @@ -4,8 +4,8 @@ "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material", "propertyLayoutVersion": 3, "properties": { - "general": { - "debugDrawMode": "BlendSource" + "blend": { + "debugDrawMode": "Displacement" } } } diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/004_UseVertexColors.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/004_UseVertexColors.material index ea3ffab467..ea3ea8b519 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/004_UseVertexColors.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/004_UseVertexColors.material @@ -5,7 +5,10 @@ "propertyLayoutVersion": 3, "properties": { "blend": { - "blendSource": "VertexColors" + "blendSource": "BlendMaskVertexColors" + }, + "parallax": { + "quality": "Medium" } } -} +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material new file mode 100644 index 0000000000..9163fe0a0c --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material @@ -0,0 +1,81 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "blendSource": "Displacement", + "displacementBlendDistance": 0.008999999612569809, + "enableLayer2": true, + "enableLayer3": true + }, + "layer1_baseColor": { + "textureMap": "TestData/Textures/cc0/Ground033_1K_Color.jpg" + }, + "layer1_normal": { + "textureMap": "TestData/Textures/cc0/Ground033_1K_Normal.jpg" + }, + "layer1_occlusion": { + "diffuseTextureMap": "TestData/Textures/cc0/Ground033_1K_AmbientOcclusion.jpg" + }, + "layer1_parallax": { + "factor": 0.017000000923871995, + "offset": -0.006000000052154064, + "textureMap": "TestData/Textures/cc0/Ground033_1K_Displacement.jpg" + }, + "layer1_roughness": { + "textureMap": "TestData/Textures/cc0/Ground033_1K_Roughness.jpg" + }, + "layer2_baseColor": { + "textureMap": "TestData/Textures/cc0/Rock030_2K_Color.jpg" + }, + "layer2_normal": { + "textureMap": "TestData/Textures/cc0/Rock030_2K_Normal.jpg" + }, + "layer2_occlusion": { + "diffuseTextureMap": "TestData/Textures/cc0/Rocks002_1K_AmbientOcclusion.jpg" + }, + "layer2_parallax": { + "factor": 0.03099999949336052, + "offset": 0.0020000000949949028, + "textureMap": "TestData/Textures/cc0/Rock030_2K_Displacement.jpg" + }, + "layer2_roughness": { + "textureMap": "TestData/Textures/cc0/Rock030_2K_Roughness.jpg" + }, + "layer2_uv": { + "center": [ + 0.0, + 0.0 + ], + "offsetU": 0.1599999964237213, + "offsetV": 0.07999999821186066, + "rotateDegrees": 90.0 + }, + "layer3_baseColor": { + "textureMap": "TestData/Textures/cc0/Rocks002_1K_Color.jpg" + }, + "layer3_normal": { + "textureMap": "TestData/Textures/cc0/Rocks002_1K_Normal.jpg" + }, + "layer3_parallax": { + "factor": 0.027000000700354577, + "textureMap": "TestData/Textures/cc0/Rocks002_1K_Displacement.jpg" + }, + "layer3_roughness": { + "textureMap": "TestData/Textures/cc0/Rocks002_1K_Roughness.jpg" + }, + "layer3_uv": { + "center": [ + 0.0, + 0.0 + ], + "scale": 3.4999988079071047 + }, + "parallax": { + "algorithm": "Relief", + "pdo": true + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_Layer2Off.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_Layer2Off.material new file mode 100644 index 0000000000..9413e35128 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_Layer2Off.material @@ -0,0 +1,11 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "enableLayer2": false + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_Layer3Off.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_Layer3Off.material new file mode 100644 index 0000000000..93e0b21780 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_Layer3Off.material @@ -0,0 +1,11 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "enableLayer3": false + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture.material new file mode 100644 index 0000000000..6b062f6d1e --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture.material @@ -0,0 +1,14 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "blendSource": "Displacement_With_BlendMaskTexture" + }, + "layer1_parallax": { + "offset": -0.004000000189989805 + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture_AllSameHeight.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture_AllSameHeight.material new file mode 100644 index 0000000000..0e6519afd4 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture_AllSameHeight.material @@ -0,0 +1,23 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "displacementBlendDistance": 0.0010000000474974514 + }, + "layer1_parallax": { + "offset": -0.00800000037997961, + "textureMap": "" + }, + "layer2_parallax": { + "offset": -0.00800000037997961, + "textureMap": "" + }, + "layer3_parallax": { + "offset": -0.00800000037997961, + "textureMap": "" + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture_NoHeightmaps.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture_NoHeightmaps.material new file mode 100644 index 0000000000..b5b5656084 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture_NoHeightmaps.material @@ -0,0 +1,20 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskTexture.material", + "propertyLayoutVersion": 3, + "properties": { + "layer1_parallax": { + "offset": -0.03200000151991844, + "textureMap": "" + }, + "layer2_parallax": { + "offset": -0.00800000037997961, + "textureMap": "" + }, + "layer3_parallax": { + "offset": -0.00800000037997961, + "textureMap": "" + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskVertexColors.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskVertexColors.material new file mode 100644 index 0000000000..8461ea429c --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement_With_BlendMaskVertexColors.material @@ -0,0 +1,15 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/005_UseDisplacement.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "blendSource": "Displacement_With_BlendMaskVertexColors", + "displacementBlendDistance": 0.02387000061571598 + }, + "parallax": { + "enable": false + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_TintedTransparent.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_TintedTransparent.material new file mode 100644 index 0000000000..1716792af1 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_TintedTransparent.material @@ -0,0 +1,23 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardPBR.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 3, + "properties": { + "baseColor": { + "color": [ + 0.5906767249107361, + 1.0, + 0.11703670024871826, + 1.0 + ], + "textureMap": "Textures/Default/default_basecolor.tif" + }, + "opacity": { + "alphaSource": "Split", + "factor": 0.75, + "mode": "TintedTransparent", + "textureMap": "TestData/Textures/checker8x8_gray_512.png" + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM.material index a94d90d04d..ed070d5de2 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM.material @@ -8,7 +8,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.02500000037252903, "quality": "High", "textureMap": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_disp.png" diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM_Cutout.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM_Cutout.material index fb862dc5d3..f5ec0e8287 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM_Cutout.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM_Cutout.material @@ -13,13 +13,15 @@ "textureMap": "TestData/Textures/checker8x8_512.png" }, "parallax": { - "algorithm": "POM", - "enable": true, "factor": 0.10000000149011612, "quality": "High", "textureMap": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_disp.png" }, "uv": { + "center": [ + 0.0, + 0.0 + ], "scale": 0.5 } } diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission.material index dbaf6cb587..38adbc70cd 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission.material @@ -6,7 +6,6 @@ "properties": { "subsurfaceScattering": { "enableSubsurfaceScattering": true, - "enableTransmission": true, "scatterDistance": 64.6464614868164, "subsurfaceScatterFactor": 1.0, "thicknessMap": "TestData/Textures/checker8x8_512.png", diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_A.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_A.material index 7bf12e5358..b3e69212db 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_A.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_A.material @@ -13,7 +13,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.10000000149011612, "quality": "High", "textureMap": "TestData/Objects/cube/cube_diff.tif" diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_B.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_B.material index 4b9f233a85..3d52f3b9e6 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_B.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/100_UvTiling_Parallax_B.material @@ -13,7 +13,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.05000000074505806, "quality": "High", "textureMap": "TestData/Objects/cube/cube_diff.tif" diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/101_DetailMaps_LucyBaseNoDetailMaps.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/101_DetailMaps_LucyBaseNoDetailMaps.material index 7b1f0ba6a9..6d77be5a49 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/101_DetailMaps_LucyBaseNoDetailMaps.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/101_DetailMaps_LucyBaseNoDetailMaps.material @@ -8,18 +8,24 @@ "textureMap": "Objects/Lucy/Lucy_bronze_BaseColor.png", "textureMapUv": "Unwrapped" }, + "detailUV": { + "center": [ + 0.0, + 0.0 + ] + }, "metallic": { - "textureMap": "Objects/Lucy/Lucy_bronze_metallic.png", + "textureMap": "Objects/Lucy/Lucy_bronze_Metallic.png", "textureMapUv": "Unwrapped" }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.png", + "textureMap": "Objects/Lucy/Lucy_Normal.png", "textureMapUv": "Unwrapped" }, "roughness": { - "textureMap": "Objects/Lucy/Lucy_bronze_roughness.png", + "textureMap": "Objects/Lucy/Lucy_bronze_Roughness.png", "textureMapUv": "Unwrapped" } } -} +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/102_DetailMaps_All.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/102_DetailMaps_All.material index 55a01866b5..1a29f392c8 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/102_DetailMaps_All.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/102_DetailMaps_All.material @@ -5,7 +5,7 @@ "propertyLayoutVersion": 3, "properties": { "baseColor": { - "textureMap": "Objects/Lucy/Lucy_bronze_baseColor.png", + "textureMap": "Objects/Lucy/Lucy_bronze_BaseColor.png", "textureMapUv": "Unwrapped" }, "detailLayerGroup": { @@ -19,20 +19,24 @@ "normalDetailStrength": 1.5 }, "detailUV": { + "center": [ + 0.0, + 0.0 + ], "scale": 10.0 }, "metallic": { - "textureMap": "Objects/Lucy/Lucy_bronze_metallic.png", + "textureMap": "Objects/Lucy/Lucy_bronze_Metallic.png", "textureMapUv": "Unwrapped" }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.png", + "textureMap": "Objects/Lucy/Lucy_Normal.png", "textureMapUv": "Unwrapped" }, "roughness": { - "textureMap": "Objects/Lucy/Lucy_bronze_roughness.png", + "textureMap": "Objects/Lucy/Lucy_bronze_Roughness.png", "textureMapUv": "Unwrapped" } } -} +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/105_DetailMaps_BlendMaskUsingDetailUVs.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/105_DetailMaps_BlendMaskUsingDetailUVs.material index 6193cf4eed..a69b72b623 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/105_DetailMaps_BlendMaskUsingDetailUVs.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/105_DetailMaps_BlendMaskUsingDetailUVs.material @@ -5,7 +5,7 @@ "propertyLayoutVersion": 3, "properties": { "baseColor": { - "textureMap": "Objects/Lucy/Lucy_bronze_baseColor.png", + "textureMap": "Objects/Lucy/Lucy_bronze_BaseColor.png", "textureMapUv": "Unwrapped" }, "detailLayerGroup": { @@ -18,20 +18,24 @@ "normalDetailStrength": 1.5 }, "detailUV": { + "center": [ + 0.0, + 0.0 + ], "scale": 10.0 }, "metallic": { - "textureMap": "Objects/Lucy/Lucy_bronze_metallic.png", + "textureMap": "Objects/Lucy/Lucy_bronze_Metallic.png", "textureMapUv": "Unwrapped" }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.png", + "textureMap": "Objects/Lucy/Lucy_Normal.png", "textureMapUv": "Unwrapped" }, "roughness": { - "textureMap": "Objects/Lucy/Lucy_bronze_roughness.png", + "textureMap": "Objects/Lucy/Lucy_bronze_Roughness.png", "textureMapUv": "Unwrapped" } } -} +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Textures/Foliage_Leaves_0_BaseColor.dds.assetinfo b/Gems/Atom/TestData/TestData/Textures/Foliage_Leaves_0_BaseColor.dds.assetinfo index 2ec31e38ce..1aa896a8d7 100644 --- a/Gems/Atom/TestData/TestData/Textures/Foliage_Leaves_0_BaseColor.dds.assetinfo +++ b/Gems/Atom/TestData/TestData/Textures/Foliage_Leaves_0_BaseColor.dds.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_AmbientOcclusion.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_AmbientOcclusion.jpg new file mode 100644 index 0000000000..04d871dd2b --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_AmbientOcclusion.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9ec269d03a9552c0ecf24778912fa6190c412b006433db8b7c40e1c0c83e6f7 +size 516915 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Color.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Color.jpg new file mode 100644 index 0000000000..d41658d177 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Color.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b53c4aca020d5833618a5b73b6578c8693dd14e603eb32681e2c7ecc90f59047 +size 1020622 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Displacement.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Displacement.jpg new file mode 100644 index 0000000000..913b35875c --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Displacement.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46ea8a9406ce6df21ce3a6045aac5b1421ce119919ff050951277dd76464ee2c +size 258716 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Normal.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Normal.jpg new file mode 100644 index 0000000000..7c216001a9 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Normal.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac1ad9bea240374bfc6aaaacc42e24eadfbf93c7185617fc1c52e610cb45cb69 +size 1292910 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Roughness.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Roughness.jpg new file mode 100644 index 0000000000..5ac9e78409 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Ground033_1K_Roughness.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fce266a7445a4d4b5bb3fbf7cb3330e0580b058aeaf2ef7f265a01641dbbdc4 +size 637326 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_AmbientOcclusion.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_AmbientOcclusion.jpg new file mode 100644 index 0000000000..772529329e --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_AmbientOcclusion.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca54f762bcddc10ab2d609f10864328cb5a567c43efb8c9bcfe9cb2955db9577 +size 433887 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Color.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Color.jpg new file mode 100644 index 0000000000..8d1d23451d --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Color.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b5f5ff297aef6470045d087cf0c3341f7782e36a750965a052d49d03dd37426 +size 1595449 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Displacement.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Displacement.jpg new file mode 100644 index 0000000000..22304c19dd --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Displacement.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8753448320e0916a70036ef77a06bce746bfa45c14e62b2fbda0987a13bfbb3 +size 277114 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Normal.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Normal.jpg new file mode 100644 index 0000000000..129ecbb1b7 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Normal.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ea23ea8d85d1fffa119e481e6ac85ad3a3096470a5e12f252d37e1b293e5e37 +size 2119669 diff --git a/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Roughness.jpg b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Roughness.jpg new file mode 100644 index 0000000000..dc5f8c20a9 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Textures/cc0/Rocks002_1K_Roughness.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:caa6e94f904135461da4d200fc6c21c86dc99cc7d413ea740e678f7758b2b156 +size 555680 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 a41c8221f0..f2c120dbcd 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -98,6 +98,7 @@ namespace AtomToolsFramework AZStd::optional ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) override; AZStd::optional ViewportScreenToWorldRay( const AzFramework::ScreenPoint& screenPosition) override; + float DeviceScalingFactor() override; //! Set interface for providing viewport specific settings (e.g. snapping properties). void SetViewportSettings(const AzToolsFramework::ViewportInteraction::ViewportSettings* viewportSettings); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp index 6fb3edfa22..896d9f8043 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,11 @@ namespace AtomToolsFramework { + AZ_CVAR( + AZ::Color, ed_cameraSystemOrbitPointColor, AZ::Color::CreateFromRgba(255, 255, 255, 255), nullptr, AZ::ConsoleFunctorFlags::Null, + ""); + AZ_CVAR(float, ed_cameraSystemOrbitPointSize, 0.5f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); + // debug void DrawPreviewAxis(AzFramework::DebugDisplayRequests& display, const AZ::Transform& transform, const float axisLength) { @@ -73,7 +79,8 @@ namespace AtomToolsFramework if (auto viewportContext = RetrieveViewportContext(GetViewportId())) { - auto handleCameraChange = [this, viewportContext](const AZ::Matrix4x4&) { + auto handleCameraChange = [this, viewportContext](const AZ::Matrix4x4&) + { if (!m_updatingTransform) { UpdateCameraFromTransform(m_targetCamera, viewportContext->GetCameraTransform()); @@ -137,7 +144,10 @@ namespace AtomToolsFramework } else if (m_cameraMode == CameraMode::Animation) { - const auto smootherStepFn = [](const float t) { return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); }; + const auto smootherStepFn = [](const float t) + { + return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); + }; const float transitionT = smootherStepFn(m_animationT); const AZ::Transform current = AZ::Transform::CreateFromQuaternionAndTranslation( @@ -169,8 +179,9 @@ namespace AtomToolsFramework { if (const float alpha = AZStd::min(-m_camera.m_lookDist / 5.0f, 1.0f); alpha > AZ::Constants::FloatEpsilon) { - debugDisplay.SetColor(1.0f, 1.0f, 1.0f, alpha); - debugDisplay.DrawWireSphere(m_camera.m_lookAt, 0.5f); + const AZ::Color orbitPointColor = ed_cameraSystemOrbitPointColor; + debugDisplay.SetColor(orbitPointColor.GetR(), orbitPointColor.GetG(), orbitPointColor.GetB(), alpha); + debugDisplay.DrawWireSphere(m_camera.m_lookAt, ed_cameraSystemOrbitPointSize); } } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index cee178c724..35edb3af5b 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -313,8 +313,7 @@ namespace AtomToolsFramework // Scale the size by the DPI of the platform to // get the proper size in pixels. const QSize uiWindowSize = size(); - const qreal deficePixelRatio = devicePixelRatioF(); - const QSize windowSize = uiWindowSize * deficePixelRatio; + const QSize windowSize = uiWindowSize * devicePixelRatioF(); const AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); AzFramework::WindowNotificationBus::Event(windowId, &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height()); @@ -465,6 +464,11 @@ namespace AtomToolsFramework return AzToolsFramework::ViewportInteraction::ProjectedViewportRay{rayOrigin, rayDirection}; } + float RenderViewportWidget::DeviceScalingFactor() + { + return aznumeric_cast(devicePixelRatioF()); + } + AzFramework::ScreenPoint RenderViewportWidget::ViewportCursorScreenPosition() { return AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(m_mousePosition.toPoint()); diff --git a/Gems/Atom/Tools/AtomToolsFramework/gem.json b/Gems/Atom/Tools/AtomToolsFramework/gem.json new file mode 100644 index 0000000000..3060d3f51a --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "AtomToolsFramework", + "display_name": "Atom Tools Framework", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentRequestBus.h b/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentRequestBus.h index c71d500d8c..7f36d3fabc 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentRequestBus.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentRequestBus.h @@ -33,7 +33,7 @@ namespace AZ namespace MaterialEditor { //! UVs are processed in a property group but will be handled differently. - static constexpr const char UvGroupName[] = "UvNames"; + static constexpr const char UvGroupName[] = "uvSets"; class MaterialDocumentRequests : public AZ::EBusTraits diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index 301fd69025..2242d3af1b 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -819,10 +819,10 @@ namespace MaterialEditor // is implemented. AtomToolsFramework::DynamicPropertyConfig propertyConfig; propertyConfig.m_dataType = AtomToolsFramework::DynamicPropertyType::Asset; - propertyConfig.m_id = "details.materialType"; + propertyConfig.m_id = "overview.materialType"; propertyConfig.m_nameId = "materialType"; propertyConfig.m_displayName = "Material Type"; - propertyConfig.m_groupName = "Details"; + propertyConfig.m_groupName = "Overview"; propertyConfig.m_description = "The material type defines the layout, properties, default values, shader connections, and other " "data needed to create and edit a derived material."; propertyConfig.m_defaultValue = AZStd::any(materialTypeAsset); @@ -834,10 +834,10 @@ namespace MaterialEditor propertyConfig = {}; propertyConfig.m_dataType = AtomToolsFramework::DynamicPropertyType::Asset; - propertyConfig.m_id = "details.parentMaterial"; + propertyConfig.m_id = "overview.parentMaterial"; propertyConfig.m_nameId = "parentMaterial"; propertyConfig.m_displayName = "Parent Material"; - propertyConfig.m_groupName = "Details"; + propertyConfig.m_groupName = "Overview"; propertyConfig.m_description = "The parent material provides an initial configuration whose properties are inherited and overriden by a derived material."; propertyConfig.m_defaultValue = AZStd::any(parentMaterialAsset); @@ -860,7 +860,7 @@ namespace MaterialEditor propertyConfig.m_id = MaterialPropertyId(UvGroupName, shaderInput).GetCStr(); propertyConfig.m_nameId = shaderInput; propertyConfig.m_displayName = shaderInput; - propertyConfig.m_groupName = "UV Names"; + propertyConfig.m_groupName = "UV Sets"; propertyConfig.m_description = shaderInput; propertyConfig.m_defaultValue = uvName; propertyConfig.m_originalValue = uvName; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp index 6a4bb8c0f4..887019787b 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/CreateMaterialDialog/CreateMaterialDialog.cpp @@ -71,6 +71,8 @@ namespace MaterialEditor QObject::connect(m_ui->m_materialTypeComboBox, static_cast(&QComboBox::currentIndexChanged), this, [this]() { UpdateMaterialTypeSelection(); }); QObject::connect(m_ui->m_materialTypeComboBox, &QComboBox::currentTextChanged, this, [this]() { UpdateMaterialTypeSelection(); }); + m_ui->m_materialTypeComboBox->model()->sort(0, Qt::AscendingOrder); + // Select the default material type from settings auto settings = AZ::UserSettings::CreateFind(AZ::Crc32("MaterialDocumentSettings"), AZ::UserSettings::CT_GLOBAL); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp index 706d365027..560c82be9b 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp @@ -79,8 +79,8 @@ namespace MaterialEditor if (!m_documentId.IsNull() && isOpen) { - // Create the top group for displaying details about the material - AddDetailsGroup(); + // Create the top group for displaying overview info about the material + AddOverviewGroup(); // Create groups for displaying editable UV names AddUvNamesGroup(); // Create groups for displaying editable properties @@ -105,25 +105,25 @@ namespace MaterialEditor return property && AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue); } - void MaterialInspector::AddDetailsGroup() + void MaterialInspector::AddOverviewGroup() { const AZ::RPI::MaterialTypeSourceData* materialTypeSourceData = nullptr; MaterialDocumentRequestBus::EventResult( materialTypeSourceData, m_documentId, &MaterialDocumentRequestBus::Events::GetMaterialTypeSourceData); - const AZStd::string groupNameId = "details"; - const AZStd::string groupDisplayName = "Details"; + const AZStd::string groupNameId = "overview"; + const AZStd::string groupDisplayName = "Overview"; const AZStd::string groupDescription = materialTypeSourceData->m_description; auto& group = m_groups[groupNameId]; AtomToolsFramework::DynamicProperty property; MaterialDocumentRequestBus::EventResult( - property, m_documentId, &MaterialDocumentRequestBus::Events::GetProperty, AZ::Name("details.materialType")); + property, m_documentId, &MaterialDocumentRequestBus::Events::GetProperty, AZ::Name("overview.materialType")); group.m_properties.push_back(property); property = {}; MaterialDocumentRequestBus::EventResult( - property, m_documentId, &MaterialDocumentRequestBus::Events::GetProperty, AZ::Name("details.parentMaterial")); + property, m_documentId, &MaterialDocumentRequestBus::Events::GetProperty, AZ::Name("overview.parentMaterial")); group.m_properties.push_back(property); // Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties @@ -139,7 +139,7 @@ namespace MaterialEditor MaterialDocumentRequestBus::EventResult(materialAsset, m_documentId, &MaterialDocumentRequestBus::Events::GetAsset); const AZStd::string groupNameId = UvGroupName; - const AZStd::string groupDisplayName = "UV Names"; + const AZStd::string groupDisplayName = "UV Sets"; const AZStd::string groupDescription = "UV set names in this material, which can be renamed to match those in the model."; auto& group = m_groups[groupNameId]; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h index 4080430ff5..65d095f42f 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h @@ -52,7 +52,7 @@ namespace MaterialEditor bool CompareInstanceNodeProperties( const AzToolsFramework::InstanceDataNode* source, const AzToolsFramework::InstanceDataNode* target) const; - void AddDetailsGroup(); + void AddOverviewGroup(); void AddUvNamesGroup(); void AddPropertiesGroup(); diff --git a/Gems/Atom/gem.json b/Gems/Atom/gem.json index c74a9013f3..91bc9bcf53 100644 --- a/Gems/Atom/gem.json +++ b/Gems/Atom/gem.json @@ -1,3 +1,5 @@ { - "gem_name": "Atom" + "gem_name": "Atom", + "display_name": "Atom", + "summary": "Next-Gen Rendering Package for the O3DE engine" } diff --git a/AutomatedTesting/Gem/Code/Platform/Android/runtime_dependencies.cmake b/Gems/AtomContent/CMakeLists.txt similarity index 100% rename from AutomatedTesting/Gem/Code/Platform/Android/runtime_dependencies.cmake rename to Gems/AtomContent/CMakeLists.txt diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Bricks038_8K/bricks038.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Bricks038_8K/bricks038.material index 25e29b55e5..82b1cdb590 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Bricks038_8K/bricks038.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Bricks038_8K/bricks038.material @@ -26,7 +26,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.02500000037252903, "pdo": true, "quality": "Ultra", diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Concrete016_8K/Concrete016.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Concrete016_8K/Concrete016.material index 336d479b30..03fb0ea5be 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Concrete016_8K/Concrete016.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Concrete016_8K/Concrete016.material @@ -31,7 +31,8 @@ "algorithm": "ContactRefinement", "factor": 0.019999999552965165, "quality": "Ultra", - "textureMap": "Materials/Concrete016_8K/Concrete016_8K_Displacement.png" + "textureMap": "Materials/Concrete016_8K/Concrete016_8K_Displacement.png", + "useTexture": false }, "roughness": { "textureMap": "Materials/Concrete016_8K/Concrete016_8K_Roughness.png" diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric001_8K/Fabric001.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric001_8K/Fabric001.material index 72610e8bb6..7d8c3d5142 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric001_8K/Fabric001.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric001_8K/Fabric001.material @@ -20,7 +20,6 @@ }, "parallax": { "algorithm": "ContactRefinement", - "enable": true, "factor": 0.004999999888241291, "quality": "Ultra", "textureMap": "Materials/Fabric001_8K/Fabric001_8K_Displacement.png" diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric030_4K/Fabric030.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric030_4K/Fabric030.material index 458ab811b6..493bfab455 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric030_4K/Fabric030.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/Fabric030_4K/Fabric030.material @@ -20,7 +20,6 @@ }, "parallax": { "algorithm": "ContactRefinement", - "enable": true, "factor": 0.0020000000949949028, "pdo": true, "quality": "Medium", diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/PaintedPlaster015_8K/PaintedPlaster015.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/PaintedPlaster015_8K/PaintedPlaster015.material index 57163f520d..a2bb2c7704 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/PaintedPlaster015_8K/PaintedPlaster015.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/PaintedPlaster015_8K/PaintedPlaster015.material @@ -19,7 +19,6 @@ }, "parallax": { "algorithm": "ContactRefinement", - "enable": true, "factor": 0.009999999776482582, "pdo": true, "quality": "Ultra", diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/baseboards.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/baseboards.material index 625d872475..f75490c2ad 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/baseboards.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/baseboards.material @@ -30,7 +30,8 @@ "factor": 0.02500000037252903, "pdo": true, "quality": "Ultra", - "textureMap": "Materials/Bricks038_8K/Bricks038_8K_Displacement.png" + "textureMap": "Materials/Bricks038_8K/Bricks038_8K_Displacement.png", + "useTexture": false }, "roughness": { "factor": 0.4343433976173401, diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/crown.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/crown.material index e3c6d9eae6..d7e2050dbd 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/crown.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Materials/crown.material @@ -22,7 +22,6 @@ }, "parallax": { "algorithm": "POM", - "enable": true, "factor": 0.02500000037252903, "pdo": true, "quality": "Ultra", diff --git a/Gems/AtomContent/ReferenceMaterials/Assets/Materials/ConcreteStucco/concrete_stucco.material b/Gems/AtomContent/ReferenceMaterials/Assets/Materials/ConcreteStucco/concrete_stucco.material index ab9f366ff6..d4e2016022 100644 --- a/Gems/AtomContent/ReferenceMaterials/Assets/Materials/ConcreteStucco/concrete_stucco.material +++ b/Gems/AtomContent/ReferenceMaterials/Assets/Materials/ConcreteStucco/concrete_stucco.material @@ -19,7 +19,8 @@ }, "parallax": { "factor": 0.0010101000079885126, - "textureMap": "Materials/ConcreteStucco/concrete_stucco_height.jpg" + "textureMap": "Materials/ConcreteStucco/concrete_stucco_height.jpg", + "useTexture": false }, "specularF0": { "factor": 0.5050504803657532 diff --git a/Gems/AtomContent/Sponza/Assets/license.txt b/Gems/AtomContent/Sponza/Assets/license.txt new file mode 100644 index 0000000000..e303d8c767 --- /dev/null +++ b/Gems/AtomContent/Sponza/Assets/license.txt @@ -0,0 +1,8 @@ +The content in this gem "O3DE\Gems\AtomContent\Sponza" is ported +from the original source, and modified for the O3DE Engine and Atom Renderer. + +The original "Crytek Sponza" scene data can be downloaded from the +"McGuire Computer Graphics Archive": https://casual-effects.com/data/ + +The original content is under the "CC BY 3.0" License: +https://creativecommons.org/licenses/by/3.0/ \ No newline at end of file diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_arch.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_arch.material index 1102fb150a..d95e84121c 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_arch.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_arch.material @@ -41,7 +41,8 @@ "factor": 0.050999999046325687, "pdo": true, "quality": "High", - "textureMap": "Textures/arch_1k_height.png" + "textureMap": "Textures/arch_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/arch_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_background.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_background.material index c1853250d7..710f790419 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_background.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_background.material @@ -47,7 +47,8 @@ "factor": 0.03099999949336052, "pdo": true, "quality": "High", - "textureMap": "Textures/background_1k_height.png" + "textureMap": "Textures/background_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/background_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_bricks.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_bricks.material index a269098b4d..26d64c7db9 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_bricks.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_bricks.material @@ -46,7 +46,8 @@ "algorithm": "ContactRefinement", "factor": 0.03500000014901161, "quality": "Medium", - "textureMap": "Textures/bricks_1k_height.png" + "textureMap": "Textures/bricks_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/bricks_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_ceiling.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_ceiling.material index 94225cd00e..95d08d398b 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_ceiling.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_ceiling.material @@ -48,7 +48,8 @@ "factor": 0.019999999552965165, "pdo": true, "quality": "Medium", - "textureMap": "Textures/ceiling_1k_height.png" + "textureMap": "Textures/ceiling_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/ceiling_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columna.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columna.material index 8f1cea8649..cc1f685c7c 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columna.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columna.material @@ -47,7 +47,8 @@ "factor": 0.017000000923871995, "pdo": true, "quality": "High", - "textureMap": "Textures/columnA_1k_height.png" + "textureMap": "Textures/columnA_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/columnA_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnb.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnb.material index ac474d7e76..a1e8747f65 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnb.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnb.material @@ -46,7 +46,8 @@ "factor": 0.020999999716877939, "pdo": true, "quality": "High", - "textureMap": "Textures/columnB_1k_height.png" + "textureMap": "Textures/columnB_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/columnB_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnc.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnc.material index 81fd03fc4a..6edbfde47c 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnc.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_columnc.material @@ -47,7 +47,8 @@ "factor": 0.014000000432133675, "pdo": true, "quality": "High", - "textureMap": "Textures/columnC_1k_height.png" + "textureMap": "Textures/columnC_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/columnC_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_details.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_details.material index fde599fd4c..1b66a51ec0 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_details.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_details.material @@ -38,7 +38,8 @@ "algorithm": "POM", "factor": 0.02500000037252903, "pdo": true, - "textureMap": "Textures/details_1k_height.png" + "textureMap": "Textures/details_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/details_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_flagpole.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_flagpole.material index e50e8a0ed2..19010d66e5 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_flagpole.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_flagpole.material @@ -41,7 +41,8 @@ "factor": 0.014000000432133675, "pdo": true, "quality": "High", - "textureMap": "Textures/flagpole_1k_height.png" + "textureMap": "Textures/flagpole_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/flagpole_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_floor.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_floor.material index 064a2b24a6..bee92e0edb 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_floor.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_floor.material @@ -43,7 +43,8 @@ "algorithm": "POM", "factor": 0.012000000104308129, "pdo": true, - "textureMap": "Textures/floor_1k_height.png" + "textureMap": "Textures/floor_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/floor_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_leaf.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_leaf.material index 269e1e5684..ac326ae935 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_leaf.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_leaf.material @@ -42,7 +42,8 @@ "mode": "Cutout" }, "parallax": { - "textureMap": "Textures/thorn_height.png" + "textureMap": "Textures/thorn_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/thorn_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_lion.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_lion.material index 8dc4852b03..b1f78aa33f 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_lion.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_lion.material @@ -38,7 +38,6 @@ }, "parallax": { "algorithm": "ContactRefinement", - "enable": true, "factor": 0.009999999776482582, "pdo": true, "quality": "Ultra", diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_roof.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_roof.material index 0a7246703c..a3e066a438 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_roof.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_roof.material @@ -35,7 +35,8 @@ "algorithm": "ContactRefinement", "factor": 0.019999999552965165, "quality": "Medium", - "textureMap": "Textures/roof_1k_height.png" + "textureMap": "Textures/roof_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/roof_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vase.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vase.material index 77adc798a0..dea9aa2a8a 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vase.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vase.material @@ -41,7 +41,8 @@ "factor": 0.027000000700354577, "pdo": true, "quality": "High", - "textureMap": "Textures/vase_1k_height.png" + "textureMap": "Textures/vase_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/vase_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vasehanging.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vasehanging.material index 22e78f03ae..b2a342dd76 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vasehanging.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vasehanging.material @@ -41,7 +41,8 @@ "factor": 0.04600000008940697, "pdo": true, "quality": "High", - "textureMap": "Textures/vaseHanging_1k_height.png" + "textureMap": "Textures/vaseHanging_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/vaseHanging_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseround.material b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseround.material index c773146b51..fba07379c0 100644 --- a/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseround.material +++ b/Gems/AtomContent/Sponza/Assets/objects/sponza_mat_vaseround.material @@ -45,7 +45,8 @@ "factor": 0.019999999552965165, "pdo": true, "quality": "High", - "textureMap": "Textures/vaseRound_1k_height.png" + "textureMap": "Textures/vaseRound_1k_height.png", + "useTexture": false }, "roughness": { "textureMap": "Textures/vaseRound_1k_roughness.png" diff --git a/Gems/AtomContent/Sponza/Assets/stub b/Gems/AtomContent/Sponza/Assets/stub deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Gems/AtomContent/gem.json b/Gems/AtomContent/gem.json new file mode 100644 index 0000000000..941e7dea20 --- /dev/null +++ b/Gems/AtomContent/gem.json @@ -0,0 +1,14 @@ +{ + "gem_name": "AtomContent", + "origin": "The primary repo for Atom goes here: i.e. http://www.mydomain.com", + "license": "What license Atom uses goes here: i.e. https://opensource.org/licenses/MIT", + "display_name": "Atom Content", + "summary": "ontains multiple packages containing source Assets that can be used with Atom", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AtomConent" + ], + "icon_path": "preview.png" +} diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt b/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt index c431746b40..4df40e3d13 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt @@ -66,6 +66,10 @@ ly_add_target( Gem::AtomViewportDisplayInfo ) +# Any 'runtime-like' applications should use Gem::Atom_AtomBridge: +ly_create_alias(NAME Atom_AtomBridge.Clients NAMESPACE Gem TARGETS Gem::Atom_AtomBridge) +ly_create_alias(NAME Atom_AtomBridge.Servers NAMESPACE Gem TARGETS Gem::Atom_AtomBridge) + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME Atom_AtomBridge.Editor ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} @@ -106,5 +110,11 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::AtomFont Gem::AtomToolsFramework.Editor Gem::AtomViewportDisplayInfo + Gem::AtomViewportDisplayIcons.Editor ) + + + # Any 'tool' and 'builder' type applications should use Gem::Atom_AtomBridge.Editor: + ly_create_alias(NAME Atom_AtomBridge.Builders NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Editor) + ly_create_alias(NAME Atom_AtomBridge.Tools NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Editor) endif() diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Include/AtomBridge/PerViewportDynamicDrawInterface.h b/Gems/AtomLyIntegration/AtomBridge/Code/Include/AtomBridge/PerViewportDynamicDrawInterface.h new file mode 100644 index 0000000000..f77e0b0b88 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Include/AtomBridge/PerViewportDynamicDrawInterface.h @@ -0,0 +1,44 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +namespace AZ::AtomBridge +{ + //! A simple interface for allocating a DynamicDrawContext on-demand for every viewport, based on + //! a registered initialization function. + class PerViewportDynamicDrawInterface + { + public: + AZ_RTTI(PerViewportDynamicDrawInterface, "{1FF054F5-55FF-4ADB-A86D-640B15FA0395}"); + + using DrawContextFactory = AZStd::function)>; + //! Register a named dynamic draw context that can be retrieved on a per-viewport basis. + //! GetNamedDynamicDraw context can be called on a registered context name to retrieve a + //! valid DynamicDrawContext for a given viewport. + virtual void RegisterDynamicDrawContext(AZ::Name name, DrawContextFactory contextInitializer) = 0; + + //! Unregister a previously registered named per-viewport dynamic draw context. + //! This will dispose of all dynamic draw contexts currently associated with this name. + virtual void UnregisterDynamicDrawContext(AZ::Name name) = 0; + + //! Get a dynamic draw context associated with the specified viewport based on a factory registered with + //! RegisterNamedDynamicDrawContext. This dynamic draw context will be created if it does not already exist. + virtual RHI::Ptr GetDynamicDrawContextForViewport(AZ::Name name, AzFramework::ViewportId viewportId) = 0; + }; + + using PerViewportDynamicDraw = AZ::Interface; +} // namespace AZ::AtomBridge diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp index 4a19c08174..5116d8a228 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -104,10 +105,12 @@ namespace AZ AzFramework::GameEntityContextRequestBus::BroadcastResult(m_entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId); AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect(); + m_dynamicDrawManager = AZStd::make_unique(); } void AtomBridgeSystemComponent::Deactivate() { + m_dynamicDrawManager.reset(); AZ::RPI::ViewportContextManagerNotificationsBus::Handler::BusDisconnect(); RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get(); // Check if scene is emptry since scene might be released already when running AtomSampleViewer diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.h b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.h index da75d3c35c..4d05ba9285 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.h +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.h @@ -33,6 +33,7 @@ namespace AZ { // forward declares class AtomDebugDisplayViewportInterface; + class PerViewportDynamicDrawManager; class AtomBridgeSystemComponent : public Component @@ -82,6 +83,7 @@ namespace AZ RPI::ViewPtr m_view = nullptr; AZStd::unordered_map > m_activeViewportsList; + AZStd::unique_ptr m_dynamicDrawManager; }; } } // namespace AZ diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp index 6c68618f78..620d5d1fb8 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp @@ -1328,7 +1328,7 @@ namespace AZ::AtomBridge params.m_hAlign = center ? AzFramework::TextHorizontalAlignment::Center : AzFramework::TextHorizontalAlignment::Left; //! Horizontal text alignment params.m_monospace = false; //! disable character proportional spacing params.m_depthTest = false; //! Test character against the depth buffer - params.m_virtual800x600ScreenSize = true; //! Text placement and size are scaled relative to a virtual 800x600 resolution + params.m_virtual800x600ScreenSize = false; //! Text placement and size are scaled in viewport pixel coordinates params.m_scaleWithWindow = false; //! Font gets bigger as the window gets bigger params.m_multiline = true; //! text respects ascii newline characters @@ -1364,7 +1364,7 @@ namespace AZ::AtomBridge params.m_hAlign = center ? AzFramework::TextHorizontalAlignment::Center : AzFramework::TextHorizontalAlignment::Left; //! Horizontal text alignment params.m_monospace = false; //! disable character proportional spacing params.m_depthTest = false; //! Test character against the depth buffer - params.m_virtual800x600ScreenSize = true; //! Text placement and size are scaled relative to a virtual 800x600 resolution + params.m_virtual800x600ScreenSize = false; //! Text placement and size are scaled in viewport pixel coordinates params.m_scaleWithWindow = false; //! Font gets bigger as the window gets bigger params.m_multiline = true; //! text respects ascii newline characters diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.cpp new file mode 100644 index 0000000000..b6ff116a86 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.cpp @@ -0,0 +1,119 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "PerViewportDynamicDrawManager.h" + +#include +#include + +namespace AZ::AtomBridge +{ + PerViewportDynamicDrawManager::PerViewportDynamicDrawManager() + { + PerViewportDynamicDraw::Register(this); + } + + PerViewportDynamicDrawManager::~PerViewportDynamicDrawManager() + { + PerViewportDynamicDraw::Unregister(this); + } + + void PerViewportDynamicDrawManager::RegisterDynamicDrawContext(AZ::Name name, DrawContextFactory contextInitializer) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + + const bool alreadyRegistered = m_registeredDrawContexts.find(name) != m_registeredDrawContexts.end(); + AZ_Error("AtomBridge", !alreadyRegistered, "Attempted to call RegisterDynamicDrawContext for already registered name: \"%s\"", name.GetCStr()); + if (alreadyRegistered) + { + return; + } + m_registeredDrawContexts[name] = contextInitializer; + } + + void PerViewportDynamicDrawManager::UnregisterDynamicDrawContext(AZ::Name name) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + + auto drawContextFactoryIt = m_registeredDrawContexts.find(name); + const bool registered = drawContextFactoryIt != m_registeredDrawContexts.end(); + AZ_Error("AtomBridge", registered, "Attempted to call UnregisterDynamicDrawContext for unregistered name: \"%s\"", name.GetCStr()); + if (!registered) + { + return; + } + m_registeredDrawContexts.erase(drawContextFactoryIt); + + for (auto& viewportData : m_viewportData) + { + viewportData.second.m_dynamicDrawContexts.erase(name); + } + } + + RHI::Ptr PerViewportDynamicDrawManager::GetDynamicDrawContextForViewport( + AZ::Name name, AzFramework::ViewportId viewportId) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + + auto contextFactoryIt = m_registeredDrawContexts.find(name); + if (contextFactoryIt == m_registeredDrawContexts.end()) + { + return nullptr; + } + + auto viewportContextManager = RPI::ViewportContextRequests::Get(); + RPI::ViewportContextPtr viewportContext = viewportContextManager->GetViewportContextById(viewportId); + if (viewportContext == nullptr) + { + return nullptr; + } + + // Get or create a ViewportData if one doesn't already exist + ViewportData& viewportData = m_viewportData[viewportId]; + if (!viewportData.m_initialized) + { + viewportData.m_pipelineChangedHandler = AZ::Event::Handler([this, viewportId](RPI::RenderPipelinePtr pipeline) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + ViewportData& viewportData = m_viewportData[viewportId]; + for (auto& context : viewportData.m_dynamicDrawContexts) + { + context.second->SetRenderPipeline(pipeline.get()); + } + }); + viewportData.m_viewportDestroyedHandler = AZ::Event::Handler([this, viewportId](AzFramework::ViewportId id) + { + AZStd::lock_guard lock(m_mutexDrawContexts); + m_viewportData.erase(id); + }); + + viewportContext->ConnectCurrentPipelineChangedHandler(viewportData.m_pipelineChangedHandler); + viewportContext->ConnectAboutToBeDestroyedHandler(viewportData.m_viewportDestroyedHandler); + + viewportData.m_initialized = true; + } + + RHI::Ptr& context = viewportData.m_dynamicDrawContexts[name]; + if (context == nullptr) + { + auto pipeline = viewportContext->GetCurrentPipeline().get(); + if (pipeline == nullptr) + { + return nullptr; + } + context = RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext(pipeline); + contextFactoryIt->second(context); + } + + return context; + } +} //namespace AZ::AtomBridge diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.h b/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.h new file mode 100644 index 0000000000..e2b442915c --- /dev/null +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/PerViewportDynamicDrawManager.h @@ -0,0 +1,48 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace AZ::AtomBridge +{ + class PerViewportDynamicDrawManager final : public PerViewportDynamicDrawInterface + { + public: + AZ_TYPE_INFO(PerViewportDynamicDrawManager, "{BED66185-00A7-43F7-BD28-C56BC8E4C535}"); + + PerViewportDynamicDrawManager(); + ~PerViewportDynamicDrawManager(); + + // PerViewportDynamicDrawInterface overrides... + void RegisterDynamicDrawContext(AZ::Name name, DrawContextFactory contextInitializer) override; + void UnregisterDynamicDrawContext(AZ::Name name) override; + RHI::Ptr GetDynamicDrawContextForViewport(AZ::Name name, AzFramework::ViewportId viewportId) override; + + private: + struct ViewportData + { + AZStd::unordered_map> m_dynamicDrawContexts; + + // Event handlers + AZ::Event::Handler m_pipelineChangedHandler; + AZ::Event::Handler m_viewportDestroyedHandler; + + // Cached state + bool m_initialized = false; + }; + AZStd::map m_viewportData; + AZStd::unordered_map m_registeredDrawContexts; + AZStd::mutex m_mutexDrawContexts; + }; +} //namespace AZ::AtomBridge diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/atombridge_files.cmake b/Gems/AtomLyIntegration/AtomBridge/Code/atombridge_files.cmake index f272d323ae..969030f922 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/atombridge_files.cmake +++ b/Gems/AtomLyIntegration/AtomBridge/Code/atombridge_files.cmake @@ -12,10 +12,13 @@ set(FILES Include/AtomBridge/AtomBridgeBus.h Include/AtomBridge/FlyCameraInputBus.h + Include/AtomBridge/PerViewportDynamicDrawInterface.h Source/AtomBridgeSystemComponent.cpp Source/AtomBridgeSystemComponent.h - Source/FlyCameraInputComponent.cpp - Source/FlyCameraInputComponent.h Source/AtomDebugDisplayViewportInterface.cpp Source/AtomDebugDisplayViewportInterface.h + Source/FlyCameraInputComponent.cpp + Source/FlyCameraInputComponent.h + Source/PerViewportDynamicDrawManager.cpp + Source/PerViewportDynamicDrawManager.h ) diff --git a/Gems/AtomLyIntegration/AtomBridge/gem.json b/Gems/AtomLyIntegration/AtomBridge/gem.json new file mode 100644 index 0000000000..329741bb8e --- /dev/null +++ b/Gems/AtomLyIntegration/AtomBridge/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "Atom_AtomBridge", + "display_name": "Atom Bridge", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp b/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp index 3c76a9c788..623a931ac9 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp +++ b/Gems/AtomLyIntegration/AtomFont/Code/Source/AtomFont.cpp @@ -896,7 +896,7 @@ AZ::RHI::Ptr AZ::AtomFont::GetOrCreateDynamicDrawFo shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("false"))); shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true"))); dynamicDraw->InitShaderWithVariant(shader, &shaderOptions); - dynamicDraw->InitVertexFormat({{"POSITION", RHI::Format::R32G32B32_FLOAT}, {"COLOR", RHI::Format::R8G8B8A8_UNORM}, {"TEXCOORD0", RHI::Format::R32G32_FLOAT}}); + dynamicDraw->InitVertexFormat({{"POSITION", RHI::Format::R32G32B32_FLOAT}, {"COLOR", RHI::Format::B8G8R8A8_UNORM}, {"TEXCOORD0", RHI::Format::R32G32_FLOAT}}); dynamicDraw->EndInit(); // exclusive lock while writing diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp index f8afaa7260..31eb089803 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp +++ b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp @@ -54,7 +54,6 @@ #include -static const AZ::Vector2 UiDraw_TextSizeFactor = AZ::Vector2(12.0f, 12.0f); static const int TabCharCount = 4; // set buffer sizes to hold max characters that can be drawn in 1 DrawString call static const size_t MaxVerts = 8 * 1024; // 2048 quads @@ -864,7 +863,7 @@ int AZ::FFont::CreateQuadsForText(const RHI::Viewport& viewport, float x, float if (drawFrame) { ColorB tempColor(255, 255, 255, 255); - uint32_t frameColor = tempColor.pack_abgr8888(); //note: this ends up in r,g,b,a order on little-endian machines + uint32_t frameColor = tempColor.pack_argb8888(); //note: this ends up in r,g,b,a order on little-endian machines Vec2 textSize = GetTextSizeUInternal(viewport, str, asciiMultiLine, ctx); @@ -1122,7 +1121,7 @@ int AZ::FFont::CreateQuadsForText(const RHI::Viewport& viewport, float x, float { ColorB tempColor = color; tempColor.a = ((uint32_t) tempColor.a * alphaBlend) >> 8; - packedColor = tempColor.pack_abgr8888(); //note: this ends up in r,g,b,a order on little-endian machines + packedColor = tempColor.pack_argb8888(); //note: this ends up in r,g,b,a order on little-endian machines } if (ctx.m_drawTextFlags & eDrawText_UseTransform) @@ -1673,6 +1672,12 @@ static void SetCommonContextFlags(AZ::TextDrawContext& ctx, const AzFramework::T { ctx.m_drawTextFlags |= eDrawText_FixedSize; } + + if (params.m_useTransform) + { + ctx.m_drawTextFlags |= eDrawText_UseTransform; + ctx.SetTransform(AZMatrix3x4ToLYMatrix3x4(params.m_transform)); + } } AZ::FFont::DrawParameters AZ::FFont::ExtractDrawParameters(const AzFramework::TextDrawParameters& params, AZStd::string_view text, bool forceCalculateSize) @@ -1696,22 +1701,25 @@ AZ::FFont::DrawParameters AZ::FFont::ExtractDrawParameters(const AzFramework::Te } internalParams.m_ctx.SetBaseState(GS_NODEPTHTEST); internalParams.m_ctx.SetColor(AZColorToLYColorF(params.m_color)); + internalParams.m_ctx.SetEffect(params.m_effectIndex); internalParams.m_ctx.SetCharWidthScale((params.m_monospace || params.m_scaleWithWindow) ? 0.5f : 1.0f); internalParams.m_ctx.EnableFrame(false); internalParams.m_ctx.SetProportional(!params.m_monospace && params.m_scaleWithWindow); internalParams.m_ctx.SetSizeIn800x600(params.m_scaleWithWindow && params.m_virtual800x600ScreenSize); - internalParams.m_ctx.SetSize(AZVec2ToLYVec2(UiDraw_TextSizeFactor * params.m_scale)); + internalParams.m_ctx.SetSize(AZVec2ToLYVec2(AZ::Vector2(params.m_textSizeFactor, params.m_textSizeFactor) * params.m_scale)); internalParams.m_ctx.SetLineSpacing(params.m_lineSpacing); - if (params.m_monospace || !params.m_scaleWithWindow) - { - ScaleCoord(viewport, posX, posY); - } if (params.m_hAlign != AzFramework::TextHorizontalAlignment::Left || params.m_vAlign != AzFramework::TextVerticalAlignment::Top || forceCalculateSize) { + // We align based on the size of the default font effect because we do not want the + // text to move when the font effect is changed + unsigned int effectIndex = internalParams.m_ctx.m_fxIdx; + internalParams.m_ctx.SetEffect(0); Vec2 textSize = GetTextSizeUInternal(viewport, text.data(), params.m_multiline, internalParams.m_ctx); + internalParams.m_ctx.SetEffect(effectIndex); + // If we're using virtual 800x600 coordinates, convert the text size from // pixels to that before using it as an offset. if (internalParams.m_ctx.m_sizeIn800x600) diff --git a/Gems/AtomLyIntegration/AtomFont/gem.json b/Gems/AtomLyIntegration/AtomFont/gem.json new file mode 100644 index 0000000000..a609061ea3 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomFont/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "AtomFont", + "display_name": "Atom Font", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/AtomLyIntegration/AtomImGuiTools/gem.json b/Gems/AtomLyIntegration/AtomImGuiTools/gem.json new file mode 100644 index 0000000000..5cee62f7bb --- /dev/null +++ b/Gems/AtomLyIntegration/AtomImGuiTools/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "AtomImGuiTools", + "display_name": "Atom ImGui", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.azsl b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.azsl new file mode 100644 index 0000000000..c4367efe6b --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.azsl @@ -0,0 +1,77 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + + #include + +ShaderResourceGroup InstanceSrg : SRG_PerDraw +{ + float2 m_viewportSize; + Texture2D m_texture; + + Sampler m_sampler + { + MaxAnisotropy = 16; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; +}; + +struct VSInput +{ + float3 m_position : POSITION; + float4 m_color : COLOR0; + float2 m_uv : TEXCOORD0; +}; + +struct VSOutput +{ + float4 m_position : SV_Position; + float4 m_color : COLOR0; + float2 m_uv : TEXCOORD0; +}; + +VSOutput MainVS(VSInput IN) +{ + // Convert from screen space to clip space + float2 posXY = float2(IN.m_position.xy) / InstanceSrg::m_viewportSize * 2.0f - float2(1.0f, 1.0f); + posXY.y *= -1.0f; + float4 posPS = float4(posXY, IN.m_position.z, 1.0f); + + VSOutput OUT; + OUT.m_position = posPS; + OUT.m_color = IN.m_color; + OUT.m_uv = IN.m_uv; + return OUT; +}; + +struct PSOutput +{ + float4 m_color : SV_Target0; +}; + +PSOutput MainPS(VSOutput IN) +{ + PSOutput OUT; + + float4 tex; + + tex = InstanceSrg::m_texture.Sample(InstanceSrg::m_sampler, IN.m_uv); + float opacity = IN.m_color.a * tex.a; + + // We use pre-multiplied alpha here since it is more flexible. For example, it enables alpha-blended rendering to + // a render target and then alpha blending that render target into another render target + OUT.m_color.rgb = IN.m_color.rgb * tex.rgb * opacity; + + OUT.m_color.a = opacity; + return OUT; +}; diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader new file mode 100644 index 0000000000..601a2664b5 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/Shaders/TexturedIcon.shader @@ -0,0 +1,39 @@ +{ + "Source" : "TexturedIcon", + + "DepthStencilState" : { + "Depth" : { + "Enable" : false, + "CompareFunc" : "Always" + } + }, + + "RasterState" : { + "DepthClipEnable" : false, + "CullMode" : "None" + }, + + "BlendState" : { + "Enable" : true, + "BlendSource" : "One", + "BlendDest" : "AlphaSourceInverse", + "BlendOp" : "Add" + }, + + "DrawList" : "2dpass", + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/AutomatedTesting/Gem/Code/Platform/Windows/runtime_dependencies.cmake b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/CMakeLists.txt similarity index 95% rename from AutomatedTesting/Gem/Code/Platform/Windows/runtime_dependencies.cmake rename to Gems/AtomLyIntegration/AtomViewportDisplayIcons/CMakeLists.txt index ffcaf7293a..20a680bce9 100644 --- a/AutomatedTesting/Gem/Code/Platform/Windows/runtime_dependencies.cmake +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/CMakeLists.txt @@ -9,5 +9,4 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -set(GEM_DEPENDENCIES -) \ No newline at end of file +add_subdirectory(Code) diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/CMakeLists.txt b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/CMakeLists.txt new file mode 100644 index 0000000000..b3e176c7a3 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/CMakeLists.txt @@ -0,0 +1,35 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +if(PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_target( + NAME AtomViewportDisplayIcons.Editor ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAMESPACE Gem + FILES_CMAKE + atomviewportdisplayicons_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzFramework + AZ::AzToolsFramework + AZ::AtomCore + 3rdParty::Qt::Core + 3rdParty::Qt::Gui + 3rdParty::Qt::Svg + Gem::Atom_RHI.Reflect + Gem::Atom_RPI.Public + Gem::Atom_Bootstrap.Headers + Gem::Atom_AtomBridge.Static + ) +endif() diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp new file mode 100644 index 0000000000..5fe8c50350 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp @@ -0,0 +1,357 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include "AtomViewportDisplayIconsSystemComponent.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace AZ::Render +{ + void AtomViewportDisplayIconsSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0) + ; + + if (AZ::EditContext* ec = serialize->GetEditContext()) + { + ec->Class("Viewport Display Icons", "Provides an interface for drawing simple icons to the Editor viewport") + ->ClassElement(Edit::ClassElements::EditorData, "") + ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b)) + ->Attribute(Edit::Attributes::AutoExpand, true) + ; + } + } + } + + void AtomViewportDisplayIconsSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("ViewportDisplayIconsService")); + } + + void AtomViewportDisplayIconsSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("ViewportDisplayIconsService")); + } + + void AtomViewportDisplayIconsSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC("RPISystem", 0xf2add773)); + required.push_back(AZ_CRC("AtomBridgeService", 0xdb816a99)); + } + + void AtomViewportDisplayIconsSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + void AtomViewportDisplayIconsSystemComponent::Activate() + { + AzToolsFramework::EditorViewportIconDisplay::Register(this); + + Bootstrap::NotificationBus::Handler::BusConnect(); + } + + void AtomViewportDisplayIconsSystemComponent::Deactivate() + { + Bootstrap::NotificationBus::Handler::BusDisconnect(); + + auto perViewportDynamicDrawInterface = AtomBridge::PerViewportDynamicDraw::Get(); + if (!perViewportDynamicDrawInterface) + { + return; + } + if (perViewportDynamicDrawInterface) + { + perViewportDynamicDrawInterface->UnregisterDynamicDrawContext(m_drawContextName); + } + + AzToolsFramework::EditorViewportIconDisplay::Unregister(this); + } + + void AtomViewportDisplayIconsSystemComponent::DrawIcon(const DrawParameters& drawParameters) + { + // Ensure we have a valid viewport context & dynamic draw interface + auto viewportContext = RPI::ViewportContextRequests::Get()->GetViewportContextById(drawParameters.m_viewport); + if (viewportContext == nullptr) + { + return; + } + + auto perViewportDynamicDrawInterface = + AtomBridge::PerViewportDynamicDraw::Get(); + if (!perViewportDynamicDrawInterface) + { + return; + } + + RHI::Ptr dynamicDraw = + perViewportDynamicDrawInterface->GetDynamicDrawContextForViewport(m_drawContextName, drawParameters.m_viewport); + if (dynamicDraw == nullptr) + { + return; + } + + // Find our icon, falling back on a grey placeholder if its image is unavailable + AZ::Data::Instance image = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::Grey); + if (auto iconIt = m_iconData.find(drawParameters.m_icon); iconIt != m_iconData.end()) + { + auto& iconData = iconIt->second; + if (iconData.m_image) + { + image = iconData.m_image; + } + } + else + { + return; + } + + // Initialize our shader + AZ::Vector2 viewportSize; + { + AzFramework::WindowSize viewportWindowSize = viewportContext->GetViewportSize(); + viewportSize = AZ::Vector2{aznumeric_cast(viewportWindowSize.m_width), aznumeric_cast(viewportWindowSize.m_height)}; + } + AZ::Data::Instance drawSrg = dynamicDraw->NewDrawSrg(); + drawSrg->SetConstant(m_viewportSizeIndex,viewportSize); + drawSrg->SetImageView(m_textureParameterIndex, image->GetImageView()); + drawSrg->Compile(); + + // Scale icons by screen DPI + float scalingFactor = 1.0f; + { + using ViewportRequestBus = AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus; + ViewportRequestBus::EventResult( + scalingFactor, drawParameters.m_viewport, &ViewportRequestBus::Events::DeviceScalingFactor); + } + + AZ::Vector3 screenPosition; + if (drawParameters.m_positionSpace == CoordinateSpace::ScreenSpace) + { + screenPosition = drawParameters.m_position; + } + else if (drawParameters.m_positionSpace == CoordinateSpace::WorldSpace) + { + // Calculate our screen space position using the viewport size + // We want this instead of RenderViewportWidget::WorldToScreen which works in QWidget virtual coordinate space + AzFramework::ScreenPoint position = AzFramework::WorldToScreen( + drawParameters.m_position, viewportContext->GetCameraViewMatrix(), viewportContext->GetCameraProjectionMatrix(), + viewportSize); + screenPosition.SetX(aznumeric_cast(position.m_x)); + screenPosition.SetY(aznumeric_cast(position.m_y)); + } + + struct Vertex + { + float m_position[3]; + AZ::u32 m_color; + float m_uv[2]; + }; + using Indice = AZ::u16; + + // Create a vertex offset from the position to draw from based on the icon size + // Vertex positions are in screen space coordinates + auto createVertex = [&](float offsetX, float offsetY, float u, float v) -> Vertex + { + Vertex vertex; + screenPosition.StoreToFloat3(vertex.m_position); + vertex.m_position[0] += offsetX * drawParameters.m_size.GetX() * scalingFactor; + vertex.m_position[1] += offsetY * drawParameters.m_size.GetY() * scalingFactor; + vertex.m_color = drawParameters.m_color.ToU32(); + vertex.m_uv[0] = u; + vertex.m_uv[1] = v; + return vertex; + }; + + AZStd::array vertices = { + createVertex(-0.5f, -0.5f, 0.f, 0.f), + createVertex(0.5f, -0.5f, 1.f, 0.f), + createVertex(0.5f, 0.5f, 1.f, 1.f), + createVertex(-0.5f, 0.5f, 0.f, 1.f) + }; + AZStd::array indices = {0, 1, 2, 0, 2, 3}; + dynamicDraw->DrawIndexed(&vertices, vertices.size(), &indices, indices.size(), RHI::IndexFormat::Uint16, drawSrg); + } + + QString AtomViewportDisplayIconsSystemComponent::FindAssetPath(const QString& path) const + { + // If we get an absolute path, just use it. + QFileInfo pathInfo(path); + if (pathInfo.isAbsolute()) + { + return path; + } + + bool found = false; + AZStd::vector scanFolders; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + found, &AzToolsFramework::AssetSystemRequestBus::Events::GetScanFolders, scanFolders); + if (!found) + { + AZ_Error("AtomViewportDisplayIconSystemComponent", false, "Failed to load asset scan folders"); + return QString(); + } + + for (const auto& folder : scanFolders) + { + QDir dir(folder.data()); + if (dir.exists(path)) + { + return dir.absoluteFilePath(path); + } + } + + return QString(); + } + + QImage AtomViewportDisplayIconsSystemComponent::RenderSvgToImage(const QString& svgPath) const + { + // Set up our SVG renderer + QSvgRenderer renderer(svgPath); + renderer.setAspectRatioMode(Qt::KeepAspectRatio); + + // Set up our target image + QSize size = renderer.defaultSize().expandedTo(MinimumRenderedSvgSize); + QImage image(size, QtImageFormat); + image.fill(0x00000000); + + // Render the SVG + QPainter painter(&image); + renderer.render(&painter); + return image; + } + + AZ::Data::Instance AtomViewportDisplayIconsSystemComponent::ConvertToAtomImage(AZ::Uuid assetId, QImage image) const + { + // Ensure our image is in the correct pixel format so we can memcpy it to our renderer image + image.convertTo(QtImageFormat); + Data::Instance streamingImagePool = RPI::ImageSystemInterface::Get()->GetSystemStreamingPool(); + return RPI::StreamingImage::CreateFromCpuData( + *streamingImagePool.get(), + RHI::ImageDimension::Image2D, + RHI::Size(image.width(), image.height(), 1), + RHI::Format::R8G8B8A8_UNORM_SRGB, + image.bits(), + image.sizeInBytes(), + assetId); + } + + AzToolsFramework::EditorViewportIconDisplayInterface::IconId AtomViewportDisplayIconsSystemComponent::GetOrLoadIconForPath( + AZStd::string_view path) + { + // Check our cache to see if the image is already loaded + auto existingEntryIt = AZStd::find_if(m_iconData.begin(), m_iconData.end(), [&path](const auto& iconData) + { + return iconData.second.m_path == path; + }); + if (existingEntryIt != m_iconData.end()) + { + return existingEntryIt->first; + } + + AZ::Uuid assetId = AZ::Uuid::CreateName(path.data()); + + // Find the asset to load on disk + QString assetPath = FindAssetPath(path.data()); + if (assetPath.isEmpty()) + { + AZ_Error("AtomViewportDisplayIconSystemComponent", false, "Failed to locate icon on disk: \"%s\"", path.data()); + return InvalidIconId; + } + + QImage loadedImage; + + AZStd::string extension; + AzFramework::StringFunc::Path::GetExtension(path.data(), extension, false); + // For SVGs, we need to actually rasterize to an image + if (extension == "svg") + { + loadedImage = RenderSvgToImage(assetPath); + } + // For everything else, we can just load it through QImage via its image plugins + else + { + const bool loaded = loadedImage.load(assetPath); + if (!loaded) + { + AZ_Error("AtomViewportDisplayIconSystemComponent", false, "Failed to load icon: \"%s\"", assetPath.toUtf8().constData()); + return InvalidIconId; + } + } + + // Cache our loaded icon + IconId id = m_currentId++; + IconData& iconData = m_iconData[id]; + iconData.m_path = path; + iconData.m_image = ConvertToAtomImage(assetId, loadedImage); + return id; + } + + AzToolsFramework::EditorViewportIconDisplayInterface::IconLoadStatus AtomViewportDisplayIconsSystemComponent::GetIconLoadStatus( + IconId icon) + { + auto iconIt = m_iconData.find(icon); + if (iconIt == m_iconData.end()) + { + return IconLoadStatus::Unloaded; + } + if (iconIt->second.m_image) + { + return IconLoadStatus::Loaded; + } + return IconLoadStatus::Error; + } + + void AtomViewportDisplayIconsSystemComponent::OnBootstrapSceneReady([[maybe_unused]]AZ::RPI::Scene* bootstrapScene) + { + AtomBridge::PerViewportDynamicDraw::Get()->RegisterDynamicDrawContext(m_drawContextName, [](RPI::Ptr drawContext) + { + auto shader = RPI::LoadShader(DrawContextShaderPath); + drawContext->InitShader(shader); + drawContext->InitVertexFormat( + {{"POSITION", RHI::Format::R32G32B32_FLOAT}, + {"COLOR", RHI::Format::R8G8B8A8_UNORM}, + {"TEXCOORD", RHI::Format::R32G32_FLOAT}}); + drawContext->EndInit(); + }); + } +} // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h new file mode 100644 index 0000000000..b44957b51d --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h @@ -0,0 +1,82 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once + +#include + +#include + +#include +#include + +#include +#include +#include + +namespace AZ +{ + class TickRequests; + + namespace Render + { + class AtomViewportDisplayIconsSystemComponent + : public AZ::Component + , public AzToolsFramework::EditorViewportIconDisplayInterface + , public AZ::Render::Bootstrap::NotificationBus::Handler + { + public: + AZ_COMPONENT(AtomViewportDisplayIconsSystemComponent, "{AEC1D3E1-1D9A-437A-B4C6-CFAEE620C160}"); + + 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); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + protected: + // AZ::Component overrides... + void Activate() override; + void Deactivate() override; + + // AzToolsFramework::EditorViewportIconDisplayInterface overrides... + void DrawIcon(const DrawParameters& drawParameters) override; + IconId GetOrLoadIconForPath(AZStd::string_view path) override; + IconLoadStatus GetIconLoadStatus(IconId icon) override; + + // AZ::Render::Bootstrap::NotificationBus::Handler overrides... + void OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) override; + + private: + static constexpr const char* DrawContextShaderPath = "Shaders/TexturedIcon.azshader"; + static constexpr QSize MinimumRenderedSvgSize = QSize(128, 128); + static constexpr QImage::Format QtImageFormat = QImage::Format_RGBA8888; + + QString FindAssetPath(const QString& path) const; + QImage RenderSvgToImage(const QString& svgPath) const; + AZ::Data::Instance ConvertToAtomImage(AZ::Uuid assetId, QImage image) const; + + Name m_drawContextName = Name("ViewportIconDisplay"); + bool m_shaderIndexesInitialized = false; + RHI::ShaderInputNameIndex m_textureParameterIndex = "m_texture"; + RHI::ShaderInputNameIndex m_viewportSizeIndex = "m_viewportSize"; + + struct IconData + { + AZStd::string m_path; + AZ::Data::Instance m_image = nullptr; + }; + AZStd::unordered_map m_iconData; + IconId m_currentId = 0; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/Module.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/Module.cpp new file mode 100644 index 0000000000..db7672186a --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/Module.cpp @@ -0,0 +1,51 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +#include "AtomViewportDisplayIconsSystemComponent.h" + +namespace AZ +{ + namespace Render + { + class AtomViewportDisplayInfoModule + : public AZ::Module + { + public: + AZ_RTTI(AtomViewportDisplayInfoModule, "{8D72F14E-958D-4225-B3BC-C5C87BDDD426}", AZ::Module); + AZ_CLASS_ALLOCATOR(AtomViewportDisplayInfoModule, AZ::SystemAllocator, 0); + + AtomViewportDisplayInfoModule() + : AZ::Module() + { + m_descriptors.insert(m_descriptors.end(), { + AtomViewportDisplayIconsSystemComponent::CreateDescriptor(), + }); + } + + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + azrtti_typeid(), + }; + } + }; + } // namespace Render +} // namespace AZ + +// DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM +// The first parameter should be GemName_GemIdLower +// The second should be the fully qualified name of the class above +AZ_DECLARE_MODULE_CLASS(Gem_AtomViewportDisplayInfo, AZ::Render::AtomViewportDisplayInfoModule) diff --git a/AutomatedTesting/Gem/Code/Platform/Windows/tool_dependencies.cmake b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/atomviewportdisplayicons_files.cmake similarity index 78% rename from AutomatedTesting/Gem/Code/Platform/Windows/tool_dependencies.cmake rename to Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/atomviewportdisplayicons_files.cmake index 933dd7927b..f02aed0856 100644 --- a/AutomatedTesting/Gem/Code/Platform/Windows/tool_dependencies.cmake +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/atomviewportdisplayicons_files.cmake @@ -9,6 +9,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -set(GEM_DEPENDENCIES - Gem::QtForPython.Editor -) \ No newline at end of file +set(FILES + Source/AtomViewportDisplayIconsSystemComponent.cpp + Source/AtomViewportDisplayIconsSystemComponent.h + Source/Module.cpp +) diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json new file mode 100644 index 0000000000..41f69e33a0 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "AtomViewportDisplayIcons", + "display_name": "Atom Viewport Display Icons", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/CMakeLists.txt b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/CMakeLists.txt index de4ee9b4b5..0f134d4218 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/CMakeLists.txt @@ -10,7 +10,7 @@ # ly_add_target( - NAME AtomViewportDisplayInfo GEM_MODULE + NAME AtomViewportDisplayInfo ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} NAMESPACE Gem FILES_CMAKE atomviewportdisplayinfo_files.cmake diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp index 672c26a9ab..d47ce44ab2 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp @@ -18,10 +18,11 @@ #include #include +#include +#include #include #include #include -#include #include #include @@ -146,7 +147,7 @@ namespace AZ::Render if (m_updateRootPassQuery) { - if (auto rootPass = AZ::RPI::PassSystemInterface::Get()->GetRootPass()) + if (auto rootPass = viewportContext->GetCurrentPipeline()->GetRootPass()) { rootPass->SetPipelineStatisticsQueryEnabled(displayLevel != AtomBridge::ViewportInfoDisplayState::CompactInfo); m_updateRootPassQuery = false; @@ -161,7 +162,7 @@ namespace AZ::Render m_drawParams.m_hAlign = AzFramework::TextHorizontalAlignment::Right; m_drawParams.m_monospace = false; m_drawParams.m_depthTest = false; - m_drawParams.m_virtual800x600ScreenSize = true; + m_drawParams.m_virtual800x600ScreenSize = false; m_drawParams.m_scaleWithWindow = false; m_drawParams.m_multiline = true; m_drawParams.m_lineSpacing = 0.5f; @@ -226,7 +227,8 @@ namespace AZ::Render void AtomViewportDisplayInfoSystemComponent::DrawPassInfo() { - auto rootPass = AZ::RPI::PassSystemInterface::Get()->GetRootPass(); + AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext(); + auto rootPass = viewportContext->GetCurrentPipeline()->GetRootPass(); const RPI::PipelineStatisticsResult stats = rootPass->GetLatestPipelineStatisticsResult(); AZStd::function)> containingPassCount = [&containingPassCount](const AZ::RPI::Ptr pass) { @@ -301,7 +303,10 @@ namespace AZ::Render lastTime = time; } - const double averageFPS = aznumeric_cast(m_fpsHistory.size()) / actualInterval.count(); + const double averageFPS = (actualInterval.count() != 0.0) + ? aznumeric_cast(m_fpsHistory.size()) / actualInterval.count() + : 0.0; + const double frameIntervalSeconds = m_fpsInterval.count(); DrawLine( diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json index dd92a99ea9..04e2464a26 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json +++ b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json @@ -1,12 +1,10 @@ { - "gem_name": "AtomLyIntegration_AtomViewportDisplayInfo", - "display_name": "Atom Viewport Display Info Overlay", - "summary": "Provides a diagnostic viewport overlay for the default O3DE Atom viewport.", + "gem_name": "AtomViewportDisplayInfo", + "display_name": "Atom Viewport Display Info", + "summary": "", "canonical_tags": [ "Gem" ], "user_tags": [ - "AtomLyIntegration", - "AtomViewportDisplayInfo" ] -} \ No newline at end of file +} diff --git a/Gems/AtomLyIntegration/CMakeLists.txt b/Gems/AtomLyIntegration/CMakeLists.txt index 35022e643b..ff6800a7ff 100644 --- a/Gems/AtomLyIntegration/CMakeLists.txt +++ b/Gems/AtomLyIntegration/CMakeLists.txt @@ -17,3 +17,4 @@ add_subdirectory(AtomFont) add_subdirectory(TechnicalArt) add_subdirectory(AtomBridge) add_subdirectory(AtomViewportDisplayInfo) +add_subdirectory(AtomViewportDisplayIcons) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp index 3b50c0a48c..f14340b4c9 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.cpp @@ -21,18 +21,42 @@ namespace AZ { namespace Render { + bool EditorAttachmentComponentVersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) + { + if (classElement.GetVersion() < 2) + { + float uniformScaleOffset = 1.0f; + + int scaleElementIndex = classElement.FindElement(AZ_CRC_CE("Scale Offset")); + if (scaleElementIndex != -1) + { + AZ::Vector3 oldScaleValue = AZ::Vector3::CreateOne(); + AZ::SerializeContext::DataElementNode& dataElementNode = classElement.GetSubElement(scaleElementIndex); + if (dataElementNode.GetData(oldScaleValue)) + { + uniformScaleOffset = oldScaleValue.GetMaxElement(); + } + classElement.RemoveElement(scaleElementIndex); + } + + classElement.AddElementWithData(context, "Uniform Scale Offset", uniformScaleOffset); + } + + return true; + } + void EditorAttachmentComponent::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { serializeContext->Class() - ->Version(1) + ->Version(2, &EditorAttachmentComponentVersionConverter) ->Field("Target ID", &EditorAttachmentComponent::m_targetId) ->Field("Target Bone Name", &EditorAttachmentComponent::m_targetBoneName) ->Field("Position Offset", &EditorAttachmentComponent::m_positionOffset) ->Field("Rotation Offset", &EditorAttachmentComponent::m_rotationOffset) - ->Field("Scale Offset", &EditorAttachmentComponent::m_scaleOffset) + ->Field("Uniform Scale Offset", &EditorAttachmentComponent::m_uniformScaleOffset) ->Field("Attached Initially", &EditorAttachmentComponent::m_attachedInitially) ->Field("Scale Source", &EditorAttachmentComponent::m_scaleSource); @@ -70,7 +94,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::Min, -AZ::RadToDeg(AZ::Constants::TwoPi)) ->Attribute(AZ::Edit::Attributes::Max, AZ::RadToDeg(AZ::Constants::TwoPi)) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnTargetOffsetChanged) - ->DataElement(0, &EditorAttachmentComponent::m_scaleOffset, "Scale offset", "Local scale offset from target entity") + ->DataElement(0, &EditorAttachmentComponent::m_uniformScaleOffset, "Scale offset", "Local scale offset from target entity") ->Attribute(AZ::Edit::Attributes::Step, 0.1f) ->Attribute(AZ::Edit::Attributes::Min, 0.001f) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnTargetOffsetChanged) @@ -128,7 +152,7 @@ namespace AZ { AZ::Transform offset = AZ::ConvertEulerDegreesToTransform(m_rotationOffset); offset.SetTranslation(m_positionOffset); - offset.MultiplyByScale(m_scaleOffset); + offset.MultiplyByUniformScale(m_uniformScaleOffset); return offset; } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.h index cac8a71a94..0f44043344 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Animation/EditorAttachmentComponent.h @@ -88,7 +88,7 @@ namespace AZ AZ::Vector3 m_rotationOffset = AZ::Vector3::CreateZero(); //! Offset from target entity's scale. - AZ::Vector3 m_scaleOffset = AZ::Vector3::CreateOne(); + float m_uniformScaleOffset = 1.0f; //! Observe scale information from the specified source. AttachmentConfiguration::ScaleSource m_scaleSource = AttachmentConfiguration::ScaleSource::WorldScale; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/QuadLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/QuadLightDelegate.cpp index 2666be6f75..6caa8f31b3 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/QuadLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/QuadLightDelegate.cpp @@ -76,12 +76,12 @@ namespace AZ float QuadLightDelegate::GetWidth() const { - return m_shapeBus->GetQuadWidth() * GetTransform().GetScale().GetX(); + return m_shapeBus->GetQuadWidth() * GetTransform().GetUniformScale(); } float QuadLightDelegate::GetHeight() const { - return m_shapeBus->GetQuadHeight() * GetTransform().GetScale().GetY(); + return m_shapeBus->GetQuadHeight() * GetTransform().GetUniformScale(); } } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp index 0ddace1f87..5fb835de15 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp @@ -44,7 +44,17 @@ namespace AZ ->Field("AmbientMultiplier", &DiffuseProbeGridComponentConfig::m_ambientMultiplier) ->Field("ViewBias", &DiffuseProbeGridComponentConfig::m_viewBias) ->Field("NormalBias", &DiffuseProbeGridComponentConfig::m_normalBias) - ; + ->Field("EditorMode", &DiffuseProbeGridComponentConfig::m_editorMode) + ->Field("RuntimeMode", &DiffuseProbeGridComponentConfig::m_runtimeMode) + ->Field("BakedIrradianceTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedIrradianceTextureRelativePath) + ->Field("BakedDistanceTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedDistanceTextureRelativePath) + ->Field("BakedRelocationTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedRelocationTextureRelativePath) + ->Field("BakedClassificationTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedClassificationTextureRelativePath) + ->Field("BakedIrradianceTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedIrradianceTextureAsset) + ->Field("BakedDistanceTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedDistanceTextureAsset) + ->Field("BakedRelocationTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedRelocationTextureAsset) + ->Field("BakedClassificationTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedClassificationTextureAsset) + ; } } @@ -110,6 +120,26 @@ namespace AZ m_boxShapeInterface = LmbrCentral::BoxShapeComponentRequestsBus::FindFirstHandler(m_entityId); AZ_Assert(m_boxShapeInterface, "DiffuseProbeGridComponentController was unable to find box shape component"); + // special handling is required if this component is being cloned in the editor: + // check to see if the baked textures are already referenced by another DiffuseProbeGrid + if (m_featureProcessor->AreBakedTexturesReferenced( + m_configuration.m_bakedIrradianceTextureRelativePath, + m_configuration.m_bakedDistanceTextureRelativePath, + m_configuration.m_bakedRelocationTextureRelativePath, + m_configuration.m_bakedClassificationTextureRelativePath)) + { + // clear the baked texture paths and assets, since they belong to the original entity (not the clone) + m_configuration.m_bakedIrradianceTextureRelativePath.clear(); + m_configuration.m_bakedDistanceTextureRelativePath.clear(); + m_configuration.m_bakedRelocationTextureRelativePath.clear(); + m_configuration.m_bakedClassificationTextureRelativePath.clear(); + + m_configuration.m_bakedIrradianceTextureAsset.Reset(); + m_configuration.m_bakedDistanceTextureAsset.Reset(); + m_configuration.m_bakedRelocationTextureAsset.Reset(); + m_configuration.m_bakedClassificationTextureAsset.Reset(); + } + // add this diffuse probe grid to the feature processor const AZ::Transform& transform = m_transformInterface->GetWorldTM(); m_handle = m_featureProcessor->AddProbeGrid(transform, m_configuration.m_extents, m_configuration.m_probeSpacing); @@ -118,11 +148,61 @@ namespace AZ m_featureProcessor->SetViewBias(m_handle, m_configuration.m_viewBias); m_featureProcessor->SetNormalBias(m_handle, m_configuration.m_normalBias); + // load the baked texture assets, but only if they are all valid + if (m_configuration.m_bakedIrradianceTextureAsset.GetId().IsValid() && + m_configuration.m_bakedDistanceTextureAsset.GetId().IsValid() && + m_configuration.m_bakedRelocationTextureAsset.GetId().IsValid() && + m_configuration.m_bakedClassificationTextureAsset.GetId().IsValid()) + { + Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedIrradianceTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedDistanceTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedRelocationTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedClassificationTextureAsset.GetId()); + + m_configuration.m_bakedIrradianceTextureAsset.QueueLoad(); + m_configuration.m_bakedDistanceTextureAsset.QueueLoad(); + m_configuration.m_bakedRelocationTextureAsset.QueueLoad(); + m_configuration.m_bakedClassificationTextureAsset.QueueLoad(); + } + else if (m_configuration.m_runtimeMode == DiffuseProbeGridMode::Baked || + m_configuration.m_runtimeMode == DiffuseProbeGridMode::AutoSelect || + m_configuration.m_editorMode == DiffuseProbeGridMode::Baked || + m_configuration.m_editorMode == DiffuseProbeGridMode::AutoSelect) + { + AZ_Error("DiffuseProbeGrid", false, "DiffuseProbeGrid mdoe is set to Baked or Auto-Select, but it does not have baked texture assets. Please re-bake this DiffuseProbeGrid."); + } + + m_featureProcessor->SetMode(m_handle, m_configuration.m_runtimeMode); + // set box shape component dimensions from the configuration // this will invoke the OnShapeChanged() handler and set the outer extents on the feature processor m_boxShapeInterface->SetBoxDimensions(m_configuration.m_extents); } + void DiffuseProbeGridComponentController::OnAssetReady(Data::Asset asset) + { + // if all assets are ready we can set the baked texture images + if (m_configuration.m_bakedIrradianceTextureAsset.IsReady() && + m_configuration.m_bakedDistanceTextureAsset.IsReady() && + m_configuration.m_bakedRelocationTextureAsset.IsReady() && + m_configuration.m_bakedClassificationTextureAsset.IsReady()) + { + Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedIrradianceTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedDistanceTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedRelocationTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedClassificationTextureAsset.GetId()); + + UpdateBakedTextures(); + } + } + + void DiffuseProbeGridComponentController::OnAssetError(Data::Asset asset) + { + Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId()); + + AZ_Error("DiffuseProbeGrid", false, "Failed to load baked texture [%s], please re-bake this DiffuseProbeGrid.", asset.GetId().ToString().c_str()); + } + void DiffuseProbeGridComponentController::Deactivate() { if (m_featureProcessor) @@ -212,20 +292,96 @@ namespace AZ void DiffuseProbeGridComponentController::SetAmbientMultiplier(float ambientMultiplier) { + if (!m_featureProcessor) + { + return; + } + m_configuration.m_ambientMultiplier = ambientMultiplier; m_featureProcessor->SetAmbientMultiplier(m_handle, m_configuration.m_ambientMultiplier); } void DiffuseProbeGridComponentController::SetViewBias(float viewBias) { + if (!m_featureProcessor) + { + return; + } + m_configuration.m_viewBias = viewBias; m_featureProcessor->SetViewBias(m_handle, m_configuration.m_viewBias); } void DiffuseProbeGridComponentController::SetNormalBias(float normalBias) { + if (!m_featureProcessor) + { + return; + } + m_configuration.m_normalBias = normalBias; m_featureProcessor->SetNormalBias(m_handle, m_configuration.m_normalBias); } + + void DiffuseProbeGridComponentController::SetEditorMode(DiffuseProbeGridMode editorMode) + { + if (!m_featureProcessor) + { + return; + } + + // update the configuration and change the DiffuseProbeGrid mode + m_configuration.m_editorMode = editorMode; + m_featureProcessor->SetMode(m_handle, m_configuration.m_editorMode); + } + + void DiffuseProbeGridComponentController::SetRuntimeMode(DiffuseProbeGridMode runtimeMode) + { + if (!m_featureProcessor) + { + return; + } + + // only update the configuration + m_configuration.m_runtimeMode = runtimeMode; + } + + void DiffuseProbeGridComponentController::BakeTextures(DiffuseProbeGridBakeTexturesCallback callback) + { + if (!m_featureProcessor) + { + return; + } + + m_featureProcessor->BakeTextures( + m_handle, + callback, + m_configuration.m_bakedIrradianceTextureRelativePath, + m_configuration.m_bakedDistanceTextureRelativePath, + m_configuration.m_bakedRelocationTextureRelativePath, + m_configuration.m_bakedClassificationTextureRelativePath); + } + + void DiffuseProbeGridComponentController::UpdateBakedTextures() + { + if (!m_featureProcessor) + { + return; + } + + DiffuseProbeGridBakedTextures bakedTextures; + bakedTextures.m_irradianceImage = RPI::StreamingImage::FindOrCreate(m_configuration.m_bakedIrradianceTextureAsset); + bakedTextures.m_irradianceImageRelativePath = m_configuration.m_bakedIrradianceTextureRelativePath; + bakedTextures.m_distanceImage = RPI::StreamingImage::FindOrCreate(m_configuration.m_bakedDistanceTextureAsset); + bakedTextures.m_distanceImageRelativePath = m_configuration.m_bakedDistanceTextureRelativePath; + bakedTextures.m_relocationImageDescriptor = m_configuration.m_bakedRelocationTextureAsset->GetImageDescriptor(); + bakedTextures.m_relocationImageData = m_configuration.m_bakedRelocationTextureAsset->GetSubImageData(0, 0); + bakedTextures.m_relocationImageRelativePath = m_configuration.m_bakedRelocationTextureRelativePath; + bakedTextures.m_classificationImageDescriptor = m_configuration.m_bakedClassificationTextureAsset->GetImageDescriptor(); + bakedTextures.m_classificationImageData = m_configuration.m_bakedClassificationTextureAsset->GetSubImageData(0, 0); + bakedTextures.m_classificationImageRelativePath = m_configuration.m_bakedClassificationTextureRelativePath; + + m_featureProcessor->SetBakedTextures(m_handle, bakedTextures); + } } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h index 2bcb4132e9..4122a07ba2 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h @@ -39,6 +39,19 @@ namespace AZ float m_ambientMultiplier = DefaultDiffuseProbeGridAmbientMultiplier; float m_viewBias = DefaultDiffuseProbeGridViewBias; float m_normalBias = DefaultDiffuseProbeGridNormalBias; + + DiffuseProbeGridMode m_editorMode = DiffuseProbeGridMode::RealTime; + DiffuseProbeGridMode m_runtimeMode = DiffuseProbeGridMode::RealTime; + + AZStd::string m_bakedIrradianceTextureRelativePath; + AZStd::string m_bakedDistanceTextureRelativePath; + AZStd::string m_bakedRelocationTextureRelativePath; + AZStd::string m_bakedClassificationTextureRelativePath; + + Data::Asset m_bakedIrradianceTextureAsset; + Data::Asset m_bakedDistanceTextureAsset; + Data::Asset m_bakedRelocationTextureAsset; + Data::Asset m_bakedClassificationTextureAsset; }; class DiffuseProbeGridComponentController final @@ -79,12 +92,24 @@ namespace AZ // ShapeComponentNotificationsBus overrides void OnShapeChanged(ShapeChangeReasons changeReason) override; + // AssetBus overrides + void OnAssetReady(Data::Asset asset) override; + void OnAssetError(Data::Asset asset) override; + // Property handlers bool ValidateProbeSpacing(const AZ::Vector3& newSpacing); void SetProbeSpacing(const AZ::Vector3& probeSpacing); void SetAmbientMultiplier(float ambientMultiplier); void SetViewBias(float viewBias); void SetNormalBias(float normalBias); + void SetEditorMode(DiffuseProbeGridMode editorMode); + void SetRuntimeMode(DiffuseProbeGridMode runtimeMode); + + // Bake the diffuse probe grid textures to assets + void BakeTextures(DiffuseProbeGridBakeTexturesCallback callback); + + // Update the baked texture assets from the configuration + void UpdateBakedTextures(); // box shape component, used for defining the outer extents of the probe area LmbrCentral::BoxShapeComponentRequests* m_boxShapeInterface = nullptr; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp index ae22ec06c9..1e5b959803 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp @@ -16,6 +16,14 @@ #include #include #include +#include +#include +#include + +AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT +#include +#include +AZ_POP_DISABLE_WARNING namespace AZ { @@ -35,7 +43,9 @@ namespace AZ ->Field("ambientMultiplier", &EditorDiffuseProbeGridComponent::m_ambientMultiplier) ->Field("viewBias", &EditorDiffuseProbeGridComponent::m_viewBias) ->Field("normalBias", &EditorDiffuseProbeGridComponent::m_normalBias) - ; + ->Field("editorMode", &EditorDiffuseProbeGridComponent::m_editorMode) + ->Field("runtimeMode", &EditorDiffuseProbeGridComponent::m_runtimeMode) + ; if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { @@ -48,25 +58,26 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo::Uuid()) - ->ClassElement(AZ::Edit::ClassElements::Group, "Probe Spacing") + ->ClassElement(AZ::Edit::ClassElements::Group, "Probe Spacing (meters between probes)") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingX, "X", "Probe spacing on the X-axis") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingX, "X", "Probe spacing on the X-axis, in meters") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateX) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingY, "Y", "Probe spacing on the Y-axis") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingY, "Y", "Probe spacing on the Y-axis, in meters") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateY) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingZ, "Z", "Probe spacing on the Z-axis") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingZ, "Z", "Probe spacing on the Z-axis, in meters") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateZ) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged) ->ClassElement(AZ::Edit::ClassElements::Group, "Grid Settings") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_ambientMultiplier, "Ambient Multiplier", "Multiplier for the irradiance intensity") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnAmbientMultiplierChanged) - ->Attribute(Edit::Attributes::Decimals, 0) - ->Attribute(Edit::Attributes::Step, 1.0f) + ->Attribute(Edit::Attributes::Decimals, 1) + ->Attribute(Edit::Attributes::Step, 0.1f) ->Attribute(Edit::Attributes::Min, 0.0f) ->Attribute(Edit::Attributes::Max, 10.0f) ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_viewBias, "View Bias", "View bias adjustment") @@ -81,6 +92,27 @@ namespace AZ ->Attribute(Edit::Attributes::Step, 0.1f) ->Attribute(Edit::Attributes::Min, 0.0f) ->Attribute(Edit::Attributes::Max, 1.0f) + ->ClassElement(AZ::Edit::ClassElements::EditorData, "Grid mode") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(Edit::UIHandlers::ComboBox, &EditorDiffuseProbeGridComponent::m_editorMode, "Editor Mode", "Controls whether the editor uses RealTime or Baked diffuse GI. RealTime requires a ray-tracing capable GPU. Auto-Select will fallback to Baked if ray-tracing is not available") + ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnModeChangeValidate) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnEditorModeChanged) + ->EnumAttribute(DiffuseProbeGridMode::RealTime, "Real Time (Ray-Traced)") + ->EnumAttribute(DiffuseProbeGridMode::Baked, "Baked") + ->EnumAttribute(DiffuseProbeGridMode::AutoSelect, "Auto Select") + ->DataElement(Edit::UIHandlers::ComboBox, &EditorDiffuseProbeGridComponent::m_runtimeMode, "Runtime Mode", "Controls whether the runtime uses RealTime or Baked diffuse GI. RealTime requires a ray-tracing capable GPU. Auto-Select will fallback to Baked if ray-tracing is not available") + ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnModeChangeValidate) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnRuntimeModeChanged) + ->EnumAttribute(DiffuseProbeGridMode::RealTime, "Real Time (Ray-Traced)") + ->EnumAttribute(DiffuseProbeGridMode::Baked, "Baked") + ->EnumAttribute(DiffuseProbeGridMode::AutoSelect, "Auto Select") + ->ClassElement(AZ::Edit::ClassElements::Group, "Bake Textures") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->UIElement(AZ::Edit::UIHandlers::Button, "Bake Textures", "Bake the Diffuse Probe Grid textures to static assets that will be used when the mode is set to Baked") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "") + ->Attribute(AZ::Edit::Attributes::ButtonText, "Bake Textures") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::BakeDiffuseProbeGrid) + ->Attribute(AZ::Edit::Attributes::Visibility, &EditorDiffuseProbeGridComponent::GetBakeDiffuseProbeGridVisibilitySetting) ; editContext->Class( @@ -90,12 +122,6 @@ namespace AZ ->DataElement(AZ::Edit::UIHandlers::Default, &DiffuseProbeGridComponentController::m_configuration, "Configuration", "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; - - editContext->Class( - "DiffuseProbeGridComponentConfig", "") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ; } } @@ -121,15 +147,73 @@ namespace AZ BaseClass::Activate(); AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId()); + AZ::TickBus::Handler::BusConnect(); + AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusConnect(); } void EditorDiffuseProbeGridComponent::Deactivate() { + AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusDisconnect(); + AZ::TickBus::Handler::BusDisconnect(); AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect(); AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); BaseClass::Deactivate(); } + void EditorDiffuseProbeGridComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + if (!m_controller.m_featureProcessor) + { + return; + } + + DiffuseProbeGridComponentConfig& configuration = m_controller.m_configuration; + + // set the editor mode, which will override the runtime mode set by the controller + if (!m_editorModeSet) + { + m_controller.m_featureProcessor->SetMode(m_controller.m_handle, configuration.m_editorMode); + m_editorModeSet = true; + } + + CheckTextureAssetNotification(configuration.m_bakedIrradianceTextureRelativePath, configuration.m_bakedIrradianceTextureAsset); + CheckTextureAssetNotification(configuration.m_bakedDistanceTextureRelativePath, configuration.m_bakedDistanceTextureAsset); + CheckTextureAssetNotification(configuration.m_bakedRelocationTextureRelativePath, configuration.m_bakedRelocationTextureAsset); + CheckTextureAssetNotification(configuration.m_bakedClassificationTextureRelativePath, configuration.m_bakedClassificationTextureAsset); + } + + void EditorDiffuseProbeGridComponent::CheckTextureAssetNotification(const AZStd::string& relativePath, Data::Asset& configurationAsset) + { + Data::Asset textureAsset; + DiffuseProbeGridTextureNotificationType notificationType = DiffuseProbeGridTextureNotificationType::None; + if (m_controller.m_featureProcessor->CheckTextureAssetNotification(relativePath + ".streamingimage", textureAsset, notificationType)) + { + if (notificationType == DiffuseProbeGridTextureNotificationType::Ready) + { + // bake is complete, update configuration with the new baked texture asset + AzToolsFramework::ScopedUndoBatch undoBatch("DiffuseProbeGrid Texture Bake"); + configurationAsset = { textureAsset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; + SetDirty(); + + if (m_controller.m_configuration.m_bakedIrradianceTextureAsset.IsReady() && + m_controller.m_configuration.m_bakedDistanceTextureAsset.IsReady() && + m_controller.m_configuration.m_bakedClassificationTextureAsset.IsReady() && + m_controller.m_configuration.m_bakedRelocationTextureAsset.IsReady()) + { + m_controller.UpdateBakedTextures(); + } + } + else if (notificationType == DiffuseProbeGridTextureNotificationType::Error) + { + QMessageBox::information( + QApplication::activeWindow(), + "Diffuse Probe Grid", + "Diffuse Probe Grid texture failed to bake, please check the Asset Processor for more information.", + QMessageBox::Ok); + } + } + } + AZ::Aabb EditorDiffuseProbeGridComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo) { return m_controller.GetAabb(); @@ -140,11 +224,19 @@ namespace AZ return false; } + void EditorDiffuseProbeGridComponent::OnEntityInfoUpdatedVisibility(AZ::EntityId entityId, bool visible) + { + if ((GetEntityId() == entityId) && !visible) + { + m_editorModeSet = false; + } + } + AZ::Outcome EditorDiffuseProbeGridComponent::OnProbeSpacingValidateX(void* newValue, [[maybe_unused]] const AZ::Uuid& valueType) { if (!m_controller.m_featureProcessor) { - return AZ::Failure(AZStd::string("Unable to adjust probe spacing, please try again")); + return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing.")); } float newProbeSpacingX = *(reinterpret_cast(newValue)); @@ -152,7 +244,7 @@ namespace AZ Vector3 newSpacing(newProbeSpacingX, m_probeSpacingY, m_probeSpacingZ); if (!m_controller.ValidateProbeSpacing(newSpacing)) { - return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents")); + return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents.")); } return AZ::Success(); @@ -162,7 +254,7 @@ namespace AZ { if (!m_controller.m_featureProcessor) { - return AZ::Failure(AZStd::string("Unable to adjust probe spacing, please try again")); + return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing.")); } float newProbeSpacingY = *(reinterpret_cast(newValue)); @@ -170,7 +262,7 @@ namespace AZ Vector3 newSpacing(m_probeSpacingX, newProbeSpacingY, m_probeSpacingZ); if (!m_controller.ValidateProbeSpacing(newSpacing)) { - return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents")); + return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents.")); } return AZ::Success(); @@ -180,7 +272,7 @@ namespace AZ { if (!m_controller.m_featureProcessor) { - return AZ::Failure(AZStd::string("Unable to adjust probe spacing, please try again")); + return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing.")); } float newProbeSpacingZ = *(reinterpret_cast(newValue)); @@ -188,7 +280,7 @@ namespace AZ Vector3 newSpacing(m_probeSpacingX, m_probeSpacingY, newProbeSpacingZ); if (!m_controller.ValidateProbeSpacing(newSpacing)) { - return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents")); + return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents.")); } return AZ::Success(); @@ -218,5 +310,226 @@ namespace AZ m_controller.SetNormalBias(m_normalBias); return AZ::Edit::PropertyRefreshLevels::None; } + + AZ::u32 EditorDiffuseProbeGridComponent::OnEditorModeChanged() + { + // this will update the configuration and also change the DiffuseProbeGrid mode + m_controller.SetEditorMode(m_editorMode); + return AZ::Edit::PropertyRefreshLevels::EntireTree; + } + + AZ::u32 EditorDiffuseProbeGridComponent::OnRuntimeModeChanged() + { + // this will only update the configuration + m_controller.SetRuntimeMode(m_runtimeMode); + return AZ::Edit::PropertyRefreshLevels::None; + } + + AZ::Outcome EditorDiffuseProbeGridComponent::OnModeChangeValidate([[maybe_unused]] void* newValue, [[maybe_unused]] const AZ::Uuid& valueType) + { + DiffuseProbeGridMode newMode = (*(reinterpret_cast(newValue))); + + if (newMode == DiffuseProbeGridMode::Baked || newMode == DiffuseProbeGridMode::AutoSelect) + { + if (!m_controller.m_configuration.m_bakedIrradianceTextureAsset.GetId().IsValid() || + !m_controller.m_configuration.m_bakedDistanceTextureAsset.GetId().IsValid() || + !m_controller.m_configuration.m_bakedRelocationTextureAsset.GetId().IsValid() || + !m_controller.m_configuration.m_bakedClassificationTextureAsset.GetId().IsValid()) + { + return AZ::Failure(AZStd::string("Please bake textures before changing the Diffuse Probe Grid to Baked or Auto-Select mode.")); + } + } + + return AZ::Success(); + } + + AZ::u32 EditorDiffuseProbeGridComponent::GetBakeDiffuseProbeGridVisibilitySetting() + { + // the Bake button is visible only when the editor mode is set to RealTime + return m_editorMode == DiffuseProbeGridMode::RealTime ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide; + } + + AZ::u32 EditorDiffuseProbeGridComponent::BakeDiffuseProbeGrid() + { + if (m_bakeInProgress) + { + return AZ::Edit::PropertyRefreshLevels::None; + } + + // retrieve entity visibility + bool isHidden = false; + AzToolsFramework::EditorEntityInfoRequestBus::EventResult( + isHidden, + GetEntityId(), + &AzToolsFramework::EditorEntityInfoRequestBus::Events::IsHidden); + + // the entity must be visible in order to bake + if (isHidden) + { + QMessageBox::information( + QApplication::activeWindow(), + "Diffuse Probe Grid", + "This Diffuse Probe Grid entity is hidden, it must be visible in order to bake textures.", + QMessageBox::Ok); + + return AZ::Edit::PropertyRefreshLevels::None; + } + + DiffuseProbeGridComponentConfig& configuration = m_controller.m_configuration; + + // retrieve the source image paths from the configuration + // Note: we need to make sure to use the same source image for each bake + AZStd::string irradianceTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedIrradianceTextureRelativePath, DiffuseProbeGridIrradianceFileName); + AZStd::string distanceTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedDistanceTextureRelativePath, DiffuseProbeGridDistanceFileName); + AZStd::string relocationTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedRelocationTextureRelativePath, DiffuseProbeGridRelocationFileName); + AZStd::string classificationTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedClassificationTextureRelativePath, DiffuseProbeGridClassificationFileName); + + // create the full paths + char projectPath[AZ_MAX_PATH_LEN]; + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN); + + AZStd::string irradianceTextureFullPath; + AzFramework::StringFunc::Path::Join(projectPath, irradianceTextureRelativePath.c_str(), irradianceTextureFullPath, true, true); + AZStd::string distanceTextureFullPath; + AzFramework::StringFunc::Path::Join(projectPath, distanceTextureRelativePath.c_str(), distanceTextureFullPath, true, true); + AZStd::string relocationTextureFullPath; + AzFramework::StringFunc::Path::Join(projectPath, relocationTextureRelativePath.c_str(), relocationTextureFullPath, true, true); + AZStd::string classificationTextureFullPath; + AzFramework::StringFunc::Path::Join(projectPath, classificationTextureRelativePath.c_str(), classificationTextureFullPath, true, true); + + // make sure the folder is created + AZStd::string diffuseProbeGridFolder; + AzFramework::StringFunc::Path::GetFolderPath(irradianceTextureFullPath.data(), diffuseProbeGridFolder); + AZ::IO::SystemFile::CreateDir(diffuseProbeGridFolder.c_str()); + + // check out the files in source control + CheckoutSourceTextureFile(irradianceTextureFullPath); + CheckoutSourceTextureFile(distanceTextureFullPath); + CheckoutSourceTextureFile(relocationTextureFullPath); + CheckoutSourceTextureFile(classificationTextureFullPath); + + // update the configuration + AzToolsFramework::ScopedUndoBatch undoBatch("DiffuseProbeGrid bake"); + configuration.m_bakedIrradianceTextureRelativePath = irradianceTextureRelativePath; + configuration.m_bakedDistanceTextureRelativePath = distanceTextureRelativePath; + configuration.m_bakedRelocationTextureRelativePath = relocationTextureRelativePath; + configuration.m_bakedClassificationTextureRelativePath = classificationTextureRelativePath; + SetDirty(); + + // callback for the texture readback + DiffuseProbeGridBakeTexturesCallback bakeTexturesCallback = [=]( + DiffuseProbeGridTexture irradianceTexture, + DiffuseProbeGridTexture distanceTexture, + DiffuseProbeGridTexture relocationTexture, + DiffuseProbeGridTexture classificationTexture) + { + // irradiance + { + AZ::DdsFile::DdsFileData fileData = { irradianceTexture.m_size, irradianceTexture.m_format, irradianceTexture.m_data.get() }; + [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(irradianceTextureFullPath, fileData); + AZ_Assert(outcome.IsSuccess(), "Failed to write Irradiance texture .dds file [%s]", irradianceTextureFullPath.c_str()); + } + + // distance + { + AZ::DdsFile::DdsFileData fileData = { distanceTexture.m_size, distanceTexture.m_format, distanceTexture.m_data.get() }; + [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(distanceTextureFullPath, fileData); + AZ_Assert(outcome.IsSuccess(), "Failed to write Distance texture .dds file [%s]", distanceTextureFullPath.c_str()); + } + + // relocation + { + AZ::DdsFile::DdsFileData fileData = { relocationTexture.m_size, relocationTexture.m_format, relocationTexture.m_data.get() }; + [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(relocationTextureFullPath, fileData); + AZ_Assert(outcome.IsSuccess(), "Failed to write Relocation texture .dds file [%s]", relocationTextureFullPath.c_str()); + } + + // classification + { + AZ::DdsFile::DdsFileData fileData = { classificationTexture.m_size, classificationTexture.m_format, classificationTexture.m_data.get() }; + [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(classificationTextureFullPath, fileData); + AZ_Assert(outcome.IsSuccess(), "Failed to write Classification texture .dds file [%s]", classificationTextureFullPath.c_str()); + } + + m_bakeInProgress = false; + }; + + m_bakeInProgress = true; + m_controller.BakeTextures(bakeTexturesCallback); + + while (m_bakeInProgress) + { + QApplication::processEvents(); + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100)); + } + + QMessageBox::information( + QApplication::activeWindow(), + "Diffuse Probe Grid", + "Successfully baked Diffuse Probe Grid textures.", + QMessageBox::Ok); + + return AZ::Edit::PropertyRefreshLevels::None; + } + + AZStd::string EditorDiffuseProbeGridComponent::ValidateOrCreateNewTexturePath(const AZStd::string& configurationRelativePath, const char* fileSuffix) + { + AZStd::string relativePath = configurationRelativePath; + AZStd::string fullPath; + + char projectPath[AZ_MAX_PATH_LEN]; + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN); + + if (!relativePath.empty()) + { + // test to see if the texture file is actually there, if it was removed we need to + // generate a new filename, otherwise it will cause an error in the asset system + AzFramework::StringFunc::Path::Join(projectPath, configurationRelativePath.c_str(), fullPath, true, true); + + if (!AZ::IO::FileIOBase::GetInstance()->Exists(fullPath.c_str())) + { + // file does not exist, clear the relative path so we generate a new name + relativePath.clear(); + } + } + + // build a new image path if necessary + if (relativePath.empty()) + { + // the file name is a combination of the entity name, a UUID, and the filemask + Entity* entity = GetEntity(); + AZ_Assert(entity, "DiffuseProbeGrid entity is null"); + + AZ::Uuid uuid = AZ::Uuid::CreateRandom(); + AZStd::string uuidString; + uuid.ToString(uuidString); + + relativePath = "DiffuseProbeGrids/" + entity->GetName() + uuidString + fileSuffix; + + // replace any invalid filename characters + auto invalidCharacters = [](char letter) + { + return + letter == ':' || letter == '"' || letter == '\'' || + letter == '{' || letter == '}' || + letter == '<' || letter == '>'; + }; + AZStd::replace_if(relativePath.begin(), relativePath.end(), invalidCharacters, '_'); + } + + return relativePath; + } + + void EditorDiffuseProbeGridComponent::CheckoutSourceTextureFile(const AZStd::string& fullPath) + { + bool checkedOutSuccessfully = false; + using ApplicationBus = AzToolsFramework::ToolsApplicationRequestBus; + ApplicationBus::BroadcastResult( + checkedOutSuccessfully, + &ApplicationBus::Events::RequestEditForFileBlocking, + fullPath.c_str(), + "Checking out for edit...", + ApplicationBus::Events::RequestEditProgressCallback()); + } } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h index 2a50c47b81..15c46d45ba 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h @@ -12,8 +12,10 @@ #pragma once +#include #include #include +#include #include #include #include @@ -26,6 +28,8 @@ namespace AZ : public EditorRenderComponentAdapter , private AzToolsFramework::EditorComponentSelectionRequestsBus::Handler , private AzFramework::EntityDebugDisplayEventBus::Handler + , private AZ::TickBus::Handler + , private AzToolsFramework::EditorEntityInfoNotificationBus::Handler { public: using BaseClass = EditorRenderComponentAdapter; @@ -41,10 +45,22 @@ namespace AZ void Deactivate() override; private: + + // AZ::TickBus overrides + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + // EditorComponentSelectionRequestsBus overrides AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override; bool SupportsEditorRayIntersect() override; + // EditorEntityInfoNotifications overrides + void OnEntityInfoUpdatedVisibility(AZ::EntityId entityId, bool visible) override; + + // helper functions + AZStd::string ValidateOrCreateNewTexturePath(const AZStd::string& relativePath, const char* fileSuffix); + void CheckoutSourceTextureFile(const AZStd::string& fullPath); + void CheckTextureAssetNotification(const AZStd::string& relativePath, Data::Asset& configurationAsset); + // property change notifications AZ::Outcome OnProbeSpacingValidateX(void* newValue, const AZ::Uuid& valueType); AZ::Outcome OnProbeSpacingValidateY(void* newValue, const AZ::Uuid& valueType); @@ -53,6 +69,13 @@ namespace AZ AZ::u32 OnAmbientMultiplierChanged(); AZ::u32 OnViewBiasChanged(); AZ::u32 OnNormalBiasChanged(); + AZ::u32 OnEditorModeChanged(); + AZ::u32 OnRuntimeModeChanged(); + AZ::Outcome OnModeChangeValidate(void* newValue, const AZ::Uuid& valueType); + + // Button handler + AZ::u32 BakeDiffuseProbeGrid(); + AZ::u32 GetBakeDiffuseProbeGridVisibilitySetting(); // properties float m_probeSpacingX = DefaultDiffuseProbeGridSpacing; @@ -61,6 +84,12 @@ namespace AZ float m_ambientMultiplier = DefaultDiffuseProbeGridAmbientMultiplier; float m_viewBias = DefaultDiffuseProbeGridViewBias; float m_normalBias = DefaultDiffuseProbeGridNormalBias; + DiffuseProbeGridMode m_editorMode = DiffuseProbeGridMode::RealTime; + DiffuseProbeGridMode m_runtimeMode = DiffuseProbeGridMode::RealTime; + + // flags + bool m_editorModeSet = false; + AZStd::atomic_bool m_bakeInProgress = false; }; } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index 3192900ca4..229606a238 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -184,7 +184,7 @@ namespace AZ void MaterialPropertyInspector::AddUvNamesGroup() { const AZStd::string groupNameId = AZ::RPI::UvGroupName; - const AZStd::string groupDisplayName = "UV Names"; + const AZStd::string groupDisplayName = "UV Sets"; const AZStd::string groupDescription = "UV set names in this material, which can be renamed to match those in the model."; auto& group = m_groups[groupNameId]; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.h index 1acaa6fdf8..41ea80f8d1 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.h @@ -35,14 +35,13 @@ namespace AZ , private MeshComponentNotificationBus::Handler { public: - using BaseClass = EditorRenderComponentAdapter; AZ_EDITOR_COMPONENT(AZ::Render::EditorMeshComponent, EditorMeshComponentTypeId, BaseClass); static void Reflect(AZ::ReflectContext* context); EditorMeshComponent() = default; - EditorMeshComponent(const MeshComponentConfig& config); + explicit EditorMeshComponent(const MeshComponentConfig& config); // AZ::Component overrides ... void Activate() override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponent.h index a57780c384..fe7f45d7d1 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponent.h @@ -25,12 +25,11 @@ namespace AZ : public AzFramework::Components::ComponentAdapter { public: - using BaseClass = AzFramework::Components::ComponentAdapter; AZ_COMPONENT(AZ::Render::MeshComponent, MeshComponentTypeId, BaseClass); MeshComponent() = default; - MeshComponent(const MeshComponentConfig& config); + explicit MeshComponent(const MeshComponentConfig& config); static void Reflect(AZ::ReflectContext* context); }; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index a4a91cf708..c089dd01f9 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -177,28 +177,33 @@ namespace AZ FixUpModelAsset(m_configuration.m_modelAsset); } - void MeshComponentController::Activate(AZ::EntityId entityId) + void MeshComponentController::Activate(const AZ::EntityComponentIdPair& entityComponentIdPair) { FixUpModelAsset(m_configuration.m_modelAsset); - m_entityId = entityId; + const AZ::EntityId entityId = entityComponentIdPair.GetEntityId(); + m_entityComponentIdPair = entityComponentIdPair; - m_transformInterface = TransformBus::FindFirstHandler(m_entityId); + m_transformInterface = TransformBus::FindFirstHandler(entityId); AZ_Warning("MeshComponentController", m_transformInterface, "Unable to attach to a TransformBus handler. This mesh will always be rendered at the origin."); - m_meshFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity(m_entityId); + m_meshFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity(entityId); AZ_Error("MeshComponentController", m_meshFeatureProcessor, "Unable to find a MeshFeatureProcessorInterface on the entityId."); m_cachedNonUniformScale = AZ::Vector3::CreateOne(); - AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, m_entityId, &AZ::NonUniformScaleRequests::GetScale); - AZ::NonUniformScaleRequestBus::Event(m_entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, + AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, entityId, &AZ::NonUniformScaleRequests::GetScale); + AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, m_nonUniformScaleChangedHandler); - MeshComponentRequestBus::Handler::BusConnect(m_entityId); - TransformNotificationBus::Handler::BusConnect(m_entityId); - MaterialReceiverRequestBus::Handler::BusConnect(m_entityId); - MaterialComponentNotificationBus::Handler::BusConnect(m_entityId); - AzFramework::BoundsRequestBus::Handler::BusConnect(m_entityId); + MeshComponentRequestBus::Handler::BusConnect(entityId); + TransformNotificationBus::Handler::BusConnect(entityId); + MaterialReceiverRequestBus::Handler::BusConnect(entityId); + MaterialComponentNotificationBus::Handler::BusConnect(entityId); + AzFramework::BoundsRequestBus::Handler::BusConnect(entityId); + AzFramework::EntityContextId contextId; + AzFramework::EntityIdContextQueryBus::EventResult( + contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId); + AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusConnect({entityId, contextId}); //Buses must be connected before RegisterModel in case requests are made as a result of HandleModelChange RegisterModel(); @@ -209,6 +214,7 @@ namespace AZ // Buses must be disconnected after unregistering the model, otherwise they can't deliver the events during the process. UnregisterModel(); + AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusDisconnect(); AzFramework::BoundsRequestBus::Handler::BusDisconnect(); MeshComponentRequestBus::Handler::BusDisconnect(); TransformNotificationBus::Handler::BusDisconnect(); @@ -219,7 +225,7 @@ namespace AZ m_meshFeatureProcessor = nullptr; m_transformInterface = nullptr; - m_entityId = AZ::EntityId(AZ::EntityId::InvalidEntityId); + m_entityComponentIdPair = AZ::EntityComponentIdPair(AZ::EntityId(), AZ::InvalidComponentId); m_configuration.m_modelAsset.Release(); } @@ -293,10 +299,11 @@ namespace AZ Data::Asset modelAsset = m_meshFeatureProcessor->GetModelAsset(m_meshHandle); if (model && modelAsset) { + const AZ::EntityId entityId = m_entityComponentIdPair.GetEntityId(); m_configuration.m_modelAsset = modelAsset; - MeshComponentNotificationBus::Event(m_entityId, &MeshComponentNotificationBus::Events::OnModelReady, m_configuration.m_modelAsset, model); - MaterialReceiverNotificationBus::Event(m_entityId, &MaterialReceiverNotificationBus::Events::OnMaterialAssignmentsChanged); - AZ::Interface::Get()->RefreshEntityLocalBoundsUnion(m_entityId); + MeshComponentNotificationBus::Event(entityId, &MeshComponentNotificationBus::Events::OnModelReady, m_configuration.m_modelAsset, model); + MaterialReceiverNotificationBus::Event(entityId, &MaterialReceiverNotificationBus::Events::OnMaterialAssignmentsChanged); + AZ::Interface::Get()->RefreshEntityLocalBoundsUnion(entityId); } } @@ -304,8 +311,10 @@ namespace AZ { if (m_meshFeatureProcessor && m_configuration.m_modelAsset.GetId().IsValid()) { + const AZ::EntityId entityId = m_entityComponentIdPair.GetEntityId(); + MaterialAssignmentMap materials; - MaterialComponentRequestBus::EventResult(materials, m_entityId, &MaterialComponentRequests::GetMaterialOverrides); + MaterialComponentRequestBus::EventResult(materials, entityId, &MaterialComponentRequests::GetMaterialOverrides); m_meshFeatureProcessor->ReleaseMesh(m_meshHandle); m_meshHandle = m_meshFeatureProcessor->AcquireMesh(m_configuration.m_modelAsset, materials, @@ -330,7 +339,8 @@ namespace AZ { if (m_meshFeatureProcessor && m_meshHandle.IsValid()) { - MeshComponentNotificationBus::Event(m_entityId, &MeshComponentNotificationBus::Events::OnModelPreDestroy); + MeshComponentNotificationBus::Event( + m_entityComponentIdPair.GetEntityId(), &MeshComponentNotificationBus::Events::OnModelPreDestroy); m_meshFeatureProcessor->ReleaseMesh(m_meshHandle); } } @@ -462,5 +472,34 @@ namespace AZ return Aabb::CreateNull(); } } + + AzFramework::RenderGeometry::RayResult MeshComponentController::RenderGeometryIntersect( + const AzFramework::RenderGeometry::RayRequest& ray) + { + AzFramework::RenderGeometry::RayResult result; + if (const Data::Instance model = GetModel()) + { + float t; + AZ::Vector3 normal; + if (model->RayIntersection( + m_transformInterface->GetWorldTM(), m_cachedNonUniformScale, ray.m_startWorldPosition, + ray.m_endWorldPosition - ray.m_startWorldPosition, t, normal)) + { + // note: this is a temporary workaround to handle cases where model->RayIntersection + // returns negative distances, follow-up ATOM-15673 + const auto absT = AZStd::abs(t); + + // fill in ray result structure after successful intersection + const auto intersectionLine = (ray.m_endWorldPosition - ray.m_startWorldPosition); + result.m_uv = AZ::Vector2::CreateZero(); + result.m_worldPosition = ray.m_startWorldPosition + intersectionLine * absT; + result.m_worldNormal = normal; + result.m_distance = intersectionLine.GetLength() * absT; + result.m_entityAndComponent = m_entityComponentIdPair; + } + } + + return result; + } } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h index 0cda34ea42..4d63e5e88d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h @@ -13,11 +13,13 @@ #pragma once #include +#include #include #include #include +#include #include #include @@ -32,9 +34,7 @@ namespace AZ { namespace Render { - /** - * A configuration structure for the MeshComponentController - */ + //! A configuration structure for the MeshComponentController class MeshComponentConfig final : public AZ::ComponentConfig { @@ -57,6 +57,7 @@ namespace AZ class MeshComponentController final : private MeshComponentRequestBus::Handler , public AzFramework::BoundsRequestBus::Handler + , public AzFramework::RenderGeometry::IntersectionRequestBus::Handler , private TransformNotificationBus::Handler , private MaterialReceiverRequestBus::Handler , private MaterialComponentNotificationBus::Handler @@ -77,7 +78,7 @@ namespace AZ MeshComponentController() = default; MeshComponentController(const MeshComponentConfig& config); - void Activate(AZ::EntityId entityId); + void Activate(const AZ::EntityComponentIdPair& entityComponentIdPair); void Deactivate(); void SetConfiguration(const MeshComponentConfig& config); const MeshComponentConfig& GetConfiguration() const; @@ -103,10 +104,13 @@ namespace AZ void SetVisibility(bool visible) override; bool GetVisibility() const override; - // BoundsRequestBus and MeshComponentRequestBus ... + // BoundsRequestBus and MeshComponentRequestBus overrides ... AZ::Aabb GetWorldBounds() override; AZ::Aabb GetLocalBounds() override; + // IntersectionRequestBus overrides ... + AzFramework::RenderGeometry::RayResult RenderGeometryIntersect(const AzFramework::RenderGeometry::RayRequest& ray) override; + // TransformNotificationBus::Handler overrides ... void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; @@ -134,7 +138,7 @@ namespace AZ Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr; Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle; TransformInterface* m_transformInterface = nullptr; - AZ::EntityId m_entityId; + AZ::EntityComponentIdPair m_entityComponentIdPair; bool m_isVisible = true; MeshComponentConfig m_configuration; AZ::Vector3 m_cachedNonUniformScale = AZ::Vector3::CreateOne(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp index 735eab5368..7880d5e88c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp @@ -209,8 +209,8 @@ namespace AZ AZ::Vector3 position = AZ::Vector3::CreateZero(); AZ::TransformBus::EventResult(position, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation); - AZ::Vector3 scale = AZ::Vector3::CreateOne(); - AZ::TransformBus::EventResult(scale, GetEntityId(), &AZ::TransformBus::Events::GetLocalScale); + float scale = 1.0f; + AZ::TransformBus::EventResult(scale, GetEntityId(), &AZ::TransformBus::Events::GetLocalUniformScale); // draw AABB at probe position using the inner dimensions Color color(0.0f, 0.0f, 1.0f, 1.0f); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp index bad7c2fe38..ef82792f32 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp @@ -32,32 +32,29 @@ namespace AZ void ReleaseResourcesStep::Start() { - m_context->GetData()->m_defaultMaterialAsset.Release(); - m_context->GetData()->m_defaultModelAsset.Release(); - m_context->GetData()->m_materialAsset.Release(); - m_context->GetData()->m_modelAsset.Release(); + auto data = m_context->GetData(); + + data->m_defaultMaterialAsset.Release(); + data->m_defaultModelAsset.Release(); + data->m_materialAsset.Release(); + data->m_modelAsset.Release(); + data->m_lightingPresetAsset.Release(); - if (m_context->GetData()->m_modelEntity) + if (data->m_modelEntity) { - AzFramework::EntityContextRequestBus::Event(m_context->GetData()->m_entityContext->GetContextId(), - &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_context->GetData()->m_modelEntity); - m_context->GetData()->m_modelEntity = nullptr; + AzFramework::EntityContextRequestBus::Event(data->m_entityContext->GetContextId(), + &AzFramework::EntityContextRequestBus::Events::DestroyEntity, data->m_modelEntity); + data->m_modelEntity = nullptr; } - m_context->GetData()->m_frameworkScene->UnsetSubsystem(); - - m_context->GetData()->m_scene->Deactivate(); - m_context->GetData()->m_scene->RemoveRenderPipeline(m_context->GetData()->m_renderPipeline->GetId()); - RPI::RPISystemInterface::Get()->UnregisterScene(m_context->GetData()->m_scene); - - auto sceneSystem = AzFramework::SceneSystemInterface::Get(); - AZ_Assert(sceneSystem, "Thumbnail system failed to get scene system implementation."); - [[maybe_unused]] bool sceneRemovedSuccessfully = sceneSystem->RemoveScene(m_context->GetData()->m_sceneName); - AZ_Assert( - sceneRemovedSuccessfully, "Thumbnail system was unable to remove scene '%s' from the scene system.", - m_context->GetData()->m_sceneName.c_str()); - m_context->GetData()->m_scene = nullptr; - m_context->GetData()->m_renderPipeline = nullptr; + data->m_scene->Deactivate(); + data->m_scene->RemoveRenderPipeline(data->m_renderPipeline->GetId()); + RPI::RPISystemInterface::Get()->UnregisterScene(data->m_scene); + data->m_frameworkScene->UnsetSubsystem(data->m_scene); + data->m_frameworkScene->UnsetSubsystem(data->m_entityContext.get()); + data->m_scene = nullptr; + data->m_frameworkScene = nullptr; + data->m_renderPipeline = nullptr; } } // namespace Thumbnails } // namespace LyIntegration diff --git a/Gems/AtomLyIntegration/CommonFeatures/gem.json b/Gems/AtomLyIntegration/CommonFeatures/gem.json new file mode 100644 index 0000000000..306c61e6d7 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "CommonFeaturesAtom", + "display_name": "Common Features Atom", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt b/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt index 6492f4f13a..9a9b389227 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt @@ -43,6 +43,8 @@ ly_add_target( PRIVATE AZ::AzCore Gem::EMotionFX_Atom.Static + RUNTIME_DEPENDENCIES + Gem::EMotionFX ) if(PAL_TRAIT_BUILD_HOST_TOOLS) diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/gem.json b/Gems/AtomLyIntegration/EMotionFXAtom/gem.json new file mode 100644 index 0000000000..e2a81d0a5e --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "EMotionFX_Atom", + "display_name": "EMotionFX Atom", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/AtomLyIntegration/ImguiAtom/gem.json b/Gems/AtomLyIntegration/ImguiAtom/gem.json new file mode 100644 index 0000000000..6d6551b5fa --- /dev/null +++ b/Gems/AtomLyIntegration/ImguiAtom/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "ImguiAtom", + "display_name": "Imgui Atom", + "summary": "", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/3rdParty/Python/.gitignore b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/3rdParty/Python/.gitignore new file mode 100644 index 0000000000..f1a223f90e --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/3rdParty/Python/.gitignore @@ -0,0 +1 @@ +pyside2-tools \ No newline at end of file diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py index 6fd8b03e9e..06b00da981 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py @@ -81,8 +81,8 @@ settings = config.get_config_settings() if __name__ == '__main__': """Run this file as main""" - _G_DEBUG = True - _G_TEST_PYSIDE = True + _G_DEBUG = False + _G_TEST_PYSIDE = False _config = get_dccsi_config() _settings = config.get_config_settings() @@ -121,7 +121,6 @@ if __name__ == '__main__': import PySide2 _LOGGER.info(f'PySide2: {PySide2}') - _LOGGER.info(f'QTFORPYTHON_PATH: {_settings.QTFORPYTHON_PATH}') _LOGGER.info(f'LY_BIN_PATH: {_settings.LY_BIN_PATH}') _LOGGER.info(f'QT_PLUGIN_PATH: {_settings.QT_PLUGIN_PATH}') _LOGGER.info(f'QT_QPA_PLATFORM_PLUGIN_PATH: {_settings.QT_QPA_PLATFORM_PLUGIN_PATH}') diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Core.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Core.bat index 4a64c43029..ab0708defd 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Core.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Launchers/Windows/Env_Core.bat @@ -68,14 +68,17 @@ IF "%DEV_REL_PATH%"=="" (set DEV_REL_PATH=..\..\..\..) echo DEV_REL_PATH = %DEV_REL_PATH% :: You can define the project name -:: if not defined we just use the DCCsi path as standin -IF "%LY_PROJECT%"=="" ( - for %%a in (%CD%..\..\..) do set LY_PROJECT=%%~na +IF "%LY_PROJECT_NAME%"=="" ( + for %%a in (%CD%..\..\..) do set LY_PROJECT_NAME=%%~na ) +echo LY_PROJECT_NAME = %LY_PROJECT_NAME% + +:: if not defined we just use the DCCsi path as stand-in +IF "%LY_PROJECT%"=="" (set LY_PROJECT=%CD%) echo LY_PROJECT = %LY_PROJECT% :: set up the default project path (dccsi) -:: if not set we lso use the DCCsi path as standin +:: if not set we also use the DCCsi path as stand-in CD /D ..\..\ IF "%LY_PROJECT_PATH%"=="" (set LY_PROJECT_PATH=%CD%) echo LY_PROJECT_PATH = %LY_PROJECT_PATH% @@ -88,7 +91,7 @@ pushd %ABS_PATH% :: Change to root Lumberyard dev dir CD /d %LY_PROJECT_PATH%\%DEV_REL_PATH% -set LY_DEV=%CD% +IF "%LY_DEV%"=="" (set LY_DEV=%CD%) echo LY_DEV = %LY_DEV% :: Restore original directory popd diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/readme.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/readme.txt index cc090d922f..21882f3d96 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/readme.txt +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/readme.txt @@ -29,7 +29,7 @@ A general goal of the DCCsi is be self-maintained, and to not taint the users in So we boostrap additional access to site-packages in our userSetup.py: "C:\Depot\Lumberyard\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\SDK\Maya\Scripts\userSetup.py" -We don't want users to have to install or use Python2.7 although with maya and possibly other dcc tools we don't have that control. Maya still is on Python2.7, so instead of forcing another install of python we can just use mayapy to manage extensions. +We don't want users to have to install or use Python2.7 although with maya and possibly other dcc tools we don't have that control. Maya 2020 and earlier versions are still on Python2.7, so instead of forcing another install of python we can just use mayapy to manage extensions. Pip may already be installed, you can check like so (your maya install path may be different): diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/requirements.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/requirements.txt index 8fd084dac8..ceb5be4dea 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/requirements.txt +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/SDK/Maya/requirements.txt @@ -4,14 +4,14 @@ # # pip-compile --generate-hashes requirements.txt # -cachetools==3.1.1 \ - --hash=sha256:428266a1c0d36dc5aca63a2d7c5942e88c2c898d72139fca0e97fdd2380517ae \ - --hash=sha256:8ea2d3ce97850f31e4a08b0e2b5e6c34997d7216a9d2c98e0f3978630d4da69a - # via -r requirements.txt certifi==2020.6.20 \ --hash=sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3 \ --hash=sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41 # via -r requirements.txt +cachetools==3.1.1 \ + --hash=sha256:428266a1c0d36dc5aca63a2d7c5942e88c2c898d72139fca0e97fdd2380517ae \ + --hash=sha256:8ea2d3ce97850f31e4a08b0e2b5e6c34997d7216a9d2c98e0f3978630d4da69a + # via -r requirements.txt click==7.1.2 \ --hash=sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a \ --hash=sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc @@ -68,6 +68,16 @@ unipath==1.1 \ --hash=sha256:09839adcc72e8a24d4f76d63656f30b5a1f721fc40c9bcd79d8c67bdd8b47dae \ --hash=sha256:e6257e508d8abbfb6ddd8ec357e33589f1f48b1599127f23b017124d90b0fff7 # via -r requirements.txt +qdarkstyle==3.0.2 \ + --hash=sha256:55d149cf5f40ee297397f1818e091118cefb855a4a9c5c38566c47acd2d8c7ae \ + --hash=sha256:7c791535cc20b3cc1e8e1bf6b88dabe53cb0615983df702be83597e73ada2558 + # via -r c:\temp\requirements.txt +qtpy==1.9.0 \ + --hash=sha256:2db72c44b55d0fe1407be8fba35c838ad0d6d3bb81f23007886dc1fc0f459c8d \ + --hash=sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea + # via + # -r c:\temp\requirements.txt + # qdarkstyle wincertstore==0.2 \ --hash=sha256:22d5eebb52df88a8d4014d5cf6d1b6c3a5d469e6c3b2e2854f3a003e48872356 \ --hash=sha256:780bd1557c9185c15d9f4221ea7f905cb20b93f7151ca8ccaed9714dce4b327a diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/__init__.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/__init__.py index 69e4543a59..ab2d5db480 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/__init__.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/__init__.py @@ -83,13 +83,13 @@ _LY_DEV = os.getenv(constants.ENVAR_LY_DEV, check_stub='engine.json')) # get/set the project name -_LY_PROJECT_TAG = os.getenv(constants.ENVAR_LY_PROJECT, - config_utils.get_current_project(_LY_DEV)) +_LY_PROJECT_NAME = os.getenv(constants.ENVAR_LY_PROJECT, + config_utils.get_current_project().name) # project cache log dir path _DCCSI_LOG_PATH = Path(os.getenv(constants.ENVAR_DCCSI_LOG_PATH, Path(_LY_DEV, - _LY_PROJECT_TAG, + _LY_PROJECT_NAME, 'Cache', 'pc', 'user', 'log', 'logs'))) @@ -223,7 +223,7 @@ if _G_DEBUG: _LOGGER.debug('MODULE_PATH: {}'.format(_MODULE_PATH)) _LOGGER.debug('LY_DEV_PATH: {}'.format(_LY_DEV)) _LOGGER.debug('DCCSI_PATH: {}'.format(_DCCSIG_PATH)) -_LOGGER.debug('LY_PROJECT_TAG: {}'.format(_LY_PROJECT_TAG)) +_LOGGER.debug('LY_PROJECT_TAG: {}'.format(_LY_PROJECT_NAME)) _LOGGER.debug('DCCSI_LOG_PATH: {}'.format(_DCCSI_LOG_PATH)) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py index 9c920a871d..0a0c6b8337 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py @@ -137,8 +137,9 @@ def get_dccsi_config(dccsi_dirpath=return_stub_dir()): # ------------------------------------------------------------------------- -def get_current_project(dev_folder=get_stub_check_path()): - """Uses regex in lumberyard Dev\\bootstrap.cfg to retreive project tag str""" +def get_current_project_cfg(dev_folder=get_stub_check_path()): + """Uses regex in lumberyard Dev\\bootstrap.cfg to retreive project tag str + Note: boostrap.cfg will be deprecated. Don't use this method anymore.""" boostrap_filepath = Path(dev_folder, "bootstrap.cfg") if boostrap_filepath.exists(): bootstrap = open(str(boostrap_filepath), "r") @@ -153,6 +154,33 @@ def get_current_project(dev_folder=get_stub_check_path()): # ------------------------------------------------------------------------- +# ------------------------------------------------------------------------- +def get_current_project(): + """Gets o3de project via .o3de data in user directory""" + + from azpy.constants import PATH_USER_O3DE_BOOTSTRAP + from collections import OrderedDict + from box import Box + + bootstrap_box = None + + try: + bootstrap_box = Box.from_json(filename=PATH_USER_O3DE_BOOTSTRAP, + encoding="utf-8", + errors="strict", + object_pairs_hook=OrderedDict) + except FileExistsError as e: + _LOGGER.error('File does not exist: {}'.format(PATH_USER_O3DE_BOOTSTRAP)) + + if bootstrap_box: + # this seems fairly hard coded - what if the data changes? + project_path=Path(bootstrap_box.Amazon.AzCore.Bootstrap.project_path) + return project_path.resolve() + else: + return None +# ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- def bootstrap_dccsi_py_libs(dccsi_dirpath=return_stub_dir()): """Builds and adds local site dir libs based on py version""" @@ -194,7 +222,11 @@ if __name__ == '__main__': _LOGGER.info('LY_DEV: {}'.format(get_stub_check_path('engine.json'))) - _LOGGER.info('LY_PROJECT: {}'.format(get_current_project(get_stub_check_path('bootstrap.cfg')))) + # this will be deprecated and shouldn't work soon (returns None) + _LOGGER.info('LY_PROJECT: {}'.format(get_current_project_cfg(get_stub_check_path('bootstrap.cfg')))) + + # new o3de version + _LOGGER.info('LY_PROJECT: {}'.format(get_current_project())) _LOGGER.info('DCCSI_PYTHON_LIB_PATH: {}'.format(bootstrap_dccsi_py_libs(return_stub_dir('dccsi_stub')))) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py index 792f0faee6..e10221d324 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py @@ -26,6 +26,7 @@ So we can make an update here once that is used elsewhere. import os import sys import site +from os.path import expanduser import logging as _logging # for this module to perform standalone @@ -91,6 +92,9 @@ TAG_DIR_DCCSI_SDK = str('SDK') TAG_DIR_LY_BUILD = str('build') TAG_QT_PLUGIN_PATH = str('QT_PLUGIN_PATH') +TAG_O3DE_FOLDER = str('.o3de') +TAG_O3DE_BOOTSTRAP = str('bootstrap.setreg') + # filesystem markers, stub file names. STUB_LY_DEV = str('engine.json') STUB_LY_ROOT_PROJECT = str('ly_project_stub') @@ -221,10 +225,17 @@ TAG_DEFAULT_PY = str('Launch_pyBASE.bat') # config file stuff FILENAME_DEFAULT_CONFIG = str('DCCSI_config.json') +# new o3de related paths +PATH_USER_O3DE = str('{home}\\{o3de}').format(home=expanduser("~"), + o3de=TAG_O3DE_FOLDER) +PATH_USER_O3DE_REGISTRY = str('{0}\\Registry').format(PATH_USER_O3DE) +PATH_USER_O3DE_BOOTSTRAP = str('{reg}\\{file}').format(reg=PATH_USER_O3DE_REGISTRY, + file=TAG_O3DE_BOOTSTRAP) + #python and site-dir TAG_DCCSI_PY_VERSION_MAJOR = str(3) TAG_DCCSI_PY_VERSION_MINOR = str(7) -TAG_DCCSI_PY_VERSION_RELEASE = str(5) +TAG_DCCSI_PY_VERSION_RELEASE = str(10) TAG_PYTHON_EXE = str('python.exe') TAG_TOOLS_DIR = str('Tools\\Python') TAG_PLATFORM = str('windows') @@ -314,6 +325,7 @@ if __name__ == '__main__': _stash_dict['QTFORPYTHON_PATH'] = Path(PATH_QTFORPYTHON_PATH) _stash_dict['QT_PLUGIN_PATH'] = Path(PATH_QT_PLUGIN_PATH) _stash_dict['SAT_INSTALL_PATH'] = Path(PATH_SAT_INSTALL_PATH) + _stash_dict['PATH_USER_O3DE_BOOTSTRAP'] = Path(PATH_USER_O3DE_BOOTSTRAP) # --------------------------------------------------------------------- # py 2 and 3 compatible iter diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down.png deleted file mode 100644 index fa98bc39a3..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:afe9402162c5b4527f12c863d389ee9d75b53a1069b7e177497beba389d91d35 -size 525 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_disabled.png deleted file mode 100644 index eaedad9b31..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dc1e37e22cb75f616d6ada02cce006bae7fb1da515b15afea0fc98fcc542a092 -size 547 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_focus.png deleted file mode 100644 index 170beb53b4..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c7c9c4c8c5bdc755cc026aa23044f546010c0d1e079ecba34ceb8f0eb9e44bde -size 530 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_pressed.png deleted file mode 100644 index 32b2aac93a..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_down_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6111d15f1dc946742b00317bda789a5c625333f65a362f38931dabc50afb2067 -size 518 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left.png deleted file mode 100644 index e84d285f63..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8ac79e7fbd6be51465e0b685dca32c1236f95ad76ab8c5877ec73d20a1de4365 -size 546 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_disabled.png deleted file mode 100644 index d21aea9e87..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a86de88cf4ee32c352776caf46d5512d27679da8b571ea7e791287495fad4514 -size 569 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_focus.png deleted file mode 100644 index 6315e4d488..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5c5b8427bd1497006b8adbcbc445f11b07ec388a3398fe2997f65bdc56f2644f -size 565 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_pressed.png deleted file mode 100644 index c01c95df2b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_left_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:95ad920de52fd198f1af6d569917be20dc24a39dabdbe6c555c812d424bd9736 -size 541 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right.png deleted file mode 100644 index 7dc1534e3c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cb5b2d9b40652764f074dcea9856d6748b0efeac57ef58f306efa999f4b411c1 -size 518 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_disabled.png deleted file mode 100644 index 0bdb8963f1..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6a38dcd5b4078df430fa05780844af3e88e0a8e01c1fa910482e4c217354d728 -size 553 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_focus.png deleted file mode 100644 index 9659eeed4d..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5f60b2dfce514a6f558134b942f255ddf81ca3dc77b0d899a87f6d4cbac38e26 -size 543 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_pressed.png deleted file mode 100644 index 8e8ae64a87..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_right_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:84379a7bd6ffa75692648c6fce328616e8a009d7ea3a83c2288f82ecce37e729 -size 544 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up.png deleted file mode 100644 index 5137aa3c5f..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d7ee7bfb0c60d4687c8a0dcc38a12ee7cacaa2cbb9eb0ae24faa82b992ade445 -size 512 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_disabled.png deleted file mode 100644 index 7c866337ea..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:571b1afb9c2d7e01f56b75e1526dd0a3ffd49a60a4bcb7981ba34823b44a74c0 -size 538 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_focus.png deleted file mode 100644 index a3eaa49ef3..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:45d805ec94b8144bf121ba74ee96dd27f2f8c0890b2eca5e78df39cb5266f94d -size 530 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_pressed.png deleted file mode 100644 index 168493204d..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/arrow_up_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d49428a947ea424fabedd5a08e1d78e0bc57dc8a2d5a231b5d9ec06977870f5a -size 518 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon.png deleted file mode 100644 index 0af10b0138..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d33b23ed0d8450413a2582d8f00bcb808d39f7e09ff394cee1669a4b7e79207e -size 1256 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_disabled.png deleted file mode 100644 index 0af10b0138..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d33b23ed0d8450413a2582d8f00bcb808d39f7e09ff394cee1669a4b7e79207e -size 1256 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_focus.png deleted file mode 100644 index 0af10b0138..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d33b23ed0d8450413a2582d8f00bcb808d39f7e09ff394cee1669a4b7e79207e -size 1256 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_pressed.png deleted file mode 100644 index 0af10b0138..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/base_icon_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d33b23ed0d8450413a2582d8f00bcb808d39f7e09ff394cee1669a4b7e79207e -size 1256 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed.png deleted file mode 100644 index b964f8985a..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:56776b4655640d46eb1031b9c19c2341fdfe1201c774a84bc5c2801fbcfefc37 -size 350 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_disabled.png deleted file mode 100644 index ad619682e6..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d48862b1c68efdf376551d1c35d5d3a68e2ce9809e7c2723f42ece7c77fca009 -size 373 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_focus.png deleted file mode 100644 index 8ea7431745..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:62cc47f4b7751e22ffe4b26289ecc632b4a2c4e5c33ac79864fcfb32398b1139 -size 380 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_pressed.png deleted file mode 100644 index 54a60293b0..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_closed_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ad6e74b57c8876fa28c3a43d1a38369415790507d65d758ea3e77b796c401da2 -size 372 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end.png deleted file mode 100644 index 0fc0630627..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:63324c154ead46027729bcf307ba45fbeb5a8de3ec5e8cef55d315a84a087115 -size 142 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_disabled.png deleted file mode 100644 index 68a6b95488..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8a8b091785c84d37de57aacb7fe5a9b854933cc94b9b6f1f6e0b155879c7d6d0 -size 146 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_focus.png deleted file mode 100644 index 84307b3375..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4f89a6b105df07325dcf7bdcea2165bb065dd99b648aa12ae4e0c6693e04d0a2 -size 146 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_pressed.png deleted file mode 100644 index 3f63a24d56..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_end_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:76f44758619badecf11b3b0d9914612e584ad750a695c942811fe215457b25be -size 146 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line.png deleted file mode 100644 index 7aebe0ec54..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:50a35383d40b4e8a646931e4057cd25045f05a48208e2cb9d9935be76b53bf94 -size 130 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_disabled.png deleted file mode 100644 index f1b83a5734..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ba7550922e9d244620f8f9ad76fe546d542764eba02378f81b188dec5fd7438a -size 134 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_focus.png deleted file mode 100644 index 5daf190e47..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:52bec8528e3c8edd583136d90a37f46bcb45e0d406fc0fb680a8b4d75cfeb731 -size 134 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_pressed.png deleted file mode 100644 index d533bb82b9..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_line_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1b8d2c9a8593a52221c91d2a8c2d3cbd837e408a5f6d1dcad6f79328a13a3bcf -size 134 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more.png deleted file mode 100644 index d0eb02b7fb..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5087f4f06a9718230e1aec2ba681f3432ecd2640a135b4e90c7b009188ec4c29 -size 155 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_disabled.png deleted file mode 100644 index a457e2822c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dec80a2e8439a0787e10aee70a365e30b5d1f43c29e4c33989cc6cd2f5cac478 -size 162 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_focus.png deleted file mode 100644 index 09b9726550..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:39930fd3e240c9ad94d748d6cda73b2943682fc4825edd1240e882be18c06198 -size 162 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_pressed.png deleted file mode 100644 index 31a17b26f0..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_more_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0d07da719927b26db1c3087bbfe7203510fce077efe9e121935b3aefbb49b95d -size 162 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open.png deleted file mode 100644 index f0f49a375a..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:05a1980b268f598ebb3520067679a8beb4fa3f00da9c87dd93be2642718ceb44 -size 354 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_disabled.png deleted file mode 100644 index d46e6138bc..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:047c00f910dd279e871a6329ea533816d8b458063539b8efbe9949d7363996bf -size 375 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_focus.png deleted file mode 100644 index d6c73e877c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6bc79f30e3fdc52ec30eb0e9c6b03d1538f7c8c7855033d24d5f993e8ceb9cc1 -size 367 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_pressed.png deleted file mode 100644 index ba1bb5e27b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/branch_open_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aa65cf58b8a02bf5f4142ad80de05aba868245c55a790af6ba0230bfd01a2a06 -size 369 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked.png deleted file mode 100644 index d82af2b4ed..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:95c1c1651a13f0562383087549a35a97bbb7899c7d3717d79d4624485b72bf9f -size 452 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_disabled.png deleted file mode 100644 index e96b6ab274..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:80a1d02e6ac7e5d0439b2a077ab8cf82739853ce46ac1556d4035e3bba713242 -size 467 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_focus.png deleted file mode 100644 index abe4bad569..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:64d5ed4d01a9778912b98c9147eb67431560acb5805d1d0832a765c441b9ed9b -size 441 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_pressed.png deleted file mode 100644 index 1bab094a68..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_checked_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f0ffa46106a643a71835056acce66ebe09745a9e6d91fac30ff1caf0589a6677 -size 418 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate.png deleted file mode 100644 index 51d0835feb..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8b291eb9180c0e27d1de6ff008ff4259b2c675a65efa94866a66c7c932fc1260 -size 581 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_disabled.png deleted file mode 100644 index 9e13859a93..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8c7d3d64e8cb5e2f8bc6620b0d58492a56800fc78fc0229a5fa495d3a43987a1 -size 614 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_focus.png deleted file mode 100644 index aea72c9cde..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:43d494685f5d2ed740b69f04fefe0e757626db94685ecc8f4411c6c68a626a5a -size 576 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_pressed.png deleted file mode 100644 index d2c86adef3..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_indeterminate_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7af182d82449663ac37955e45ccc3f8fc86d185649fa9bed24c10167351bd5ee -size 563 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked.png deleted file mode 100644 index bf34be7606..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:232451d0bd9cf1d54c777862030b667cd5078f2f4ff387ec03c44d56eb207c03 -size 397 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_disabled.png deleted file mode 100644 index 596e553c14..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7b54771eaad56ee45f8871248ee1c6b18035aa732f9e4e9257d008a73be04c25 -size 386 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_focus.png deleted file mode 100644 index 96cf982f58..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ae293d387fda8a89d68fc3f15db07ef084e3537a033d02351ff918cfcc82ea8a -size 394 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_pressed.png deleted file mode 100644 index 0984a1fd5d..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/checkbox_unchecked_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f8ccf5cb638a090f0e64f6d336e4c6312cfaf53971afd0f00cd16d0c0759f1b4 -size 403 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal.png deleted file mode 100644 index 4d069c17b1..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:256f010c3084112888189bcbea2995a37f8acbf12d61a4a261a94aca797cd964 -size 117 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_disabled.png deleted file mode 100644 index 06465a0a29..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:85522a94ec26125f65dcafc6158665f40ea570e4a08cbdfbbdf5f6772b887eb7 -size 121 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_focus.png deleted file mode 100644 index 5f1332e6dd..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ae4766527d9e5a2226107ede231878118538e8be89f2dc2ac92b7c5a68ad0fc6 -size 120 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_pressed.png deleted file mode 100644 index f0f11abeb0..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_horizontal_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5f4bdbb4d207aa40366ad90d363a95d80e2b4a43574ca1ad3256ee6e0617f25e -size 120 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical.png deleted file mode 100644 index 7aebe0ec54..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:50a35383d40b4e8a646931e4057cd25045f05a48208e2cb9d9935be76b53bf94 -size 130 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_disabled.png deleted file mode 100644 index f1b83a5734..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ba7550922e9d244620f8f9ad76fe546d542764eba02378f81b188dec5fd7438a -size 134 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_focus.png deleted file mode 100644 index 5daf190e47..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:52bec8528e3c8edd583136d90a37f46bcb45e0d406fc0fb680a8b4d75cfeb731 -size 134 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_pressed.png deleted file mode 100644 index d533bb82b9..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/line_vertical_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1b8d2c9a8593a52221c91d2a8c2d3cbd837e408a5f6d1dcad6f79328a13a3bcf -size 134 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked.png deleted file mode 100644 index 99c9969237..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:abee85006ecef454df64f65a6aa7dbb85bff9c51f53ed87b47e9f4ef1adefec3 -size 1224 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_disabled.png deleted file mode 100644 index 13daed68f4..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7e22fc4d6bf116cebab4655b6bf81b1c384789b45a96f05d8a4b535e34978cb9 -size 1325 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_focus.png deleted file mode 100644 index e42389445a..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3db59af5ba4caa97ac07834e1a919adafdcd610324dacac68cbd2cde551c2397 -size 1293 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_pressed.png deleted file mode 100644 index 4153bb2ed5..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_checked_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:34bce34b70cf7c24d1e54f87f4225f0c4cf8e7dd8c6fd8a38f81e981bae2a2ce -size 1276 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked.png deleted file mode 100644 index 748ab5998b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f675766febe18edf774b0dd7db11177ca76793c0d2b693b1ffb6a447b79369d2 -size 963 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_disabled.png deleted file mode 100644 index 34230cbb40..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6d5af9296f23e58fc7fde5c9b278a801bd51bb3650d0e5f84dd9eb84434308cc -size 1040 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_focus.png deleted file mode 100644 index 3428ad46be..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c0b650dda797331b8c23ece7e313babb8e6e9118bd3d53e689a7381a33cb5e00 -size 1032 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_pressed.png deleted file mode 100644 index b60ab09f6c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/radio_unchecked_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2dd40885e1d7b1d3f37cfc3afff07fe47db552602bdc46aa9a8ce7a0c8df30db -size 1022 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal.png deleted file mode 100644 index ad5243fcb8..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:054d47979c0879378a6f5e36d9e5b251c31e15610adeed109b8f128115d4b5ec -size 150 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_disabled.png deleted file mode 100644 index 94ef75054c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9eca88a1ebba5d42107da4c3b3af3b52a8de1c76bc1bae8d27190ff5e69f8198 -size 155 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_focus.png deleted file mode 100644 index c4fe22a169..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e11a73fcc79cebd854cbdf3c6539eca99b016440c590b5326f90fa9790e9a69d -size 154 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_pressed.png deleted file mode 100644 index e6d3f5a2c6..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_horizontal_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7371a89813b6f4843dc90b4abe866de943ba670057e0802dcc7f0284d9aa079b -size 154 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical.png deleted file mode 100644 index 6f47c7e52c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8e4d88b8da4d94d4ecaa0eda448d18adcad14c5a62d4e6c9d0ffb2673683d855 -size 137 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_disabled.png deleted file mode 100644 index 43b5911860..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e7422317ab56297babc9025f42dd1f7179588ac4e066cb1b976b1bb56efca656 -size 140 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_focus.png deleted file mode 100644 index 0b918dcdc9..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b9a1520ad62a2b20f53c0709d643af3e8e0d775891597c4bc05e46ed75617bd9 -size 144 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_pressed.png deleted file mode 100644 index 7b104f52b0..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_move_vertical_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:73b0e2dddb1c22848b9b858975cdaae02f0b7b47696922a863358afa30f81dfa -size 143 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal.png deleted file mode 100644 index e7174cd081..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e2986d7bca86f3359817f002ecf125afab71281561925a6ecbffe844a2be9699 -size 145 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_disabled.png deleted file mode 100644 index b45f02655c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4f7909f6aa1843cb2382ea0e49afb10967a331e128cc8103ab8082c3e46a90aa -size 151 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_focus.png deleted file mode 100644 index e2898bd5bf..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b4c628767e58d08e929a4bfc3f730e1347b186a44a1b0d4159fa722776a660ea -size 149 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_pressed.png deleted file mode 100644 index 3a71bdc89e..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_horizontal_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c2bcf16dee85252fbf33d3eb05009b988f4c7b3795c01d66e471712d76def3ab -size 149 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical.png deleted file mode 100644 index 02c38086c4..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5db0c6a32f562204a4dc77c8958ef29e016af621f11891ca1795da808a879288 -size 133 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_disabled.png deleted file mode 100644 index f9b739bb93..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c705854e5a7aae10edb4d0cd28d1217a0b6599845031053d502aa05030ce5134 -size 135 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_focus.png deleted file mode 100644 index 08661141b3..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:39bd3c687b5bc56d6a62728d3dbfa522f3e1e74ec4de752987768f60c947adf5 -size 139 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_pressed.png deleted file mode 100644 index 5baf760e59..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/toolbar_separator_vertical_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0966303eb702647e3463ffa644611d817741b1ee0d016ecd56c600e9f04ac114 -size 138 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent.png deleted file mode 100644 index 02ade9b47b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:17cedbceacd7ae3a97319a3db9606b40d8ef31428828b08bdbfe73f5642b4ae5 -size 104 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_disabled.png deleted file mode 100644 index 02ade9b47b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:17cedbceacd7ae3a97319a3db9606b40d8ef31428828b08bdbfe73f5642b4ae5 -size 104 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_focus.png deleted file mode 100644 index 02ade9b47b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:17cedbceacd7ae3a97319a3db9606b40d8ef31428828b08bdbfe73f5642b4ae5 -size 104 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_pressed.png deleted file mode 100644 index 02ade9b47b..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/transparent_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:17cedbceacd7ae3a97319a3db9606b40d8ef31428828b08bdbfe73f5642b4ae5 -size 104 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close.png deleted file mode 100644 index dd99b7b8ed..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f7b31e2fb43e9c3dbd9f0e32680422a7d8c8e7ff7cd600e446103e45b0df0523 -size 766 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_disabled.png deleted file mode 100644 index 1f506f9543..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:528a22b6955681f34373fc72a2dfdd19e6255e18c73f0804c09b34ea01c1f0a0 -size 838 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_focus.png deleted file mode 100644 index 244b91f5b8..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bbc009bf89ac37957e2ff6532a9328f71e63f5edb45bd918048c8a69f61a72e2 -size 756 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_pressed.png deleted file mode 100644 index 4a45bda1b2..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_close_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cd117b3874dec17ebf78f136784eca821f4d916fe34081187c8c28bc2223f545 -size 745 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip.png deleted file mode 100644 index 0f176f5949..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c2ebc9c32505a0489879f7429034143af2ed48e71d2b2eba449a45c7253a2b7 -size 426 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_disabled.png deleted file mode 100644 index f07d5f0de8..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0e7ffdf7643cd2078127953690098b8ea8899428f7906efa778d70422d254f1e -size 447 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_focus.png deleted file mode 100644 index d7271e6e0a..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:055a219ffe9015ed50585caa1cedd31cb99c034c41c17f3ff97cc3c9a1b9b68c -size 435 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_pressed.png deleted file mode 100644 index 000da02699..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_grip_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:776e4b8d509743bb682783085bd139ad8e419a1bbc02a93480b1a4aaffd89041 -size 444 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize.png deleted file mode 100644 index 1846176c40..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8ddd70f8069320fb09ee8800cecaa190076baaa4b1c251900220541cc83434bd -size 193 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_disabled.png deleted file mode 100644 index d9df85b122..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4bc0af0fc3119eb066333d3c3e4fbdcc65e23bfdb43b5c5dfd65aae7b6916b3b -size 206 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_focus.png deleted file mode 100644 index 30a7f49ed0..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7575b3458ef002ebfe1fece1427dd3170326d35144b0d03f215d091f625f7286 -size 208 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_pressed.png deleted file mode 100644 index 9cd26589f5..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_minimize_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e3dd9475a6db26f45e85d5a688a5e724b7278af4d472caf4b16c1fde346f95db -size 202 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock.png deleted file mode 100644 index 8126d26228..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b07a032c3109f93770c149c4b3199c17cc446e58e50b0d86c4254268a3dc00b0 -size 510 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_disabled.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_disabled.png deleted file mode 100644 index 573cd467ed..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_disabled.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0c97ecbc47699b4ec1189831ae2b8f08eed95e96def84ea48ac63597ecd3d40a -size 541 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_focus.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_focus.png deleted file mode 100644 index 5044d402af..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_focus.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0ed098cc564cc6c45cdf43103f06a670089149cbc4e81a733becd49d6d115d44 -size 519 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_pressed.png b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_pressed.png deleted file mode 100644 index be8c5637dd..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/rc/window_undock_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ef37bb94ebabd4d37ed1c8fcd5a095a5d10e2a20a667861d13c772b903c32bb1 -size 523 diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/readme.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/readme.txt deleted file mode 100644 index e100551564..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/readme.txt +++ /dev/null @@ -1,5 +0,0 @@ -LICENSE -https://github.com/ColinDuquesnoy/QDarkStyleSheet/blob/master/LICENSE.rst - -DEPOT -https://github.com/ColinDuquesnoy/QDarkStyleSheet diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/style.qrc b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/style.qrc deleted file mode 100644 index e301854e2c..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/style.qrc +++ /dev/null @@ -1,216 +0,0 @@ - - - - rc/arrow_down.png - rc/arrow_down@2x.png - rc/arrow_down_disabled.png - rc/arrow_down_disabled@2x.png - rc/arrow_down_focus.png - rc/arrow_down_focus@2x.png - rc/arrow_down_pressed.png - rc/arrow_down_pressed@2x.png - rc/arrow_left.png - rc/arrow_left@2x.png - rc/arrow_left_disabled.png - rc/arrow_left_disabled@2x.png - rc/arrow_left_focus.png - rc/arrow_left_focus@2x.png - rc/arrow_left_pressed.png - rc/arrow_left_pressed@2x.png - rc/arrow_right.png - rc/arrow_right@2x.png - rc/arrow_right_disabled.png - rc/arrow_right_disabled@2x.png - rc/arrow_right_focus.png - rc/arrow_right_focus@2x.png - rc/arrow_right_pressed.png - rc/arrow_right_pressed@2x.png - rc/arrow_up.png - rc/arrow_up@2x.png - rc/arrow_up_disabled.png - rc/arrow_up_disabled@2x.png - rc/arrow_up_focus.png - rc/arrow_up_focus@2x.png - rc/arrow_up_pressed.png - rc/arrow_up_pressed@2x.png - rc/base_icon.png - rc/base_icon@2x.png - rc/base_icon_disabled.png - rc/base_icon_disabled@2x.png - rc/base_icon_focus.png - rc/base_icon_focus@2x.png - rc/base_icon_pressed.png - rc/base_icon_pressed@2x.png - rc/branch_closed.png - rc/branch_closed@2x.png - rc/branch_closed_disabled.png - rc/branch_closed_disabled@2x.png - rc/branch_closed_focus.png - rc/branch_closed_focus@2x.png - rc/branch_closed_pressed.png - rc/branch_closed_pressed@2x.png - rc/branch_end.png - rc/branch_end@2x.png - rc/branch_end_disabled.png - rc/branch_end_disabled@2x.png - rc/branch_end_focus.png - rc/branch_end_focus@2x.png - rc/branch_end_pressed.png - rc/branch_end_pressed@2x.png - rc/branch_line.png - rc/branch_line@2x.png - rc/branch_line_disabled.png - rc/branch_line_disabled@2x.png - rc/branch_line_focus.png - rc/branch_line_focus@2x.png - rc/branch_line_pressed.png - rc/branch_line_pressed@2x.png - rc/branch_more.png - rc/branch_more@2x.png - rc/branch_more_disabled.png - rc/branch_more_disabled@2x.png - rc/branch_more_focus.png - rc/branch_more_focus@2x.png - rc/branch_more_pressed.png - rc/branch_more_pressed@2x.png - rc/branch_open.png - rc/branch_open@2x.png - rc/branch_open_disabled.png - rc/branch_open_disabled@2x.png - rc/branch_open_focus.png - rc/branch_open_focus@2x.png - rc/branch_open_pressed.png - rc/branch_open_pressed@2x.png - rc/checkbox_checked.png - rc/checkbox_checked@2x.png - rc/checkbox_checked_disabled.png - rc/checkbox_checked_disabled@2x.png - rc/checkbox_checked_focus.png - rc/checkbox_checked_focus@2x.png - rc/checkbox_checked_pressed.png - rc/checkbox_checked_pressed@2x.png - rc/checkbox_indeterminate.png - rc/checkbox_indeterminate@2x.png - rc/checkbox_indeterminate_disabled.png - rc/checkbox_indeterminate_disabled@2x.png - rc/checkbox_indeterminate_focus.png - rc/checkbox_indeterminate_focus@2x.png - rc/checkbox_indeterminate_pressed.png - rc/checkbox_indeterminate_pressed@2x.png - rc/checkbox_unchecked.png - rc/checkbox_unchecked@2x.png - rc/checkbox_unchecked_disabled.png - rc/checkbox_unchecked_disabled@2x.png - rc/checkbox_unchecked_focus.png - rc/checkbox_unchecked_focus@2x.png - rc/checkbox_unchecked_pressed.png - rc/checkbox_unchecked_pressed@2x.png - rc/line_horizontal.png - rc/line_horizontal@2x.png - rc/line_horizontal_disabled.png - rc/line_horizontal_disabled@2x.png - rc/line_horizontal_focus.png - rc/line_horizontal_focus@2x.png - rc/line_horizontal_pressed.png - rc/line_horizontal_pressed@2x.png - rc/line_vertical.png - rc/line_vertical@2x.png - rc/line_vertical_disabled.png - rc/line_vertical_disabled@2x.png - rc/line_vertical_focus.png - rc/line_vertical_focus@2x.png - rc/line_vertical_pressed.png - rc/line_vertical_pressed@2x.png - rc/radio_checked.png - rc/radio_checked@2x.png - rc/radio_checked_disabled.png - rc/radio_checked_disabled@2x.png - rc/radio_checked_focus.png - rc/radio_checked_focus@2x.png - rc/radio_checked_pressed.png - rc/radio_checked_pressed@2x.png - rc/radio_unchecked.png - rc/radio_unchecked@2x.png - rc/radio_unchecked_disabled.png - rc/radio_unchecked_disabled@2x.png - rc/radio_unchecked_focus.png - rc/radio_unchecked_focus@2x.png - rc/radio_unchecked_pressed.png - rc/radio_unchecked_pressed@2x.png - rc/toolbar_move_horizontal.png - rc/toolbar_move_horizontal@2x.png - rc/toolbar_move_horizontal_disabled.png - rc/toolbar_move_horizontal_disabled@2x.png - rc/toolbar_move_horizontal_focus.png - rc/toolbar_move_horizontal_focus@2x.png - rc/toolbar_move_horizontal_pressed.png - rc/toolbar_move_horizontal_pressed@2x.png - rc/toolbar_move_vertical.png - rc/toolbar_move_vertical@2x.png - rc/toolbar_move_vertical_disabled.png - rc/toolbar_move_vertical_disabled@2x.png - rc/toolbar_move_vertical_focus.png - rc/toolbar_move_vertical_focus@2x.png - rc/toolbar_move_vertical_pressed.png - rc/toolbar_move_vertical_pressed@2x.png - rc/toolbar_separator_horizontal.png - rc/toolbar_separator_horizontal@2x.png - rc/toolbar_separator_horizontal_disabled.png - rc/toolbar_separator_horizontal_disabled@2x.png - rc/toolbar_separator_horizontal_focus.png - rc/toolbar_separator_horizontal_focus@2x.png - rc/toolbar_separator_horizontal_pressed.png - rc/toolbar_separator_horizontal_pressed@2x.png - rc/toolbar_separator_vertical.png - rc/toolbar_separator_vertical@2x.png - rc/toolbar_separator_vertical_disabled.png - rc/toolbar_separator_vertical_disabled@2x.png - rc/toolbar_separator_vertical_focus.png - rc/toolbar_separator_vertical_focus@2x.png - rc/toolbar_separator_vertical_pressed.png - rc/toolbar_separator_vertical_pressed@2x.png - rc/transparent.png - rc/transparent@2x.png - rc/transparent_disabled.png - rc/transparent_disabled@2x.png - rc/transparent_focus.png - rc/transparent_focus@2x.png - rc/transparent_pressed.png - rc/transparent_pressed@2x.png - rc/window_close.png - rc/window_close@2x.png - rc/window_close_disabled.png - rc/window_close_disabled@2x.png - rc/window_close_focus.png - rc/window_close_focus@2x.png - rc/window_close_pressed.png - rc/window_close_pressed@2x.png - rc/window_grip.png - rc/window_grip@2x.png - rc/window_grip_disabled.png - rc/window_grip_disabled@2x.png - rc/window_grip_focus.png - rc/window_grip_focus@2x.png - rc/window_grip_pressed.png - rc/window_grip_pressed@2x.png - rc/window_minimize.png - rc/window_minimize@2x.png - rc/window_minimize_disabled.png - rc/window_minimize_disabled@2x.png - rc/window_minimize_focus.png - rc/window_minimize_focus@2x.png - rc/window_minimize_pressed.png - rc/window_minimize_pressed@2x.png - rc/window_undock.png - rc/window_undock@2x.png - rc/window_undock_disabled.png - rc/window_undock_disabled@2x.png - rc/window_undock_focus.png - rc/window_undock_focus@2x.png - rc/window_undock_pressed.png - rc/window_undock_pressed@2x.png - - - style.qss - - diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/style.qss b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/style.qss deleted file mode 100644 index 55dfe093d9..0000000000 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/ui/resources/qdarkstyle/style.qss +++ /dev/null @@ -1,2165 +0,0 @@ -/* --------------------------------------------------------------------------- - - Created by the qtsass compiler v0.1.1 - - The definitions are in the "qdarkstyle.qss._styles.scss" module - - WARNING! All changes made in this file will be lost! - ---------------------------------------------------------------------------- */ -/* QDarkStyleSheet ----------------------------------------------------------- - -This is the main style sheet, the palette has nine colors. - -It is based on three selecting colors, three greyish (background) colors -plus three whitish (foreground) colors. Each set of widgets of the same -type have a header like this: - - ------------------ - GroupName -------- - ------------------ - -And each widget is separated with a header like this: - - QWidgetName ------ - -This makes more easy to find and change some css field. The basic -configuration is described bellow. - - BACKGROUND ----------- - - Light (unpressed) - Normal (border, disabled, pressed, checked, toolbars, menus) - Dark (background) - - FOREGROUND ----------- - - Light (texts/labels) - Normal (not used yet) - Dark (disabled texts) - - SELECTION ------------ - - Light (selection/hover/active) - Normal (selected) - Dark (selected disabled) - -If a stranger configuration is required because of a bugfix or anything -else, keep the comment on the line above so nobody changes it, including the -issue number. - -*/ -/* - -See Qt documentation: - - - https://doc.qt.io/qt-5/stylesheet.html - - https://doc.qt.io/qt-5/stylesheet-reference.html - - https://doc.qt.io/qt-5/stylesheet-examples.html - ---------------------------------------------------------------------------- */ -/* QWidget ---------------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QWidget { - background-color: #19232D; - border: 0px solid #32414B; - padding: 0px; - color: #F0F0F0; - selection-background-color: #1464A0; - selection-color: #F0F0F0; -} - -QWidget:disabled { - background-color: #19232D; - color: #787878; - selection-background-color: #14506E; - selection-color: #787878; -} - -QWidget::item:selected { - background-color: #1464A0; -} - -QWidget::item:hover { - background-color: #148CD2; - color: #32414B; -} - -/* QMainWindow ------------------------------------------------------------ - -This adjusts the splitter in the dock widget, not qsplitter -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmainwindow - ---------------------------------------------------------------------------- */ -QMainWindow::separator { - background-color: #32414B; - border: 0px solid #19232D; - spacing: 0px; - padding: 2px; -} - -QMainWindow::separator:hover { - background-color: #505F69; - border: 0px solid #148CD2; -} - -QMainWindow::separator:horizontal { - width: 5px; - margin-top: 2px; - margin-bottom: 2px; - image: url(":/qss_icons/rc/toolbar_separator_vertical.png"); -} - -QMainWindow::separator:vertical { - height: 5px; - margin-left: 2px; - margin-right: 2px; - image: url(":/qss_icons/rc/toolbar_separator_horizontal.png"); -} - -/* QToolTip --------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtooltip - ---------------------------------------------------------------------------- */ -QToolTip { - background-color: #148CD2; - border: 1px solid #19232D; - color: #19232D; - /* Remove padding, for fix combo box tooltip */ - padding: 0px; - /* Remove opacity, fix #174 - may need to use RGBA */ -} - -/* QStatusBar ------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qstatusbar - ---------------------------------------------------------------------------- */ -QStatusBar { - border: 1px solid #32414B; - /* Fixes Spyder #9120, #9121 */ - background: #32414B; - /* Fixes #205, white vertical borders separating items */ -} - -QStatusBar::item { - border: none; -} - -QStatusBar QToolTip { - background-color: #148CD2; - border: 1px solid #19232D; - color: #19232D; - /* Remove padding, for fix combo box tooltip */ - padding: 0px; - /* Reducing transparency to read better */ - opacity: 230; -} - -QStatusBar QLabel { - /* Fixes Spyder #9120, #9121 */ - background: transparent; -} - -/* QCheckBox -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcheckbox - ---------------------------------------------------------------------------- */ -QCheckBox { - background-color: #19232D; - color: #F0F0F0; - spacing: 4px; - outline: none; - padding-top: 4px; - padding-bottom: 4px; -} - -QCheckBox:focus { - border: none; -} - -QCheckBox QWidget:disabled { - background-color: #19232D; - color: #787878; -} - -QCheckBox::indicator { - margin-left: 4px; - height: 16px; - width: 16px; -} - -QCheckBox::indicator:unchecked { - image: url(":/qss_icons/rc/checkbox_unchecked.png"); -} - -QCheckBox::indicator:unchecked:hover, QCheckBox::indicator:unchecked:focus, QCheckBox::indicator:unchecked:pressed { - border: none; - image: url(":/qss_icons/rc/checkbox_unchecked_focus.png"); -} - -QCheckBox::indicator:unchecked:disabled { - image: url(":/qss_icons/rc/checkbox_unchecked_disabled.png"); -} - -QCheckBox::indicator:checked { - image: url(":/qss_icons/rc/checkbox_checked.png"); -} - -QCheckBox::indicator:checked:hover, QCheckBox::indicator:checked:focus, QCheckBox::indicator:checked:pressed { - border: none; - image: url(":/qss_icons/rc/checkbox_checked_focus.png"); -} - -QCheckBox::indicator:checked:disabled { - image: url(":/qss_icons/rc/checkbox_checked_disabled.png"); -} - -QCheckBox::indicator:indeterminate { - image: url(":/qss_icons/rc/checkbox_indeterminate.png"); -} - -QCheckBox::indicator:indeterminate:disabled { - image: url(":/qss_icons/rc/checkbox_indeterminate_disabled.png"); -} - -QCheckBox::indicator:indeterminate:focus, QCheckBox::indicator:indeterminate:hover, QCheckBox::indicator:indeterminate:pressed { - image: url(":/qss_icons/rc/checkbox_indeterminate_focus.png"); -} - -/* QGroupBox -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qgroupbox - ---------------------------------------------------------------------------- */ -QGroupBox { - font-weight: bold; - border: 1px solid #32414B; - border-radius: 4px; - padding: 4px; - margin-top: 16px; -} - -QGroupBox::title { - subcontrol-origin: margin; - subcontrol-position: top left; - left: 3px; - padding-left: 3px; - padding-right: 5px; - padding-top: 8px; - padding-bottom: 16px; -} - -QGroupBox::indicator { - margin-left: 2px; - height: 12px; - width: 12px; -} - -QGroupBox::indicator:unchecked:hover, QGroupBox::indicator:unchecked:focus, QGroupBox::indicator:unchecked:pressed { - border: none; - image: url(":/qss_icons/rc/checkbox_unchecked_focus.png"); -} - -QGroupBox::indicator:unchecked:disabled { - image: url(":/qss_icons/rc/checkbox_unchecked_disabled.png"); -} - -QGroupBox::indicator:checked:hover, QGroupBox::indicator:checked:focus, QGroupBox::indicator:checked:pressed { - border: none; - image: url(":/qss_icons/rc/checkbox_checked_focus.png"); -} - -QGroupBox::indicator:checked:disabled { - image: url(":/qss_icons/rc/checkbox_checked_disabled.png"); -} - -/* QRadioButton ----------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qradiobutton - ---------------------------------------------------------------------------- */ -QRadioButton { - background-color: #19232D; - color: #F0F0F0; - spacing: 4px; - padding: 0px; - border: none; - outline: none; -} - -QRadioButton:focus { - border: none; -} - -QRadioButton:disabled { - background-color: #19232D; - color: #787878; - border: none; - outline: none; -} - -QRadioButton QWidget { - background-color: #19232D; - color: #F0F0F0; - spacing: 0px; - padding: 0px; - outline: none; - border: none; -} - -QRadioButton::indicator { - border: none; - outline: none; - margin-left: 4px; - height: 16px; - width: 16px; -} - -QRadioButton::indicator:unchecked { - image: url(":/qss_icons/rc/radio_unchecked.png"); -} - -QRadioButton::indicator:unchecked:hover, QRadioButton::indicator:unchecked:focus, QRadioButton::indicator:unchecked:pressed { - border: none; - outline: none; - image: url(":/qss_icons/rc/radio_unchecked_focus.png"); -} - -QRadioButton::indicator:unchecked:disabled { - image: url(":/qss_icons/rc/radio_unchecked_disabled.png"); -} - -QRadioButton::indicator:checked { - border: none; - outline: none; - image: url(":/qss_icons/rc/radio_checked.png"); -} - -QRadioButton::indicator:checked:hover, QRadioButton::indicator:checked:focus, QRadioButton::indicator:checked:pressed { - border: none; - outline: none; - image: url(":/qss_icons/rc/radio_checked_focus.png"); -} - -QRadioButton::indicator:checked:disabled { - outline: none; - image: url(":/qss_icons/rc/radio_checked_disabled.png"); -} - -/* QMenuBar --------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenubar - ---------------------------------------------------------------------------- */ -QMenuBar { - background-color: #32414B; - padding: 2px; - border: 1px solid #19232D; - color: #F0F0F0; -} - -QMenuBar:focus { - border: 1px solid #148CD2; -} - -QMenuBar::item { - background: transparent; - padding: 4px; -} - -QMenuBar::item:selected { - padding: 4px; - background: transparent; - border: 0px solid #32414B; -} - -QMenuBar::item:pressed { - padding: 4px; - border: 0px solid #32414B; - background-color: #148CD2; - color: #F0F0F0; - margin-bottom: 0px; - padding-bottom: 0px; -} - -/* QMenu ------------------------------------------------------------------ - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenu - ---------------------------------------------------------------------------- */ -QMenu { - border: 0px solid #32414B; - color: #F0F0F0; - margin: 0px; -} - -QMenu::separator { - height: 1px; - background-color: #505F69; - color: #F0F0F0; -} - -QMenu::icon { - margin: 0px; - padding-left: 8px; -} - -QMenu::item { - background-color: #32414B; - padding: 4px 24px 4px 24px; - /* Reserve space for selection border */ - border: 1px transparent #32414B; -} - -QMenu::item:selected { - color: #F0F0F0; -} - -QMenu::indicator { - width: 12px; - height: 12px; - padding-left: 6px; - /* non-exclusive indicator = check box style indicator (see QActionGroup::setExclusive) */ - /* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ -} - -QMenu::indicator:non-exclusive:unchecked { - image: url(":/qss_icons/rc/checkbox_unchecked.png"); -} - -QMenu::indicator:non-exclusive:unchecked:selected { - image: url(":/qss_icons/rc/checkbox_unchecked_disabled.png"); -} - -QMenu::indicator:non-exclusive:checked { - image: url(":/qss_icons/rc/checkbox_checked.png"); -} - -QMenu::indicator:non-exclusive:checked:selected { - image: url(":/qss_icons/rc/checkbox_checked_disabled.png"); -} - -QMenu::indicator:exclusive:unchecked { - image: url(":/qss_icons/rc/radio_unchecked.png"); -} - -QMenu::indicator:exclusive:unchecked:selected { - image: url(":/qss_icons/rc/radio_unchecked_disabled.png"); -} - -QMenu::indicator:exclusive:checked { - image: url(":/qss_icons/rc/radio_checked.png"); -} - -QMenu::indicator:exclusive:checked:selected { - image: url(":/qss_icons/rc/radio_checked_disabled.png"); -} - -QMenu::right-arrow { - margin: 5px; - image: url(":/qss_icons/rc/arrow_right.png"); - height: 12px; - width: 12px; -} - -/* QAbstractItemView ------------------------------------------------------ - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox - ---------------------------------------------------------------------------- */ -QAbstractItemView { - alternate-background-color: #19232D; - color: #F0F0F0; - border: 1px solid #32414B; - border-radius: 4px; -} - -QAbstractItemView QLineEdit { - padding: 2px; -} - -/* QAbstractScrollArea ---------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea - ---------------------------------------------------------------------------- */ -QAbstractScrollArea { - background-color: #19232D; - border: 1px solid #32414B; - border-radius: 4px; - padding: 2px; - /* fix #159 */ - min-height: 1.25em; - /* fix #159 */ - color: #F0F0F0; -} - -QAbstractScrollArea:disabled { - color: #787878; -} - -/* QScrollArea ------------------------------------------------------------ - ---------------------------------------------------------------------------- */ -QScrollArea QWidget QWidget:disabled { - background-color: #19232D; -} - -/* QScrollBar ------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qscrollbar - ---------------------------------------------------------------------------- */ -QScrollBar:horizontal { - height: 16px; - margin: 2px 16px 2px 16px; - border: 1px solid #32414B; - border-radius: 4px; - background-color: #19232D; -} - -QScrollBar:vertical { - background-color: #19232D; - width: 16px; - margin: 16px 2px 16px 2px; - border: 1px solid #32414B; - border-radius: 4px; -} - -QScrollBar::handle:horizontal { - background-color: #787878; - border: 1px solid #32414B; - border-radius: 4px; - min-width: 8px; -} - -QScrollBar::handle:horizontal:hover { - background-color: #148CD2; - border: 1px solid #148CD2; - border-radius: 4px; - min-width: 8px; -} - -QScrollBar::handle:horizontal:focus { - border: 1px solid #1464A0; -} - -QScrollBar::handle:vertical { - background-color: #787878; - border: 1px solid #32414B; - min-height: 8px; - border-radius: 4px; -} - -QScrollBar::handle:vertical:hover { - background-color: #148CD2; - border: 1px solid #148CD2; - border-radius: 4px; - min-height: 8px; -} - -QScrollBar::handle:vertical:focus { - border: 1px solid #1464A0; -} - -QScrollBar::add-line:horizontal { - margin: 0px 0px 0px 0px; - border-image: url(":/qss_icons/rc/arrow_right_disabled.png"); - height: 12px; - width: 12px; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::add-line:horizontal:hover, QScrollBar::add-line:horizontal:on { - border-image: url(":/qss_icons/rc/arrow_right.png"); - height: 12px; - width: 12px; - subcontrol-position: right; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical { - margin: 3px 0px 3px 0px; - border-image: url(":/qss_icons/rc/arrow_down_disabled.png"); - height: 12px; - width: 12px; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on { - border-image: url(":/qss_icons/rc/arrow_down.png"); - height: 12px; - width: 12px; - subcontrol-position: bottom; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal { - margin: 0px 3px 0px 3px; - border-image: url(":/qss_icons/rc/arrow_left_disabled.png"); - height: 12px; - width: 12px; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on { - border-image: url(":/qss_icons/rc/arrow_left.png"); - height: 12px; - width: 12px; - subcontrol-position: left; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:vertical { - margin: 3px 0px 3px 0px; - border-image: url(":/qss_icons/rc/arrow_up_disabled.png"); - height: 12px; - width: 12px; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::sub-line:vertical:hover, QScrollBar::sub-line:vertical:on { - border-image: url(":/qss_icons/rc/arrow_up.png"); - height: 12px; - width: 12px; - subcontrol-position: top; - subcontrol-origin: margin; -} - -QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal { - background: none; -} - -QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { - background: none; -} - -QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { - background: none; -} - -QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - background: none; -} - -/* QTextEdit -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-specific-widgets - ---------------------------------------------------------------------------- */ -QTextEdit { - background-color: #19232D; - color: #F0F0F0; - border-radius: 4px; - border: 1px solid #32414B; -} - -QTextEdit:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QTextEdit:focus { - border: 1px solid #1464A0; -} - -QTextEdit:selected { - background: #1464A0; - color: #32414B; -} - -/* QPlainTextEdit --------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QPlainTextEdit { - background-color: #19232D; - color: #F0F0F0; - border-radius: 4px; - border: 1px solid #32414B; -} - -QPlainTextEdit:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QPlainTextEdit:focus { - border: 1px solid #1464A0; -} - -QPlainTextEdit:selected { - background: #1464A0; - color: #32414B; -} - -/* QSizeGrip -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qsizegrip - ---------------------------------------------------------------------------- */ -QSizeGrip { - background: transparent; - width: 12px; - height: 12px; - image: url(":/qss_icons/rc/window_grip.png"); -} - -/* QStackedWidget --------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QStackedWidget { - padding: 2px; - border: 1px solid #32414B; - border: 1px solid #19232D; -} - -/* QToolBar --------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbar - ---------------------------------------------------------------------------- */ -QToolBar { - background-color: #32414B; - border-bottom: 1px solid #19232D; - padding: 2px; - font-weight: bold; - spacing: 2px; -} - -QToolBar QToolButton { - background-color: #32414B; - border: 1px solid #32414B; -} - -QToolBar QToolButton:hover { - border: 1px solid #148CD2; -} - -QToolBar QToolButton:checked { - border: 1px solid #19232D; - background-color: #19232D; -} - -QToolBar QToolButton:checked:hover { - border: 1px solid #148CD2; -} - -QToolBar::handle:horizontal { - width: 16px; - image: url(":/qss_icons/rc/toolbar_move_horizontal.png"); -} - -QToolBar::handle:vertical { - height: 16px; - image: url(":/qss_icons/rc/toolbar_move_vertical.png"); -} - -QToolBar::separator:horizontal { - width: 16px; - image: url(":/qss_icons/rc/toolbar_separator_horizontal.png"); -} - -QToolBar::separator:vertical { - height: 16px; - image: url(":/qss_icons/rc/toolbar_separator_vertical.png"); -} - -QToolButton#qt_toolbar_ext_button { - background: #32414B; - border: 0px; - color: #F0F0F0; - image: url(":/qss_icons/rc/arrow_right.png"); -} - -/* QAbstractSpinBox ------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QAbstractSpinBox { - background-color: #19232D; - border: 1px solid #32414B; - color: #F0F0F0; - /* This fixes 103, 111 */ - padding-top: 2px; - /* This fixes 103, 111 */ - padding-bottom: 2px; - padding-left: 4px; - padding-right: 4px; - border-radius: 4px; - /* min-width: 5px; removed to fix 109 */ -} - -QAbstractSpinBox:up-button { - background-color: transparent #19232D; - subcontrol-origin: border; - subcontrol-position: top right; - border-left: 1px solid #32414B; - border-bottom: 1px solid #32414B; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - margin: 1px; - width: 12px; - margin-bottom: -1px; -} - -QAbstractSpinBox::up-arrow, QAbstractSpinBox::up-arrow:disabled, QAbstractSpinBox::up-arrow:off { - image: url(":/qss_icons/rc/arrow_up_disabled.png"); - height: 8px; - width: 8px; -} - -QAbstractSpinBox::up-arrow:hover { - image: url(":/qss_icons/rc/arrow_up.png"); -} - -QAbstractSpinBox:down-button { - background-color: transparent #19232D; - subcontrol-origin: border; - subcontrol-position: bottom right; - border-left: 1px solid #32414B; - border-top: 1px solid #32414B; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - margin: 1px; - width: 12px; - margin-top: -1px; -} - -QAbstractSpinBox::down-arrow, QAbstractSpinBox::down-arrow:disabled, QAbstractSpinBox::down-arrow:off { - image: url(":/qss_icons/rc/arrow_down_disabled.png"); - height: 8px; - width: 8px; -} - -QAbstractSpinBox::down-arrow:hover { - image: url(":/qss_icons/rc/arrow_down.png"); -} - -QAbstractSpinBox:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QAbstractSpinBox:focus { - border: 1px solid #1464A0; -} - -QAbstractSpinBox:selected { - background: #1464A0; - color: #32414B; -} - -/* ------------------------------------------------------------------------ */ -/* DISPLAYS --------------------------------------------------------------- */ -/* ------------------------------------------------------------------------ */ -/* QLabel ----------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qframe - ---------------------------------------------------------------------------- */ -QLabel { - background-color: #19232D; - border: 0px solid #32414B; - padding: 2px; - margin: 0px; - color: #F0F0F0; -} - -QLabel:disabled { - background-color: #19232D; - border: 0px solid #32414B; - color: #787878; -} - -/* QTextBrowser ----------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea - ---------------------------------------------------------------------------- */ -QTextBrowser { - background-color: #19232D; - border: 1px solid #32414B; - color: #F0F0F0; - border-radius: 4px; -} - -QTextBrowser:disabled { - background-color: #19232D; - border: 1px solid #32414B; - color: #787878; - border-radius: 4px; -} - -QTextBrowser:hover, QTextBrowser:!hover, QTextBrowser:selected, QTextBrowser:pressed { - border: 1px solid #32414B; -} - -/* QGraphicsView ---------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QGraphicsView { - background-color: #19232D; - border: 1px solid #32414B; - color: #F0F0F0; - border-radius: 4px; -} - -QGraphicsView:disabled { - background-color: #19232D; - border: 1px solid #32414B; - color: #787878; - border-radius: 4px; -} - -QGraphicsView:hover, QGraphicsView:!hover, QGraphicsView:selected, QGraphicsView:pressed { - border: 1px solid #32414B; -} - -/* QCalendarWidget -------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QCalendarWidget { - border: 1px solid #32414B; - border-radius: 4px; -} - -QCalendarWidget:disabled { - background-color: #19232D; - color: #787878; -} - -/* QLCDNumber ------------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QLCDNumber { - background-color: #19232D; - color: #F0F0F0; -} - -QLCDNumber:disabled { - background-color: #19232D; - color: #787878; -} - -/* QProgressBar ----------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qprogressbar - ---------------------------------------------------------------------------- */ -QProgressBar { - background-color: #19232D; - border: 1px solid #32414B; - color: #F0F0F0; - border-radius: 4px; - text-align: center; -} - -QProgressBar:disabled { - background-color: #19232D; - border: 1px solid #32414B; - color: #787878; - border-radius: 4px; - text-align: center; -} - -QProgressBar::chunk { - background-color: #1464A0; - color: #19232D; - border-radius: 4px; -} - -QProgressBar::chunk:disabled { - background-color: #14506E; - color: #787878; - border-radius: 4px; -} - -/* ------------------------------------------------------------------------ */ -/* BUTTONS ---------------------------------------------------------------- */ -/* ------------------------------------------------------------------------ */ -/* QPushButton ------------------------------------------------------------ - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qpushbutton - ---------------------------------------------------------------------------- */ -QPushButton { - background-color: #505F69; - border: 1px solid #32414B; - color: #F0F0F0; - border-radius: 4px; - padding: 3px; - outline: none; - /* Issue #194 - Special case of QPushButton inside dialogs, for better UI */ - min-width: 80px; -} - -QPushButton:disabled { - background-color: #32414B; - border: 1px solid #32414B; - color: #787878; - border-radius: 4px; - padding: 3px; -} - -QPushButton:checked { - background-color: #32414B; - border: 1px solid #32414B; - border-radius: 4px; - padding: 3px; - outline: none; -} - -QPushButton:checked:disabled { - background-color: #19232D; - border: 1px solid #32414B; - color: #787878; - border-radius: 4px; - padding: 3px; - outline: none; -} - -QPushButton:checked:selected { - background: #1464A0; - color: #32414B; -} - -QPushButton::menu-indicator { - subcontrol-origin: padding; - subcontrol-position: bottom right; - bottom: 4px; -} - -QPushButton:pressed { - background-color: #19232D; - border: 1px solid #19232D; -} - -QPushButton:pressed:hover { - border: 1px solid #148CD2; -} - -QPushButton:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QPushButton:selected { - background: #1464A0; - color: #32414B; -} - -QPushButton:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QPushButton:focus { - border: 1px solid #1464A0; -} - -/* QToolButton ------------------------------------------------------------ - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbutton - ---------------------------------------------------------------------------- */ -QToolButton { - background-color: transparent; - border: 1px solid transparent; - border-radius: 4px; - margin: 0px; - padding: 2px; - /* The subcontrols below are used only in the DelayedPopup mode */ - /* The subcontrols below are used only in the MenuButtonPopup mode */ - /* The subcontrol below is used only in the InstantPopup or DelayedPopup mode */ -} - -QToolButton:checked { - background-color: transparent; - border: 1px solid #1464A0; -} - -QToolButton:checked:disabled { - border: 1px solid #14506E; -} - -QToolButton:pressed { - margin: 1px; - background-color: transparent; - border: 1px solid #1464A0; -} - -QToolButton:disabled { - border: none; -} - -QToolButton:hover { - border: 1px solid #148CD2; -} - -QToolButton[popupMode="0"] { - /* Only for DelayedPopup */ - padding-right: 2px; -} - -QToolButton[popupMode="1"] { - /* Only for MenuButtonPopup */ - padding-right: 20px; -} - -QToolButton[popupMode="1"]::menu-button { - border: none; -} - -QToolButton[popupMode="1"]::menu-button:hover { - border: none; - border-left: 1px solid #148CD2; - border-radius: 0; -} - -QToolButton[popupMode="2"] { - /* Only for InstantPopup */ - padding-right: 2px; -} - -QToolButton::menu-button { - padding: 2px; - border-radius: 4px; - border: 1px solid #32414B; - width: 12px; - outline: none; -} - -QToolButton::menu-button:hover { - border: 1px solid #148CD2; -} - -QToolButton::menu-button:checked:hover { - border: 1px solid #148CD2; -} - -QToolButton::menu-indicator { - image: url(":/qss_icons/rc/arrow_down.png"); - height: 8px; - width: 8px; - top: 0; - /* Exclude a shift for better image */ - left: -2px; - /* Shift it a bit */ -} - -QToolButton::menu-arrow { - image: url(":/qss_icons/rc/arrow_down.png"); - height: 8px; - width: 8px; -} - -QToolButton::menu-arrow:hover { - image: url(":/qss_icons/rc/arrow_down_focus.png"); -} - -/* QCommandLinkButton ----------------------------------------------------- - ---------------------------------------------------------------------------- */ -QCommandLinkButton { - background-color: transparent; - border: 1px solid #32414B; - color: #F0F0F0; - border-radius: 4px; - padding: 0px; - margin: 0px; -} - -QCommandLinkButton:disabled { - background-color: transparent; - color: #787878; -} - -/* ------------------------------------------------------------------------ */ -/* INPUTS - NO FIELDS ----------------------------------------------------- */ -/* ------------------------------------------------------------------------ */ -/* QComboBox -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox - ---------------------------------------------------------------------------- */ -QComboBox { - border: 1px solid #32414B; - border-radius: 4px; - selection-background-color: #1464A0; - padding-left: 4px; - padding-right: 36px; - /* 4 + 16*2 See scrollbar size */ - /* Fixes #103, #111 */ - min-height: 1.5em; - /* padding-top: 2px; removed to fix #132 */ - /* padding-bottom: 2px; removed to fix #132 */ - /* min-width: 75px; removed to fix #109 */ - /* Needed to remove indicator - fix #132 */ -} - -QComboBox QAbstractItemView { - border: 1px solid #32414B; - border-radius: 0; - background-color: #19232D; - selection-background-color: #1464A0; -} - -QComboBox QAbstractItemView:hover { - background-color: #19232D; - color: #F0F0F0; -} - -QComboBox QAbstractItemView:selected { - background: #1464A0; - color: #32414B; -} - -QComboBox QAbstractItemView:alternate { - background: #19232D; -} - -QComboBox:disabled { - background-color: #19232D; - color: #787878; -} - -QComboBox:hover { - border: 1px solid #148CD2; -} - -QComboBox:focus { - border: 1px solid #1464A0; -} - -QComboBox:on { - selection-background-color: #1464A0; -} - -QComboBox::indicator { - border: none; - border-radius: 0; - background-color: transparent; - selection-background-color: transparent; - color: transparent; - selection-color: transparent; - /* Needed to remove indicator - fix #132 */ -} - -QComboBox::indicator:alternate { - background: #19232D; -} - -QComboBox::item:alternate { - background: #19232D; -} - -QComboBox::item:checked { - font-weight: bold; -} - -QComboBox::item:selected { - border: 0px solid transparent; -} - -QComboBox::drop-down { - subcontrol-origin: padding; - subcontrol-position: top right; - width: 12px; - border-left: 1px solid #32414B; -} - -QComboBox::down-arrow { - image: url(":/qss_icons/rc/arrow_down_disabled.png"); - height: 8px; - width: 8px; -} - -QComboBox::down-arrow:on, QComboBox::down-arrow:hover, QComboBox::down-arrow:focus { - image: url(":/qss_icons/rc/arrow_down.png"); -} - -/* QSlider ---------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qslider - ---------------------------------------------------------------------------- */ -QSlider:disabled { - background: #19232D; -} - -QSlider:focus { - border: none; -} - -QSlider::groove:horizontal { - background: #32414B; - border: 1px solid #32414B; - height: 4px; - margin: 0px; - border-radius: 4px; -} - -QSlider::groove:vertical { - background: #32414B; - border: 1px solid #32414B; - width: 4px; - margin: 0px; - border-radius: 4px; -} - -QSlider::add-page:vertical { - background: #1464A0; - border: 1px solid #32414B; - width: 4px; - margin: 0px; - border-radius: 4px; -} - -QSlider::add-page:vertical :disabled { - background: #14506E; -} - -QSlider::sub-page:horizontal { - background: #1464A0; - border: 1px solid #32414B; - height: 4px; - margin: 0px; - border-radius: 4px; -} - -QSlider::sub-page:horizontal:disabled { - background: #14506E; -} - -QSlider::handle:horizontal { - background: #787878; - border: 1px solid #32414B; - width: 8px; - height: 8px; - margin: -8px 0px; - border-radius: 4px; -} - -QSlider::handle:horizontal:hover { - background: #148CD2; - border: 1px solid #148CD2; -} - -QSlider::handle:horizontal:focus { - border: 1px solid #1464A0; -} - -QSlider::handle:vertical { - background: #787878; - border: 1px solid #32414B; - width: 8px; - height: 8px; - margin: 0 -8px; - border-radius: 4px; -} - -QSlider::handle:vertical:hover { - background: #148CD2; - border: 1px solid #148CD2; -} - -QSlider::handle:vertical:focus { - border: 1px solid #1464A0; -} - -/* QLineEdit -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qlineedit - ---------------------------------------------------------------------------- */ -QLineEdit { - background-color: #19232D; - padding-top: 2px; - /* This QLineEdit fix 103, 111 */ - padding-bottom: 2px; - /* This QLineEdit fix 103, 111 */ - padding-left: 4px; - padding-right: 4px; - border-style: solid; - border: 1px solid #32414B; - border-radius: 4px; - color: #F0F0F0; -} - -QLineEdit:disabled { - background-color: #19232D; - color: #787878; -} - -QLineEdit:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QLineEdit:focus { - border: 1px solid #1464A0; -} - -QLineEdit:selected { - background-color: #1464A0; - color: #32414B; -} - -/* QTabWiget -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtabwidget-and-qtabbar - ---------------------------------------------------------------------------- */ -QTabWidget { - padding: 2px; - selection-background-color: #32414B; -} - -QTabWidget QWidget { - /* Fixes #189 */ - border-radius: 4px; -} - -QTabWidget::pane { - border: 1px solid #32414B; - border-radius: 4px; - margin: 0px; - /* Fixes double border inside pane with pyqt5 */ - padding: 0px; -} - -QTabWidget::pane:selected { - background-color: #32414B; - border: 1px solid #1464A0; -} - -/* QTabBar ---------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtabwidget-and-qtabbar - ---------------------------------------------------------------------------- */ -QTabBar { - qproperty-drawBase: 0; - border-radius: 4px; - margin: 0px; - padding: 2px; - border: 0; - /* left: 5px; move to the right by 5px - removed for fix */ -} - -QTabBar::close-button { - border: 0; - margin: 2px; - padding: 2px; - image: url(":/qss_icons/rc/window_close.png"); -} - -QTabBar::close-button:hover { - image: url(":/qss_icons/rc/window_close_focus.png"); -} - -QTabBar::close-button:pressed { - image: url(":/qss_icons/rc/window_close_pressed.png"); -} - -/* QTabBar::tab - selected ------------------------------------------------ - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtabwidget-and-qtabbar - ---------------------------------------------------------------------------- */ -QTabBar::tab { - /* !selected and disabled ----------------------------------------- */ - /* selected ------------------------------------------------------- */ -} - -QTabBar::tab:top:selected:disabled { - border-bottom: 3px solid #14506E; - color: #787878; - background-color: #32414B; -} - -QTabBar::tab:bottom:selected:disabled { - border-top: 3px solid #14506E; - color: #787878; - background-color: #32414B; -} - -QTabBar::tab:left:selected:disabled { - border-right: 3px solid #14506E; - color: #787878; - background-color: #32414B; -} - -QTabBar::tab:right:selected:disabled { - border-left: 3px solid #14506E; - color: #787878; - background-color: #32414B; -} - -QTabBar::tab:top:!selected:disabled { - border-bottom: 3px solid #19232D; - color: #787878; - background-color: #19232D; -} - -QTabBar::tab:bottom:!selected:disabled { - border-top: 3px solid #19232D; - color: #787878; - background-color: #19232D; -} - -QTabBar::tab:left:!selected:disabled { - border-right: 3px solid #19232D; - color: #787878; - background-color: #19232D; -} - -QTabBar::tab:right:!selected:disabled { - border-left: 3px solid #19232D; - color: #787878; - background-color: #19232D; -} - -QTabBar::tab:top:!selected { - border-bottom: 2px solid #19232D; - margin-top: 2px; -} - -QTabBar::tab:bottom:!selected { - border-top: 2px solid #19232D; - margin-bottom: 3px; -} - -QTabBar::tab:left:!selected { - border-left: 2px solid #19232D; - margin-right: 2px; -} - -QTabBar::tab:right:!selected { - border-right: 2px solid #19232D; - margin-left: 2px; -} - -QTabBar::tab:top { - background-color: #32414B; - color: #F0F0F0; - margin-left: 2px; - padding-left: 4px; - padding-right: 4px; - padding-top: 2px; - padding-bottom: 2px; - min-width: 5px; - border-bottom: 3px solid #32414B; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} - -QTabBar::tab:top:selected { - background-color: #505F69; - color: #F0F0F0; - border-bottom: 3px solid #1464A0; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} - -QTabBar::tab:top:!selected:hover { - border: 1px solid #148CD2; - border-bottom: 3px solid #148CD2; - /* Fixes spyder-ide/spyder#9766 */ - padding-left: 4px; - padding-right: 4px; -} - -QTabBar::tab:bottom { - color: #F0F0F0; - border-top: 3px solid #32414B; - background-color: #32414B; - margin-left: 2px; - padding-left: 4px; - padding-right: 4px; - padding-top: 2px; - padding-bottom: 2px; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - min-width: 5px; -} - -QTabBar::tab:bottom:selected { - color: #F0F0F0; - background-color: #505F69; - border-top: 3px solid #1464A0; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; -} - -QTabBar::tab:bottom:!selected:hover { - border: 1px solid #148CD2; - border-top: 3px solid #148CD2; - /* Fixes spyder-ide/spyder#9766 */ - padding-left: 4px; - padding-right: 4px; -} - -QTabBar::tab:left { - color: #F0F0F0; - background-color: #32414B; - margin-top: 2px; - padding-left: 2px; - padding-right: 2px; - padding-top: 4px; - padding-bottom: 4px; - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; - min-height: 5px; -} - -QTabBar::tab:left:selected { - color: #F0F0F0; - background-color: #505F69; - border-right: 3px solid #1464A0; -} - -QTabBar::tab:left:!selected:hover { - border: 1px solid #148CD2; - border-right: 3px solid #148CD2; - padding: 0px; -} - -QTabBar::tab:right { - color: #F0F0F0; - background-color: #32414B; - margin-top: 2px; - padding-left: 2px; - padding-right: 2px; - padding-top: 4px; - padding-bottom: 4px; - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - min-height: 5px; -} - -QTabBar::tab:right:selected { - color: #F0F0F0; - background-color: #505F69; - border-left: 3px solid #1464A0; -} - -QTabBar::tab:right:!selected:hover { - border: 1px solid #148CD2; - border-left: 3px solid #148CD2; - padding: 0px; -} - -QTabBar QToolButton { - /* Fixes #136 */ - background-color: #32414B; - height: 12px; - width: 12px; -} - -QTabBar QToolButton:pressed { - background-color: #32414B; -} - -QTabBar QToolButton:pressed:hover { - border: 1px solid #148CD2; -} - -QTabBar QToolButton::left-arrow:enabled { - image: url(":/qss_icons/rc/arrow_left.png"); -} - -QTabBar QToolButton::left-arrow:disabled { - image: url(":/qss_icons/rc/arrow_left_disabled.png"); -} - -QTabBar QToolButton::right-arrow:enabled { - image: url(":/qss_icons/rc/arrow_right.png"); -} - -QTabBar QToolButton::right-arrow:disabled { - image: url(":/qss_icons/rc/arrow_right_disabled.png"); -} - -/* QDockWiget ------------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QDockWidget { - outline: 1px solid #32414B; - background-color: #19232D; - border: 1px solid #32414B; - border-radius: 4px; - titlebar-close-icon: url(":/qss_icons/rc/window_close.png"); - titlebar-normal-icon: url(":/qss_icons/rc/window_undock.png"); -} - -QDockWidget::title { - /* Better size for title bar */ - padding: 6px; - spacing: 4px; - border: none; - background-color: #32414B; -} - -QDockWidget::close-button { - background-color: #32414B; - border-radius: 4px; - border: none; -} - -QDockWidget::close-button:hover { - image: url(":/qss_icons/rc/window_close_focus.png"); -} - -QDockWidget::close-button:pressed { - image: url(":/qss_icons/rc/window_close_pressed.png"); -} - -QDockWidget::float-button { - background-color: #32414B; - border-radius: 4px; - border: none; -} - -QDockWidget::float-button:hover { - image: url(":/qss_icons/rc/window_undock_focus.png"); -} - -QDockWidget::float-button:pressed { - image: url(":/qss_icons/rc/window_undock_pressed.png"); -} - -/* QTreeView QListView QTableView ----------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtreeview -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qlistview -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtableview - ---------------------------------------------------------------------------- */ -QTreeView:branch:selected, QTreeView:branch:hover { - background: url(":/qss_icons/rc/transparent.png"); -} - -QTreeView:branch:has-siblings:!adjoins-item { - border-image: url(":/qss_icons/rc/branch_line.png") 0; -} - -QTreeView:branch:has-siblings:adjoins-item { - border-image: url(":/qss_icons/rc/branch_more.png") 0; -} - -QTreeView:branch:!has-children:!has-siblings:adjoins-item { - border-image: url(":/qss_icons/rc/branch_end.png") 0; -} - -QTreeView:branch:has-children:!has-siblings:closed, QTreeView:branch:closed:has-children:has-siblings { - border-image: none; - image: url(":/qss_icons/rc/branch_closed.png"); -} - -QTreeView:branch:open:has-children:!has-siblings, QTreeView:branch:open:has-children:has-siblings { - border-image: none; - image: url(":/qss_icons/rc/branch_open.png"); -} - -QTreeView:branch:has-children:!has-siblings:closed:hover, QTreeView:branch:closed:has-children:has-siblings:hover { - image: url(":/qss_icons/rc/branch_closed_focus.png"); -} - -QTreeView:branch:open:has-children:!has-siblings:hover, QTreeView:branch:open:has-children:has-siblings:hover { - image: url(":/qss_icons/rc/branch_open_focus.png"); -} - -QTreeView::indicator:checked, -QListView::indicator:checked { - image: url(":/qss_icons/rc/checkbox_checked.png"); -} - -QTreeView::indicator:checked:hover, QTreeView::indicator:checked:focus, QTreeView::indicator:checked:pressed, -QListView::indicator:checked:hover, -QListView::indicator:checked:focus, -QListView::indicator:checked:pressed { - image: url(":/qss_icons/rc/checkbox_checked_focus.png"); -} - -QTreeView::indicator:unchecked, -QListView::indicator:unchecked { - image: url(":/qss_icons/rc/checkbox_unchecked.png"); -} - -QTreeView::indicator:unchecked:hover, QTreeView::indicator:unchecked:focus, QTreeView::indicator:unchecked:pressed, -QListView::indicator:unchecked:hover, -QListView::indicator:unchecked:focus, -QListView::indicator:unchecked:pressed { - image: url(":/qss_icons/rc/checkbox_unchecked_focus.png"); -} - -QTreeView::indicator:indeterminate, -QListView::indicator:indeterminate { - image: url(":/qss_icons/rc/checkbox_indeterminate.png"); -} - -QTreeView::indicator:indeterminate:hover, QTreeView::indicator:indeterminate:focus, QTreeView::indicator:indeterminate:pressed, -QListView::indicator:indeterminate:hover, -QListView::indicator:indeterminate:focus, -QListView::indicator:indeterminate:pressed { - image: url(":/qss_icons/rc/checkbox_indeterminate_focus.png"); -} - -QTreeView, -QListView, -QTableView, -QColumnView { - background-color: #19232D; - border: 1px solid #32414B; - color: #F0F0F0; - gridline-color: #32414B; - border-radius: 4px; -} - -QTreeView:disabled, -QListView:disabled, -QTableView:disabled, -QColumnView:disabled { - background-color: #19232D; - color: #787878; -} - -QTreeView:selected, -QListView:selected, -QTableView:selected, -QColumnView:selected { - background-color: #1464A0; - color: #32414B; -} - -QTreeView:hover, -QListView:hover, -QTableView:hover, -QColumnView:hover { - background-color: #19232D; - border: 1px solid #148CD2; -} - -QTreeView::item:pressed, -QListView::item:pressed, -QTableView::item:pressed, -QColumnView::item:pressed { - background-color: #1464A0; -} - -QTreeView::item:selected:hover, -QListView::item:selected:hover, -QTableView::item:selected:hover, -QColumnView::item:selected:hover { - background: #1464A0; - color: #19232D; -} - -QTreeView::item:selected:active, -QListView::item:selected:active, -QTableView::item:selected:active, -QColumnView::item:selected:active { - background-color: #1464A0; -} - -QTreeView::item:!selected:hover, -QListView::item:!selected:hover, -QTableView::item:!selected:hover, -QColumnView::item:!selected:hover { - outline: 0; - color: #148CD2; - background-color: #32414B; -} - -QTableCornerButton::section { - background-color: #19232D; - border: 1px transparent #32414B; - border-radius: 0px; -} - -/* QHeaderView ------------------------------------------------------------ - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qheaderview - ---------------------------------------------------------------------------- */ -QHeaderView { - background-color: #32414B; - border: 0px transparent #32414B; - padding: 0px; - margin: 0px; - border-radius: 0px; -} - -QHeaderView:disabled { - background-color: #32414B; - border: 1px transparent #32414B; - padding: 2px; -} - -QHeaderView::section { - background-color: #32414B; - color: #F0F0F0; - padding: 2px; - border-radius: 0px; - text-align: left; -} - -QHeaderView::section:checked { - color: #F0F0F0; - background-color: #1464A0; -} - -QHeaderView::section:checked:disabled { - color: #787878; - background-color: #14506E; -} - -QHeaderView::section::horizontal { - padding-left: 4px; - padding-right: 4px; - border-left: 1px solid #19232D; -} - -QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one { - border-left: 1px solid #32414B; -} - -QHeaderView::section::horizontal:disabled { - color: #787878; -} - -QHeaderView::section::vertical { - padding-left: 4px; - padding-right: 4px; - border-top: 1px solid #19232D; -} - -QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one { - border-top: 1px solid #32414B; -} - -QHeaderView::section::vertical:disabled { - color: #787878; -} - -QHeaderView::down-arrow { - /* Those settings (border/width/height/background-color) solve bug */ - /* transparent arrow background and size */ - background-color: #32414B; - border: none; - height: 12px; - width: 12px; - padding-left: 2px; - padding-right: 2px; - image: url(":/qss_icons/rc/arrow_down.png"); -} - -QHeaderView::up-arrow { - background-color: #32414B; - border: none; - height: 12px; - width: 12px; - padding-left: 2px; - padding-right: 2px; - image: url(":/qss_icons/rc/arrow_up.png"); -} - -/* QToolBox -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbox - ---------------------------------------------------------------------------- */ -QToolBox { - padding: 0px; - border: 0px; - border: 1px solid #32414B; -} - -QToolBox:selected { - padding: 0px; - border: 2px solid #1464A0; -} - -QToolBox::tab { - background-color: #19232D; - border: 1px solid #32414B; - color: #F0F0F0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -QToolBox::tab:disabled { - color: #787878; -} - -QToolBox::tab:selected { - background-color: #505F69; - border-bottom: 2px solid #1464A0; -} - -QToolBox::tab:selected:disabled { - background-color: #32414B; - border-bottom: 2px solid #14506E; -} - -QToolBox::tab:!selected { - background-color: #32414B; - border-bottom: 2px solid #32414B; -} - -QToolBox::tab:!selected:disabled { - background-color: #19232D; -} - -QToolBox::tab:hover { - border-color: #148CD2; - border-bottom: 2px solid #148CD2; -} - -QToolBox QScrollArea QWidget QWidget { - padding: 0px; - border: 0px; - background-color: #19232D; -} - -/* QFrame ----------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qframe -https://doc.qt.io/qt-5/qframe.html#-prop -https://doc.qt.io/qt-5/qframe.html#details -https://stackoverflow.com/questions/14581498/qt-stylesheet-for-hline-vline-color - ---------------------------------------------------------------------------- */ -/* (dot) .QFrame fix #141, #126, #123 */ -.QFrame { - border-radius: 4px; - border: 1px solid #32414B; - /* No frame */ - /* HLine */ - /* HLine */ -} - -.QFrame[frameShape="0"] { - border-radius: 4px; - border: 1px transparent #32414B; -} - -.QFrame[frameShape="4"] { - max-height: 2px; - border: none; - background-color: #32414B; -} - -.QFrame[frameShape="5"] { - max-width: 2px; - border: none; - background-color: #32414B; -} - -/* QSplitter -------------------------------------------------------------- - -https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qsplitter - ---------------------------------------------------------------------------- */ -QSplitter { - background-color: #32414B; - spacing: 0px; - padding: 0px; - margin: 0px; -} - -QSplitter::handle { - background-color: #32414B; - border: 0px solid #19232D; - spacing: 0px; - padding: 1px; - margin: 0px; -} - -QSplitter::handle:hover { - background-color: #787878; -} - -QSplitter::handle:horizontal { - width: 5px; - image: url(":/qss_icons/rc/line_vertical.png"); -} - -QSplitter::handle:vertical { - height: 5px; - image: url(":/qss_icons/rc/line_horizontal.png"); -} - -/* QDateEdit -------------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QDateEdit { - selection-background-color: #1464A0; - border-style: solid; - border: 1px solid #32414B; - border-radius: 4px; - /* This fixes 103, 111 */ - padding-top: 2px; - /* This fixes 103, 111 */ - padding-bottom: 2px; - padding-left: 4px; - padding-right: 4px; - min-width: 10px; -} - -QDateEdit:on { - selection-background-color: #1464A0; -} - -QDateEdit::drop-down { - subcontrol-origin: padding; - subcontrol-position: top right; - width: 12px; - border-left: 1px solid #32414B; -} - -QDateEdit::down-arrow { - image: url(":/qss_icons/rc/arrow_down_disabled.png"); - height: 8px; - width: 8px; -} - -QDateEdit::down-arrow:on, QDateEdit::down-arrow:hover, QDateEdit::down-arrow:focus { - image: url(":/qss_icons/rc/arrow_down.png"); -} - -QDateEdit QAbstractItemView { - background-color: #19232D; - border-radius: 4px; - border: 1px solid #32414B; - selection-background-color: #1464A0; -} - -/* QAbstractView ---------------------------------------------------------- - ---------------------------------------------------------------------------- */ -QAbstractView:hover { - border: 1px solid #148CD2; - color: #F0F0F0; -} - -QAbstractView:selected { - background: #1464A0; - color: #32414B; -} - -/* PlotWidget ------------------------------------------------------------- - ---------------------------------------------------------------------------- */ -PlotWidget { - /* Fix cut labels in plots #134 */ - padding: 0px; -} diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/synthetic_env.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/synthetic_env.py index 86e4b68488..6a3340788b 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/synthetic_env.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/synthetic_env.py @@ -384,8 +384,7 @@ def stash_env(_SYNTH_ENV_DICT = OrderedDict()): # changed to just make the fallback what is set in boostrap # so now it's less of a fallnack and more correct if not # explicitly set - _LY_PROJECT = os.getenv(ENVAR_LY_PROJECT, - get_current_project(_LY_DEV)) + _LY_PROJECT = os.getenv(ENVAR_LY_PROJECT) _SYNTH_ENV_DICT[ENVAR_LY_PROJECT] = _LY_PROJECT _LY_BUILD_DIR_NAME = os.getenv(ENVAR_LY_BUILD_DIR_NAME, diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py index c62571b2f5..0fa0b6ee67 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py @@ -109,11 +109,11 @@ def init_ly_pyside(LY_DEV=None): 'bin', 'profile').resolve() - # allows to retreive from settings.QTFORPYTHON_PATH - from azpy.constants import STR_QTFORPYTHON_PATH # a path string constructor - QTFORPYTHON_PATH = Path(STR_QTFORPYTHON_PATH.format(LY_DEV)).resolve() - os.environ["DYNACONF_QTFORPYTHON_PATH"] = str(QTFORPYTHON_PATH) - site.addsitedir(str(QTFORPYTHON_PATH)) # PYTHONPATH + # # allows to retreive from settings.QTFORPYTHON_PATH + # from azpy.constants import STR_QTFORPYTHON_PATH # a path string constructor + # QTFORPYTHON_PATH = Path(STR_QTFORPYTHON_PATH.format(LY_DEV)).resolve() + # os.environ["DYNACONF_QTFORPYTHON_PATH"] = str(QTFORPYTHON_PATH) + # site.addsitedir(str(QTFORPYTHON_PATH)) # PYTHONPATH QT_PLUGIN_PATH = Path.joinpath(LY_BIN_PATH, 'EditorPlugins').resolve() @@ -131,15 +131,15 @@ def init_ly_pyside(LY_DEV=None): # add Qt binaries to the Windows path to handle findings DLL file dependencies if sys.platform.startswith('win'): - path = os.environ['PATH'] - newPath = '' - newPath += str(LY_BIN_PATH) + os.pathsep - newPath += str(Path.joinpath(QTFORPYTHON_PATH, - 'shiboken2').resolve()) + os.pathsep - newPath += str(Path.joinpath(QTFORPYTHON_PATH, - 'PySide2').resolve()) + os.pathsep - newPath += path - os.environ['PATH']=newPath + # path = os.environ['PATH'] + # newPath = '' + # newPath += str(LY_BIN_PATH) + os.pathsep + # newPath += str(Path.joinpath(QTFORPYTHON_PATH, + # 'shiboken2').resolve()) + os.pathsep + # newPath += str(Path.joinpath(QTFORPYTHON_PATH, + # 'PySide2').resolve()) + os.pathsep + # newPath += path + # os.environ['PATH']=newPath _LOGGER.debug('PySide2 bootstrapped PATH for Windows.') try: @@ -223,8 +223,8 @@ os.environ["DYNACONF_DCCSI_DEV_MODE"] = str(_DCCSI_DEV_MODE) _LY_DEV = azpy.config_utils.get_stub_check_path(in_path=_DCCSIG_PATH, check_stub='engine.json') os.environ["DYNACONF_LY_DEV"] = str(_LY_DEV.resolve()) -_LY_PROJECT = azpy.config_utils.get_current_project(_LY_DEV) -os.environ["DYNACONF_LY_PROJECT"] = _LY_PROJECT +_LY_PROJECT = azpy.config_utils.get_current_project() +os.environ["DYNACONF_LY_PROJECT"] = str(_LY_PROJECT.resolve()) _LY_PROJECT_PATH = Path(_LY_DEV, _LY_PROJECT) os.environ["DYNACONF_LY_PROJECT_PATH"] = str(_LY_PROJECT_PATH) os.environ["DYNACONF_DCCSIG_PATH"] = str(_DCCSIG_PATH) @@ -319,7 +319,7 @@ if __name__ == '__main__': settings.setenv() # doing this will add/set the additional DYNACONF_ envars - _LOGGER.info('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH)) + #_LOGGER.info('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH)) _LOGGER.info('LY_BIN_PATH: {}'.format(settings.LY_BIN_PATH)) _LOGGER.info('QT_PLUGIN_PATH: {}'.format(settings.QT_PLUGIN_PATH)) _LOGGER.info('QT_QPA_PLATFORM_PLUGIN_PATH: {}'.format(settings.QT_QPA_PLATFORM_PLUGIN_PATH)) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json new file mode 100644 index 0000000000..a867fe0f0f --- /dev/null +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json @@ -0,0 +1,14 @@ +{ + "gem_name": "DccScriptingInterface", + "display_name": "Atom DccScriptingInterface (DCCsi)", + "summary": "A python framework for working with various DCC tools and workflows.", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "DCC", + "Digital", + "Content", + "Creation" + ] +} diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/requirements.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/requirements.txt index 2f7626addb..1536b79f3d 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/requirements.txt +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/requirements.txt @@ -36,6 +36,16 @@ unipath==1.1 \ --hash=sha256:09839adcc72e8a24d4f76d63656f30b5a1f721fc40c9bcd79d8c67bdd8b47dae \ --hash=sha256:e6257e508d8abbfb6ddd8ec357e33589f1f48b1599127f23b017124d90b0fff7 # via -r requirements.txt +qdarkstyle==3.0.2 \ + --hash=sha256:55d149cf5f40ee297397f1818e091118cefb855a4a9c5c38566c47acd2d8c7ae \ + --hash=sha256:7c791535cc20b3cc1e8e1bf6b88dabe53cb0615983df702be83597e73ada2558 + # via -r c:\temp\requirements.txt +qtpy==1.9.0 \ + --hash=sha256:2db72c44b55d0fe1407be8fba35c838ad0d6d3bb81f23007886dc1fc0f459c8d \ + --hash=sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea + # via + # -r c:\temp\requirements.txt + # qdarkstyle wincertstore==0.2 \ --hash=sha256:22d5eebb52df88a8d4014d5cf6d1b6c3a5d469e6c3b2e2854f3a003e48872356 \ --hash=sha256:780bd1557c9185c15d9f4221ea7f905cb20b93f7151ca8ccaed9714dce4b327a diff --git a/Gems/AtomLyIntegration/gem.json b/Gems/AtomLyIntegration/gem.json index 0971ad53c2..4f587a8806 100644 --- a/Gems/AtomLyIntegration/gem.json +++ b/Gems/AtomLyIntegration/gem.json @@ -1,3 +1,5 @@ { - "gem_name": "AtomLyIntegration" + "gem_name": "AtomLyIntegration", + "display_name": "Atom O3DE Integration", + "summary": "Collection of module targets for integrating Atom with the O3DE engine" } diff --git a/AutomatedTesting/Gem/Code/Platform/Android/tool_dependencies.cmake b/Gems/AtomTressFX/CMakeLists.txt similarity index 100% rename from AutomatedTesting/Gem/Code/Platform/Android/tool_dependencies.cmake rename to Gems/AtomTressFX/CMakeLists.txt diff --git a/Gems/AudioEngineWwise/Code/CMakeLists.txt b/Gems/AudioEngineWwise/Code/CMakeLists.txt index 5ea6a6d461..f90064908a 100644 --- a/Gems/AudioEngineWwise/Code/CMakeLists.txt +++ b/Gems/AudioEngineWwise/Code/CMakeLists.txt @@ -93,6 +93,9 @@ ly_add_target( Gem::AudioEngineWwise.Static ) +# we'll load the above "Gem::AudioEngineWwise" module in clients. +ly_create_alias(NAME AudioEngineWwise.Clients NAMESPACE Gem TARGETS Gem::AudioEngineWwise) + ################################################################################ # Tests ################################################################################ @@ -206,6 +209,8 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AssetBuilderSDK Gem::AudioEngineWwise.Static Gem::AudioSystem.Editor + RUNTIME_DEPENDENCIES + Gem::AudioSystem.Editor ) ly_add_target( @@ -230,6 +235,10 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Gem::AudioSystem.Editor ) + # by default, we'll load the above "Gem::AudioEngineWwise.Editor" module in builders and tools. + ly_create_alias(NAME AudioEngineWwise.Builders NAMESPACE Gem TARGETS Gem::AudioEngineWwise.Editor) + ly_create_alias(NAME AudioEngineWwise.Tools NAMESPACE Gem TARGETS Gem::AudioEngineWwise.Editor) + if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_add_target( NAME AudioEngineWwise.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} diff --git a/Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp b/Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp index f7b54a44d9..b08aa6fa1d 100644 --- a/Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp +++ b/Gems/AudioEngineWwise/Code/Source/Builder/AudioControlBuilderWorker.cpp @@ -57,11 +57,11 @@ namespace AudioControlBuilder { atlPlatform = "windows"; } - else if (platform == "es3") + else if (platform == "android") { atlPlatform = "android"; } - else if (platform == "osx_gl") + else if (platform == "mac") { atlPlatform = "mac"; } diff --git a/Gems/AudioEngineWwise/Code/Source/Engine/Config_wwise.h b/Gems/AudioEngineWwise/Code/Source/Engine/Config_wwise.h index ad2ba28269..7fabfb75b3 100644 --- a/Gems/AudioEngineWwise/Code/Source/Engine/Config_wwise.h +++ b/Gems/AudioEngineWwise/Code/Source/Engine/Config_wwise.h @@ -46,7 +46,7 @@ namespace Audio::Wwise ~PlatformMapping() = default; // Serialized Data... - AZStd::string m_assetPlatform; // LY Asset Platform name (i.e. "pc", "osx_gl", "es3", ...) + AZStd::string m_assetPlatform; // LY Asset Platform name (i.e. "pc", "mac", "android", ...) AZStd::string m_altAssetPlatform; // Some platforms can be run using a different asset platform. Useful for builder worker. AZStd::string m_enginePlatform; // LY Engine Platform name (i.e. "Windows", "Mac", "Android", ...) AZStd::string m_wwisePlatform; // Wwise Platform name (i.e. "Windows", "Mac", "Android", ...) diff --git a/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Android/wwise_config_android.json b/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Android/wwise_config_android.json index 22cc632cbd..38f6ff142e 100644 --- a/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Android/wwise_config_android.json +++ b/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Android/wwise_config_android.json @@ -1,5 +1,5 @@ { - "assetPlatform": "es3", + "assetPlatform": "android", "altAssetPlatform": "", "enginePlatform": "Android", "wwisePlatform": "Android", diff --git a/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Mac/wwise_config_mac.json b/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Mac/wwise_config_mac.json index 4069d8add7..a996b85150 100644 --- a/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Mac/wwise_config_mac.json +++ b/Gems/AudioEngineWwise/Tools/WwiseConfig/Platform/Mac/wwise_config_mac.json @@ -1,5 +1,5 @@ { - "assetPlatform": "osx_gl", + "assetPlatform": "mac", "altAssetPlatform": "", "enginePlatform": "Mac", "wwisePlatform": "Mac", diff --git a/Gems/AudioSystem/Code/CMakeLists.txt b/Gems/AudioSystem/Code/CMakeLists.txt index 8a6f2c417e..3963a71ad0 100644 --- a/Gems/AudioSystem/Code/CMakeLists.txt +++ b/Gems/AudioSystem/Code/CMakeLists.txt @@ -61,22 +61,8 @@ ly_add_target( Gem::AudioSystem.Static ) -################################################################################ -# Server -################################################################################ -if (PAL_TRAIT_BUILD_SERVER_SUPPORTED) - # Stub gem for server. Audio system is client only - ly_add_target( - NAME AudioSystem.Server GEM_MODULE - - NAMESPACE Gem - FILES_CMAKE - audiosystem_stub_files.cmake - BUILD_DEPENDENCIES - PRIVATE - AZ::AzCore - ) -endif () +# AudioSystem should use the above target on clients. +ly_create_alias(NAME AudioSystem.Clients NAMESPACE Gem TARGETS Gem::AudioSystem) ################################################################################ # Tests @@ -101,7 +87,8 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzFramework Legacy::CryCommon Gem::AudioSystem.Static - Gem::LmbrCentral + RUNTIME_DEPENDENCIES + Gem::LmbrCentral ) ly_add_googletest( NAME Gem::AudioSystem.Tests @@ -230,6 +217,10 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Gem::AudioSystem.Editor.Static ) + # use the above "Editor" target in tools and builders: + ly_create_alias(NAME AudioSystem.Tools NAMESPACE Gem TARGETS Gem::AudioSystem.Editor) + ly_create_alias(NAME AudioSystem.Builders NAMESPACE Gem TARGETS Gem::AudioSystem.Editor) + if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_add_target( NAME AudioSystem.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} @@ -253,3 +244,6 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) ) endif() endif () + + + diff --git a/Gems/AutomatedLauncherTesting/Code/CMakeLists.txt b/Gems/AutomatedLauncherTesting/Code/CMakeLists.txt index 551f76da02..6215ae7697 100644 --- a/Gems/AutomatedLauncherTesting/Code/CMakeLists.txt +++ b/Gems/AutomatedLauncherTesting/Code/CMakeLists.txt @@ -42,3 +42,7 @@ ly_add_target( RUNTIME_DEPENDENCIES Gem::LmbrCentral ) + +# servers and clients use the above module. +ly_create_alias(NAME AutomatedLauncherTesting.Servers NAMESPACE Gem TARGETS Gem::AutomatedLauncherTesting) +ly_create_alias(NAME AutomatedLauncherTesting.Clients NAMESPACE Gem TARGETS Gem::AutomatedLauncherTesting) diff --git a/Gems/Blast/Code/CMakeLists.txt b/Gems/Blast/Code/CMakeLists.txt index 6c90357364..143e3af095 100644 --- a/Gems/Blast/Code/CMakeLists.txt +++ b/Gems/Blast/Code/CMakeLists.txt @@ -59,6 +59,11 @@ ly_add_target( Gem::PhysX ) +# clients and servers use the above Gem module. +ly_create_alias(NAME Blast.Servers NAMESPACE Gem TARGETS Gem::Blast) +ly_create_alias(NAME Blast.Clients NAMESPACE Gem TARGETS Gem::Blast) + + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( @@ -110,6 +115,9 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::PhysX.Editor ) + # tools and builders use the above module. + ly_create_alias(NAME Blast.Tools NAMESPACE Gem TARGETS Gem::Blast.Editor) + ly_create_alias(NAME Blast.Builders NAMESPACE Gem TARGETS Gem::Blast.Editor) endif() ################################################################################ diff --git a/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp b/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp index 6c4c78c412..8f82f3366f 100644 --- a/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp +++ b/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp @@ -151,8 +151,6 @@ namespace Blast colliderConfiguration.m_position = transform.GetTranslation(); colliderConfiguration.m_rotation = transform.GetRotation(); colliderConfiguration.m_isExclusive = true; - colliderConfiguration.m_materialSelection.SetMaterialLibrary( - AZ::Interface::Get()->GetDefaultMaterialLibrary()->GetId()); colliderConfiguration.m_materialSelection.SetMaterialId(material); colliderConfiguration.m_collisionGroupId = actorConfiguration.m_collisionGroupId; colliderConfiguration.m_collisionLayer = actorConfiguration.m_collisionLayer; diff --git a/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp b/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp index 0f2668442c..1da9663b8f 100644 --- a/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -265,16 +266,29 @@ namespace Blast auto solverPtr = Nv::Blast::ExtStressSolver::create( const_cast(*m_family->GetTkFamily()->getFamilyLL()), stressSolverSettings); m_solver = physx::unique_ptr(solverPtr); - Physics::MaterialFromAssetConfiguration material; - AZ::Interface::Get()->GetDefaultMaterialLibrary()->GetDataForMaterialId( - m_physicsMaterialId, material); - m_solver->setAllNodesInfoFromLL(material.m_configuration.m_density); + + AZStd::shared_ptr physicsMaterial; + Physics::PhysicsMaterialRequestBus::BroadcastResult( + physicsMaterial, + &Physics::PhysicsMaterialRequestBus::Events::GetMaterialById, + m_physicsMaterialId); + if (!physicsMaterial) + { + AZ_Warning("BlastFamilyComponent", false, "Material Id %s was not found, using default material instead.", + m_physicsMaterialId.GetUuid().ToString().c_str()); + + Physics::PhysicsMaterialRequestBus::BroadcastResult( + physicsMaterial, + &Physics::PhysicsMaterialRequestBus::Events::GetGenericDefaultMaterial); + AZ_Assert(physicsMaterial, "BlastFamilyComponent: Invalid default physics material"); + } + m_solver->setAllNodesInfoFromLL(physicsMaterial->GetDensity()); // Create damage and actor render managers m_damageManager = AZStd::make_unique(blastMaterial, m_family->GetActorTracker()); m_actorRenderManager = AZStd::make_unique( AZ::RPI::Scene::GetFeatureProcessorForEntity(GetEntityId()), - m_meshDataComponent, GetEntityId(), m_blastAsset->GetPxAsset()->getChunkCount(), transform.GetScale()); + m_meshDataComponent, GetEntityId(), m_blastAsset->GetPxAsset()->getChunkCount(), AZ::Vector3(transform.GetUniformScale())); // Spawn the family m_family->Spawn(transform); diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp index 9241449483..873ef7248d 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp @@ -131,6 +131,6 @@ namespace Blast AZ::Data::AssetId EditorBlastFamilyComponent::GetPhysicsMaterialLibraryAssetId() const { - return AZ::Interface::Get()->GetDefaultMaterialLibrary()->GetId(); + return AZ::Interface::Get()->GetConfiguration()->m_materialLibraryAsset.GetId(); } } // namespace Blast diff --git a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h index bd03a442d9..ca78623a8a 100644 --- a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h +++ b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h @@ -644,19 +644,7 @@ namespace Blast MOCK_METHOD0(GetLocalX, float()); MOCK_METHOD0(GetLocalY, float()); MOCK_METHOD0(GetLocalZ, float()); - MOCK_METHOD1(SetRotation, void(const AZ::Vector3&)); - MOCK_METHOD1(SetRotationX, void(float)); - MOCK_METHOD1(SetRotationY, void(float)); - MOCK_METHOD1(SetRotationZ, void(float)); - MOCK_METHOD1(SetRotationQuaternion, void(const AZ::Quaternion&)); - MOCK_METHOD1(RotateByX, void(float)); - MOCK_METHOD1(RotateByY, void(float)); - MOCK_METHOD1(RotateByZ, void(float)); - MOCK_METHOD0(GetRotationEulerRadians, AZ::Vector3()); - MOCK_METHOD0(GetRotationQuaternion, AZ::Quaternion()); - MOCK_METHOD0(GetRotationX, float()); - MOCK_METHOD0(GetRotationY, float()); - MOCK_METHOD0(GetRotationZ, float()); + MOCK_METHOD1(SetWorldRotationQuaternion, void(const AZ::Quaternion&)); MOCK_METHOD0(GetWorldRotation, AZ::Vector3()); MOCK_METHOD0(GetWorldRotationQuaternion, AZ::Quaternion()); MOCK_METHOD1(SetLocalRotation, void(const AZ::Vector3&)); @@ -666,9 +654,7 @@ namespace Blast MOCK_METHOD1(RotateAroundLocalZ, void(float)); MOCK_METHOD0(GetLocalRotation, AZ::Vector3()); MOCK_METHOD0(GetLocalRotationQuaternion, AZ::Quaternion()); - MOCK_METHOD1(SetLocalScale, void(const AZ::Vector3&)); MOCK_METHOD0(GetLocalScale, AZ::Vector3()); - MOCK_METHOD0(GetWorldScale, AZ::Vector3()); MOCK_METHOD1(SetLocalUniformScale, void(float)); MOCK_METHOD0(GetLocalUniformScale, float()); MOCK_METHOD0(GetWorldUniformScale, float()); diff --git a/Gems/Camera/Code/CMakeLists.txt b/Gems/Camera/Code/CMakeLists.txt index 950ff451ff..703424416b 100644 --- a/Gems/Camera/Code/CMakeLists.txt +++ b/Gems/Camera/Code/CMakeLists.txt @@ -39,6 +39,10 @@ ly_add_target( Gem::Camera.Static ) +# clients and servers use the above module: +ly_create_alias(NAME Camera.Clients NAMESPACE Gem TARGETS Gem::Camera) +ly_create_alias(NAME Camera.Servers NAMESPACE Gem TARGETS Gem::Camera) + if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( @@ -62,4 +66,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Gem::Camera.Static ) + # tools and builders use the above module. + ly_create_alias(NAME Camera.Tools NAMESPACE Gem TARGETS Gem::Camera.Editor) + ly_create_alias(NAME Camera.Builders NAMESPACE Gem TARGETS Gem::Camera.Editor) endif() diff --git a/Gems/Camera/Code/Source/CameraComponentController.cpp b/Gems/Camera/Code/Source/CameraComponentController.cpp index 3dcee68169..d0a124067b 100644 --- a/Gems/Camera/Code/Source/CameraComponentController.cpp +++ b/Gems/Camera/Code/Source/CameraComponentController.cpp @@ -240,7 +240,9 @@ namespace Camera CameraBus::Handler::BusConnect(); CameraNotificationBus::Broadcast(&CameraNotificationBus::Events::OnCameraAdded, m_entityId); - if (m_config.m_makeActiveViewOnActivation) + // 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())) { MakeActiveView(); } diff --git a/Gems/Camera/Code/Source/EditorCameraComponent.cpp b/Gems/Camera/Code/Source/EditorCameraComponent.cpp index 359fa936bc..80057e7a77 100644 --- a/Gems/Camera/Code/Source/EditorCameraComponent.cpp +++ b/Gems/Camera/Code/Source/EditorCameraComponent.cpp @@ -34,20 +34,9 @@ namespace Camera auto controllerConfig = m_controller.GetConfiguration(); controllerConfig.m_editorEntityId = GetEntityId().operator AZ::u64(); - // The Editor manages active camera state, so while we're in Editor we explicitly - // disable the request to make this the active view at edit component activation time. - bool prevShouldActivateViewOnActivation = controllerConfig.m_makeActiveViewOnActivation; - controllerConfig.m_makeActiveViewOnActivation = false; - - m_controller.SetConfiguration(controllerConfig); - // Call base class activate, which in turn calls Activate on our controller. EditorCameraComponentBase::Activate(); - // Reset the original `m_makeActiveViewOnActivation' setting, so that the intended value is serialized, used in BuildGameEntity, etc. - controllerConfig.m_makeActiveViewOnActivation = prevShouldActivateViewOnActivation; - m_controller.SetConfiguration(controllerConfig); - AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); EditorCameraNotificationBus::Handler::BusConnect(); EditorCameraViewRequestBus::Handler::BusConnect(GetEntityId()); diff --git a/Gems/CameraFramework/Code/CMakeLists.txt b/Gems/CameraFramework/Code/CMakeLists.txt index 1ec9dc0ad9..6b0d084e28 100644 --- a/Gems/CameraFramework/Code/CMakeLists.txt +++ b/Gems/CameraFramework/Code/CMakeLists.txt @@ -38,3 +38,9 @@ ly_add_target( PRIVATE Gem::CameraFramework.Static ) + +# Every kind of application uses the above target module. +ly_create_alias(NAME CameraFramework.Clients NAMESPACE Gem TARGETS Gem::CameraFramework) +ly_create_alias(NAME CameraFramework.Servers NAMESPACE Gem TARGETS Gem::CameraFramework) +ly_create_alias(NAME CameraFramework.Tools NAMESPACE Gem TARGETS Gem::CameraFramework) +ly_create_alias(NAME CameraFramework.Builders NAMESPACE Gem TARGETS Gem::CameraFramework) diff --git a/Gems/CertificateManager/Code/CMakeLists.txt b/Gems/CertificateManager/Code/CMakeLists.txt index 93e78bb86a..2307ebed40 100644 --- a/Gems/CertificateManager/Code/CMakeLists.txt +++ b/Gems/CertificateManager/Code/CMakeLists.txt @@ -41,3 +41,7 @@ ly_add_target( PRIVATE Gem::CertificateManager.Static ) + +# we'll load the above "Gem::CertificateManager" module in Clients and Servers +ly_create_alias(NAME CertificateManager.Clients NAMESPACE Gem TARGETS Gem::CertificateManager) +ly_create_alias(NAME CertificateManager.Servers NAMESPACE Gem TARGETS Gem::CertificateManager) diff --git a/Gems/CrashReporting/Code/CMakeLists.txt b/Gems/CrashReporting/Code/CMakeLists.txt index d52600ea9b..2d77d563d9 100644 --- a/Gems/CrashReporting/Code/CMakeLists.txt +++ b/Gems/CrashReporting/Code/CMakeLists.txt @@ -33,6 +33,11 @@ ly_add_target( AZ::CrashHandler ) +# Load the "Gem::CrashReporting" module in Clients and Servers +ly_create_alias(NAME CrashReporting.Clients NAMESPACE Gem TARGETS Gem::CrashReporting) +ly_create_alias(NAME CrashReporting.Servers NAMESPACE Gem TARGETS Gem::CrashReporting) + + ly_add_target( NAME CrashReporting.Uploader APPLICATION NAMESPACE AZ diff --git a/Gems/CustomAssetExample/Code/CMakeLists.txt b/Gems/CustomAssetExample/Code/CMakeLists.txt index 661b1950ce..3debe27919 100644 --- a/Gems/CustomAssetExample/Code/CMakeLists.txt +++ b/Gems/CustomAssetExample/Code/CMakeLists.txt @@ -22,6 +22,10 @@ ly_add_target( AZ::AzCore ) +# clients and servers use the above module. +ly_create_alias(NAME CustomAssetExample.Clients NAMESPACE Gem TARGETS CustomAssetExample) +ly_create_alias(NAME CustomAssetExample.Servers NAMESPACE Gem TARGETS CustomAssetExample) + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME CustomAssetExample.Editor GEM_MODULE @@ -37,4 +41,9 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AzCore AZ::AssetBuilderSDK ) + + # other tools use the above tools module: + ly_create_alias(NAME CustomAssetExample.Builders NAMESPACE Gem TARGETS CustomAssetExample.Editor) + ly_create_alias(NAME CustomAssetExample.Tools NAMESPACE Gem TARGETS CustomAssetExample.Editor) + endif() diff --git a/Gems/DebugDraw/Code/CMakeLists.txt b/Gems/DebugDraw/Code/CMakeLists.txt index 0954d6366c..69488b1493 100644 --- a/Gems/DebugDraw/Code/CMakeLists.txt +++ b/Gems/DebugDraw/Code/CMakeLists.txt @@ -42,6 +42,9 @@ ly_add_target( Gem::DebugDraw.Static ) +# servers do not need debug draw components, only clients +ly_create_alias(NAME DebugDraw.Clients NAMESPACE Gem TARGETS DebugDraw) + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME DebugDraw.Editor GEM_MODULE @@ -62,4 +65,9 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::DebugDraw.Static AZ::AzToolsFramework ) + + # builders and tools use DebugDraw.Editor + ly_create_alias(NAME DebugDraw.Builders NAMESPACE Gem TARGETS DebugDraw.Editor) + ly_create_alias(NAME DebugDraw.Tools NAMESPACE Gem TARGETS DebugDraw.Editor) + endif() diff --git a/AutomatedTesting/Gem/Code/Platform/Linux/runtime_dependencies.cmake b/Gems/DevTextures/CMakeLists.txt similarity index 100% rename from AutomatedTesting/Gem/Code/Platform/Linux/runtime_dependencies.cmake rename to Gems/DevTextures/CMakeLists.txt diff --git a/Gems/EMotionFX/Code/CMakeLists.txt b/Gems/EMotionFX/Code/CMakeLists.txt index b90902a948..bc0268cd60 100644 --- a/Gems/EMotionFX/Code/CMakeLists.txt +++ b/Gems/EMotionFX/Code/CMakeLists.txt @@ -36,10 +36,10 @@ ly_add_target( AZ::AzCore AZ::AzFramework Legacy::CryCommon - Gem::LmbrCentral PUBLIC AZ::AtomCore Gem::Atom_RPI.Public + Gem::LmbrCentral COMPILE_DEFINITIONS PUBLIC EMFX_DEVELOPMENT_BUILD @@ -67,6 +67,10 @@ ly_add_target( Gem::LmbrCentral ) +# Clients and servers use the above EMotionFX module +ly_create_alias(NAME EMotionFX.Clients NAMESPACE Gem TARGETS EMotionFX) +ly_create_alias(NAME EMotionFX.Servers NAMESPACE Gem TARGETS EMotionFX) + if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( @@ -129,6 +133,11 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) RUNTIME_DEPENDENCIES Gem::LmbrCentral.Editor ) + + # builders and tools use the above EMotionFX.Editor module + ly_create_alias(NAME EMotionFX.Builders NAMESPACE Gem TARGETS EMotionFX.Editor) + ly_create_alias(NAME EMotionFX.Tools NAMESPACE Gem TARGETS EMotionFX.Editor) + endif() ################################################################################ diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp index 3a23241385..b2389a3086 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp @@ -1297,18 +1297,6 @@ namespace MCommon } - // render a cube - void RenderUtil::RenderCube(const AZ::Vector3& size, const AZ::Vector3& position, const MCore::RGBAColor& color) - { - // setup the world space matrix of the cube - AZ::Transform cubeTransform = AZ::Transform::CreateScale(size); - cubeTransform.SetTranslation(position); - - // render the cube - RenderCube(color, cubeTransform); - } - - // construct the arrow head mesh used for rendering RenderUtil::UtilMesh* RenderUtil::CreateArrowHead(float height, float radius) { diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h index 86e41e56ef..b724c28720 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h @@ -297,14 +297,6 @@ namespace MCommon */ void RenderCylinder(float baseRadius, float topRadius, float length, const AZ::Vector3& position, const AZ::Vector3& direction, const MCore::RGBAColor& color); - /** - * Render a cube. - * @param size The size of the cube. - * @param position The position of the center of the cube. - * @param color The desired cube color. - */ - void RenderCube(const AZ::Vector3& size, const AZ::Vector3& position, const MCore::RGBAColor& color); - /** * Render a triangle (CCW). * @param v1 The first corner of the triangle. diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/ScaleManipulator.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/ScaleManipulator.cpp index 32b779f385..54ccd00535 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/ScaleManipulator.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/ScaleManipulator.cpp @@ -169,7 +169,6 @@ namespace MCommon if (mXAxisVisible) { renderUtil->RenderLine(mPosition, mPosition + mSignX * AZ::Vector3(mScaledSize.GetX() + 0.5f * mBaseRadius, 0.0f, 0.0f), xAxisColor); - //renderUtil->RenderCube( Vector3(mBaseRadius, mBaseRadius, mBaseRadius), mPosition + mSignX * Vector3(mScaledSize.x+mBaseRadius, 0, 0), ManipulatorColors::mRed ); AZ::Vector3 quadPos = MCore::Project(mPosition + mSignX * AZ::Vector3(mScaledSize.GetX() + mBaseRadius, 0, 0), camera->GetViewProjMatrix(), screenWidth, screenHeight); renderUtil->RenderBorderedRect(static_cast(quadPos.GetX() - 2.0f), static_cast(quadPos.GetX() + 3.0f), static_cast(quadPos.GetY() - 2.0f), static_cast(quadPos.GetY() + 3.0f), ManipulatorColors::mRed, ManipulatorColors::mRed); @@ -186,7 +185,6 @@ namespace MCommon if (mYAxisVisible) { renderUtil->RenderLine(mPosition, mPosition + mSignY * AZ::Vector3(0.0f, mScaledSize.GetY(), 0.0f), yAxisColor); - //renderUtil->RenderCube( Vector3(mBaseRadius, mBaseRadius, mBaseRadius), mPosition + mSignY * Vector3(0, mScaledSize.y+0.5*mBaseRadius, 0), ManipulatorColors::mGreen ); AZ::Vector3 quadPos = MCore::Project(mPosition + mSignY * AZ::Vector3(0, mScaledSize.GetY() + 0.5f * mBaseRadius, 0), camera->GetViewProjMatrix(), screenWidth, screenHeight); renderUtil->RenderBorderedRect(static_cast(quadPos.GetX() - 2.0f), static_cast(quadPos.GetX() + 3.0f), static_cast(quadPos.GetY() - 2.0f), static_cast(quadPos.GetY() + 3.0f), ManipulatorColors::mGreen, ManipulatorColors::mGreen); @@ -203,7 +201,6 @@ namespace MCommon if (mZAxisVisible) { renderUtil->RenderLine(mPosition, mPosition + mSignZ * AZ::Vector3(0.0f, 0.0f, mScaledSize.GetZ()), zAxisColor); - //renderUtil->RenderCube( Vector3(mBaseRadius, mBaseRadius, mBaseRadius), mPosition + mSignZ * Vector3(0, 0, mScaledSize.z+0.5*mBaseRadius), ManipulatorColors::mBlue ); AZ::Vector3 quadPos = MCore::Project(mPosition + mSignZ * AZ::Vector3(0, 0, mScaledSize.GetZ() + 0.5f * mBaseRadius), camera->GetViewProjMatrix(), screenWidth, screenHeight); renderUtil->RenderBorderedRect(static_cast(quadPos.GetX() - 2.0f), static_cast(quadPos.GetX() + 3.0f), static_cast(quadPos.GetY() - 2.0f), static_cast(quadPos.GetY() + 3.0f), ManipulatorColors::mBlue, ManipulatorColors::mBlue); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Transform.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Transform.cpp index b2322b5379..e3195beba9 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Transform.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Transform.cpp @@ -183,7 +183,7 @@ namespace EMotionFX { #ifndef EMFX_SCALE_DISABLED mPosition = transform.GetTranslation(); - mScale = transform.GetScale(); + mScale = AZ::Vector3(transform.GetUniformScale()); mRotation = transform.GetRotation(); #else mPosition = transform.GetTranslation(); diff --git a/Gems/EMotionFX/Code/MCore/Source/AzCoreConversions.h b/Gems/EMotionFX/Code/MCore/Source/AzCoreConversions.h index 519d44dace..fdead82491 100644 --- a/Gems/EMotionFX/Code/MCore/Source/AzCoreConversions.h +++ b/Gems/EMotionFX/Code/MCore/Source/AzCoreConversions.h @@ -58,7 +58,7 @@ namespace MCore AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(emfxTransform.mRotation, emfxTransform.mPosition); EMFX_SCALECODE ( - transform.MultiplyByScale(emfxTransform.mScale); + transform.MultiplyByUniformScale(emfxTransform.mScale.GetMaxElement()); ) return transform; } @@ -386,7 +386,7 @@ namespace MCore AZ::Transform result; result.SetTranslation(translation); result.SetRotation(rotation); - result.SetScale(scale); + result.SetUniformScale(scale.GetMaxElement()); return result; } diff --git a/Gems/EMotionFX/Code/MCore/Source/OBB.cpp b/Gems/EMotionFX/Code/MCore/Source/OBB.cpp index bb73f9413b..e7a0011684 100644 --- a/Gems/EMotionFX/Code/MCore/Source/OBB.cpp +++ b/Gems/EMotionFX/Code/MCore/Source/OBB.cpp @@ -96,9 +96,9 @@ namespace MCore // create the AABB of (box1 in space of box0) const AZ::Transform& mtx = _1in0.mRotation; - AZ::Vector3 transformedAxisX = mtx.GetScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisX())); - AZ::Vector3 transformedAxisY = mtx.GetScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisY())); - AZ::Vector3 transformedAxisZ = mtx.GetScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisZ())); + AZ::Vector3 transformedAxisX = mtx.GetUniformScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisX())); + AZ::Vector3 transformedAxisY = mtx.GetUniformScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisY())); + AZ::Vector3 transformedAxisZ = mtx.GetUniformScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisZ())); float f = transformedAxisX.GetAbs().Dot(mExtents) - box.mExtents.GetX(); if (f > _1in0.mCenter.GetX()) diff --git a/Gems/EMotionFX/Code/Tests/AnimGraphParameterConditionTests.cpp b/Gems/EMotionFX/Code/Tests/AnimGraphParameterConditionTests.cpp index ef3e9820a5..b1ddede059 100644 --- a/Gems/EMotionFX/Code/Tests/AnimGraphParameterConditionTests.cpp +++ b/Gems/EMotionFX/Code/Tests/AnimGraphParameterConditionTests.cpp @@ -10,6 +10,7 @@ * */ +#include "EMotionFX/CommandSystem/Source/AnimGraphParameterCommands.h" #include #include #include @@ -87,26 +88,16 @@ namespace EMotionFX const AnimGraphParameterCondition* condition = GetAnimGraph()->GetParameterCondition(); EXPECT_EQ(condition->GetParameterType(), azrtti_typeid()); - { - AZStd::string result; - EXPECT_TRUE(manager.ExecuteCommand("AnimGraphRemoveParameter -animGraphID 0 -name P0", result)) << result.c_str(); - } - + EXPECT_TRUE(CommandSystem::BuildRemoveParametersCommandGroup(GetAnimGraph(), {"P0"})); EXPECT_EQ(condition->GetParameterType(), azrtti_typeid()); + EXPECT_TRUE(CommandSystem::BuildRemoveParametersCommandGroup(GetAnimGraph(), {"P1"})); + EXPECT_EQ(condition->GetParameterType(), AZ::TypeId::CreateNull()); + { AZStd::string result; - EXPECT_TRUE(manager.ExecuteCommand("AnimGraphRemoveParameter -animGraphID 0 -name P1", result)) << result.c_str(); + EXPECT_TRUE(manager.Undo(result)) << result.c_str(); } - - EXPECT_EQ(condition->GetParameterType(), AZ::TypeId::CreateNull()); - - // Will be fixed by LY-109269 - //{ - // AZStd::string result; - // EXPECT_TRUE(manager.Undo(result)) << result.c_str(); - //} - - //EXPECT_EQ(condition->GetParameterType(), azrtti_typeid()); + EXPECT_EQ(condition->GetParameterType(), azrtti_typeid()); } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Tests/BlendTreeBlendNNodeTests.cpp b/Gems/EMotionFX/Code/Tests/BlendTreeBlendNNodeTests.cpp index 657599bcc9..1e910fb62d 100644 --- a/Gems/EMotionFX/Code/Tests/BlendTreeBlendNNodeTests.cpp +++ b/Gems/EMotionFX/Code/Tests/BlendTreeBlendNNodeTests.cpp @@ -264,8 +264,8 @@ namespace EMotionFX BlendTreeBlendNNode* m_blendNNode = nullptr; }; - // Make sure we don't crash when we have no inputs, such as reported by bug LY-114828 - // Also make sure removing connections on BlendN doesn't crash, as reported by LY-114846 + // Make sure we don't crash when we have no inputs + // Also make sure removing connections on BlendN doesn't crash TEST_F(BlendTreeBlendNNodeTests, NoInputsNoCrashTest) { // Remove all input connections of the blendN node. diff --git a/Gems/EMotionFX/Code/Tests/Bugs/LY-92860.cpp b/Gems/EMotionFX/Code/Tests/Bugs/CanUndoParameterDeletionAndRestoreBlendTreeConnections.cpp similarity index 98% rename from Gems/EMotionFX/Code/Tests/Bugs/LY-92860.cpp rename to Gems/EMotionFX/Code/Tests/Bugs/CanUndoParameterDeletionAndRestoreBlendTreeConnections.cpp index cd4773b221..0d47b86606 100644 --- a/Gems/EMotionFX/Code/Tests/Bugs/LY-92860.cpp +++ b/Gems/EMotionFX/Code/Tests/Bugs/CanUndoParameterDeletionAndRestoreBlendTreeConnections.cpp @@ -106,7 +106,7 @@ namespace EMotionFX R"str(AnimGraphCreateConnection -animGraphID 0 -sourceNode Parameters0 -targetNode Smoothing2 -sourcePort 2 -targetPort 0 -startOffsetX 119 -startOffsetY 70 -endOffsetX -2 -endOffsetY 40)str" }; - class LY92860Fixture + class UndoParameterDeletionTests : public CommandRunnerFixture { public: @@ -183,10 +183,10 @@ namespace EMotionFX } }; - TEST_P(LY92860Fixture, ExecuteCommands) + TEST_P(UndoParameterDeletionTests, CanUndoParameterDeletionAndRestoreBlendTreeConnections) { Run(); }; - INSTANTIATE_TEST_CASE_P(LY92860, LY92860Fixture, ::testing::Values(prepareLY92860Commands)); + INSTANTIATE_TEST_CASE_P(UndoParameterDeletionTests, UndoParameterDeletionTests, ::testing::Values(prepareLY92860Commands)); } // EMotionFX diff --git a/Gems/EMotionFX/Code/Tests/Mocks/PhysicsRagdoll.h b/Gems/EMotionFX/Code/Tests/Mocks/PhysicsRagdoll.h index b3acb73e19..446abbf6af 100644 --- a/Gems/EMotionFX/Code/Tests/Mocks/PhysicsRagdoll.h +++ b/Gems/EMotionFX/Code/Tests/Mocks/PhysicsRagdoll.h @@ -26,7 +26,7 @@ namespace EMotionFX MOCK_METHOD0(DisableSimulation, void()); MOCK_METHOD0(DisableSimulationQueued, void()); - MOCK_METHOD0(IsSimulated, bool()); + MOCK_CONST_METHOD0(IsSimulated, bool()); MOCK_CONST_METHOD1(GetState, void(Physics::RagdollState&)); MOCK_METHOD1(SetState, void(const Physics::RagdollState&)); diff --git a/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h b/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h index 22102fd38a..3aafdb4e2b 100644 --- a/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h +++ b/Gems/EMotionFX/Code/Tests/Mocks/PhysicsSystem.h @@ -35,9 +35,6 @@ namespace Physics MOCK_METHOD2(CreateShape, AZStd::shared_ptr(const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& configuration)); MOCK_METHOD1(ReleaseNativeMeshObject, void(void* nativeMeshObject)); MOCK_METHOD1(CreateMaterial, AZStd::shared_ptr(const Physics::MaterialConfiguration& materialConfiguration)); - MOCK_METHOD0(GetDefaultMaterial, AZStd::shared_ptr()); - MOCK_METHOD1(CreateMaterialsFromLibrary, AZStd::vector>(const Physics::MaterialSelection& materialSelection)); - MOCK_METHOD2(UpdateMaterialSelection, bool(const Physics::ShapeConfiguration& shapeConfiguration, Physics::ColliderConfiguration& colliderConfiguration)); MOCK_METHOD0(GetSupportedJointTypes, AZStd::vector()); MOCK_METHOD1(CreateJointLimitConfiguration, AZStd::shared_ptr(AZ::TypeId jointType)); MOCK_METHOD3(CreateJoint, AZStd::shared_ptr(const AZStd::shared_ptr& configuration, AzPhysics::SimulatedBody* parentBody, AzPhysics::SimulatedBody* childBody)); @@ -59,7 +56,6 @@ namespace Physics void Shutdown() override {} void Simulate([[maybe_unused]] float deltaTime) override {} void UpdateConfiguration([[maybe_unused]] const AzPhysics::SystemConfiguration* newConfig, [[maybe_unused]] bool forceReinitialization = false) override {} - void UpdateDefaultMaterialLibrary([[maybe_unused]] const AZ::Data::Asset& materialLibrary) override {} void UpdateDefaultSceneConfiguration([[maybe_unused]] const AzPhysics::SceneConfiguration& sceneConfiguration) override {} void RemoveScene([[maybe_unused]] AzPhysics::SceneHandle handle) override {} void RemoveScenes([[maybe_unused]] const AzPhysics::SceneHandleList& handles) override {} @@ -73,7 +69,6 @@ namespace Physics MOCK_METHOD0(GetAllScenes, AzPhysics::SceneList& ()); MOCK_METHOD1(FindAttachedBodyHandleFromEntityId, AZStd::pair(AZ::EntityId entityId)); MOCK_CONST_METHOD0(GetConfiguration, const AzPhysics::SystemConfiguration* ()); - MOCK_CONST_METHOD0(GetDefaultMaterialLibrary, const AZ::Data::Asset& ()); MOCK_CONST_METHOD0(GetDefaultSceneConfiguration, const AzPhysics::SceneConfiguration& ()); }; diff --git a/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteColliders.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteColliders.cpp index 858980fbe8..eaef6f31ee 100644 --- a/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteColliders.cpp +++ b/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteColliders.cpp @@ -54,14 +54,11 @@ namespace EMotionFX [[maybe_unused]] const AZ::Vector3& axis, [[maybe_unused]] const AZStd::vector& exampleLocalRotations) { return AZStd::make_unique(); }); - EXPECT_CALL(m_physicsInterface, GetDefaultMaterialLibrary) - .WillRepeatedly(testing::ReturnRef(m_materialLibraryAsset)); } private: Physics::MockPhysicsSystem m_physicsSystem; Physics::MockPhysicsInterface m_physicsInterface; - AZ::Data::Asset m_materialLibraryAsset; }; #if AZ_TRAIT_DISABLE_FAILED_EMOTION_FX_EDITOR_TESTS diff --git a/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteJointLimits.cpp b/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteJointLimits.cpp index b4b4d14d58..6223af3599 100644 --- a/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteJointLimits.cpp +++ b/Gems/EMotionFX/Code/Tests/ProvidesUI/Ragdoll/CanCopyPasteJointLimits.cpp @@ -59,9 +59,6 @@ namespace EMotionFX .WillRepeatedly(testing::Return(AZStd::vector{azrtti_typeid()})); EXPECT_CALL(physicsSystem, ComputeInitialJointLimitConfiguration(azrtti_typeid(), _, _, _, _)) .WillRepeatedly([]([[maybe_unused]] const AZ::TypeId& jointLimitTypeId, [[maybe_unused]] const AZ::Quaternion& parentWorldRotation, [[maybe_unused]] const AZ::Quaternion& childWorldRotation, [[maybe_unused]] const AZ::Vector3& axis, [[maybe_unused]] const AZStd::vector& exampleLocalRotations) { return AZStd::make_unique(); }); - AZ::Data::Asset materialLibraryAsset; - EXPECT_CALL(physicsInterface, GetDefaultMaterialLibrary) - .WillRepeatedly(testing::ReturnRef(materialLibraryAsset)); AutoRegisteredActor actor {ActorFactory::CreateAndInit(4)}; diff --git a/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h b/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h index 04a1ca4f81..f8c70a5fee 100644 --- a/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h +++ b/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h @@ -59,7 +59,16 @@ namespace EMotionFX { public: - ComponentFixtureApp() = default; + ComponentFixtureApp() + { + using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + constexpr auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + if(auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry); + } + } AZ::ComponentTypeList GetRequiredSystemComponents() const override { diff --git a/Gems/EMotionFX/Code/Tests/UI/LY-93621.cpp b/Gems/EMotionFX/Code/Tests/UI/CanAdjustGroupParameter.cpp similarity index 95% rename from Gems/EMotionFX/Code/Tests/UI/LY-93621.cpp rename to Gems/EMotionFX/Code/Tests/UI/CanAdjustGroupParameter.cpp index 8e4f9a1fc9..4568dd1b03 100644 --- a/Gems/EMotionFX/Code/Tests/UI/LY-93621.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanAdjustGroupParameter.cpp @@ -14,7 +14,7 @@ namespace EMotionFX { - INSTANTIATE_TEST_CASE_P(LY93621, CommandRunnerFixture, + INSTANTIATE_TEST_CASE_P(CanAdjustGroupParameter, CommandRunnerFixture, ::testing::Values(std::vector { R"str(CreateAnimGraph)str", R"str(AnimGraphAddGroupParameter -animGraphID 0 -name Group0)str", diff --git a/Gems/EMotionFX/Code/Tests/UI/LY-92748.cpp b/Gems/EMotionFX/Code/Tests/UI/CanDeleteAnimGraphNode_AnimGraphModelUpdates.cpp similarity index 95% rename from Gems/EMotionFX/Code/Tests/UI/LY-92748.cpp rename to Gems/EMotionFX/Code/Tests/UI/CanDeleteAnimGraphNode_AnimGraphModelUpdates.cpp index 9534edb8eb..ad6172d02b 100644 --- a/Gems/EMotionFX/Code/Tests/UI/LY-92748.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanDeleteAnimGraphNode_AnimGraphModelUpdates.cpp @@ -21,12 +21,12 @@ namespace EMotionFX { - class LY92748Fixture + class CanDeleteAnimGraphNode : public CommandRunnerFixture { }; - TEST_P(LY92748Fixture, ExecuteCommands) + TEST_P(CanDeleteAnimGraphNode, CanDeleteAnimGraphNode_AnimGraphModelUpdates) { ExecuteCommands(GetParam()); @@ -68,7 +68,7 @@ namespace EMotionFX } - INSTANTIATE_TEST_CASE_P(DISABLED_LY92748, LY92748Fixture, + INSTANTIATE_TEST_CASE_P(CanDeleteAnimGraphNode_AnimGraphModelUpdates, CanDeleteAnimGraphNode, ::testing::Values(std::vector { R"str(CreateAnimGraph)str", R"str(Select -animGraphID 0)str", diff --git a/Gems/EMotionFX/Code/Tests/UI/LY-92269.cpp b/Gems/EMotionFX/Code/Tests/UI/CanRenameParameter_ParameterNodeUpdates.cpp similarity index 98% rename from Gems/EMotionFX/Code/Tests/UI/LY-92269.cpp rename to Gems/EMotionFX/Code/Tests/UI/CanRenameParameter_ParameterNodeUpdates.cpp index 95f42f7e33..fac6f51943 100644 --- a/Gems/EMotionFX/Code/Tests/UI/LY-92269.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanRenameParameter_ParameterNodeUpdates.cpp @@ -14,7 +14,7 @@ namespace EMotionFX { - INSTANTIATE_TEST_CASE_P(LY92269, CommandRunnerFixture, + INSTANTIATE_TEST_CASE_P(CanRenameParameter_ParameterNodeUpdates, CommandRunnerFixture, ::testing::Values(std::vector { R"str(CreateAnimGraph)str", R"str(AnimGraphCreateNode -animGraphID 0 -type {A8B5BB1E-5BA9-4B0A-88E9-21BB7A199ED2} -parentName Root -xPos 240 -yPos 230 -name GENERATE -namePrefix BlendTree)str", diff --git a/Gems/EMotionFX/Code/Tests/UI/CanUseFileMenu.cpp b/Gems/EMotionFX/Code/Tests/UI/CanUseFileMenu.cpp index 8045499b4a..8e3a19c1ca 100644 --- a/Gems/EMotionFX/Code/Tests/UI/CanUseFileMenu.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanUseFileMenu.cpp @@ -77,7 +77,6 @@ namespace EMotionFX { auto testAssetsPath = AZ::IO::Path(GetEMotionFX().GetAssetCacheFolder()) / "TmpTestAssets"; QString dataDir = QString::fromUtf8(testAssetsPath.c_str(), aznumeric_cast(testAssetsPath.Native().size())); - dataDir += "TmpTestAssets"; if (!QDir(dataDir).exists()) { diff --git a/Gems/EMotionFX/Code/emotionfx_editor_tests_files.cmake b/Gems/EMotionFX/Code/emotionfx_editor_tests_files.cmake index 4a7d1414f9..e2670c20f7 100644 --- a/Gems/EMotionFX/Code/emotionfx_editor_tests_files.cmake +++ b/Gems/EMotionFX/Code/emotionfx_editor_tests_files.cmake @@ -54,15 +54,15 @@ set(FILES Tests/UI/AnimGraphUIFixture.h Tests/UI/MenuUIFixture.cpp Tests/UI/MenuUIFixture.h - Tests/UI/LY-92269.cpp - Tests/UI/LY-92748.cpp - Tests/UI/LY-93621.cpp + Tests/UI/CanRenameParameter_ParameterNodeUpdates.cpp + Tests/UI/CanDeleteAnimGraphNode_AnimGraphModelUpdates.cpp + Tests/UI/CanAdjustGroupParameter.cpp Tests/UI/CanAddJointAndChildren.cpp Tests/Integration/CanAddActor.cpp Tests/Integration/CanAddSimpleMotionComponent.cpp Tests/Integration/CanDeleteJackEntity.cpp Tests/Bugs/CanDeleteMotionWhenMotionIsBeingBlended.cpp - Tests/Bugs/LY-92860.cpp + Tests/Bugs/CanUndoParameterDeletionAndRestoreBlendTreeConnections.cpp Tests/D6JointLimitConfiguration.cpp Tests/D6JointLimitConfiguration.h Tests/Editor/FileManagerTests.cpp diff --git a/Gems/EditorPythonBindings/Code/CMakeLists.txt b/Gems/EditorPythonBindings/Code/CMakeLists.txt index 3a34a8491d..a8d4382b45 100644 --- a/Gems/EditorPythonBindings/Code/CMakeLists.txt +++ b/Gems/EditorPythonBindings/Code/CMakeLists.txt @@ -64,6 +64,10 @@ ly_add_target( Gem::EditorPythonBindings.Static ) +# builders and tools use EditorPythonBindings.Editor +ly_create_alias(NAME EditorPythonBindings.Builders NAMESPACE Gem TARGETS EditorPythonBindings.Editor) +ly_create_alias(NAME EditorPythonBindings.Tools NAMESPACE Gem TARGETS EditorPythonBindings.Editor) + ################################################################################ # Tests ################################################################################ diff --git a/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp b/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp index 86628d08a1..440638c61f 100644 --- a/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -322,6 +323,13 @@ sys.version void SetUp() override { PythonTestingFixture::SetUp(); + + auto registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.RegisterComponentDescriptor(EditorPythonBindings::PythonSystemComponent::CreateDescriptor()); } diff --git a/Gems/ExpressionEvaluation/Code/CMakeLists.txt b/Gems/ExpressionEvaluation/Code/CMakeLists.txt index 563e3f3341..456129f05d 100644 --- a/Gems/ExpressionEvaluation/Code/CMakeLists.txt +++ b/Gems/ExpressionEvaluation/Code/CMakeLists.txt @@ -41,6 +41,12 @@ ly_add_target( Gem::ExpressionEvaluation.Static ) +# all types of applications use the above module +ly_create_alias(NAME ExpressionEvaluation.Clients NAMESPACE Gem TARGETS ExpressionEvaluation) +ly_create_alias(NAME ExpressionEvaluation.Servers NAMESPACE Gem TARGETS ExpressionEvaluation) +ly_create_alias(NAME ExpressionEvaluation.Builders NAMESPACE Gem TARGETS ExpressionEvaluation) +ly_create_alias(NAME ExpressionEvaluation.Tools NAMESPACE Gem TARGETS ExpressionEvaluation) + ################################################################################ # Tests ################################################################################ diff --git a/Gems/FastNoise/Code/CMakeLists.txt b/Gems/FastNoise/Code/CMakeLists.txt index a49126303a..ae42af771a 100644 --- a/Gems/FastNoise/Code/CMakeLists.txt +++ b/Gems/FastNoise/Code/CMakeLists.txt @@ -23,6 +23,7 @@ ly_add_target( PUBLIC Legacy::CryCommon Gem::GradientSignal + PRIVATE Gem::LmbrCentral ) @@ -42,6 +43,10 @@ ly_add_target( Gem::GradientSignal ) +# Clients and Servers use the above module +ly_create_alias(NAME FastNoise.Clients NAMESPACE Gem TARGETS FastNoise) +ly_create_alias(NAME FastNoise.Servers NAMESPACE Gem TARGETS FastNoise) + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME FastNoise.Editor.Static STATIC @@ -57,6 +62,8 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) PUBLIC Include BUILD_DEPENDENCIES + PRIVATE + Gem::LmbrCentral.Editor PUBLIC Gem::FastNoise.Static AZ::AzToolsFramework @@ -65,7 +72,6 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME FastNoise.Editor GEM_MODULE - NAMESPACE Gem FILES_CMAKE fastnoise_editor_shared_files.cmake @@ -76,11 +82,18 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Include BUILD_DEPENDENCIES PRIVATE - FastNoise.Editor.Static + Gem::FastNoise.Editor.Static + Gem::LmbrCentral.Editor RUNTIME_DEPENDENCIES Gem::LmbrCentral.Editor Gem::SurfaceData.Editor ) + + # builders and tools load the above tool module. + ly_create_alias(NAME FastNoise.Builders NAMESPACE Gem TARGETS FastNoise.Editor) + ly_create_alias(NAME FastNoise.Tools NAMESPACE Gem TARGETS FastNoise.Editor) + + endif() ################################################################################ @@ -103,6 +116,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) PRIVATE AZ::AzTest FastNoise.Editor.Static + Gem::LmbrCentral.Editor ) ly_add_googletest( NAME Gem::FastNoise.Editor.Tests @@ -120,6 +134,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) PRIVATE AZ::AzTest Gem::FastNoise.Static + Gem::LmbrCentral ) ly_add_googletest( NAME Gem::FastNoise.Tests diff --git a/Gems/GameState/Code/CMakeLists.txt b/Gems/GameState/Code/CMakeLists.txt index d57cf8feed..828dfcbb35 100644 --- a/Gems/GameState/Code/CMakeLists.txt +++ b/Gems/GameState/Code/CMakeLists.txt @@ -40,6 +40,10 @@ ly_add_target( Gem::GameState.Static ) +# Clients and Servers use the above module. There is no editor or tools module required. +ly_create_alias(NAME GameState.Clients NAMESPACE Gem TARGETS GameState) +ly_create_alias(NAME GameState.Servers NAMESPACE Gem TARGETS GameState) + ################################################################################ # Tests ################################################################################ diff --git a/Gems/GameStateSamples/Code/CMakeLists.txt b/Gems/GameStateSamples/Code/CMakeLists.txt index e3ebc25016..abf3ca6121 100644 --- a/Gems/GameStateSamples/Code/CMakeLists.txt +++ b/Gems/GameStateSamples/Code/CMakeLists.txt @@ -44,4 +44,12 @@ ly_add_target( AZ::AzFramework Gem::LmbrCentral Gem::GameStateSamples.Headers + RUNTIME_DEPENDENCIES + Gem::GameState + Gem::LocalUser + Gem::LmbrCentral ) + +# Clients and Servers use the above module. There is no editor or tools module required. +ly_create_alias(NAME GameStateSamples.Clients NAMESPACE Gem TARGETS GameStateSamples) +ly_create_alias(NAME GameStateSamples.Servers NAMESPACE Gem TARGETS GameStateSamples) diff --git a/Gems/Gestures/Code/CMakeLists.txt b/Gems/Gestures/Code/CMakeLists.txt index 677886c0ed..8c4ec5ad55 100644 --- a/Gems/Gestures/Code/CMakeLists.txt +++ b/Gems/Gestures/Code/CMakeLists.txt @@ -43,6 +43,12 @@ ly_add_target( Gem::Gestures.Static ) +# All types of applications use the same module. +ly_create_alias(NAME Gestures.Clients NAMESPACE Gem TARGETS Gestures) +ly_create_alias(NAME Gestures.Servers NAMESPACE Gem TARGETS Gestures) +ly_create_alias(NAME Gestures.Builders NAMESPACE Gem TARGETS Gestures) +ly_create_alias(NAME Gestures.Tools NAMESPACE Gem TARGETS Gestures) + ################################################################################ # Tests ################################################################################ diff --git a/Gems/GradientSignal/Code/CMakeLists.txt b/Gems/GradientSignal/Code/CMakeLists.txt index 7b8c9813e6..244f7360ea 100644 --- a/Gems/GradientSignal/Code/CMakeLists.txt +++ b/Gems/GradientSignal/Code/CMakeLists.txt @@ -22,9 +22,10 @@ ly_add_target( BUILD_DEPENDENCIES PUBLIC Legacy::CryCommon - Gem::LmbrCentral Gem::SurfaceData Gem::ImageProcessingAtom.Headers + PRIVATE + Gem::LmbrCentral ) ly_add_target( @@ -40,13 +41,17 @@ ly_add_target( BUILD_DEPENDENCIES PRIVATE Gem::GradientSignal.Static + Gem::LmbrCentral PUBLIC Gem::ImageProcessingAtom.Headers # Atom/ImageProcessing/PixelFormats.h is part of a header in Includes RUNTIME_DEPENDENCIES Gem::LmbrCentral - Gem::SurfaceData ) +# Load the "Gem::GradientSignal" module in Clients and Servers +ly_create_alias(NAME GradientSignal.Clients NAMESPACE Gem TARGETS Gem::GradientSignal) +ly_create_alias(NAME GradientSignal.Servers NAMESPACE Gem TARGETS Gem::GradientSignal) + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( @@ -63,19 +68,21 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) PUBLIC GRADIENTSIGNAL_EDITOR BUILD_DEPENDENCIES + PRIVATE + Gem::LmbrCentral.Editor PUBLIC 3rdParty::Qt::Widgets Legacy::CryCommon AZ::AzToolsFramework - Gem::LmbrCentral - Gem::SurfaceData AZ::AssetBuilderSDK Gem::GradientSignal.Static + Gem::SurfaceData + RUNTIME_DEPENDENCIES + Gem::LmbrCentral.Editor ) ly_add_target( NAME GradientSignal.Editor GEM_MODULE - NAMESPACE Gem FILES_CMAKE gradientsignal_editor_shared_files.cmake @@ -87,11 +94,16 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) BUILD_DEPENDENCIES PRIVATE Gem::GradientSignal.Editor.Static + Gem::LmbrCentral.Editor RUNTIME_DEPENDENCIES Gem::LmbrCentral.Editor - Gem::SurfaceData.Editor ) + # Load the "Gem::GradientSignal.Editor" module in Builders and Tools + ly_create_alias(NAME GradientSignal.Builders NAMESPACE Gem TARGETS Gem::GradientSignal.Editor) + ly_create_alias(NAME GradientSignal.Tools NAMESPACE Gem TARGETS Gem::GradientSignal.Editor) + + endif() ################################################################################ @@ -112,6 +124,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) PRIVATE AZ::AzTest Gem::GradientSignal.Static + Gem::LmbrCentral ) ly_add_googletest( NAME Gem::GradientSignal.Tests @@ -132,6 +145,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest Gem::GradientSignal.Static Gem::GradientSignal.Editor.Static + Gem::LmbrCentral.Editor ) ly_add_googletest( NAME Gem::GradientSignal.Editor.Tests diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/GradientSampler.h b/Gems/GradientSignal/Code/Include/GradientSignal/GradientSampler.h index 8a8eacc952..9e7823c8d3 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/GradientSampler.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/GradientSampler.h @@ -103,12 +103,12 @@ namespace GradientSignal //apply transform if set if (m_enableTransform && GradientSamplerUtil::AreTransformParamsSet(*this)) { - const AZ::Transform transform = - AZ::Transform::CreateTranslation(m_translate) * - AZ::ConvertEulerDegreesToTransform(m_rotate) * - AZ::Transform::CreateScale(m_scale); + AZ::Matrix3x4 matrix3x4; + matrix3x4.SetFromEulerDegrees(m_rotate); + matrix3x4.MultiplyByScale(m_scale); + matrix3x4.SetTranslation(m_translate); - sampleParamsTransformed.m_position = transform.TransformPoint(sampleParamsTransformed.m_position); + sampleParamsTransformed.m_position = matrix3x4 * sampleParamsTransformed.m_position; } float output = 0.0f; diff --git a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp index 336f428582..a2d979313e 100644 --- a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp @@ -493,7 +493,7 @@ namespace GradientSignal if (!m_configuration.m_advancedMode || !m_configuration.m_overrideScale) { - m_configuration.m_scale = shapeTransform.GetScale(); + m_configuration.m_scale = AZ::Vector3(shapeTransform.GetUniformScale()); } //rebuild bounds from parameters diff --git a/Gems/GraphCanvas/Code/CMakeLists.txt b/Gems/GraphCanvas/Code/CMakeLists.txt index 683b0e4bdf..fe24686f95 100644 --- a/Gems/GraphCanvas/Code/CMakeLists.txt +++ b/Gems/GraphCanvas/Code/CMakeLists.txt @@ -51,7 +51,6 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME GraphCanvas.Editor GEM_MODULE - NAMESPACE Gem AUTOMOC AUTORCC @@ -75,4 +74,10 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) 3rdParty::Qt::Xml AZ::AzQtComponents ) + + # Load the "Gem::GraphCanvas" module in Builders and Tools + ly_create_alias(NAME GraphCanvas.Builders NAMESPACE Gem TARGETS Gem::GraphCanvas.Editor) + ly_create_alias(NAME GraphCanvas.Tools NAMESPACE Gem TARGETS Gem::GraphCanvas.Editor) + + endif () diff --git a/Gems/GraphModel/Code/CMakeLists.txt b/Gems/GraphModel/Code/CMakeLists.txt index 2c4fc57252..86141140ee 100644 --- a/Gems/GraphModel/Code/CMakeLists.txt +++ b/Gems/GraphModel/Code/CMakeLists.txt @@ -52,6 +52,11 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) RUNTIME_DEPENDENCIES Gem::GraphCanvas.Editor ) + + # Load the "Gem::GraphModel" module in Builders and Tools + ly_create_alias(NAME GraphModel.Builders NAMESPACE Gem TARGETS Gem::GraphModel.Editor) + ly_create_alias(NAME GraphModel.Tools NAMESPACE Gem TARGETS Gem::GraphModel.Editor) + endif() ################################################################################ diff --git a/Gems/HttpRequestor/Code/CMakeLists.txt b/Gems/HttpRequestor/Code/CMakeLists.txt index 71181f9ff4..bfbc4305b0 100644 --- a/Gems/HttpRequestor/Code/CMakeLists.txt +++ b/Gems/HttpRequestor/Code/CMakeLists.txt @@ -48,6 +48,12 @@ ly_add_target( Gem::HttpRequestor.Static ) +# Load the "Gem::HttpRequestor" module in all types of applicatons. +ly_create_alias(NAME HttpRequestor.Clients NAMESPACE Gem TARGETS Gem::HttpRequestor) +ly_create_alias(NAME HttpRequestor.Servers NAMESPACE Gem TARGETS Gem::HttpRequestor) +ly_create_alias(NAME HttpRequestor.Builders NAMESPACE Gem TARGETS Gem::HttpRequestor) +ly_create_alias(NAME HttpRequestor.Tools NAMESPACE Gem TARGETS Gem::HttpRequestor) + ################################################################################ # Tests ################################################################################ diff --git a/Gems/ImGui/Code/CMakeLists.txt b/Gems/ImGui/Code/CMakeLists.txt index 2f7d6c6ce7..fccdb6fc08 100644 --- a/Gems/ImGui/Code/CMakeLists.txt +++ b/Gems/ImGui/Code/CMakeLists.txt @@ -53,6 +53,8 @@ ly_add_target( PUBLIC Gem::ImGui.imguilib Legacy::CryCommon + RUNTIME_DEPENDENCIES + Gem::ImGui.imguilib ) ly_add_target( @@ -70,6 +72,7 @@ ly_add_target( BUILD_DEPENDENCIES PUBLIC Gem::ImGui.ImGuiLYUtils + PRIVATE Gem::LmbrCentral ) @@ -90,6 +93,10 @@ ly_add_target( Gem::LmbrCentral ) +# Load the above "Gem::ImGui" module in Clients and Servers: +ly_create_alias(NAME ImGui.Clients NAMESPACE Gem TARGETS Gem::ImGui) +ly_create_alias(NAME ImGui.Servers NAMESPACE Gem TARGETS Gem::ImGui) + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME ImGui.Editor GEM_MODULE @@ -113,4 +120,9 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) RUNTIME_DEPENDENCIES Gem::LmbrCentral.Editor ) + + # Load the above "Gem::ImGui.Editor" module in only tools and builders. + ly_create_alias(NAME ImGui.Builders NAMESPACE Gem TARGETS Gem::ImGui.Editor) + ly_create_alias(NAME ImGui.Tools NAMESPACE Gem TARGETS Gem::ImGui.Editor) + endif() diff --git a/Gems/InAppPurchases/Code/CMakeLists.txt b/Gems/InAppPurchases/Code/CMakeLists.txt index 61f9839841..889b651838 100644 --- a/Gems/InAppPurchases/Code/CMakeLists.txt +++ b/Gems/InAppPurchases/Code/CMakeLists.txt @@ -43,3 +43,8 @@ ly_add_target( PRIVATE Gem::InAppPurchases.Static ) +# Load the above "Gem::InAppPurchases" module in all app types +ly_create_alias(NAME InAppPurchases.Clients NAMESPACE Gem TARGETS Gem::InAppPurchases) +ly_create_alias(NAME InAppPurchases.Servers NAMESPACE Gem TARGETS Gem::InAppPurchases) +ly_create_alias(NAME InAppPurchases.Builders NAMESPACE Gem TARGETS Gem::InAppPurchases) +ly_create_alias(NAME InAppPurchases.Tools NAMESPACE Gem TARGETS Gem::InAppPurchases) diff --git a/Gems/LandscapeCanvas/Code/CMakeLists.txt b/Gems/LandscapeCanvas/Code/CMakeLists.txt index e8e2fce689..5288db9cc1 100644 --- a/Gems/LandscapeCanvas/Code/CMakeLists.txt +++ b/Gems/LandscapeCanvas/Code/CMakeLists.txt @@ -35,16 +35,21 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Legacy::CryCommon Legacy::Editor.Headers Legacy::EditorCommon - Gem::LmbrCentral - Gem::GraphCanvasWidgets Gem::GraphModel.Editor.Static Gem::GradientSignal.Editor Gem::SurfaceData.Editor Gem::Vegetation.Editor + Gem::LmbrCentral.Editor + PUBLIC + Gem::GraphCanvasWidgets + RUNTIME_DEPENDENCIES + Gem::GradientSignal.Editor + Gem::SurfaceData.Editor + Gem::Vegetation.Editor + Gem::LmbrCentral.Editor ) ly_add_target( NAME LandscapeCanvas.Editor GEM_MODULE - NAMESPACE Gem FILES_CMAKE landscapecanvas_editor_files.cmake @@ -61,7 +66,6 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AzCore AZ::AzToolsFramework Legacy::Editor.Headers - Gem::GraphCanvasWidgets Gem::GraphModel.Editor.Static Gem::LandscapeCanvas.Editor.Static RUNTIME_DEPENDENCIES @@ -72,6 +76,11 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::SurfaceData.Editor Gem::Vegetation.Editor ) + + # by default, load the above "Gem::LandscapeCanvas.Editor" module in dev applications + ly_create_alias(NAME LandscapeCanvas.Builders NAMESPACE Gem TARGETS Gem::LandscapeCanvas.Editor) + ly_create_alias(NAME LandscapeCanvas.Tools NAMESPACE Gem TARGETS Gem::LandscapeCanvas.Editor) + endif() ################################################################################ @@ -97,9 +106,10 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest AZ::AzFramework AZ::AzToolsFramework - Gem::GraphCanvasWidgets Gem::GraphModel.Editor.Static Gem::LandscapeCanvas.Editor.Static + RUNTIME_DEPENDENCIES + Gem::GraphCanvasWidgets ) ly_add_googletest( NAME Gem::LandscapeCanvas.Editor.Tests diff --git a/Gems/LmbrCentral/Code/CMakeLists.txt b/Gems/LmbrCentral/Code/CMakeLists.txt index c1fbd744e6..4d03d30923 100644 --- a/Gems/LmbrCentral/Code/CMakeLists.txt +++ b/Gems/LmbrCentral/Code/CMakeLists.txt @@ -48,6 +48,11 @@ ly_add_target( Gem::LmbrCentral.Static ) +# by default, load the above "Gem::LmbrCentral" module in Client and Server +ly_create_alias(NAME LmbrCentral.Clients NAMESPACE Gem TARGETS Gem::LmbrCentral) +ly_create_alias(NAME LmbrCentral.Servers NAMESPACE Gem TARGETS Gem::LmbrCentral) + + if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME LmbrCentral.Editor.Static STATIC @@ -102,6 +107,10 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) FILES ${QT_LRELEASE_EXECUTABLE} ) + # by default, load the above "Gem::LmbrCentral.Editor" module in dev tools + ly_create_alias(NAME LmbrCentral.Builders NAMESPACE Gem TARGETS Gem::LmbrCentral.Editor) + ly_create_alias(NAME LmbrCentral.Tools NAMESPACE Gem TARGETS Gem::LmbrCentral.Editor) + endif() ################################################################################ diff --git a/Gems/LmbrCentral/Code/Source/Shape/QuadShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/QuadShape.cpp index 052eac47d0..a395dbac2b 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/QuadShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/QuadShape.cpp @@ -205,8 +205,8 @@ namespace LmbrCentral { m_position = currentTransform.GetTranslation(); m_quaternion = currentTransform.GetRotation(); - m_scaledWidth = configuration.m_width * currentTransform.GetScale().GetX() * currentNonUniformScale.GetX(); - m_scaledHeight = configuration.m_height * currentTransform.GetScale().GetY() * currentNonUniformScale.GetY(); + m_scaledWidth = configuration.m_width * currentTransform.GetUniformScale() * currentNonUniformScale.GetX(); + m_scaledHeight = configuration.m_height * currentTransform.GetUniformScale() * currentNonUniformScale.GetY(); } const QuadShapeConfig& QuadShape::GetQuadConfiguration() const diff --git a/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp b/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp index f082ef74f9..44fe70af80 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/CopyDependencyBuilderTest.cpp @@ -217,6 +217,9 @@ protected: 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 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; } diff --git a/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp b/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp index 58e7e9e093..470a2abff6 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -98,6 +99,12 @@ class LevelBuilderTest protected: void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(m_descriptor); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash diff --git a/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp index b8911738f7..99213484fc 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,12 @@ namespace UnitTest { void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(m_descriptor); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash diff --git a/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp index 20dd3db446..49fc06c160 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp @@ -12,6 +12,7 @@ #include "LmbrCentral_precompiled.h" #include +#include #include #include #include @@ -22,6 +23,12 @@ class SeedBuilderTests { void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(AZ::ComponentApplication::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is diff --git a/Gems/LocalUser/Code/CMakeLists.txt b/Gems/LocalUser/Code/CMakeLists.txt index 6198d88e15..3f2b513282 100644 --- a/Gems/LocalUser/Code/CMakeLists.txt +++ b/Gems/LocalUser/Code/CMakeLists.txt @@ -43,6 +43,9 @@ ly_add_target( Gem::LocalUser.Static ) +# by default, load the above "Gem::LocalUser" module in client applications +ly_create_alias(NAME LocalUser.Clients NAMESPACE Gem TARGETS Gem::LocalUser) + ################################################################################ # Tests ################################################################################ diff --git a/Gems/LyShine/Assets/Editor/Icons/Viewport/Canvas_Background.tif.assetinfo b/Gems/LyShine/Assets/Editor/Icons/Viewport/Canvas_Background.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShine/Assets/Editor/Icons/Viewport/Canvas_Background.tif.assetinfo +++ b/Gems/LyShine/Assets/Editor/Icons/Viewport/Canvas_Background.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShine/Code/CMakeLists.txt b/Gems/LyShine/Code/CMakeLists.txt index 796cb22292..4dede1c6ac 100644 --- a/Gems/LyShine/Code/CMakeLists.txt +++ b/Gems/LyShine/Code/CMakeLists.txt @@ -27,12 +27,12 @@ ly_add_target( PRIVATE Legacy::CryCommon Gem::LmbrCentral - Gem::TextureAtlas PUBLIC Gem::Atom_RPI.Public Gem::Atom_Utils.Static Gem::Atom_Bootstrap.Headers Gem::AtomFont + Gem::TextureAtlas ) ly_add_target( @@ -50,12 +50,14 @@ ly_add_target( Gem::LyShine.Static Legacy::CryCommon Gem::LmbrCentral - Gem::TextureAtlas RUNTIME_DEPENDENCIES Gem::LmbrCentral Gem::TextureAtlas ) +# by default, load the above "Gem::LyShine" module in Client applications: +ly_create_alias(NAME LyShine.Clients NAMESPACE Gem TARGETS Gem::LyShine) + if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME LyShine.Editor.Static STATIC @@ -85,7 +87,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Legacy::EditorCore Gem::LyShine.Static Legacy::CryCommon - Gem::LmbrCentral + Gem::LmbrCentral.Editor Gem::TextureAtlas.Editor Gem::AtomToolsFramework.Static Gem::AtomToolsFramework.Editor @@ -94,6 +96,8 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Gem::Atom_RPI.Public Gem::Atom_Utils.Static Gem::Atom_Bootstrap.Headers + RUNTIME_DEPENDENCIES + Gem::TextureAtlas.Editor ) ly_add_target( @@ -117,12 +121,16 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Legacy::CryCommon AZ::AssetBuilderSDK Gem::LyShine.Editor.Static - Gem::LmbrCentral + Gem::LmbrCentral.Editor Gem::TextureAtlas.Editor RUNTIME_DEPENDENCIES Gem::LmbrCentral.Editor Gem::TextureAtlas.Editor ) + + # by default, load the above "Gem::LyShine.Editor" module in dev tools: + ly_create_alias(NAME LyShine.Builders NAMESPACE Gem TARGETS Gem::LyShine.Editor) + ly_create_alias(NAME LyShine.Tools NAMESPACE Gem TARGETS Gem::LyShine.Editor) endif() ################################################################################ @@ -146,7 +154,9 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) Gem::LyShine.Static Legacy::CryCommon Gem::LmbrCentral - Gem::TextureAtlas + RUNTIME_DEPENDENCIES + Gem::LmbrCentral + Gem::TextureAtlas ) ly_add_googletest( NAME Gem::LyShine.Tests @@ -175,9 +185,12 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest Legacy::CryCommon AZ::AssetBuilderSDK - Gem::LmbrCentral - Gem::TextureAtlas.Editor Gem::LyShine.Editor.Static + Gem::LmbrCentral.Editor + Gem::TextureAtlas + RUNTIME_DEPENDENCIES + Gem::LmbrCentral.Editor + Gem::TextureAtlas.Editor ) ly_add_googletest( NAME Gem::LyShine.Editor.Tests diff --git a/Gems/LyShine/Code/Editor/PropertyHandlerDirectory.cpp b/Gems/LyShine/Code/Editor/PropertyHandlerDirectory.cpp index 61f6e0f3dc..893790d0f5 100644 --- a/Gems/LyShine/Code/Editor/PropertyHandlerDirectory.cpp +++ b/Gems/LyShine/Code/Editor/PropertyHandlerDirectory.cpp @@ -158,7 +158,10 @@ bool PropertyHandlerDirectory::ReadValuesIntoGUI(size_t index, PropertyDirectory ctrl->blockSignals(true); { + // Set currently selected folder path + // Note: this must be done before setting asset type below which updates the GUI display ctrl->SetCurrentAssetHint(instance); + ctrl->SetFolderSelection(instance); // We need to set the asset type so the property panel labels get // populated properly (via SetCurrentAssetType). To avoid defining @@ -166,8 +169,6 @@ bool PropertyHandlerDirectory::ReadValuesIntoGUI(size_t index, PropertyDirectory // logic to run (otherwise it will early-out due to invalid asset type). const char* throwAwayAssetType = "{43EDD212-F589-43C8-BC02-A8F9243271CB}"; ctrl->SetCurrentAssetType(AZ::Data::AssetType(throwAwayAssetType)); - - ctrl->SetFolderSelection(instance); } ctrl->blockSignals(false); diff --git a/Gems/LyShine/Code/Editor/ViewportHelpers.cpp b/Gems/LyShine/Code/Editor/ViewportHelpers.cpp index 428554a461..2195d2c9d4 100644 --- a/Gems/LyShine/Code/Editor/ViewportHelpers.cpp +++ b/Gems/LyShine/Code/Editor/ViewportHelpers.cpp @@ -30,6 +30,11 @@ namespace ViewportHelpers return isControlledByParent; } + float GetDpiScaledSize(float size) + { + return size * ViewportIcon::GetDpiScaleFactor(); + } + bool IsHorizontallyFit(const AZ::Entity* element) { bool isHorizontallyFit = false; @@ -332,11 +337,12 @@ namespace ViewportHelpers AZ::Vector2 pivotPos; EBUS_EVENT_ID_RESULT(pivotPos, element->GetId(), UiTransformBus, GetViewportSpacePivot); - AZ::Vector2 rotationStringPos(pivotPos.GetX(), pivotPos.GetY() - ((viewportPivot->GetSize().GetY() * 0.5f) + 4.0f)); + float offset = (viewportPivot->GetSize().GetY() * 0.5f) + (GetDpiScaledSize(4.0f)); + AZ::Vector2 rotationStringPos(pivotPos.GetX(), pivotPos.GetY() - offset); draw2d.SetTextAlignment(IDraw2d::HAlign::Center, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(0.0f); - draw2d.DrawText(rotationString.toUtf8().data(), rotationStringPos, 16.0f, 1.0f); + draw2d.DrawText(rotationString.toUtf8().data(), rotationStringPos, GetDpiScaledSize(16.0f), 1.0f); } } @@ -350,6 +356,6 @@ namespace ViewportHelpers draw2d.SetTextAlignment(IDraw2d::HAlign::Left, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(0.0f); - draw2d.DrawText(textLabel.c_str(), textPos, 16.0f, 1.0f); + draw2d.DrawText(textLabel.c_str(), textPos, GetDpiScaledSize(16.0f), 1.0f); } } // namespace ViewportHelpers diff --git a/Gems/LyShine/Code/Editor/ViewportIcon.cpp b/Gems/LyShine/Code/Editor/ViewportIcon.cpp index b1866efb00..4be06b2543 100644 --- a/Gems/LyShine/Code/Editor/ViewportIcon.cpp +++ b/Gems/LyShine/Code/Editor/ViewportIcon.cpp @@ -303,7 +303,7 @@ void ViewportIcon::DrawDistanceLine(Draw2dHelper& draw2d, AZ::Vector2 start, AZ: draw2d.SetTextAlignment(IDraw2d::HAlign::Center, IDraw2d::VAlign::Bottom); draw2d.SetTextRotation(rotation); - draw2d.DrawText(textBuf, textPos, 16.0f, 1.0f); + draw2d.DrawText(textBuf, textPos, 16.0f * ViewportIcon::GetDpiScaleFactor(), 1.0f); } void ViewportIcon::DrawAnchorLinesSplit(Draw2dHelper& draw2d, AZ::Vector2 anchorPos1, AZ::Vector2 anchorPos2, diff --git a/Gems/LyShine/Code/Include/LyShine/Draw2d.h b/Gems/LyShine/Code/Include/LyShine/Draw2d.h index 8270461e73..b83ec4794b 100644 --- a/Gems/LyShine/Code/Include/LyShine/Draw2d.h +++ b/Gems/LyShine/Code/Include/LyShine/Draw2d.h @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -256,9 +257,8 @@ protected: // types and constants const Draw2dShaderData& shaderData, AZ::RPI::ViewportContextPtr viewportContext) const override; - STextDrawContext m_fontContext; - IFFont* m_font; - AZ::Vector2 m_position; + AzFramework::TextDrawParameters m_drawParameters; + AzFramework::FontId m_fontId; std::string m_string; }; @@ -288,7 +288,7 @@ protected: // member functions void RotatePointsAboutPivot(AZ::Vector2* points, int numPoints, AZ::Vector2 pivot, float angle) const; //! Helper function to render a text string - void DrawTextInternal(const char* textString, IFFont* font, unsigned int effectIndex, + void DrawTextInternal(const char* textString, AzFramework::FontId fontId, unsigned int effectIndex, AZ::Vector2 position, float pointSize, AZ::Color color, float rotation, HAlign horizontalAlignment, VAlign verticalAlignment, int baseState); @@ -298,6 +298,9 @@ protected: // member functions //! Draw or defer a line void DrawOrDeferLine(const DeferredLine* line); + //! Draw or defer a text string + void DrawOrDeferTextString(const DeferredText* text); + //! Draw or defer a rect outline void DrawOrDeferRectOutline(const DeferredRectOutline* outlineRect); @@ -491,7 +494,7 @@ public: // member functions void SetImageBaseState(int state) { m_imageOptions.baseState = state; } //! Set the text font. - void SetTextFont(IFFont* font) { m_textOptions.font = font; } + void SetTextFont(AZStd::string_view fontName) { m_textOptions.fontName = fontName; } //! Set the text font effect index. void SetTextEffectIndex(unsigned int effectIndex) { m_textOptions.effectIndex = effectIndex; } diff --git a/Gems/LyShine/Code/Source/Draw2d.cpp b/Gems/LyShine/Code/Source/Draw2d.cpp index 1a639ea299..6feb47419d 100644 --- a/Gems/LyShine/Code/Source/Draw2d.cpp +++ b/Gems/LyShine/Code/Source/Draw2d.cpp @@ -11,11 +11,13 @@ */ #include "LyShine_precompiled.h" #include "IFont.h" +#include // for SVF_P3F_C4B_T2F which will be removed in a coming PR #include #include #include +#include #include #include @@ -55,7 +57,7 @@ CDraw2d::CDraw2d(AZ::RPI::ViewportContextPtr viewportContext) m_defaultImageOptions.pixelRounding = Rounding::Nearest; m_defaultImageOptions.baseState = g_defaultBaseState; - m_defaultTextOptions.font = (gEnv && gEnv->pCryFont != nullptr) ? gEnv->pCryFont->GetFont("default") : nullptr; + m_defaultTextOptions.fontName = "default"; m_defaultTextOptions.effectIndex = 0; m_defaultTextOptions.color.Set(1.0f, 1.0f, 1.0f); m_defaultTextOptions.horizontalAlignment = HAlign::Left; @@ -283,13 +285,20 @@ void CDraw2d::DrawText(const char* textString, AZ::Vector2 position, float point { TextOptions* actualTextOptions = (textOptions) ? textOptions : &m_defaultTextOptions; + AzFramework::FontId fontId = AzFramework::InvalidFontId; + AzFramework::FontQueryInterface* fontQueryInterface = AZ::Interface::Get(); + if (fontQueryInterface) + { + fontId = fontQueryInterface->GetFontId(actualTextOptions->fontName); + } + // render the drop shadow, if needed if ((actualTextOptions->dropShadowColor.GetA() > 0.0f) && (actualTextOptions->dropShadowOffset.GetX() || actualTextOptions->dropShadowOffset.GetY())) { // calculate the drop shadow pos and render it AZ::Vector2 dropShadowPosition(position + actualTextOptions->dropShadowOffset); - DrawTextInternal(textString, actualTextOptions->font, actualTextOptions->effectIndex, + DrawTextInternal(textString, fontId, actualTextOptions->effectIndex, dropShadowPosition, pointSize, actualTextOptions->dropShadowColor, actualTextOptions->rotation, actualTextOptions->horizontalAlignment, actualTextOptions->verticalAlignment, @@ -298,7 +307,7 @@ void CDraw2d::DrawText(const char* textString, AZ::Vector2 position, float point // draw the text string AZ::Color textColor = AZ::Color::CreateFromVector3AndFloat(actualTextOptions->color, opacity); - DrawTextInternal(textString, actualTextOptions->font, actualTextOptions->effectIndex, + DrawTextInternal(textString, fontId, actualTextOptions->effectIndex, position, pointSize, textColor, actualTextOptions->rotation, actualTextOptions->horizontalAlignment, actualTextOptions->verticalAlignment, @@ -398,20 +407,35 @@ void CDraw2d::DrawRectOutlineTextured(AZ::Data::Instance image, //////////////////////////////////////////////////////////////////////////////////////////////////// AZ::Vector2 CDraw2d::GetTextSize(const char* textString, float pointSize, TextOptions* textOptions) { - TextOptions* actualTextOptions = (textOptions) ? textOptions : &m_defaultTextOptions; - - if (!actualTextOptions->font) + AzFramework::FontDrawInterface* fontDrawInterface = nullptr; + AzFramework::FontQueryInterface* fontQueryInterface = AZ::Interface::Get(); + if (fontQueryInterface) + { + TextOptions* actualTextOptions = (textOptions) ? textOptions : &m_defaultTextOptions; + AzFramework::FontId fontId = fontQueryInterface->GetFontId(actualTextOptions->fontName); + fontDrawInterface = fontQueryInterface->GetFontDrawInterface(fontId); + } + if (!fontDrawInterface) { return AZ::Vector2(0.0f, 0.0f); } - STextDrawContext fontContext; - fontContext.SetEffect(actualTextOptions->effectIndex); - fontContext.SetSizeIn800x600(false); - fontContext.SetSize(vector2f(pointSize, pointSize)); + // Set up draw parameters + AzFramework::TextDrawParameters drawParams; + drawParams.m_drawViewportId = GetViewportContext()->GetId(); + drawParams.m_position = AZ::Vector3(0.0f, 0.0f, 1.0f); + drawParams.m_effectIndex = 0; + drawParams.m_textSizeFactor = pointSize; + drawParams.m_scale = AZ::Vector2(1.0f, 1.0f); + drawParams.m_lineSpacing = 1.0f; + drawParams.m_monospace = false; + drawParams.m_depthTest = false; + drawParams.m_virtual800x600ScreenSize = false; + drawParams.m_scaleWithWindow = false; + drawParams.m_multiline = true; - Vec2 textSize = actualTextOptions->font->GetTextSize(textString, true, fontContext); - return AZ::Vector2(textSize.x, textSize.y); + AZ::Vector2 textSize = fontDrawInterface->GetTextSize(drawParams, textString); + return textSize; } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -559,100 +583,89 @@ void CDraw2d::RotatePointsAboutPivot(AZ::Vector2* points, [[maybe_unused]] int n } //////////////////////////////////////////////////////////////////////////////////////////////////// -void CDraw2d::DrawTextInternal(const char* textString, IFFont* font, unsigned int effectIndex, +void CDraw2d::DrawTextInternal(const char* textString, AzFramework::FontId fontId, unsigned int effectIndex, AZ::Vector2 position, float pointSize, AZ::Color color, float rotation, - HAlign horizontalAlignment, VAlign verticalAlignment, int baseState) + HAlign horizontalAlignment, VAlign verticalAlignment, [[maybe_unused]] int baseState) { - if (!font) - { - return; - } - - STextDrawContext fontContext; - fontContext.SetEffect(effectIndex); - fontContext.SetSizeIn800x600(false); - fontContext.SetSize(vector2f(pointSize, pointSize)); - fontContext.SetColor(ColorF(color.GetR(), color.GetG(), color.GetB(), color.GetA())); - fontContext.m_baseState = baseState; - fontContext.SetOverrideViewProjMatrices(false); - // FFont.cpp uses the alpha value of the color to decide whether to use the color, if the alpha value is zero // (in a ColorB format) then the color set via SetColor is ignored and it usually ends up drawing with an alpha of 1. // This is not what we want so in this case do not draw at all. - if (!fontContext.IsColorOverridden()) + if (AZ::IsClose(color.GetA(), 0.0f)) { return; } - AZ::Vector2 alignedPosition; - if (horizontalAlignment == HAlign::Left && verticalAlignment == VAlign::Top) + // Convert Draw2d alignment to text alignment + AzFramework::TextHorizontalAlignment hAlignment = AzFramework::TextHorizontalAlignment::Left; + switch (horizontalAlignment) { - alignedPosition = position; + case HAlign::Left: + hAlignment = AzFramework::TextHorizontalAlignment::Left; + break; + case HAlign::Center: + hAlignment = AzFramework::TextHorizontalAlignment::Center; + break; + case HAlign::Right: + hAlignment = AzFramework::TextHorizontalAlignment::Right; + break; + default: + AZ_Assert(false, "Attempting to draw text with unsupported horizontal alignment."); + break; } - else - { - // we align based on the size of the default font effect, because we do not want the - // text to move when the font effect is changed - unsigned int fontEffectIndex = fontContext.m_fxIdx; - fontContext.SetEffect(0); - Vec2 textSize = font->GetTextSize(textString, true, fontContext); - fontContext.SetEffect(fontEffectIndex); - alignedPosition = Align(position, AZ::Vector2(textSize.x, textSize.y), horizontalAlignment, verticalAlignment); + AzFramework::TextVerticalAlignment vAlignment = AzFramework::TextVerticalAlignment::Top; + switch (verticalAlignment) + { + case VAlign::Top: + vAlignment = AzFramework::TextVerticalAlignment::Top; + break; + case VAlign::Center: + vAlignment = AzFramework::TextVerticalAlignment::Center; + break; + case VAlign::Bottom: + vAlignment = AzFramework::TextVerticalAlignment::Bottom; + break; + default: + AZ_Assert(false, "Attempting to draw text with unsupported vertical alignment."); + break; } - int flags = 0; + // Set up draw parameters for font interface + AzFramework::TextDrawParameters drawParams; + drawParams.m_drawViewportId = GetViewportContext()->GetId(); + drawParams.m_position = AZ::Vector3(position.GetX(), position.GetY(), 1.0f); + drawParams.m_color = color; + drawParams.m_effectIndex = effectIndex; + drawParams.m_textSizeFactor = pointSize; + drawParams.m_scale = AZ::Vector2(1.0f, 1.0f); + drawParams.m_lineSpacing = 1.0f; //!< Spacing between new lines, as a percentage of m_scale. + drawParams.m_hAlign = hAlignment; + drawParams.m_vAlign = vAlignment; + drawParams.m_monospace = false; + drawParams.m_depthTest = false; + drawParams.m_virtual800x600ScreenSize = false; + drawParams.m_scaleWithWindow = false; + drawParams.m_multiline = true; + if (rotation != 0.0f) { // rotate around the position (if aligned to center will rotate about center etc) float rotRad = DEG2RAD(rotation); - Vec3 pivot(position.GetX(), position.GetY(), 0.0f); - Matrix34A moveToPivotSpaceMat = Matrix34A::CreateTranslationMat(-pivot); - Matrix34A rotMat = Matrix34A::CreateRotationZ(rotRad); - Matrix34A moveFromPivotSpaceMat = Matrix34A::CreateTranslationMat(pivot); + AZ::Vector3 pivot(position.GetX(), position.GetY(), 0.0f); + AZ::Matrix3x4 moveToPivotSpaceMat = AZ::Matrix3x4::CreateTranslation(-pivot); + AZ::Matrix3x4 rotMat = AZ::Matrix3x4::CreateRotationZ(rotRad); + AZ::Matrix3x4 moveFromPivotSpaceMat = AZ::Matrix3x4::CreateTranslation(pivot); - Matrix34A transform = moveFromPivotSpaceMat * rotMat * moveToPivotSpaceMat; - fontContext.SetTransform(transform); - flags |= eDrawText_UseTransform; + drawParams.m_transform = moveFromPivotSpaceMat * rotMat * moveToPivotSpaceMat; + drawParams.m_useTransform = true; } - // The font system uses these alignment flags to force text to be in the safe zone - // depending on overscan etc - if (horizontalAlignment == HAlign::Center) - { - flags |= eDrawText_Center; - } - else if (horizontalAlignment == HAlign::Right) - { - flags |= eDrawText_Right; - } - - if (verticalAlignment == VAlign::Center) - { - flags |= eDrawText_CenterV; - } - else if (verticalAlignment == VAlign::Bottom) - { - flags |= eDrawText_Bottom; - } + DeferredText newText; + newText.m_drawParameters = drawParams; + newText.m_fontId = fontId; + newText.m_string = textString; - fontContext.SetFlags(flags); - - if (m_deferCalls) - { - DeferredText* newText = new DeferredText; - - newText->m_fontContext = fontContext; - newText->m_font = font; - newText->m_position = alignedPosition; - newText->m_string = textString; - - m_deferredPrimitives.push_back(newText); - } - else - { - font->DrawString(alignedPosition.GetX(), alignedPosition.GetY(), textString, true, fontContext); - } + DrawOrDeferTextString(&newText); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -685,6 +698,20 @@ void CDraw2d::DrawOrDeferLine(const DeferredLine* line) } } +void CDraw2d::DrawOrDeferTextString(const DeferredText* text) +{ + if (m_deferCalls) + { + DeferredText* newText = new DeferredText; + *newText = *text; + m_deferredPrimitives.push_back(newText); + } + else + { + text->Draw(m_dynamicDraw, m_shaderData, GetViewportContext()); + } +} + void CDraw2d::DrawOrDeferRectOutline(const DeferredRectOutline* rectOutline) { if (m_deferCalls) @@ -919,6 +946,15 @@ void CDraw2d::DeferredText::Draw([[maybe_unused]] AZ::RHI::PtrDrawString(m_position.GetX(), m_position.GetY(), m_string.c_str(), true, m_fontContext); + AzFramework::FontDrawInterface* fontDrawInterface = nullptr; + AzFramework::FontQueryInterface* fontQueryInterface = AZ::Interface::Get(); + if (fontQueryInterface) + { + fontDrawInterface = fontQueryInterface->GetFontDrawInterface(m_fontId); + if (fontDrawInterface) + { + fontDrawInterface->DrawScreenAlignedText2d(m_drawParameters, m_string.c_str()); + } + } } diff --git a/Gems/LyShine/Code/Source/LyShine.cpp b/Gems/LyShine/Code/Source/LyShine.cpp index 679cae3421..fb6dcb2628 100644 --- a/Gems/LyShine/Code/Source/LyShine.cpp +++ b/Gems/LyShine/Code/Source/LyShine.cpp @@ -454,7 +454,6 @@ void CLyShine::Render() GetUiRenderer()->EndUiFrameRender(); -#ifdef LYSHINE_ATOM_TODO // convert debug info to Atom #ifndef _RELEASE if (CV_ui_DisplayElemBounds) { @@ -474,7 +473,6 @@ void CLyShine::Render() m_uiCanvasManager->DebugDisplayDrawCallData(); } #endif -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/LyShine/Code/Source/LyShineDebug.cpp b/Gems/LyShine/Code/Source/LyShineDebug.cpp index 76b4030106..44e0e187ec 100644 --- a/Gems/LyShine/Code/Source/LyShineDebug.cpp +++ b/Gems/LyShine/Code/Source/LyShineDebug.cpp @@ -12,6 +12,7 @@ #include "LyShine_precompiled.h" #include "LyShineDebug.h" #include "IConsole.h" +#include "IRenderer.h" #include #include @@ -392,15 +393,15 @@ static void DebugDrawColoredBox(AZ::Vector2 pos, AZ::Vector2 size, AZ::Color col //////////////////////////////////////////////////////////////////////////////////////////////////// #if !defined(_RELEASE) -static void DebugDrawStringWithSizeBox(IFFont* font, unsigned int effectIndex, const char* sizeString, +static void DebugDrawStringWithSizeBox(AZStd::string_view font, unsigned int effectIndex, const char* sizeString, const char* testString, AZ::Vector2 pos, float spacing, float size) { CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); IDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); - if (font) + if (!font.empty()) { - textOptions.font = font; + textOptions.fontName = font; } textOptions.effectIndex = effectIndex; @@ -427,7 +428,7 @@ static void DebugDrawStringWithSizeBox(IFFont* font, unsigned int effectIndex, c //////////////////////////////////////////////////////////////////////////////////////////////////// #if !defined(_RELEASE) -static void DebugDraw2dFontSizes(IFFont* font, unsigned int effectIndex, const char* fontName) +static void DebugDraw2dFontSizes(AZStd::string_view font, unsigned int effectIndex) { CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); @@ -436,7 +437,7 @@ static void DebugDraw2dFontSizes(IFFont* font, unsigned int effectIndex, const c float xSpacing = 20.0f; char buffer[32]; - sprintf_s(buffer, "Font = %s, effect = %d", fontName, effectIndex); + sprintf_s(buffer, "Font = %s, effect = %d", font.data(), effectIndex); draw2d->DrawText(buffer, AZ::Vector2(xOffset, yOffset), 32); yOffset += 40.0f; draw2d->DrawText("NOTE: if the effect includes a drop shadow baked into font then the pixel size", @@ -1441,10 +1442,10 @@ void LyShineDebug::RenderDebug() switch (CV_r_DebugUIDraw2dFont) { case 1: // test font sizes (default font, effect 0) - DebugDraw2dFontSizes(0, 0, "default"); + DebugDraw2dFontSizes("default", 0); break; case 2: // test font sizes (default font, effect 1) - DebugDraw2dFontSizes(0, 1, "default"); + DebugDraw2dFontSizes("default", 1); break; case 3: // test font alignment DebugDraw2dFontAlignment(); diff --git a/Gems/LyShine/Code/Source/LyShineDebug.h b/Gems/LyShine/Code/Source/LyShineDebug.h index ed03fd10b2..e50689710a 100644 --- a/Gems/LyShine/Code/Source/LyShineDebug.h +++ b/Gems/LyShine/Code/Source/LyShineDebug.h @@ -14,7 +14,9 @@ #ifndef _RELEASE #include -class ITexture; +#include +#include + #endif //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -66,7 +68,7 @@ public: // static member functions struct DebugInfoTextureUsage { - ITexture* m_texture; + AZ::Data::Instance m_texture; bool m_isClampTextureUsage; int m_numCanvasesUsed; int m_numDrawCallsUsed; diff --git a/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp b/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp index f55c8c5957..0eab7705f6 100644 --- a/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp +++ b/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp @@ -157,6 +157,7 @@ namespace LyShine UiSystemBus::Handler::BusConnect(); UiSystemToolsBus::Handler::BusConnect(); UiFrameworkBus::Handler::BusConnect(); + CrySystemEventBus::Handler::BusConnect(); // register all the component types internal to the LyShine module // These are registered in the order we want them to appear in the Add Component menu @@ -201,6 +202,7 @@ namespace LyShine UiSystemToolsBus::Handler::BusDisconnect(); UiFrameworkBus::Handler::BusDisconnect(); LyShineRequestBus::Handler::BusDisconnect(); + CrySystemEventBus::Handler::BusDisconnect(); LyShineAllocatorScope::DeactivateAllocators(); } @@ -208,8 +210,6 @@ namespace LyShine //////////////////////////////////////////////////////////////////////////////////////////////////// void LyShineSystemComponent::InitializeSystem() { - m_pLyShine = new CLyShine(gEnv->pSystem); - gEnv->pLyShine = m_pLyShine; BroadcastCursorImagePathname(); } @@ -374,6 +374,25 @@ namespace LyShine } } + /////////////////////////////////////////////////////////////////////////////////////////////// + void LyShineSystemComponent::OnCrySystemInitialized(ISystem& system, [[maybe_unused]] const SSystemInitParams& startupParams) + { +#if !defined(AZ_MONOLITHIC_BUILD) + // When module is linked dynamically, we must set our gEnv pointer. + // When module is linked statically, we'll share the application's gEnv pointer. + gEnv = system.GetGlobalEnvironment(); +#endif + m_pLyShine = new CLyShine(gEnv->pSystem); + gEnv->pLyShine = m_pLyShine; + } + + void LyShineSystemComponent::OnCrySystemShutdown([[maybe_unused]] ISystem& system) + { + gEnv->pLyShine = nullptr; + delete m_pLyShine; + m_pLyShine = nullptr; + } + //////////////////////////////////////////////////////////////////////////////////////////////////// void LyShineSystemComponent::BroadcastCursorImagePathname() { diff --git a/Gems/LyShine/Code/Source/LyShineSystemComponent.h b/Gems/LyShine/Code/Source/LyShineSystemComponent.h index 5f45f22823..f65dc75463 100644 --- a/Gems/LyShine/Code/Source/LyShineSystemComponent.h +++ b/Gems/LyShine/Code/Source/LyShineSystemComponent.h @@ -38,6 +38,7 @@ namespace LyShine , protected UiSystemToolsBus::Handler , protected LyShineAllocatorScope , protected UiFrameworkBus::Handler + , protected CrySystemEventBus::Handler { public: AZ_COMPONENT(LyShineSystemComponent, lyShineSystemComponentUuid); @@ -89,6 +90,11 @@ namespace LyShine void HandleEditorOnlyEntities(const EntityList& exportSliceEntities, const EntityIdSet& editorOnlyEntityIds) override; //////////////////////////////////////////////////////////////////////// + // CrySystemEventBus /////////////////////////////////////////////////////// + void OnCrySystemInitialized(ISystem& system, const SSystemInitParams&) override; + virtual void OnCrySystemShutdown(ISystem&) override; + //////////////////////////////////////////////////////////////////////////// + void BroadcastCursorImagePathname(); protected: // data diff --git a/Gems/LyShine/Code/Source/RenderGraph.cpp b/Gems/LyShine/Code/Source/RenderGraph.cpp index e5ac6c7b8f..d5a1df7b15 100644 --- a/Gems/LyShine/Code/Source/RenderGraph.cpp +++ b/Gems/LyShine/Code/Source/RenderGraph.cpp @@ -18,6 +18,7 @@ #include #ifndef _RELEASE +#include #include #endif @@ -1115,7 +1116,7 @@ namespace LyShine m_wasBuiltThisFrame = false; - AZStd::set uniqueTextures; + AZStd::set> uniqueTextures; // If we are rendering to the render targets this frame then record the stats for doing that if (m_renderToRenderTargetCount < 2) @@ -1144,13 +1145,11 @@ namespace LyShine } //////////////////////////////////////////////////////////////////////////////////////////////////// - void RenderGraph::GetDebugInfoRenderNodeList(const AZStd::vector& renderNodeList, LyShineDebug::DebugInfoRenderGraph& info, AZStd::set& uniqueTextures) const + void RenderGraph::GetDebugInfoRenderNodeList( + const AZStd::vector& renderNodeList, + LyShineDebug::DebugInfoRenderGraph& info, + AZStd::set>& uniqueTextures) const { - AZ_UNUSED(renderNodeList); - AZ_UNUSED(info); - AZ_UNUSED(uniqueTextures); - -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (convert debug info to use Atom) const PrimitiveListRenderNode* prevPrimListNode = nullptr; bool isFirstNode = true; bool wasLastNodeAMask = false; @@ -1235,7 +1234,6 @@ namespace LyShine isFirstNode = false; } -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1290,13 +1288,6 @@ namespace LyShine void* context, const AZStd::string& indent) const { - AZ_UNUSED(renderNodeList); - AZ_UNUSED(fileHandle); - AZ_UNUSED(reportInfo); - AZ_UNUSED(context); - AZ_UNUSED(indent); - -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (convert debug info to use Atom) AZStd::string logLine; bool previousNodeAlreadyCounted = false; @@ -1355,10 +1346,10 @@ namespace LyShine { for (int i = 0; i < prevPrimListNode->GetNumTextures(); ++i) { - ITexture* texture = prevPrimListNode->GetTexture(i); + AZ::Data::Instance texture = prevPrimListNode->GetTexture(i); if (!texture) { - texture = gEnv->pRenderer->GetWhiteTexture(); + texture = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White); } bool isClampTextureUsage = prevPrimListNode->GetTextureIsClampMode(i); @@ -1405,17 +1396,19 @@ namespace LyShine for (int i = 0; i < primListRenderNode->GetNumTextures(); ++i) { - ITexture* texture = primListRenderNode->GetTexture(i); + AZ::Data::Instance texture = primListRenderNode->GetTexture(i); if (!texture) { - texture = gEnv->pRenderer->GetWhiteTexture(); + texture = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White); } bool isClampTextureUsage = primListRenderNode->GetTextureIsClampMode(i); LyShineDebug::DebugInfoTextureUsage* matchingTextureUsage = nullptr; // Write line to logfile for this texture - logLine = AZStd::string::format("%s %s\r\n", indent.c_str(), texture->GetName()); + AZStd::string textureName; + AZ::Data::AssetCatalogRequestBus::BroadcastResult(textureName, &AZ::Data::AssetCatalogRequests::GetAssetPathById, texture->GetAssetId()); + logLine = AZStd::string::format("%s %s\r\n", indent.c_str(), textureName.c_str()); AZ::IO::LocalFileIO::GetInstance()->Write(fileHandle, logLine.c_str(), logLine.size()); // see if texture is in reportInfo @@ -1459,7 +1452,6 @@ namespace LyShine prevPrimListNode = primListRenderNode; } } -#endif } #endif diff --git a/Gems/LyShine/Code/Source/RenderGraph.h b/Gems/LyShine/Code/Source/RenderGraph.h index f9d16cf8b7..2f1586e857 100644 --- a/Gems/LyShine/Code/Source/RenderGraph.h +++ b/Gems/LyShine/Code/Source/RenderGraph.h @@ -13,7 +13,6 @@ #pragma once #include -#include #include #include #include @@ -294,7 +293,10 @@ namespace LyShine void ValidateGraph(); void GetDebugInfoRenderGraph(LyShineDebug::DebugInfoRenderGraph& info) const; - void GetDebugInfoRenderNodeList(const AZStd::vector& renderNodeList, LyShineDebug::DebugInfoRenderGraph& info, AZStd::set& uniqueTextures) const; + void GetDebugInfoRenderNodeList( + const AZStd::vector& renderNodeList, + LyShineDebug::DebugInfoRenderGraph& info, + AZStd::set>& uniqueTextures) const; void DebugReportDrawCalls(AZ::IO::HandleType fileHandle, LyShineDebug::DebugInfoDrawCallReport& reportInfo, void* context) const; void DebugReportDrawCallsRenderNodeList(const AZStd::vector& renderNodeList, AZ::IO::HandleType fileHandle, diff --git a/Gems/LyShine/Code/Source/UiCanvasManager.cpp b/Gems/LyShine/Code/Source/UiCanvasManager.cpp index 4115feaf22..b64a13280f 100644 --- a/Gems/LyShine/Code/Source/UiCanvasManager.cpp +++ b/Gems/LyShine/Code/Source/UiCanvasManager.cpp @@ -1425,7 +1425,8 @@ void UiCanvasManager::DebugReportDrawCalls(const AZStd::string& name) const if (reportTextureUsage.m_numCanvasesUsed > 1 && reportTextureUsage.m_numDrawCallsWhereExceedingMaxTextures) { - AZStd::string textureName = reportTextureUsage.m_texture->GetName(); + AZStd::string textureName; + AZ::Data::AssetCatalogRequestBus::BroadcastResult(textureName, &AZ::Data::AssetCatalogRequests::GetAssetPathById, reportTextureUsage.m_texture->GetAssetId()); if (textureName.compare(0, fontTexturePrefix.length(), fontTexturePrefix) != 0) { logLine = AZStd::string::format("%s\r\n", textureName.c_str()); @@ -1457,7 +1458,8 @@ void UiCanvasManager::DebugReportDrawCalls(const AZStd::string& name) const reportTextureUsage.m_lastContextUsed == canvas && reportTextureUsage.m_numDrawCallsWhereExceedingMaxTextures) { - AZStd::string textureName = reportTextureUsage.m_texture->GetName(); + AZStd::string textureName; + AZ::Data::AssetCatalogRequestBus::BroadcastResult(textureName, &AZ::Data::AssetCatalogRequests::GetAssetPathById, reportTextureUsage.m_texture->GetAssetId()); // exclude font textures if (textureName.compare(0, fontTexturePrefix.length(), fontTexturePrefix) != 0) diff --git a/Gems/LyShine/Code/Source/UiRenderer.cpp b/Gems/LyShine/Code/Source/UiRenderer.cpp index 2a2c950e82..57acbed5d5 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.cpp +++ b/Gems/LyShine/Code/Source/UiRenderer.cpp @@ -12,6 +12,7 @@ #include "LyShine_precompiled.h" #include "UiRenderer.h" +#include #include #include #include @@ -24,7 +25,7 @@ #include #include -#include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC MEMBER FUNCTIONS @@ -353,7 +354,6 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) { if (recordingOption > 0) { -#ifdef LYSHINE_ATOM_TODO // compute the total area of all the textures, also create a vector that we can sort by area AZStd::vector textures; int totalArea = 0; @@ -374,15 +374,14 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) return lhs->GetDataSize() > rhs->GetDataSize(); }); - IDraw2d* draw2d = Draw2dHelper::GetDraw2d(); + CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); // setup to render lines of text for the debug display - draw2d->BeginDraw2d(false); float xOffset = 20.0f; float yOffset = 20.0f; - int blackTexture = gEnv->pRenderer->GetBlackTextureId(); + auto blackTexture = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::Black); float textOpacity = 1.0f; float backgroundRectOpacity = 0.75f; const float lineSpacing = 20.0f; @@ -432,9 +431,6 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) texture->GetWidth(), texture->GetHeight(), texture->GetDataSize(), texture->GetFormatName(), texture->GetName()); WriteLine(buffer, white); } - - draw2d->EndDraw2d(); -#endif } } diff --git a/Gems/LyShine/Code/Source/UiRenderer.h b/Gems/LyShine/Code/Source/UiRenderer.h index 260bd8278c..888c88586a 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.h +++ b/Gems/LyShine/Code/Source/UiRenderer.h @@ -20,6 +20,8 @@ #include #endif +class ITexture; + //////////////////////////////////////////////////////////////////////////////////////////////////// //! UI render interface // @@ -136,8 +138,6 @@ protected: // attributes #ifndef _RELEASE int m_debugTextureDataRecordLevel = 0; -#ifdef LYSHINE_ATOM_TODO // Convert debug code to Atom - AZStd::unordered_set m_texturesUsedInFrame; -#endif + AZStd::unordered_set m_texturesUsedInFrame; // LYSHINE_ATOM_TODO - convert to RPI::Image #endif }; diff --git a/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp b/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp index 9e57ac26fd..68da6e6009 100644 --- a/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp +++ b/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -88,6 +89,12 @@ protected: m_data->m_stubEnv.pSystem = &m_data->m_mockSystem; gEnv = &m_data->m_stubEnv; + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(m_descriptor); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleFrame.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleFrame.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleFrame.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleFrame.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleGradient.png.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleGradient.png.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleGradient.png.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleGradient.png.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/Circle_Shadow.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/Circle_Shadow.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/Circle_Shadow.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/Circle_Shadow.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTest.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTest.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTest.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTest.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTestPow2.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTestPow2.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTestPow2.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ColorTestPow2.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ParticleGlow.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ParticleGlow.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ParticleGlow.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/ParticleGlow.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/button.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/button.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/button.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/button.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonPressed.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonPressed.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonPressed.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonPressed.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonSlider.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonSlider.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonSlider.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/buttonSlider.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkbox_spritesheet.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkbox_spritesheet.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkbox_spritesheet.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkbox_spritesheet.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkered3.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkered3.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkered3.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/checkered3.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/empty_icon.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/empty_icon.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/empty_icon.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/empty_icon.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/fixed_image.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/fixed_image.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/fixed_image.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/fixed_image.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/flipbook_walking.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/flipbook_walking.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/flipbook_walking.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/flipbook_walking.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/mask.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/mask.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/mask.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/mask.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outline.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outline.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outline.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outline.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outlineRounded.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outlineRounded.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outlineRounded.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/outlineRounded.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/panelBkgd.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/panelBkgd.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/panelBkgd.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/panelBkgd.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02_big.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02_big.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02_big.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02_big.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical_big.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical_big.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical_big.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern02vertical_big.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03_big.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03_big.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03_big.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/pattern03_big.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_1.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_1.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_1.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_1.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_10.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_10.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_10.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_10.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_2.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_2.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_2.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_2.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_3.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_3.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_3.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_3.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_4.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_4.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_4.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_4.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_5.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_5.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_5.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_5.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_6.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_6.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_6.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_6.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_7.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_7.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_7.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_7.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_8.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_8.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_8.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_8.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_9.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_9.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_9.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_icon_9.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_map.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_map.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_map.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/scroll_box_map.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/selected.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/selected.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/selected.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/selected.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInside2.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInside2.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInside2.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInside2.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInsideSquare.tif.assetinfo b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInsideSquare.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInsideSquare.tif.assetinfo +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/shadowInsideSquare.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/LyShineExamples/Code/CMakeLists.txt b/Gems/LyShineExamples/Code/CMakeLists.txt index 372bfa948b..96c41bbb64 100644 --- a/Gems/LyShineExamples/Code/CMakeLists.txt +++ b/Gems/LyShineExamples/Code/CMakeLists.txt @@ -20,9 +20,10 @@ ly_add_target( PUBLIC Include BUILD_DEPENDENCIES + PRIVATE + Gem::LmbrCentral PUBLIC Legacy::CryCommon - Gem::LmbrCentral Gem::LyShine.Static ) @@ -39,4 +40,13 @@ ly_add_target( BUILD_DEPENDENCIES PRIVATE Gem::LyShineExamples.Static + Gem::LmbrCentral ) + +# if enabled, LyShineExamples is used by all kinds of applications, however, the dependency to LmbrCentral is different +# per application type +ly_create_alias(NAME LyShineExamples.Builders NAMESPACE Gem TARGETS Gem::LyShineExamples Gem::LmbrCentral.Editor) +ly_create_alias(NAME LyShineExamples.Tools NAMESPACE Gem TARGETS Gem::LyShineExamples Gem::LmbrCentral.Editor) +ly_create_alias(NAME LyShineExamples.Clients NAMESPACE Gem TARGETS Gem::LyShineExamples Gem::LmbrCentral) +ly_create_alias(NAME LyShineExamples.Servers NAMESPACE Gem TARGETS Gem::LyShineExamples Gem::LmbrCentral) + diff --git a/Gems/Maestro/Code/CMakeLists.txt b/Gems/Maestro/Code/CMakeLists.txt index fe58ba03a6..01b22b1abf 100644 --- a/Gems/Maestro/Code/CMakeLists.txt +++ b/Gems/Maestro/Code/CMakeLists.txt @@ -22,7 +22,6 @@ ly_add_target( BUILD_DEPENDENCIES PRIVATE Legacy::CryCommon - Gem::LmbrCentral ) ly_add_target( @@ -39,11 +38,14 @@ ly_add_target( PRIVATE Legacy::CryCommon Gem::Maestro.Static - Gem::LmbrCentral RUNTIME_DEPENDENCIES Gem::LmbrCentral ) +# if enabled, "Maestro" module is used for Clients and Servers: +ly_create_alias(NAME Maestro.Clients NAMESPACE Gem TARGETS Gem::Maestro) +ly_create_alias(NAME Maestro.Servers NAMESPACE Gem TARGETS Gem::Maestro) + if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME Maestro.Editor GEM_MODULE @@ -69,10 +71,13 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AzToolsFramework AZ::AssetBuilderSDK Gem::Maestro.Static - Gem::LmbrCentral RUNTIME_DEPENDENCIES Gem::LmbrCentral.Editor ) + # the .Editor variant is used in dev tools: + ly_create_alias(NAME Maestro.Tools NAMESPACE Gem TARGETS Gem::Maestro.Editor) + ly_create_alias(NAME Maestro.Builders NAMESPACE Gem TARGETS Gem::Maestro.Editor) + endif() ################################################################################ diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimComponentNode.cpp b/Gems/Maestro/Code/Source/Cinematics/AnimComponentNode.cpp index ea7322014c..324b712f40 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimComponentNode.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/AnimComponentNode.cpp @@ -324,11 +324,11 @@ void CAnimComponentNode::ConvertBetweenWorldAndLocalRotation(Quat& rotation, ETr { AZ::Quaternion rot(rotation.v.x, rotation.v.y, rotation.v.z, rotation.w); AZ::Transform rotTransform = AZ::Transform::CreateFromQuaternion(rot); - rotTransform.ExtractScale(); + rotTransform.ExtractUniformScale(); AZ::Transform parentTransform = AZ::Transform::Identity(); GetParentWorldTransform(parentTransform); - parentTransform.ExtractScale(); + parentTransform.ExtractUniformScale(); if (conversionDirection == eTransformConverstionDirection_toLocalSpace) { parentTransform.Invert(); @@ -344,7 +344,7 @@ void CAnimComponentNode::ConvertBetweenWorldAndLocalRotation(Quat& rotation, ETr void CAnimComponentNode::ConvertBetweenWorldAndLocalScale(Vec3& scale, ETransformSpaceConversionDirection conversionDirection) const { AZ::Transform parentTransform = AZ::Transform::Identity(); - AZ::Transform scaleTransform = AZ::Transform::CreateScale(AZ::Vector3(scale.x, scale.y, scale.z)); + AZ::Transform scaleTransform = AZ::Transform::CreateUniformScale(AZ::Vector3(scale.x, scale.y, scale.z).GetMaxElement()); GetParentWorldTransform(parentTransform); if (conversionDirection == eTransformConverstionDirection_toLocalSpace) @@ -353,8 +353,8 @@ void CAnimComponentNode::ConvertBetweenWorldAndLocalScale(Vec3& scale, ETransfor } scaleTransform = parentTransform * scaleTransform; - AZ::Vector3 vScale = scaleTransform.GetScale(); - scale.Set(vScale.GetX(), vScale.GetY(), vScale.GetZ()); + const float uniformScale = scaleTransform.GetUniformScale(); + scale.Set(uniformScale, uniformScale, uniformScale); } ////////////////////////////////////////////////////////////////////////// diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimNode.cpp b/Gems/Maestro/Code/Source/Cinematics/AnimNode.cpp index ec6bf4258a..7fea8097ed 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimNode.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/AnimNode.cpp @@ -280,6 +280,45 @@ static bool AnimNodeVersionConverter( rootElement.AddElement(serializeContext, "BaseClass1", azrtti_typeid()); } + if (rootElement.GetVersion() < 4) + { + // remove vector scale tracks from transform anim nodes + AZStd::string name; + if (rootElement.FindSubElementAndGetData(AZ_CRC_CE("Name"), name) && name == "Transform") + { + auto tracksElement = rootElement.FindSubElement(AZ_CRC_CE("Tracks")); + if (tracksElement) + { + for (int trackIndex = tracksElement->GetNumSubElements() - 1; trackIndex >= 0; trackIndex--) + { + auto trackElement = tracksElement->GetSubElement(trackIndex); + bool isScale = false; + + // trackElement should be an intrusive_ptr with one child + if (trackElement.GetNumSubElements() == 1) + { + auto ptrElement = trackElement.GetSubElement(0); + auto paramTypeElement = ptrElement.FindSubElement(AZ_CRC_CE("ParamType")); + if (paramTypeElement) + { + AZStd::string paramName; + if (paramTypeElement->FindSubElementAndGetData(AZ_CRC_CE("Name"), paramName) && paramName == "Scale") + { + isScale = true; + } + } + } + + if (isScale) + { + tracksElement->RemoveElement(trackIndex); + } + } + } + } + + } + return true; } @@ -288,7 +327,7 @@ void CAnimNode::Reflect(AZ::ReflectContext* context) if (auto serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(3, &AnimNodeVersionConverter) + ->Version(4, &AnimNodeVersionConverter) ->Field("ID", &CAnimNode::m_id) ->Field("Name", &CAnimNode::m_name) ->Field("Flags", &CAnimNode::m_flags) diff --git a/Gems/MessagePopup/Code/CMakeLists.txt b/Gems/MessagePopup/Code/CMakeLists.txt index 2d0ad1ebcc..fa89b61f21 100644 --- a/Gems/MessagePopup/Code/CMakeLists.txt +++ b/Gems/MessagePopup/Code/CMakeLists.txt @@ -38,3 +38,7 @@ ly_add_target( PRIVATE Gem::MessagePopup.Static ) + +# MessagePopup is used only in client applications +ly_create_alias(NAME MessagePopup.Clients NAMESPACE Gem TARGETS Gem::MessagePopup) + diff --git a/Gems/Metastream/Code/CMakeLists.txt b/Gems/Metastream/Code/CMakeLists.txt index 326c21c5a9..95f7746b91 100644 --- a/Gems/Metastream/Code/CMakeLists.txt +++ b/Gems/Metastream/Code/CMakeLists.txt @@ -50,6 +50,11 @@ ly_add_target( Legacy::CryCommon ) +# The above "Metastream" target is used by all types of applications, including dev tools. +ly_create_alias(NAME Metastream.Clients NAMESPACE Gem TARGETS Gem::Metastream) +ly_create_alias(NAME Metastream.Servers NAMESPACE Gem TARGETS Gem::Metastream) +ly_create_alias(NAME Metastream.Builders NAMESPACE Gem TARGETS Gem::Metastream) +ly_create_alias(NAME Metastream.Tools NAMESPACE Gem TARGETS Gem::Metastream) ################################################################################ # Tests diff --git a/Gems/Microphone/Code/CMakeLists.txt b/Gems/Microphone/Code/CMakeLists.txt index 942899735c..17d492d786 100644 --- a/Gems/Microphone/Code/CMakeLists.txt +++ b/Gems/Microphone/Code/CMakeLists.txt @@ -46,3 +46,7 @@ ly_add_target( RUNTIME_DEPENDENCIES Gem::AudioSystem ) + +# The above "Microphone" target is used by all interactive applications +ly_create_alias(NAME Microphone.Clients NAMESPACE Gem TARGETS Gem::Microphone) +ly_create_alias(NAME Microphone.Tools NAMESPACE Gem TARGETS Gem::Microphone) diff --git a/Gems/Multiplayer/Code/CMakeLists.txt b/Gems/Multiplayer/Code/CMakeLists.txt index 019f341d0c..430fe5ca4b 100644 --- a/Gems/Multiplayer/Code/CMakeLists.txt +++ b/Gems/Multiplayer/Code/CMakeLists.txt @@ -58,9 +58,35 @@ ly_add_target( Gem::CertificateManager ) +ly_add_target( + NAME Multiplayer.Debug ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAMESPACE Gem + FILES_CMAKE + multiplayer_debug_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + . + PUBLIC + Include + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AtomCore + AZ::AzFramework + AZ::AzNetworking + Gem::Atom_Feature_Common.Static + Gem::Multiplayer.Static + Gem::ImGui.Static +) + +# The "Multiplayer" target is used by clients and servers, Debug is used only on clients. +ly_create_alias(NAME Multiplayer.Clients NAMESPACE Gem TARGETS Gem::Multiplayer Gem::Multiplayer.Debug) +ly_create_alias(NAME Multiplayer.Servers NAMESPACE Gem TARGETS Gem::Multiplayer) + if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( - NAME Multiplayer.Tools.Static STATIC + NAME Multiplayer.Builders.Static STATIC NAMESPACE Gem FILES_CMAKE multiplayer_tools_files.cmake @@ -80,10 +106,12 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Gem::Multiplayer.Static ) + # by naming this target Multiplayer.Builders it ensures that it is loaded + # in any pipeline tools (Like Asset Processor, AssetBuilder, etc) ly_add_target( - NAME Multiplayer.Tools MODULE + NAME Multiplayer.Builders GEM_MODULE NAMESPACE Gem - OUTPUT_NAME Gem.Multiplayer.Tools + OUTPUT_NAME Gem.Multiplayer.Builders FILES_CMAKE multiplayer_tools_files.cmake INCLUDE_DIRECTORIES @@ -94,7 +122,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Include BUILD_DEPENDENCIES PRIVATE - Gem::Multiplayer.Tools.Static + Gem::Multiplayer.Builders.Static ) ly_add_target( @@ -121,9 +149,11 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AzNetworking AZ::AzToolsFramework Gem::Multiplayer.Static - Gem::Multiplayer.Tools + Gem::Multiplayer.Builders ) + # use the Multiplayer.Editor module in tools like the Editor: Such tools also get the visual debug view: + ly_create_alias(NAME Multiplayer.Tools NAMESPACE Gem TARGETS Gem::Multiplayer.Editor Gem::Multiplayer.Debug) endif() if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) @@ -151,7 +181,7 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( - NAME Multiplayer.Tools.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAME Multiplayer.Builders.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} NAMESPACE Gem FILES_CMAKE multiplayer_tools_tests_files.cmake @@ -165,33 +195,11 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest AZ::AzTestShared AZ::AzToolsFrameworkTestCommon - Gem::Multiplayer.Tools.Static + Gem::Multiplayer.Builders.Static ) ly_add_googletest( - NAME Gem::Multiplayer.Tools.Tests + NAME Gem::Multiplayer.Builders.Tests ) endif() endif() - -ly_add_target( - NAME Multiplayer.Debug ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} - NAMESPACE Gem - FILES_CMAKE - multiplayer_debug_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - Source - . - PUBLIC - Include - BUILD_DEPENDENCIES - PRIVATE - AZ::AzCore - AZ::AtomCore - AZ::AzFramework - AZ::AzNetworking - Gem::Atom_Feature_Common.Static - Gem::Multiplayer.Static - Gem::ImGui.Static -) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h index 2a3b5fb3cc..f3eb1922fd 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h @@ -34,11 +34,11 @@ namespace Multiplayer private: void OnRotationChangedEvent(const AZ::Quaternion& rotation); void OnTranslationChangedEvent(const AZ::Vector3& translation); - void OnScaleChangedEvent(const AZ::Vector3& scale); + void OnScaleChangedEvent(float scale); AZ::Event::Handler m_rotationEventHandler; AZ::Event::Handler m_translationEventHandler; - AZ::Event::Handler m_scaleEventHandler; + AZ::Event::Handler m_scaleEventHandler; }; class NetworkTransformComponentController diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h index 16cc4146dd..85c7e85c0a 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -41,7 +42,7 @@ namespace Multiplayer //! This is a strong typedef for representing the number of application frames since application start. AZ_TYPE_SAFE_INTEGRAL(HostFrameId, uint32_t); - static constexpr HostFrameId InvalidHostFrameId = HostFrameId{ 0xFFFFFFFF }; + static constexpr HostFrameId InvalidHostFrameId = HostFrameId{ AzPhysics::SimulatedBody::UndefinedFrameId }; using LongNetworkString = AZ::CVarFixedString; using ReliabilityType = AzNetworking::ReliabilityType; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.h new file mode 100644 index 0000000000..01ae7b1207 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.h @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +#include + +namespace Multiplayer +{ + //! @class RewindableArray + //! @brief Data structure that has a compile-time upper bound, provides array semantics and supports network serialization + template + class RewindableArray + : public AZStd::array, SIZE> + { + public: + //! Serialization method for array contained rewindable objects + //! @param serializer ISerializer instance to use for serialization + //! @return bool true for success, false for serialization failure + bool Serialize(AzNetworking::ISerializer& serializer); + + //! Serialization method for array contained rewindable objects + //! @param serializer ISerializer instance to use for serialization + //! @param deltaRecord Bitset delta record used to detect state change during reconciliation + //! @return bool true for success, false for serialization failure + bool Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset &deltaRecord); + }; +} + +#include diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.inl b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.inl new file mode 100644 index 0000000000..6e496ae4ea --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.inl @@ -0,0 +1,53 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +namespace Multiplayer +{ + template + bool RewindableArray::Serialize(AzNetworking::ISerializer& serializer) + { + for (uint32_t i = 0; i < SIZE; ++i) + { + if(!this[i].Serialize(serializer)) + { + return false; + } + } + + return serializer.IsValid(); + } + + template + bool RewindableArray::Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset& deltaRecord) + { + for (uint32_t i = 0; i < SIZE; ++i) + { + if (deltaRecord.GetBit(i)) + { + serializer.ClearTrackedChangesFlag(); + if(!this[i].Serialize(serializer)) + { + return false; + } + + if ((serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) && !serializer.GetTrackedChangesFlag()) + { + deltaRecord.SetBit(i, false); + } + } + } + + return serializer.IsValid(); + } +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h new file mode 100644 index 0000000000..c05fb98f72 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h @@ -0,0 +1,127 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +#include + +namespace Multiplayer +{ + //! @class RewindableFixedVector + //! @brief Data structure that has a compile-time upper bound, provides vector semantics and supports network serialization + template + class RewindableFixedVector + { + public: + //! Default constructor + constexpr RewindableFixedVector() = default; + + //! Construct and initialize buffer to the provided value + //! @param initialValue initial value to set the internal buffer to + //! @param count initial value to reserve in the vector + constexpr RewindableFixedVector(const TYPE& initialValue, uint32_t count); + + //! Destructor + ~RewindableFixedVector(); + + //! Serialization method for fixed vector contained rewindable objects + //! @param serializer ISerializer instance to use for serialization + //! @return bool true for success, false for serialization failure + bool Serialize(AzNetworking::ISerializer& serializer); + + //! Serialization method for fixed vector contained rewindable objects + //! @param serializer ISerializer instance to use for serialization + //! @param deltaRecord Bitset delta record used to detect state change during reconciliation + //! @return bool true for success, false for serialization failure + bool Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset &deltaRecord); + + //! Copies elements from the buffer pointed to by Buffer to this FixedSizeVector instance, vector size will be set to BufferSize + //! @param buffer pointer to the buffer to copy + //! @param bufferSize number of elements in the buffer to copy + //! @return bool true on success, false if the input data was too large to fit in the vector + constexpr bool copy_values(const TYPE* buffer, uint32_t bufferSize); + + //! Copy buffer from the provided vector + //! @param RHS instance to copy from + constexpr RewindableFixedVector& operator=(const RewindableFixedVector& rhs); + + //! Equality operator, returns true if the current instance is equal to RHS + //! @param rhs the FixedSizeVector instance to test for equality against + //! @return bool true if equal, false if not + constexpr bool operator ==(const RewindableFixedVector& rhs) const; + + //! Inequality operator, returns true if the current instance is not equal to RHS + //! @param rhs the FixedSizeVector instance to test for inequality against + //! @return bool false if equal, true if not equal + constexpr bool operator !=(const RewindableFixedVector& rhs) const; + + //! Resizes the vector to the requested number of elements, initializing new elements if necessary + //! @param count the number of elements to size the vector to + //! @return bool true on success + constexpr bool resize(uint32_t count); + + //! Resizes the vector to the requested number of elements, without initialization + //! @param count the number of elements to size the vector to + //! @return bool true on success + constexpr bool resize_no_construct(uint32_t count); + + //! Resets the vector, returning it to size 0 + constexpr void clear(); + + //! Const element access + //! @param Index index of the element to return + //! @return const reference to the requested element + constexpr const TYPE& operator[](uint32_t index) const; + + //! Non-const element access + //! @param Index index of the element to return + //! @return non-const reference to the requested element + constexpr TYPE& operator[](uint32_t index); + + //! Pushes a new element to the back of the vector + //! @param Value value to append to the back of this vector + //! @return boolean true on success, false if the vector was full + constexpr bool push_back(const TYPE& value); + + //! Pops the last element off the vector, decreasing the vector's size by one + //! @return bool true on success, false if the vector was empty + constexpr bool pop_back(); + + //! Returns if the vector is empty + //! @return bool true on empty, false if the vector contains valid elements + constexpr bool empty() const; + + //! Gets the last element of the vector + constexpr const TYPE& back() const; + + //! Gets the size of the vector + constexpr uint32_t size() const; + + typedef const RewindableObject* const_iterator; + const_iterator begin() const { return m_container.cbegin(); } + const_iterator end() const { return m_container.cbegin() + aznumeric_cast(size()); } + typedef RewindableObject* iterator; + constexpr iterator begin() { return m_container.begin(); } + constexpr iterator end() { return m_container.begin() + aznumeric_cast(size()); } + + private: + AZStd::array, SIZE> m_container; + // Synchronized value for vector size, prefer using size() locally which checks m_container.size() + RewindableObject m_rewindableSize; + }; +} + +#include diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl new file mode 100644 index 0000000000..5690e51c35 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl @@ -0,0 +1,230 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +namespace Multiplayer +{ + template + constexpr RewindableFixedVector::RewindableFixedVector(const TYPE& initialValue, uint32_t count) + { + m_container.fill(initialValue); + m_rewindableSize = count; + } + + template + RewindableFixedVector::~RewindableFixedVector() + { + ; + } + + template + bool RewindableFixedVector::Serialize(AzNetworking::ISerializer& serializer) + { + if(!m_rewindableSize.Serialize(serializer)) + { + return false; + } + + for (uint32_t idx = 0; idx < size(); ++idx) + { + if(!m_container[idx].Serialize(serializer)) + { + return false; + } + } + + return serializer.IsValid(); + } + + template + bool RewindableFixedVector::Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset& deltaRecord) + { + if (deltaRecord.GetBit(SIZE)) + { + const uint32_t origSize = m_rewindableSize; + if(!m_rewindableSize.Serialize(serializer)) + { + return false; + } + + if ((serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) && origSize == m_rewindableSize) + { + deltaRecord.SetBit(SIZE, false); + } + } + for (uint32_t idx = 0; idx < size(); ++idx) + { + if (deltaRecord.GetBit(idx)) + { + serializer.ClearTrackedChangesFlag(); + if(!m_container[idx].Serialize(serializer)) + { + return false; + } + + if ((serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) && !serializer.GetTrackedChangesFlag()) + { + deltaRecord.SetBit(idx, false); + } + } + } + + return serializer.IsValid(); + } + + template + constexpr bool RewindableFixedVector::copy_values(const TYPE* buffer, uint32_t bufferSize) + { + if (!resize(bufferSize)) + { + return false; + } + + for (uint32_t idx = 0; idx < bufferSize; ++idx) + { + m_container[idx] = buffer[idx]; + } + + return true; + } + + template + constexpr RewindableFixedVector& RewindableFixedVector::operator=(const RewindableFixedVector& rhs) + { + resize(rhs.size()); + for (uint32_t idx = 0; idx < size(); ++idx) + { + m_container[idx] = rhs.m_container[idx].Get(); + } + return *this; + } + + template + constexpr bool RewindableFixedVector::operator ==(const RewindableFixedVector& rhs) const + { + return m_container == rhs.m_container && m_rewindableSize == rhs.m_rewindableSize; + } + + template + constexpr bool RewindableFixedVector::operator !=(const RewindableFixedVector& rhs) const + { + return !(*this == rhs); + } + + template + constexpr bool RewindableFixedVector::resize(uint32_t count) + { + if (count > SIZE) + { + return false; + } + + if (count == size()) + { + return true; + } + + if (count > size()) + { + for (uint32_t idx = size(); idx < count; ++idx) + { + m_container[idx] = TYPE(); + } + } + m_rewindableSize = count; + + return true; + } + + template + constexpr bool RewindableFixedVector::resize_no_construct(uint32_t count) + { + if (count > SIZE) + { + return false; + } + + m_rewindableSize = count; + + return true; + } + + template + constexpr void RewindableFixedVector::clear() + { + for (uint32_t idx = 0; idx < SIZE; ++idx) + { + m_container[idx] = TYPE(); + } + m_rewindableSize = 0; + } + + template + constexpr const TYPE& RewindableFixedVector::operator[](uint32_t index) const + { + AZ_Assert(index < size(), "Out of bounds access (requested %u, reserved %u)", index, size()); + return m_container[index].Get(); + } + + template + constexpr TYPE& RewindableFixedVector::operator[](uint32_t index) + { + AZ_Assert(index < size(), "Out of bounds access (requested %u, reserved %u)", index, size()); + return m_container[index].Modify(); + } + + template + constexpr bool RewindableFixedVector::push_back(const TYPE& value) + { + if (size() < SIZE) + { + m_container[m_rewindableSize] = value; + m_rewindableSize = m_rewindableSize + 1; + return true; + } + + return false; + } + + template + constexpr bool RewindableFixedVector::pop_back() + { + if (size() > 0) + { + m_rewindableSize = m_rewindableSize - 1; + m_container[m_rewindableSize] = TYPE(); + return true; + } + + return false; + } + + template + constexpr bool RewindableFixedVector::empty() const + { + return m_rewindableSize.Get() == 0; + } + + template + constexpr const TYPE& RewindableFixedVector::back() const + { + AZ_Assert(size() > 0, "Attempted to get back element of an empty RewindableFixedVector"); + return m_container[m_rewindableSize - 1].Get(); + } + + template + constexpr uint32_t RewindableFixedVector::size() const + { + return m_rewindableSize; + } +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h index d5b7d563ab..f7e92bbe26 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h @@ -32,7 +32,7 @@ namespace Multiplayer RewindableObject() = default; //! Constructor. - //! @param connectionId the connectionId of the connection that owns the object. + //! @param value base type value to construct from RewindableObject(const BASE_TYPE& value); //! Copy construct from underlying base type. diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Physics/PhysicsUtils.h b/Gems/Multiplayer/Code/Include/Multiplayer/Physics/PhysicsUtils.h new file mode 100644 index 0000000000..4013e20a1e --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Physics/PhysicsUtils.h @@ -0,0 +1,37 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace Multiplayer +{ + namespace Physics + { + //! Performs rewind-aware ray cast in the default physics world. + //! @param request The ray cast request to make. + //! @return Returns a structure that contains a list of Hits. + AzPhysics::SceneQueryHits RayCast(const AzPhysics::RayCastRequest& request); + + //! Performs rewind-aware shape cast in the default physics world. + //! @param request The shape cast request to make. + //! @return Returns a structure that contains a list of Hits. + AzPhysics::SceneQueryHits ShapeCast(const AzPhysics::ShapeCastRequest& request); + + //! Performs rewind-aware overlap in the default physics world. + //! @param request The overlap request to make. + //! @return Returns a structure that contains a list of Hits. + AzPhysics::SceneQueryHits Overlap(const AzPhysics::OverlapRequest& request); + + } // namespace Physics +} // namespace Multiplayer diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index 71e81b6bfb..79fbb4a99e 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -7,13 +7,21 @@ {% macro DeclareNetworkPropertyGetter(Property) %} {% set PropertyName = UpperFirst(Property.attrib['Name']) %} {% if Property.attrib['Container'] == 'Array' %} -const AZStd::array<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::k_RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Array() const; +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Array() const; +{% else %} +const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Array() const; +{% 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 %} +const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Vector() const; +{% else %} const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Vector() const; +{% endif %} const {{ Property.attrib['Type'] }} &Get{{ PropertyName }}(int32_t index) const; const {{ Property.attrib['Type'] }} &{{ PropertyName }}GetBack() const; uint32_t {{ PropertyName }}GetSize() const; @@ -156,9 +164,17 @@ AZ::Event<{{ Property.attrib['Type'] }}> m_{{ LowerFirst(Property.attrib['Name'] {% macro DeclareNetworkPropertyVars(Component, ReplicateFrom, ReplicateTo) %} {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %} {% if Property.attrib['Container'] == 'Array' %} -AZStd::array<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% if Property.attrib['IsRewindable']|booleanTrue %} +RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% else %} +AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% endif %} {% elif Property.attrib['Container'] == 'Vector' %} +{% if Property.attrib['IsRewindable']|booleanTrue %} +RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% else %} AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% endif %} {% elif Property.attrib['IsRewindable']|booleanTrue %} Multiplayer::RewindableObject<{{ Property.attrib['Type'] }}, Multiplayer::RewindHistorySize> m_{{ LowerFirst(Property.attrib['Name']) }} = {{ Property.attrib['Init'] }}; {% else %} @@ -228,6 +244,8 @@ AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] } #include #include #include +#include +#include #include {% call(Include) AutoComponentMacros.ParseIncludes(Component) %} #include <{{ Include.attrib['File'] }}> @@ -365,6 +383,8 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Client', true)|indent(8) -}} {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', false)|indent(8) -}} {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', true)|indent(8) -}} + {{ DeclareNetworkPropertyAccessors(Component, 'Autonomous', 'Authority', false)|indent(8) -}} + {{ DeclareNetworkPropertyAccessors(Component, 'Autonomous', 'Authority', true)|indent(8) -}} {{ DeclareArchetypePropertyGetters(Component)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}} @@ -438,7 +458,6 @@ namespace {{ Component.attrib['Namespace'] }} {% endif %} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Server', false)|indent(8) -}} - {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Autonomous', false)|indent(8) -}} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', false)|indent(8) -}} {{ DeclareArchetypePropertyGetters(Component)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) -}} @@ -463,8 +482,6 @@ namespace {{ Component.attrib['Namespace'] }} //! @} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Server', true)|indent(8) -}} - {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Autonomous', true)|indent(8) -}} - {{ DeclareNetworkPropertyGetters(Component, 'Autonomous', 'Authority', true)|indent(8) -}} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', true)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}} {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Client', false)|indent(8) -}} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 7d5295aabb..5cfd0bfc4d 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -3,7 +3,11 @@ {% macro LowerFirst(text) %}{{ text[0] | lower}}{{ text[1:] }}{% endmacro %} {% macro DefineNetworkPropertyGet(ClassName, Property, Prefix = '') %} {% if Property.attrib['Container'] == 'Array' %} -const AZStd::array<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% else %} +const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -21,7 +25,11 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}AddEvent(AZ::Even {% endif %} {% elif Property.attrib['Container'] == 'Vector' %} +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const +{% else %} const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -111,24 +119,28 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(int32_t index bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PushBack(const {{ Property.attrib['Type'] }} &value) { int32_t indexToSet = GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size(); - GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value); - int32_t bitIndex = indexToSet + static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); - GetParent().MarkDirty(); - return true; + if (indexToSet < {{ Property.attrib['Count'] }}) + { + GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value); + int32_t bitIndex = indexToSet + static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().MarkDirty(); + return true; + } + return false; } bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PopBack(const Multiplayer::NetworkInput&) { - if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.empty()) + if (!GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.empty()) { - return false; + GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back(); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().MarkDirty(); + return true; } - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); - GetParent().MarkDirty(); - GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back(); - return true; + return false; } void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}Clear(const Multiplayer::NetworkInput&) @@ -207,30 +219,34 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(int32_t index bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PushBack(const {{ Property.attrib['Type'] }} &value) { - uint32_t indexToSet = aznumeric_cast(GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size()); - GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value); - uint32_t bitIndex = indexToSet + aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); - GetParent().MarkDirty(); - return true; + int32_t indexToSet = GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size(); + if (indexToSet < {{ Property.attrib['Count'] }}) + { + GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value); + int32_t bitIndex = indexToSet + static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().MarkDirty(); + return true; + } + return false; } bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PopBack() { - if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.empty()) + if (!GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.empty()) { - return false; + GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back(); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().MarkDirty(); + return true; } - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); - GetParent().MarkDirty(); - GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back(); - return true; + return false; } void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}Clear() { - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.clear(); GetParent().MarkDirty(); } @@ -562,12 +578,12 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re {%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%} {% endcall %} {% if networkPropertyCount.value > 0 %} - Multiplayer::MultiplayerStats& stats = Multiplayer::GetMultiplayer()->GetStats(); + [[maybe_unused]] Multiplayer::MultiplayerStats& stats = Multiplayer::GetMultiplayer()->GetStats(); // We modify the record if we are writing an update so that we don't notify for a change that really didn't change the value (just a duplicated send from the server) [[maybe_unused]] bool modifyRecord = serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject; {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %} {% if Property.attrib['Container'] != 'None' and Property.attrib['Container'] != 'Object' %} - { /* @todo Implement serialization for Vector and Array Network Properties + { // Serialization for Vector and Array Network Properties const uint32_t firstBit = static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); {% if Property.attrib['Container'] == 'Vector' %} const uint32_t lastBit = static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}); @@ -575,17 +591,16 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re const uint32_t lastBit = static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'End') }}); {% endif %} - AzNetworking::BitsetView deltaRecord(replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}, firstBit, lastBit - firstBit + 1); - if (deltaRecord.AnySet()) - { -{% if Property.attrib['Container'] == 'Vector' %} - Multiplayer::SerializableFixedSizeVectorDeltaStruct<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> deltaStruct(m_{{ LowerFirst(Property.attrib['Name']) }}, deltaRecord); -{% else %} - Multiplayer::SerializableFixedSizeArrayDeltaStruct<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> deltaStruct(m_{{ Property.attrib['Name'] }}, deltaRecord); -{% endif %} - serializer.Serialize(deltaStruct, "{{ UpperFirst(Property.attrib['Name']) }}"); - } - */ +{% if Property.attrib['IsRewindable']|booleanTrue %} + AzNetworking::FixedSizeBitsetView deltaRecord(replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}, firstBit, lastBit - firstBit + 1); + m_{{ LowerFirst(Property.attrib['Name']) }}.Serialize(serializer, deltaRecord); +{% else %} +{% if Property.attrib['Container'] == 'Vector' %} + serializer.Serialize>(m_{{ LowerFirst(Property.attrib['Name']) }}, "{{ LowerFirst(Property.attrib['Name']) }}"); +{% elif Property.attrib['Container'] == 'Array' %} + serializer.Serialize>(m_{{ LowerFirst(Property.attrib['Name']) }}, "{{ LowerFirst(Property.attrib['Name']) }}"); +{% endif %} +{% endif %} } {% else %} Multiplayer::SerializeNetworkPropertyHelper @@ -615,19 +630,19 @@ void {{ ClassName }}::NotifyChanges{{ AutoComponentMacros.GetNetPropertiesSetNam {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %} {% if (Property.attrib['GenerateEventBindings']|booleanTrue) %} {% if Property.attrib['Container'] != 'None' and Property.attrib['Container'] != 'Object' %} - /* todo Implement NotifyChangesAuthorityToClientProperties for Arrays and Vectors - for (uint32_t bitIndex = static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}), elementIndex = 0; bitIndex <= static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component, ReplicateFrom, ReplicateTo, Property, 'End') }}); ++bitIndex, ++elementIndex) + // NotifyChangesAuthorityToClientProperties for Arrays and Vectors + for (uint32_t bitIndex = static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}), elementIndex = 0; bitIndex <= static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'End') }}); ++bitIndex, ++elementIndex) { - if (replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.GetBit(bitIndex){% if Property.attrib['Container'] == 'Vector' %} && elementIndex < m_{{ Property.attrib['Name'] }}.GetSize(){% endif %}) + if (replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.GetBit(bitIndex){% if Property.attrib['Container'] == 'Vector' %} && elementIndex < m_{{ LowerFirst(Property.attrib['Name']) }}.size(){% endif %}) { - m_LowerFirst( Property.attrib['Name']) }}Event.Signal(elementIndex, m_{{ LowerFirst(Property.attrib['Name']) }}[elementIndex]); + m_{{ LowerFirst(Property.attrib['Name']) }}Event.Signal(elementIndex, m_{{ LowerFirst(Property.attrib['Name']) }}[elementIndex]); } } {% if Property.attrib['Container'] == 'Vector' %} if (replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.GetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}))) { m_{{ LowerFirst(Property.attrib['Name']) }}SizeChangedEvent.Signal(m_{{ LowerFirst(Property.attrib['Name']) }}.size()); - } */ + } {% endif %} {% else %} if (replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.GetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property) }}))) @@ -645,7 +660,11 @@ void {{ ClassName }}::NotifyChanges{{ AutoComponentMacros.GetNetPropertiesSetNam {% macro DefineArchetypePropertyGet(Property, ClassType, ClassName, Prefix = '') %} {% if ClassType == '' or Property.attrib['ExportTo'] == ClassType or Property.attrib['ExportTo'] == "Common" %} {% if Property.attrib['Container'] == 'Array' %} +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% else %} const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -656,7 +675,11 @@ const {{ Property.attrib['Type'] }}& {{ ClassName }}::Get{{ UpperFirst(Property. } {% elif Property.attrib['Container'] == 'Vector' %} +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const +{% else %} const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -773,12 +796,27 @@ enum class NetworkProperties AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} failed. Entity '%s' (id: %s) is missing {{ ClassName }}, be sure to add {{ ClassName }} to this entity.", entity->GetName().c_str(), id.ToString().c_str()) return {{ Property.attrib['Type'] }}(); } +{% if (ReplicateTo == 'Autonomous') or (ReplicateFrom == 'Autonomous' and ReplicateTo == 'Authority') %} + // {{ UpperFirst(Property.attrib['Name']) }} is only sent and received between contoller objects (ie Authority, Autonomous); we must go through the controller in order to get this property + {{ ClassName }}Controller* controller = static_cast<{{ ClassName }}Controller*>(networkComponent->GetController()); + if (!controller) + { + AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This property is replicated to autonomous network entities, because this entity doesn't have a controller, it must not be automonous. Please check your network context before attempting to get {{ UpperFirst(Property.attrib['Name']) }}.", entity->GetName().c_str(), id.ToString().c_str()) + return {{ Property.attrib['Type'] }}(); + } +{% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' %} + return controller->Get{{ UpperFirst(Property.attrib['Name']) }}(index); +{% else %} + return controller->Get{{ UpperFirst(Property.attrib['Name']) }}(); +{% endif %} +{% else %} {% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' %} return networkComponent->Get{{ UpperFirst(Property.attrib['Name']) }}(index); {% else %} return networkComponent->Get{{ UpperFirst(Property.attrib['Name']) }}(); {% endif %} +{% endif %} }) {% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' %} ->Method("Set{{ UpperFirst(Property.attrib['Name']) }}", [](AZ::EntityId id, int32_t index, const {{ Property.attrib['Type'] }}& value) -> void @@ -1457,12 +1495,8 @@ namespace {{ Component.attrib['Namespace'] }} } {{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', false, ComponentBaseName)|indent(4) -}} -{{ DefineNetworkPropertyGets(Component, 'Authority', 'Autonomous', false, ComponentBaseName)|indent(4) -}} -{{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', false, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Client', false, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', true, ComponentBaseName)|indent(4) -}} -{{ DefineNetworkPropertyGets(Component, 'Authority', 'Autonomous', true, ComponentBaseName)|indent(4) -}} -{{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', true, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Client', true, ComponentBaseName)|indent(4) }} {{ DefineArchetypePropertyGets(Component, ClassType, ComponentBaseName)|indent(4) -}} {{ DefineRpcInvocations(Component, ComponentBaseName, 'Server', 'Authority', false)|indent(4) -}} @@ -1472,7 +1506,14 @@ namespace {{ Component.attrib['Namespace'] }} { {% for Property in Component.iter('NetworkProperty') %} {% if Property.attrib['IsRewindable']|booleanTrue %} +{% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' %} + for ( auto& element: m_{{ LowerFirst(Property.attrib['Name']) }}) + { + element.SetOwningConnectionId(connectionId); + } +{% else %} m_{{ LowerFirst(Property.attrib['Name']) }}.SetOwningConnectionId(connectionId); +{% endif %} {% endif %} {% endfor %} } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml index 96653a607c..a112cde4e6 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml @@ -14,7 +14,7 @@ - + diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index 0cc4cb131e..682f7ea988 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -32,7 +32,7 @@ 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](const AZ::Vector3& scale) { OnScaleChangedEvent(scale); }) + , m_scaleEventHandler([this](float scale) { OnScaleChangedEvent(scale); }) { ; } @@ -68,10 +68,10 @@ namespace Multiplayer GetTransformComponent()->SetWorldTM(worldTm); } - void NetworkTransformComponent::OnScaleChangedEvent(const AZ::Vector3& scale) + void NetworkTransformComponent::OnScaleChangedEvent(float scale) { AZ::Transform worldTm = GetTransformComponent()->GetWorldTM(); - worldTm.SetScale(scale); + worldTm.SetUniformScale(scale); GetTransformComponent()->SetWorldTM(worldTm); } @@ -100,7 +100,7 @@ namespace Multiplayer { SetRotation(worldTm.GetRotation()); SetTranslation(worldTm.GetTranslation()); - SetScale(worldTm.GetScale()); + SetScale(worldTm.GetUniformScale()); } } } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index ef8627fe54..0818f605df 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -438,19 +438,16 @@ namespace Multiplayer m_connAcquiredEvent.Signal(datum); } + // Hosts will spawn a new default player prefab for the user that just connected if (GetAgentType() == MultiplayerAgentType::ClientServer || GetAgentType() == MultiplayerAgentType::DedicatedServer) { - PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast(sv_defaultPlayerSpawnAsset).c_str()), 1); - INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity()); - - NetworkEntityHandle controlledEntity; - if (entityList.size() > 0) + NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(); + if (controlledEntity.Exists()) { - controlledEntity = entityList[0]; controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId()); } - + if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so { connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity)); @@ -522,6 +519,17 @@ namespace Multiplayer } } m_agentType = multiplayerType; + + // Spawn the default player for this host since the host is also a player (not a dedicated server) + if (m_agentType == MultiplayerAgentType::ClientServer) + { + NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(); + if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent()) + { + controlledEntityNetBindComponent->SetAllowAutonomy(true); + } + } + AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType)); } @@ -629,6 +637,19 @@ namespace Multiplayer } } + NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab() + { + PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast(sv_defaultPlayerSpawnAsset).c_str()), 1); + INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity()); + + NetworkEntityHandle controlledEntity; + if (entityList.size() > 0) + { + controlledEntity = entityList[0]; + } + return controlledEntity; + } + void host([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { Multiplayer::MultiplayerAgentType serverType = sv_isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index db83c50fb5..e8e05a9d4c 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -104,7 +104,8 @@ namespace Multiplayer void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom); void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector& commands); - + NetworkEntityHandle SpawnDefaultPlayerPrefab(); + AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session"); AzNetworking::INetworkInterface* m_networkInterface = nullptr; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index dda3f18ad4..eaf89f3489 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -402,8 +402,12 @@ namespace Multiplayer const AZ::Transform& transform ) { - INetworkEntityManager::EntityList returnList; - + EntityList returnList; + if (!AZ::Data::AssetManager::IsReady()) + { + return returnList; + } + auto spawnableAssetId = m_networkPrefabLibrary.GetAssetIdByName(prefabEntryId.m_prefabName); // Required for sync-instantiation. Todo: keep the reference in NetworkSpawnableLibrary auto netSpawnableAsset = AZ::Data::AssetManager::Instance().GetAsset(spawnableAssetId, AZ::Data::AssetLoadBehavior::PreLoad); diff --git a/Gems/Multiplayer/Code/Source/Physics/PhysicsUtils.cpp b/Gems/Multiplayer/Code/Source/Physics/PhysicsUtils.cpp new file mode 100644 index 0000000000..6341f5b060 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Physics/PhysicsUtils.cpp @@ -0,0 +1,104 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include +#include +#include + +namespace +{ + template + AzPhysics::SceneQueryHits SceneQueryInternal(const RequestT& request) + { + auto* sceneInterface = AZ::Interface::Get(); + if (!sceneInterface) + { + return {}; + } + + AzPhysics::SceneHandle sceneHandle = sceneInterface->GetSceneHandle(AzPhysics::DefaultPhysicsSceneName); + if (sceneHandle == AzPhysics::InvalidSceneHandle) + { + return {}; + } + + Multiplayer::INetworkTime* currentNetTime = Multiplayer::GetNetworkTime(); + + if(!currentNetTime->IsTimeRewound()) + { + // If the time is not rewound, we simply execute the scene query as is. + AzPhysics::SceneQueryHits result = sceneInterface->QueryScene(sceneHandle, &request); + return result; + } + + // If the time is rewound, we want to query against rigid bodies present at the same frame ID: the same as the current rewound time is. + RequestT netSceneQueryRequest = request; + netSceneQueryRequest.m_filterCallback = [&request, currentFrameId = (uint32_t)currentNetTime->GetHostFrameId()]( + const AzPhysics::SimulatedBody* body, const ::Physics::Shape* shape) + { + if (body->GetFrameId() == AzPhysics::SimulatedBody::UndefinedFrameId || body->GetFrameId() == currentFrameId) + { + if (request.m_filterCallback) + { + return request.m_filterCallback(body, shape); + } + + // Overlap filter callbacks return true/false rather than Touch/Block/None + if constexpr (AZStd::is_same_v) + { + return true; + } + else + { + return AzPhysics::SceneQuery::QueryHitType::Touch; + } + } + + if constexpr (AZStd::is_same_v) + { + return false; + } + else + { + return AzPhysics::SceneQuery::QueryHitType::None; + } + }; + + // Execute the scene query modified for the time rewind. + AzPhysics::SceneQueryHits result = sceneInterface->QueryScene(sceneHandle, &netSceneQueryRequest); + return result; + } +} + +namespace Multiplayer +{ + namespace Physics + { + AzPhysics::SceneQueryHits RayCast(const AzPhysics::RayCastRequest& request) + { + return SceneQueryInternal(request); + } + + AzPhysics::SceneQueryHits ShapeCast(const AzPhysics::ShapeCastRequest& request) + { + return SceneQueryInternal(request); + } + + AzPhysics::SceneQueryHits Overlap(const AzPhysics::OverlapRequest& request) + { + return SceneQueryInternal(request); + } + } // namespace Physics +} // namespace Multiplayer diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp index 1696e851a5..c93c09cfbc 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp @@ -59,7 +59,7 @@ namespace Multiplayer AZ::Transform worldTm = GetEntity()->FindComponent()->GetWorldTM(); auto preInsertionCallback = [worldTm = AZStd::move(worldTm), netEntityIndex = m_netEntityIndex, spawnableAssetId = m_networkSpawnableAsset.GetId()] - (AzFramework::EntitySpawnTicket&, AzFramework::SpawnableEntityContainerView entities) + (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities) { if (entities.size() == 1) { @@ -81,7 +81,8 @@ namespace Multiplayer }; m_netSpawnTicket = AzFramework::EntitySpawnTicket(m_networkSpawnableAsset); - AzFramework::SpawnableEntitiesInterface::Get()->SpawnEntities(m_netSpawnTicket, {m_netEntityIndex}, preInsertionCallback); + AzFramework::SpawnableEntitiesInterface::Get()->SpawnEntities( + m_netSpawnTicket, AzFramework::SpawnablePriority_Default, { m_netEntityIndex }, preInsertionCallback); } } @@ -89,7 +90,7 @@ namespace Multiplayer { if(m_netSpawnTicket.IsValid()) { - AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket); + AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket, AzFramework::SpawnablePriority_Default); } } diff --git a/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp b/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp new file mode 100644 index 0000000000..0653dd5e08 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp @@ -0,0 +1,121 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class RewindableContainerTests + : public AllocatorsFixture + { + public: + Multiplayer::NetworkTime m_networkTime; + AZ::LoggerSystemComponent m_loggerComponent; + AZ::TimeSystemComponent m_timeComponent; + }; + + static constexpr uint32_t RewindableContainerSize = 7; + + TEST_F(RewindableContainerTests, BasicVectorTest) + { + Multiplayer::RewindableFixedVector test(0, 0); + + // Test push_back + for (uint32_t idx = 0; idx < RewindableContainerSize; ++idx) + { + test.push_back(idx); + EXPECT_EQ(idx, test[idx]); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + } + + // Test rewind for all pushed values and overall size + for (uint32_t idx = 0; idx < RewindableContainerSize; ++idx) + { + Multiplayer::ScopedAlterTime time(static_cast(idx), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + EXPECT_EQ(idx + 1, test.size()); + EXPECT_EQ(idx, test.back()); + } + + // Test pop_back + test.pop_back(); + EXPECT_EQ(RewindableContainerSize - 1, test.size()); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + + // Test iterator + uint32_t iterCount = 0; + auto iter = test.begin(); + while (iter != test.end()) + { + ++iterCount; + ++iter; + } + EXPECT_EQ(RewindableContainerSize - 1, iterCount); + + // Test clear and empty + test.clear(); + EXPECT_EQ(0, test.size()); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + EXPECT_TRUE(test.empty()); + + // Test rewind for pop_back and clear + Multiplayer::ScopedAlterTime pop_time(static_cast(RewindableContainerSize), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + EXPECT_EQ(RewindableContainerSize - 1, test.size()); + Multiplayer::ScopedAlterTime clear_time(static_cast(RewindableContainerSize + 1), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + EXPECT_EQ(0, test.size()); + + // Test copy_values and resize_no_construct + test.resize_no_construct(RewindableContainerSize); + test.copy_values(&test[RewindableContainerSize-1], 1); + EXPECT_EQ(1, test.size()); + test.resize_no_construct(RewindableContainerSize); + EXPECT_EQ(test[0], test[RewindableContainerSize - 1]); + } + + TEST_F(RewindableContainerTests, BasicArrayTest) + { + Multiplayer::RewindableArray test; + + test.fill(0); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + // Test push_back + for (uint32_t idx = 0; idx < RewindableContainerSize; ++idx) + { + test[idx] = idx; + EXPECT_EQ(idx, test[idx].Get()); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + } + + // Test rewind for all values and overall size + for (uint32_t idx = 1; idx <= RewindableContainerSize; ++idx) + { + Multiplayer::ScopedAlterTime time(static_cast(idx), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + for (uint32_t testIdx = 0; testIdx < RewindableContainerSize; ++testIdx) + { + if (testIdx < idx) + { + EXPECT_EQ(testIdx, test[testIdx].Get()); + } + else + { + EXPECT_EQ(0, test[testIdx].Get()); + } + } + } + } +} diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index eb856a48db..73de45ba9d 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -33,8 +33,13 @@ set(FILES Include/Multiplayer/NetworkInput/IMultiplayerComponentInput.h Include/Multiplayer/NetworkInput/NetworkInput.h Include/Multiplayer/NetworkTime/INetworkTime.h + Include/Multiplayer/NetworkTime/RewindableArray.h + Include/Multiplayer/NetworkTime/RewindableArray.inl + Include/Multiplayer/NetworkTime/RewindableFixedVector.h + Include/Multiplayer/NetworkTime/RewindableFixedVector.inl Include/Multiplayer/NetworkTime/RewindableObject.h Include/Multiplayer/NetworkTime/RewindableObject.inl + Include/Multiplayer/Physics/PhysicsUtils.h Include/Multiplayer/ReplicationWindows/IReplicationWindow.h Source/Multiplayer_precompiled.cpp Source/Multiplayer_precompiled.h @@ -103,6 +108,7 @@ set(FILES 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 fe1ca38186..0731c25d3b 100644 --- a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake @@ -13,5 +13,6 @@ set(FILES Tests/Main.cpp Tests/IMultiplayerConnectionMock.h Tests/MultiplayerSystemTests.cpp + Tests/RewindableContainerTests.cpp Tests/RewindableObjectTests.cpp ) diff --git a/Gems/MultiplayerCompression/Code/CMakeLists.txt b/Gems/MultiplayerCompression/Code/CMakeLists.txt index 58ce546543..acc7978e88 100644 --- a/Gems/MultiplayerCompression/Code/CMakeLists.txt +++ b/Gems/MultiplayerCompression/Code/CMakeLists.txt @@ -39,6 +39,11 @@ ly_add_target( Gem::MultiplayerCompression.Static ) +# use the MultiplayerCompression module everywhere except builders: +ly_create_alias(NAME MultiplayerCompression.Clients NAMESPACE Gem TARGETS Gem::MultiplayerCompression) +ly_create_alias(NAME MultiplayerCompression.Tools NAMESPACE Gem TARGETS Gem::MultiplayerCompression) +ly_create_alias(NAME MultiplayerCompression.Servers NAMESPACE Gem TARGETS Gem::MultiplayerCompression) + ################################################################################ # Tests ################################################################################ diff --git a/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_diff.png.imagesettings b/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_diff.png.imagesettings index dd621c891f..adbf7d20f9 100644 --- a/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_diff.png.imagesettings +++ b/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_diff.png.imagesettings @@ -17,7 +17,7 @@ - + @@ -36,7 +36,7 @@ - + diff --git a/Gems/NvCloth/Code/CMakeLists.txt b/Gems/NvCloth/Code/CMakeLists.txt index 0f019a985f..d7eaf80b16 100644 --- a/Gems/NvCloth/Code/CMakeLists.txt +++ b/Gems/NvCloth/Code/CMakeLists.txt @@ -56,6 +56,10 @@ ly_add_target( Gem::AtomLyIntegration_CommonFeatures ) +# use the NvCloth module in clients and servers: +ly_create_alias(NAME NvCloth.Clients NAMESPACE Gem TARGETS Gem::NvCloth) +ly_create_alias(NAME NvCloth.Servers NAMESPACE Gem TARGETS Gem::NvCloth) + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME NvCloth.Editor.Static STATIC @@ -97,6 +101,10 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) RUNTIME_DEPENDENCIES Gem::AtomLyIntegration_CommonFeatures.Editor ) + + # use the NvCloth.Editor module in dev tools: + ly_create_alias(NAME NvCloth.Builders NAMESPACE Gem TARGETS Gem::NvCloth.Editor) + ly_create_alias(NAME NvCloth.Tools NAMESPACE Gem TARGETS Gem::NvCloth.Editor) endif() ################################################################################ diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif.exportsettings index 8d01ab1ac2..e97c4e452c 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/anodized_metal_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="es3:0,ios:0,osx_gl:0,pc:2,provo:0,wiiu:0" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:0,ios:0,mac:0,pc:2,provo:0,wiiu:0" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif.exportsettings index f1f2f06410..a098bdcad9 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/brushed_steel_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif.exportsettings index 2a29854fae..19e899701a 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/dark_leather_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif.exportsettings index 93bcddc494..08861692ea 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/galvanized_steel_spec.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Reflectance /reduce="es3:0,ios:0,osx_gl:0,pc:1,provo:0,wiiu:0" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Reflectance /reduce="android:0,ios:0,mac:0,pc:1,provo:0,wiiu:0" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif.exportsettings index f1f2f06410..a098bdcad9 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/leather_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif.exportsettings index 2a29854fae..19e899701a 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/light_leather_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif.exportsettings index f1f2f06410..a098bdcad9 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif.exportsettings index 2a29854fae..19e899701a 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/mixed_stones_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif.exportsettings index 54586c2db1..48c18e1fe4 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/red_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="es3:0,ios:0,osx_gl:0,pc:3,provo:0,wiiu:0" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:0,ios:0,mac:0,pc:3,provo:0,wiiu:0" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif.exportsettings index f1f2f06410..a098bdcad9 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rotary_brushed_steel_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif.exportsettings index f1f2f06410..a098bdcad9 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif.exportsettings index 2a29854fae..19e899701a 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/rust_diff.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=Albedo /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif.exportsettings b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif.exportsettings index f1f2f06410..a098bdcad9 100644 --- a/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif.exportsettings +++ b/Gems/PBSreferenceMaterials/Assets/materials/pbs_reference/wood_planks_ddna.tif.exportsettings @@ -1 +1 @@ -/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="es3:1,ios:1,osx_gl:1,pc:0,provo:1,wiiu:1" +/autooptimizefile=0 /M=50,50,0,50,50,50 /preset=NormalsWithSmoothness /reduce="android:1,ios:1,mac:1,pc:0,provo:1,wiiu:1" diff --git a/AutomatedTesting/Gem/Code/Platform/Linux/tool_dependencies.cmake b/Gems/PBSreferenceMaterials/CMakeLists.txt similarity index 100% rename from AutomatedTesting/Gem/Code/Platform/Linux/tool_dependencies.cmake rename to Gems/PBSreferenceMaterials/CMakeLists.txt diff --git a/Gems/PhysX/Assets/PhysX/TemplateMaterialLibrary.physmaterial b/Gems/PhysX/Assets/PhysX/TemplateMaterialLibrary.physmaterial new file mode 100644 index 0000000000..481cd2fbfa --- /dev/null +++ b/Gems/PhysX/Assets/PhysX/TemplateMaterialLibrary.physmaterial @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gems/PhysX/Code/CMakeLists.txt b/Gems/PhysX/Code/CMakeLists.txt index b4c7b580a6..d281270ce4 100644 --- a/Gems/PhysX/Code/CMakeLists.txt +++ b/Gems/PhysX/Code/CMakeLists.txt @@ -46,6 +46,7 @@ ly_add_target( AZ::AzCore AZ::AzFramework Legacy::CryCommon + PRIVATE Gem::LmbrCentral ) @@ -66,10 +67,15 @@ ly_add_target( BUILD_DEPENDENCIES PRIVATE Gem::PhysX.Static + Gem::LmbrCentral RUNTIME_DEPENDENCIES Gem::LmbrCentral ) +# use the PhysX module in clients and servers: +ly_create_alias(NAME PhysX.Clients NAMESPACE Gem TARGETS Gem::PhysX) +ly_create_alias(NAME PhysX.Servers NAMESPACE Gem TARGETS Gem::PhysX) + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_associate_package(PACKAGE_NAME poly2tri-0.3.3-rev2-multiplatform TARGETS poly2tri PACKAGE_HASH 04092d06716f59b936b61906eaf3647db23b685d81d8b66131eb53e0aeaa1a38) @@ -107,7 +113,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) AZ::SceneCore AZ::SceneData Legacy::CryCommon - Gem::LmbrCentral + Gem::LmbrCentral.Editor Gem::PhysX.NumericalMethods Gem::PhysX.Static Gem::AtomLyIntegration_CommonFeatures.Static @@ -122,6 +128,9 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) AUTOMOC FILES_CMAKE physx_editor_shared_files.cmake + COMPILE_DEFINITIONS + PUBLIC + PHYSX_ENABLE_MULTI_THREADING INCLUDE_DIRECTORIES PRIVATE . @@ -136,6 +145,10 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::LmbrCentral.Editor ) + # use the PhysX.Editor module in dev tools: + ly_create_alias(NAME PhysX.Builders NAMESPACE Gem TARGETS Gem::PhysX.Editor) + ly_create_alias(NAME PhysX.Tools NAMESPACE Gem TARGETS Gem::PhysX.Editor) + endif() ################################################################################ @@ -157,6 +170,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTestShared AZ::AzTest Gem::PhysX.Static + Gem::LmbrCentral RUNTIME_DEPENDENCIES Gem::LmbrCentral ) diff --git a/Gems/PhysX/Code/Editor/DebugDraw.cpp b/Gems/PhysX/Code/Editor/DebugDraw.cpp index b73e3f22bd..829b776e28 100644 --- a/Gems/PhysX/Code/Editor/DebugDraw.cpp +++ b/Gems/PhysX/Code/Editor/DebugDraw.cpp @@ -17,13 +17,13 @@ #include #include #include +#include #include #include #include #include -#include -#include +#include namespace PhysX { @@ -415,11 +415,16 @@ namespace PhysX { case GlobalCollisionDebugColorMode::MaterialColor: { - Physics::MaterialFromAssetConfiguration materialConfiguration; const Physics::MaterialId materialId = colliderConfig.m_materialSelection.GetMaterialId(elementDebugInfo.m_materialSlotIndex); - if (colliderConfig.m_materialSelection.GetMaterialConfiguration(materialConfiguration, materialId)) + + AZStd::shared_ptr physicsMaterial; + Physics::PhysicsMaterialRequestBus::BroadcastResult( + physicsMaterial, + &Physics::PhysicsMaterialRequestBus::Events::GetMaterialById, + materialId); + if (physicsMaterial) { - debugColor = materialConfiguration.m_configuration.m_debugColor; + debugColor = physicsMaterial->GetDebugColor(); } break; } @@ -555,25 +560,37 @@ namespace PhysX if (meshConfig.GetCachedNativeMesh()) { - const AZ::Transform scaleMatrix = AZ::Transform::CreateScale(meshScale); - debugDisplay.PushMatrix(GetColliderLocalTransform(colliderConfig) * scaleMatrix); + debugDisplay.PushMatrix(GetColliderLocalTransform(colliderConfig)); if (meshConfig.GetMeshType() == Physics::CookedMeshShapeConfiguration::MeshType::TriangleMesh) { - DrawTriangleMesh(debugDisplay, colliderConfig, geomIndex); + DrawTriangleMesh(debugDisplay, colliderConfig, geomIndex, meshScale); } else { - DrawConvexMesh(debugDisplay, colliderConfig, geomIndex); + DrawConvexMesh(debugDisplay, colliderConfig, geomIndex, meshScale); } debugDisplay.PopMatrix(); } } - void Collider::DrawTriangleMesh(AzFramework::DebugDisplayRequests& debugDisplay, - const Physics::ColliderConfiguration& colliderConfig, - AZ::u32 geomIndex) const + AZStd::vector ScalePoints(const AZ::Vector3& scale, const AZStd::vector& points) + { + AZStd::vector scaledPoints; + scaledPoints.resize_no_construct(points.size()); + AZStd::transform( + points.begin(), points.end(), scaledPoints.begin(), + [scale](const AZ::Vector3& point) + { + return scale * point; + }); + return scaledPoints; + } + + void Collider::DrawTriangleMesh( + AzFramework::DebugDisplayRequests& debugDisplay, const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex, + const AZ::Vector3& meshScale) const { AZ_Assert(geomIndex < m_geometry.size(), "DrawTriangleMesh: geomIndex is out of range"); @@ -581,10 +598,10 @@ namespace PhysX const AZStd::unordered_map>& triangleIndexesByMaterialSlot = geom.m_triangleIndexesByMaterialSlot; - const AZStd::vector& verts = geom.m_verts; - const AZStd::vector& points = geom.m_points; + AZStd::vector scaledVerts = ScalePoints(meshScale, geom.m_verts); + AZStd::vector scaledPoints = ScalePoints(meshScale, geom.m_points); - if (!verts.empty()) + if (!scaledVerts.empty()) { for (const auto& element : triangleIndexesByMaterialSlot) { @@ -596,30 +613,31 @@ namespace PhysX triangleMeshInfo.m_numTriangles = triangleCount; triangleMeshInfo.m_materialSlotIndex = materialSlot; - debugDisplay.DrawTrianglesIndexed(verts, triangleIndexes + debugDisplay.DrawTrianglesIndexed(scaledVerts, triangleIndexes , CalcDebugColor(colliderConfig, triangleMeshInfo)); } - debugDisplay.DrawLines(points, WireframeColor); + debugDisplay.DrawLines(scaledPoints, WireframeColor); } } - void Collider::DrawConvexMesh(AzFramework::DebugDisplayRequests& debugDisplay, - const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex) const + void Collider::DrawConvexMesh( + AzFramework::DebugDisplayRequests& debugDisplay, const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex, + const AZ::Vector3& meshScale) const { AZ_Assert(geomIndex < m_geometry.size(), "DrawConvexMesh: geomIndex is out of range"); const GeometryData& geom = m_geometry[geomIndex]; - const AZStd::vector& verts = geom.m_verts; - const AZStd::vector& points = geom.m_points; + AZStd::vector scaledVerts = ScalePoints(meshScale, geom.m_verts); + AZStd::vector scaledPoints = ScalePoints(meshScale, geom.m_points); - if (!verts.empty()) + if (!scaledVerts.empty()) { - const AZ::u32 triangleCount = static_cast(verts.size() / 3); + const AZ::u32 triangleCount = static_cast(scaledVerts.size() / 3); ElementDebugInfo convexMeshInfo; convexMeshInfo.m_numTriangles = triangleCount; - debugDisplay.DrawTriangles(verts, CalcDebugColor(colliderConfig, convexMeshInfo)); - debugDisplay.DrawLines(points, WireframeColor); + debugDisplay.DrawTriangles(scaledVerts, CalcDebugColor(colliderConfig, convexMeshInfo)); + debugDisplay.DrawLines(scaledPoints, WireframeColor); } } diff --git a/Gems/PhysX/Code/Editor/DebugDraw.h b/Gems/PhysX/Code/Editor/DebugDraw.h index fcff961412..c43634717a 100644 --- a/Gems/PhysX/Code/Editor/DebugDraw.h +++ b/Gems/PhysX/Code/Editor/DebugDraw.h @@ -115,11 +115,13 @@ namespace PhysX AzFramework::DebugDisplayRequests& debugDisplay) override; // Internal mesh drawing subroutines - void DrawTriangleMesh(AzFramework::DebugDisplayRequests& debugDisplay, - const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex) const; + void DrawTriangleMesh( + AzFramework::DebugDisplayRequests& debugDisplay, const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex, + const AZ::Vector3& meshScale = AZ::Vector3::CreateOne()) const; - void DrawConvexMesh(AzFramework::DebugDisplayRequests& debugDisplay, - const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex) const; + void DrawConvexMesh( + AzFramework::DebugDisplayRequests& debugDisplay, const Physics::ColliderConfiguration& colliderConfig, AZ::u32 geomIndex, + const AZ::Vector3& meshScale = AZ::Vector3::CreateOne()) const; void BuildTriangleMesh(physx::PxBase* meshData, AZ::u32 geomIndex) const; diff --git a/Gems/PhysX/Code/Editor/SettingsWidget.cpp b/Gems/PhysX/Code/Editor/SettingsWidget.cpp index 38022e8c92..20a67a778d 100644 --- a/Gems/PhysX/Code/Editor/SettingsWidget.cpp +++ b/Gems/PhysX/Code/Editor/SettingsWidget.cpp @@ -37,14 +37,15 @@ namespace PhysX const Debug::DebugDisplayData& debugDisplayData) { m_physxSystemConfiguration = physxSystemConfiguration; - m_defaultPhysicsMaterialLibrary.m_asset = m_physxSystemConfiguration.m_defaultMaterialLibrary; + m_physicsMaterialInfo.m_defaultMaterialConfiguration = m_physxSystemConfiguration.m_defaultMaterialConfiguration; + m_physicsMaterialInfo.m_materialLibraryAsset = m_physxSystemConfiguration.m_materialLibraryAsset; m_defaultSceneConfiguration = defaultSceneConfiguration; m_debugDisplayData = debugDisplayData; blockSignals(true); m_propertyEditor->ClearInstances(); m_propertyEditor->AddInstance(&m_physxSystemConfiguration); - m_propertyEditor->AddInstance(&m_defaultPhysicsMaterialLibrary); + m_propertyEditor->AddInstance(&m_physicsMaterialInfo); m_propertyEditor->AddInstance(&m_defaultSceneConfiguration); m_propertyEditor->AddInstance(&m_debugDisplayData); m_propertyEditor->AddInstance(&m_physxSystemConfiguration.m_windConfiguration); @@ -88,7 +89,8 @@ namespace PhysX void SettingsWidget::SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* /*node*/) { - m_physxSystemConfiguration.m_defaultMaterialLibrary = m_defaultPhysicsMaterialLibrary.m_asset; + m_physxSystemConfiguration.m_defaultMaterialConfiguration = m_physicsMaterialInfo.m_defaultMaterialConfiguration; + m_physxSystemConfiguration.m_materialLibraryAsset = m_physicsMaterialInfo.m_materialLibraryAsset; emit onValueChanged(m_physxSystemConfiguration, m_defaultSceneConfiguration, m_debugDisplayData diff --git a/Gems/PhysX/Code/Editor/SettingsWidget.h b/Gems/PhysX/Code/Editor/SettingsWidget.h index 78df72b6d3..a4001e13b7 100644 --- a/Gems/PhysX/Code/Editor/SettingsWidget.h +++ b/Gems/PhysX/Code/Editor/SettingsWidget.h @@ -56,7 +56,7 @@ namespace PhysX AzToolsFramework::ReflectedPropertyEditor* m_propertyEditor; DocumentationLinkWidget* m_documentationLinkWidget; - Physics::DefaultMaterialLibraryAssetReflectionWrapper m_defaultPhysicsMaterialLibrary; + Physics::MaterialInfoReflectionWrapper m_physicsMaterialInfo; PhysX::PhysXSystemConfiguration m_physxSystemConfiguration; AzPhysics::SceneConfiguration m_defaultSceneConfiguration; Debug::DebugDisplayData m_debugDisplayData; diff --git a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp index e510aa505e..b28bf6ab4a 100644 --- a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp +++ b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp @@ -30,40 +30,91 @@ namespace PhysX { - static bool CreateSurfaceTypeMaterialLibrary(const AZStd::string & targetFilePath) + constexpr const char* DefaultAssetFilename = "SurfaceTypeMaterialLibrary"; + constexpr const char* TemplateAssetFilename = "PhysX/TemplateMaterialLibrary"; + + static AZStd::optional> GetMaterialLibraryTemplate() { - auto assetType = AZ::AzTypeInfo::Uuid(); + const auto& assetType = AZ::AzTypeInfo::Uuid(); + + AZStd::vector assetTypeExtensions; + AZ::AssetTypeInfoBus::Event(assetType, &AZ::AssetTypeInfo::GetAssetTypeExtensions, assetTypeExtensions); + + if (assetTypeExtensions.size() == 1) + { + // Constructing the path to the library asset + const AZStd::string& assetExtension = assetTypeExtensions[0]; - // Create File - AZ::Data::Asset newAsset = AZ::Data::AssetManager::Instance().CreateAsset(AZ::Uuid::CreateRandom(), assetType, AZ::Data::AssetLoadBehavior::Default); + // Use the path relative to the asset root to avoid hardcoding full path in the configuration + AZStd::string relativePath = TemplateAssetFilename; + AzFramework::StringFunc::Path::ReplaceExtension(relativePath, assetExtension.c_str()); + + AZ::Data::AssetId assetId; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, relativePath.c_str(), assetType, false /*autoRegisterIfNotFound*/); + + if (assetId.IsValid()) + { + return AZ::Data::AssetManager::Instance().GetAsset(assetId, AZ::Data::AssetLoadBehavior::NoLoad); + } + } + + return AZStd::nullopt; + } - AZ::IO::FileIOStream fileStream(targetFilePath.c_str(), AZ::IO::OpenMode::ModeWrite); + static AZStd::optional> CreateMaterialLibrary(const AZStd::string& fullTargetFilePath, const AZStd::string& relativePath) + { + AZ::IO::FileIOStream fileStream(fullTargetFilePath.c_str(), AZ::IO::OpenMode::ModeWrite); if (fileStream.IsOpen()) { - Physics::MaterialLibraryAsset* materialLibraryAsset = azrtti_cast(newAsset.GetData()); - if (materialLibraryAsset) + const auto& assetType = AZ::AzTypeInfo::Uuid(); + AZ::Data::AssetId assetId; + + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, relativePath.c_str(), assetType, true /*autoRegisterIfNotFound*/); + + AZ::Data::Asset newAsset = + AZ::Data::AssetManager::Instance().GetAsset(assetId, assetType, AZ::Data::AssetLoadBehavior::Default); + + if (auto* newMaterialLibraryData = azrtti_cast(newAsset.GetData())) { - // check it out in the source control system - AzToolsFramework::SourceControlCommandBus::Broadcast( - &AzToolsFramework::SourceControlCommandBus::Events::RequestEdit, targetFilePath.c_str(), true, - [](bool /*success*/, const AzToolsFramework::SourceControlFileInfo& /*info*/) {}); - - // Save the material library asset into a file - auto assetHandler = const_cast(AZ::Data::AssetManager::Instance().GetHandler(assetType)); - if (assetHandler->SaveAssetData(newAsset, &fileStream)) + if (auto templateLibraryOpt = GetMaterialLibraryTemplate()) { - return true; - } - else - { - AZ_Error("PhysX", false, - "CreateSurfaceTypeMaterialLibrary: Unable to save Surface Types Material Library Asset to %s", - targetFilePath.c_str()); + if (const auto* templateMaterialLibData = azrtti_cast(templateLibraryOpt->GetData())) + { + templateLibraryOpt->QueueLoad(); + templateLibraryOpt->BlockUntilLoadComplete(); + + // Fill the newly created material library using the template data + for (const auto& materialData : templateMaterialLibData->GetMaterialsData()) + { + newMaterialLibraryData->AddMaterialData(materialData); + } + + // check it out in the source control system + AzToolsFramework::SourceControlCommandBus::Broadcast( + &AzToolsFramework::SourceControlCommandBus::Events::RequestEdit, fullTargetFilePath.c_str(), true /*allowMultiCheckout*/, + [](bool /*success*/, const AzToolsFramework::SourceControlFileInfo& /*info*/) {}); + + // Save the material library asset into a file + auto assetHandler = AZ::Data::AssetManager::Instance().GetHandler(assetType); + if (assetHandler->SaveAssetData(newAsset, &fileStream)) + { + return newAsset; + } + else + { + AZ_Error( + "PhysX", false, + "CreateSurfaceTypeMaterialLibrary: Unable to save Surface Types Material Library Asset to %s", + fullTargetFilePath.c_str()); + } + } } } } - return false; + return AZStd::nullopt; } void EditorSystemComponent::Reflect(AZ::ReflectContext* context) @@ -84,11 +135,26 @@ namespace PhysX { Physics::EditorWorldBus::Handler::BusConnect(); + m_onMaterialLibraryLoadErrorEventHandler = AzPhysics::SystemEvents::OnMaterialLibraryLoadErrorEvent::Handler( + [this]([[maybe_unused]] AzPhysics::SystemEvents::MaterialLibraryLoadErrorType error) + { + // Attempt to set/create the default material library if there was an error + if (auto* physxSystem = GetPhysXSystem()) + { + if (auto retrievedMaterialLibrary = RetrieveDefaultMaterialLibrary()) + { + physxSystem->UpdateMaterialLibrary(retrievedMaterialLibrary.value()); + } + } + } + ); + if (auto* physicsSystem = AZ::Interface::Get()) { AzPhysics::SceneConfiguration editorWorldConfiguration = physicsSystem->GetDefaultSceneConfiguration(); editorWorldConfiguration.m_sceneName = AzPhysics::EditorPhysicsSceneName; m_editorWorldSceneHandle = physicsSystem->AddScene(editorWorldConfiguration); + physicsSystem->RegisterOnMaterialLibraryLoadErrorEventHandler(m_onMaterialLibraryLoadErrorEventHandler); } PhysX::RegisterConfigStringLineEditHandler(); // Register custom unique string line edit control @@ -109,6 +175,8 @@ namespace PhysX physicsSystem->RemoveScene(m_editorWorldSceneHandle); } m_editorWorldSceneHandle = AzPhysics::InvalidSceneHandle; + + m_onMaterialLibraryLoadErrorEventHandler.Disconnect(); } AzPhysics::SceneHandle EditorSystemComponent::GetEditorSceneHandle() const @@ -148,7 +216,7 @@ namespace PhysX PhysX::Editor::EditorWindow::RegisterViewClass(); } - AZ::Data::AssetId EditorSystemComponent::GenerateSurfaceTypesLibrary() + AZStd::optional> EditorSystemComponent::RetrieveDefaultMaterialLibrary() { AZ::Data::AssetId resultAssetId; @@ -159,8 +227,6 @@ namespace PhysX if (assetTypeExtensions.size() == 1) { - const char* DefaultAssetFilename = "SurfaceTypeMaterialLibrary"; - // Constructing the path to the library asset const AZStd::string& assetExtension = assetTypeExtensions[0]; @@ -169,40 +235,44 @@ namespace PhysX AzFramework::StringFunc::Path::ReplaceExtension(relativePath, assetExtension.c_str()); // Try to find an already existing material library - AZ::Data::AssetCatalogRequestBus::BroadcastResult(resultAssetId, &AZ::Data::AssetCatalogRequests::GetAssetIdByPath, relativePath.c_str(), azrtti_typeid(), false); + AZ::Data::AssetCatalogRequestBus::BroadcastResult(resultAssetId, + &AZ::Data::AssetCatalogRequests::GetAssetIdByPath, relativePath.c_str(), azrtti_typeid(), false /*autoRegisterIfNotFound*/); if (!resultAssetId.IsValid()) { + // No file for the default material library, create it const char* assetRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@"); - AZStd::string fullPath; AzFramework::StringFunc::Path::ConstructFull(assetRoot, DefaultAssetFilename, assetExtension.c_str(), fullPath); - if (CreateSurfaceTypeMaterialLibrary(fullPath)) + if (auto materialLibraryOpt = CreateMaterialLibrary(fullPath, relativePath)) { - // Find out the asset ID for the material library we've just created - AZ::Data::AssetCatalogRequestBus::BroadcastResult( - resultAssetId, &AZ::Data::AssetCatalogRequests::GetAssetIdByPath, - relativePath.c_str(), - azrtti_typeid(), true); + return materialLibraryOpt; } else { AZ_Warning("PhysX", false, - "GenerateSurfaceTypesLibrary: Failed to create material library at %s. " + "CreateMaterialLibrary: Failed to create material library at %s. " "Please check if the file is writable", fullPath.c_str()); } } + else + { + AZ::Data::Asset existingMaterialLibrary = + AZ::Data::AssetManager::Instance().GetAsset(resultAssetId, AZ::Data::AssetLoadBehavior::NoLoad); + + return existingMaterialLibrary; + } } else { AZ_Warning("PhysX", false, - "GenerateSurfaceTypesLibrary: Number of extensions for the physics material library asset is %u" + "RetrieveDefaultMaterialLibrary: Number of extensions for the physics material library asset is %u" " but should be 1. Please check if the asset registered itself with the asset system correctly", assetTypeExtensions.size()) } - return resultAssetId; + return AZStd::nullopt; } } diff --git a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.h b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.h index 9ebca05ccd..4bd11a951f 100644 --- a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.h +++ b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.h @@ -14,6 +14,7 @@ #include #include +#include #include namespace AzPhysics @@ -65,8 +66,9 @@ namespace PhysX void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override; void NotifyRegisterViews() override; - AZ::Data::AssetId GenerateSurfaceTypesLibrary(); + AZStd::optional> RetrieveDefaultMaterialLibrary(); + AzPhysics::SystemEvents::OnMaterialLibraryLoadErrorEvent::Handler m_onMaterialLibraryLoadErrorEventHandler; AzPhysics::SceneHandle m_editorWorldSceneHandle = AzPhysics::InvalidSceneHandle; }; } diff --git a/Gems/PhysX/Code/Include/PhysX/Configuration/PhysXConfiguration.h b/Gems/PhysX/Code/Include/PhysX/Configuration/PhysXConfiguration.h index 936eadad18..598cdc1b4c 100644 --- a/Gems/PhysX/Code/Include/PhysX/Configuration/PhysXConfiguration.h +++ b/Gems/PhysX/Code/Include/PhysX/Configuration/PhysXConfiguration.h @@ -13,7 +13,6 @@ #pragma once #include #include -#include #include #include @@ -55,7 +54,6 @@ namespace PhysX static PhysXSystemConfiguration CreateDefault(); WindConfiguration m_windConfiguration; //!< Wind configuration for PhysX. - AZ::Data::Asset m_defaultMaterialLibrary = AZ::Data::AssetLoadBehavior::NoLoad; //!< Material Library exposed by the system component SystemBus API. bool operator==(const PhysXSystemConfiguration& other) const; bool operator!=(const PhysXSystemConfiguration& other) const; diff --git a/Gems/PhysX/Code/Include/PhysX/MeshColliderComponentBus.h b/Gems/PhysX/Code/Include/PhysX/MeshColliderComponentBus.h index 33305be145..ac36ebbc7f 100644 --- a/Gems/PhysX/Code/Include/PhysX/MeshColliderComponentBus.h +++ b/Gems/PhysX/Code/Include/PhysX/MeshColliderComponentBus.h @@ -38,10 +38,6 @@ namespace PhysX /// @param id The asset ID to set it to. virtual void SetMeshAsset(const AZ::Data::AssetId& id) = 0; - /// Sets the material library asset to the collider. - /// @param id The asset ID to set it to. - virtual void SetMaterialAsset(const AZ::Data::AssetId& id) = 0; - /// Sets the material id from the material library. /// @param id The asset ID to set it to. virtual void SetMaterialId(const Physics::MaterialId& id) = 0; diff --git a/Gems/PhysX/Code/Source/Configuration/PhysXConfiguration.cpp b/Gems/PhysX/Code/Source/Configuration/PhysXConfiguration.cpp index 3ba520513f..a0db4d5d30 100644 --- a/Gems/PhysX/Code/Source/Configuration/PhysXConfiguration.cpp +++ b/Gems/PhysX/Code/Source/Configuration/PhysXConfiguration.cpp @@ -36,6 +36,18 @@ namespace PhysX return configuration; } + + bool PhysXSystemConfigurationConverter([[maybe_unused]] AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& dataElement) + { + if (dataElement.GetVersion() <= 1) + { + dataElement.RemoveElementByName(AZ_CRC_CE("DefaultMaterialLibrary")); + AZ_Warning("PhysXSystemConfigurationConverter", false, + "Old version of PhysX Configuration data found. Physics material library will be reset to default."); + } + + return true; + } } AZ_CLASS_ALLOCATOR_IMPL(WindConfiguration, AZ::SystemAllocator, 0); @@ -89,9 +101,8 @@ namespace PhysX if (auto* serializeContext = azdynamic_cast(context)) { serializeContext->Class() - ->Version(1) + ->Version(2, &PhysXInternal::PhysXSystemConfigurationConverter) ->Field("WindConfiguration", &PhysXSystemConfiguration::m_windConfiguration) - ->Field("MaterialLibrary", &PhysXSystemConfiguration::m_defaultMaterialLibrary) ; if (AZ::EditContext* editContext = serializeContext->GetEditContext()) @@ -115,7 +126,6 @@ namespace PhysX bool PhysXSystemConfiguration::operator==(const PhysXSystemConfiguration& other) const { return AzPhysics::SystemConfiguration::operator==(other) && - m_defaultMaterialLibrary == other.m_defaultMaterialLibrary && m_windConfiguration == other.m_windConfiguration ; } diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index 26700a7103..4454429092 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -352,10 +352,13 @@ namespace PhysX AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues); }); - m_onDefaultMaterialLibraryChangedEventHandler = AzPhysics::SystemEvents::OnDefaultMaterialLibraryChangedEvent::Handler( + m_onMaterialLibraryChangedEventHandler = AzPhysics::SystemEvents::OnMaterialLibraryChangedEvent::Handler( [this](const AZ::Data::AssetId& defaultMaterialLibrary) { - m_configuration.m_materialSelection.OnDefaultMaterialLibraryChanged(defaultMaterialLibrary); + m_configuration.m_materialSelection.OnMaterialLibraryChanged(defaultMaterialLibrary); + + AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, + AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues); }); AzToolsFramework::Components::EditorComponentBase::Activate(); @@ -463,13 +466,13 @@ namespace PhysX if (auto* physXSystem = GetPhysXSystem()) { physXSystem->RegisterSystemConfigurationChangedEvent(m_physXConfigChangedHandler); - physXSystem->RegisterOnDefaultMaterialLibraryChangedEventHandler(m_onDefaultMaterialLibraryChangedEventHandler); + physXSystem->RegisterOnMaterialLibraryChangedEventHandler(m_onMaterialLibraryChangedEventHandler); } } void EditorColliderComponent::OnDeselected() { - m_onDefaultMaterialLibraryChangedEventHandler.Disconnect(); + m_onMaterialLibraryChangedEventHandler.Disconnect(); m_physXConfigChangedHandler.Disconnect(); } @@ -681,11 +684,6 @@ namespace PhysX } } - void EditorColliderComponent::SetMaterialAsset(const AZ::Data::AssetId& id) - { - m_configuration.m_materialSelection.SetMaterialLibrary(id); - } - void EditorColliderComponent::SetMaterialId(const Physics::MaterialId& id) { m_configuration.m_materialSelection.SetMaterialId(id); @@ -693,8 +691,10 @@ namespace PhysX void EditorColliderComponent::UpdateMaterialSlotsFromMeshAsset() { - Physics::SystemRequestBus::Broadcast(&Physics::SystemRequests::UpdateMaterialSelection, - m_shapeConfiguration.GetCurrent(), m_configuration); + Physics::PhysicsMaterialRequestBus::Broadcast( + &Physics::PhysicsMaterialRequestBus::Events::UpdateMaterialSelectionFromPhysicsAsset, + m_shapeConfiguration.GetCurrent(), + m_configuration.m_materialSelection); AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); @@ -868,7 +868,7 @@ namespace PhysX colliderConfigNoOffset.m_rotation = AZ::Quaternion::CreateIdentity(); colliderConfigNoOffset.m_position = AZ::Vector3::CreateZero(); m_colliderDebugDraw.DrawMesh(debugDisplay, colliderConfigNoOffset, m_scaledPrimitive.value(), - GetWorldTM().GetScale() * m_cachedNonUniformScale, shapeIndex); + GetWorldTM().GetUniformScale() * m_cachedNonUniformScale, shapeIndex); } } @@ -1007,7 +1007,7 @@ namespace PhysX AZ::Vector3 EditorColliderComponent::GetBoxScale() { - return GetWorldTM().GetScale(); + return AZ::Vector3(GetWorldTM().GetUniformScale()); } void EditorColliderComponent::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world) @@ -1049,7 +1049,7 @@ namespace PhysX void EditorColliderComponent::UpdateShapeConfigurationScale() { auto& shapeConfiguration = m_shapeConfiguration.GetCurrent(); - shapeConfiguration.m_scale = GetWorldTM().ExtractScale() * m_cachedNonUniformScale; + shapeConfiguration.m_scale = GetWorldTM().ExtractUniformScale() * m_cachedNonUniformScale; m_colliderDebugDraw.ClearCachedGeometry(); } diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.h b/Gems/PhysX/Code/Source/EditorColliderComponent.h index 818de62a04..07e1131ea9 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.h @@ -158,7 +158,6 @@ namespace PhysX AZ::Data::Asset GetMeshAsset() const override; Physics::MaterialId GetMaterialId() const override; void SetMeshAsset(const AZ::Data::AssetId& id) override; - void SetMaterialAsset(const AZ::Data::AssetId& id) override; void SetMaterialId(const Physics::MaterialId& id) override; void UpdateMaterialSlotsFromMeshAsset(); @@ -251,7 +250,7 @@ namespace PhysX DebugDraw::Collider m_colliderDebugDraw; AzPhysics::SystemEvents::OnConfigurationChangedEvent::Handler m_physXConfigChangedHandler; - AzPhysics::SystemEvents::OnDefaultMaterialLibraryChangedEvent::Handler m_onDefaultMaterialLibraryChangedEventHandler; + AzPhysics::SystemEvents::OnMaterialLibraryChangedEvent::Handler m_onMaterialLibraryChangedEventHandler; AZ::Transform m_cachedWorldTransform; AZ::NonUniformScaleChangedEvent::Handler m_nonUniformScaleChangedHandler; //!< Responds to changes in non-uniform scale. diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index 692fbf96f3..0cdba94a34 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -44,11 +44,14 @@ namespace PhysX AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues); }) - , m_onDefaultMaterialLibraryChangedEventHandler( + , m_onMaterialLibraryChangedEventHandler( [this](const AZ::Data::AssetId& defaultMaterialLibrary) { - m_colliderConfig.m_materialSelection.OnDefaultMaterialLibraryChanged(defaultMaterialLibrary); + m_colliderConfig.m_materialSelection.OnMaterialLibraryChanged(defaultMaterialLibrary); Physics::ColliderComponentEventBus::Event(GetEntityId(), &Physics::ColliderComponentEvents::OnColliderChanged); + + AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, + AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues); }) , m_nonUniformScaleChangedHandler([this](const AZ::Vector3& scale) {OnNonUniformScaleChanged(scale);}) { @@ -694,16 +697,16 @@ namespace PhysX { physXSystem->RegisterSystemConfigurationChangedEvent(m_physXConfigChangedHandler); } - if (!m_onDefaultMaterialLibraryChangedEventHandler.IsConnected()) + if (!m_onMaterialLibraryChangedEventHandler.IsConnected()) { - physXSystem->RegisterOnDefaultMaterialLibraryChangedEventHandler(m_onDefaultMaterialLibraryChangedEventHandler); + physXSystem->RegisterOnMaterialLibraryChangedEventHandler(m_onMaterialLibraryChangedEventHandler); } } } void EditorShapeColliderComponent::OnDeselected() { - m_onDefaultMaterialLibraryChangedEventHandler.Disconnect(); + m_onMaterialLibraryChangedEventHandler.Disconnect(); m_physXConfigChangedHandler.Disconnect(); } diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h index 7b7fab789a..1ee87c9564 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h @@ -155,7 +155,7 @@ namespace PhysX mutable GeometryCache m_geometryCache; //!< Cached data for generating sample points inside the attached shape. AzPhysics::SystemEvents::OnConfigurationChangedEvent::Handler m_physXConfigChangedHandler; - AzPhysics::SystemEvents::OnDefaultMaterialLibraryChangedEvent::Handler m_onDefaultMaterialLibraryChangedEventHandler; + AzPhysics::SystemEvents::OnMaterialLibraryChangedEvent::Handler m_onMaterialLibraryChangedEventHandler; AZ::Transform m_cachedWorldTransform; AZ::NonUniformScaleChangedEvent::Handler m_nonUniformScaleChangedHandler; //!< Responds to changes in non-uniform scale. AZ::Vector3 m_currentNonUniformScale = AZ::Vector3::CreateOne(); //!< Caches the current non-uniform scale. diff --git a/Gems/PhysX/Code/Source/ForceRegion.cpp b/Gems/PhysX/Code/Source/ForceRegion.cpp index c41ae47a0e..2cdd0dea3f 100644 --- a/Gems/PhysX/Code/Source/ForceRegion.cpp +++ b/Gems/PhysX/Code/Source/ForceRegion.cpp @@ -148,7 +148,7 @@ namespace PhysX { m_worldTransform = world; m_regionParams.m_position = world.GetTranslation(); - m_regionParams.m_scale = world.GetScale(); + m_regionParams.m_scale = world.GetUniformScale(); m_regionParams.m_rotation = world.GetRotation(); AZ::EBusReduceResult triggerAabb; triggerAabb.value = AZ::Aabb::CreateNull(); @@ -223,7 +223,7 @@ namespace PhysX , entityId , &AZ::TransformBus::Events::GetWorldTM); regionParams.m_position = worldTransform.GetTranslation(); - regionParams.m_scale = worldTransform.GetScale(); + regionParams.m_scale = worldTransform.GetUniformScale(); regionParams.m_rotation = worldTransform.GetRotation(); LmbrCentral::SplineComponentRequestBus::EventResult(regionParams.m_spline diff --git a/Gems/PhysX/Code/Source/ForceRegionForces.cpp b/Gems/PhysX/Code/Source/ForceRegionForces.cpp index 8ea74c0de7..61679e9cb4 100644 --- a/Gems/PhysX/Code/Source/ForceRegionForces.cpp +++ b/Gems/PhysX/Code/Source/ForceRegionForces.cpp @@ -294,8 +294,7 @@ namespace PhysX rotateInverse.InvertFull(); } - AZ::Vector3 scaleInverse = region.m_scale; - scaleInverse = scaleInverse.GetReciprocal(); + float scaleInverse = 1.0f / region.m_scale; AZ::Vector3 position = entity.m_position + entity.m_velocity * m_lookAhead; AZ::Vector3 localPos = position - region.m_position; diff --git a/Gems/PhysX/Code/Source/ForceRegionForces.h b/Gems/PhysX/Code/Source/ForceRegionForces.h index 206e35c195..6f7eb6b277 100644 --- a/Gems/PhysX/Code/Source/ForceRegionForces.h +++ b/Gems/PhysX/Code/Source/ForceRegionForces.h @@ -36,7 +36,7 @@ namespace PhysX AZ::EntityId m_id; AZ::Vector3 m_position; AZ::Quaternion m_rotation; - AZ::Vector3 m_scale; + float m_scale; AZ::SplinePtr m_spline; AZ::Aabb m_aabb; }; diff --git a/Gems/PhysX/Code/Source/Material.cpp b/Gems/PhysX/Code/Source/Material.cpp index e8e5cab76e..5e8e3bf759 100644 --- a/Gems/PhysX/Code/Source/Material.cpp +++ b/Gems/PhysX/Code/Source/Material.cpp @@ -15,6 +15,8 @@ #include "Material.h" #include #include +#include +#include namespace PhysX { @@ -22,6 +24,9 @@ namespace PhysX : m_pxMaterial(AZStd::move(material.m_pxMaterial)) , m_surfaceType(material.m_surfaceType) , m_surfaceString(AZStd::move(material.m_surfaceString)) + , m_cryEngineSurfaceId(material.m_cryEngineSurfaceId) + , m_density(material.m_density) + , m_debugColor(AZStd::move(material.m_debugColor)) { m_pxMaterial->userData = this; } @@ -31,6 +36,11 @@ namespace PhysX m_pxMaterial = AZStd::move(material.m_pxMaterial); m_surfaceType = material.m_surfaceType; m_surfaceString = AZStd::move(material.m_surfaceString); + m_cryEngineSurfaceId = material.m_cryEngineSurfaceId; + m_density = material.m_density; + m_debugColor = AZStd::move(material.m_debugColor); + + m_pxMaterial->userData = this; return *this; } @@ -93,8 +103,10 @@ namespace PhysX pxMaterial->userData = this; m_pxMaterial = PxMaterialUniquePtr(pxMaterial, materialDestructor); - m_surfaceType = AZ::Crc32(materialConfiguration.m_surfaceType.c_str()); - m_surfaceString = materialConfiguration.m_surfaceType; + + SetSurfaceTypeName(materialConfiguration.m_surfaceType); + + SetDebugColor(materialConfiguration.m_debugColor); Physics::LegacySurfaceTypeRequestsBus::BroadcastResult( m_cryEngineSurfaceId, @@ -115,8 +127,9 @@ namespace PhysX SetDensity(configuration.m_density); - m_surfaceType = AZ::Crc32(configuration.m_surfaceType.c_str()); - m_surfaceString = configuration.m_surfaceType; + SetSurfaceTypeName(configuration.m_surfaceType); + + SetDebugColor(configuration.m_debugColor); Physics::LegacySurfaceTypeRequestsBus::BroadcastResult( m_cryEngineSurfaceId, @@ -134,9 +147,15 @@ namespace PhysX return m_surfaceType; } - void Material::SetSurfaceType(AZ::Crc32 surfaceType) + const AZStd::string& Material::GetSurfaceTypeName() const + { + return m_surfaceString; + } + + void Material::SetSurfaceTypeName(const AZStd::string& surfaceTypeName) { - m_surfaceType = surfaceType; + m_surfaceString = surfaceTypeName; + m_surfaceType = AZ::Crc32(m_surfaceString.c_str()); } float Material::GetDynamicFriction() const @@ -232,6 +251,16 @@ namespace PhysX MaterialConfiguration::MinDensityLimit, MaterialConfiguration::MaxDensityLimit); } + AZ::Color Material::GetDebugColor() const + { + return m_debugColor; + } + + void Material::SetDebugColor(const AZ::Color& debugColor) + { + m_debugColor = debugColor; + } + AZ::u32 Material::GetCryEngineSurfaceId() const { return m_cryEngineSurfaceId; @@ -243,6 +272,16 @@ namespace PhysX } MaterialsManager::MaterialsManager() + : m_physicsConfigChangedHandler( + [this](const AzPhysics::SystemConfiguration* config) + { + OnPhysicsConfigurationChanged(config); + }) + , m_materialLibraryChangedHandler( + [this](const AZ::Data::AssetId& materialLibraryAssetId) + { + OnMaterialLibraryChanged(materialLibraryAssetId); + }) { } @@ -254,133 +293,152 @@ namespace PhysX { Physics::PhysicsMaterialRequestBus::Handler::BusConnect(); MaterialManagerRequestsBus::Handler::BusConnect(); + + if (auto* physicsSystem = AZ::Interface::Get()) + { + physicsSystem->RegisterSystemConfigurationChangedEvent(m_physicsConfigChangedHandler); + physicsSystem->RegisterOnMaterialLibraryChangedEventHandler(m_materialLibraryChangedHandler); + } } void MaterialsManager::Disconnect() { + m_materialLibraryChangedHandler.Disconnect(); + m_physicsConfigChangedHandler.Disconnect(); MaterialManagerRequestsBus::Handler::BusDisconnect(); Physics::PhysicsMaterialRequestBus::Handler::BusDisconnect(); } void MaterialsManager::GetMaterials(const Physics::MaterialSelection& materialSelection - , AZStd::vector>& outMaterials) + , AZStd::vector>& outMaterials) { outMaterials.clear(); - outMaterials.reserve(materialSelection.GetMaterialIdsAssignedToSlots().size()); - // Ensure PxMaterial instances are initialized if possible. - InitializeMaterials(materialSelection); + const auto& materialIdsAssignedToSlots = materialSelection.GetMaterialIdsAssignedToSlots(); + if (materialIdsAssignedToSlots.empty()) + { + // The material selection doesn't have any slots, return empty list. + return; + } + + // It is important to return exactly the amount of materials specified in materialSelection + // If a number of materials different to what was cooked is assigned on a physx mesh it will lead to undefined + // behavior and subtle bugs. Unfortunately, there's no warning or assertion on physx side at the shape creation time, + // nor mention of this in the documentation + outMaterials.resize(materialIdsAssignedToSlots.size(), GetDefaultMaterial()); - for (const auto& id : materialSelection.GetMaterialIdsAssignedToSlots()) + for (size_t slotIndex = 0; slotIndex < materialIdsAssignedToSlots.size(); ++slotIndex) { - Physics::MaterialFromAssetConfiguration configuration; - if (materialSelection.GetMaterialConfiguration(configuration, id)) - { - auto iterator = m_materialsFromAssets.find(id.GetUuid()); - if (iterator != m_materialsFromAssets.end()) - { - outMaterials.push_back(iterator->second); - } - else - { - outMaterials.push_back(GetDefaultMaterial()); - } - } - else + const auto& materialId = materialIdsAssignedToSlots[slotIndex]; + + if (auto iterator = FindOrCreateMaterial(materialId); + iterator != m_materials.end()) { - // It is important to return exactly the amount of materials specified in materialSelection - // If a number of materials different to what was cooked is assigned on a physx mesh it will lead to undefined - // behavior and subtle bugs. Unfortunately, there's no warning or assertion on physx side at the shape creation time, - // nor mention of this in the documentation - outMaterials.push_back(GetDefaultMaterial()); + outMaterials[slotIndex] = iterator->second; } } } - AZStd::weak_ptr MaterialsManager::GetMaterialByName(const AZStd::string& name) + AZStd::shared_ptr MaterialsManager::GetMaterialById(Physics::MaterialId id) { - auto it = AZStd::find_if(m_materialsFromAssets.begin(), m_materialsFromAssets.end(), - [&name](const AZStd::pair>& elem) + if (auto it = FindOrCreateMaterial(id); + it != m_materials.end()) { - return elem.second.get()->GetSurfaceTypeName() == name; - }); + return it->second; + } + return nullptr; + } - if (it != m_materialsFromAssets.end()) + AZStd::shared_ptr MaterialsManager::GetMaterialByName(const AZStd::string& name) + { + if (auto it = FindOrCreateMaterial(name); + it != m_materials.end()) { return it->second; } - return {}; + return nullptr; } - AZ::u32 MaterialsManager::GetFirstSelectedMaterialIndex(const Physics::MaterialSelection& materialSelection) + void MaterialsManager::GetPxMaterials(const Physics::MaterialSelection& materialSelection + , AZStd::vector& outMaterials) { - const AZ::u32 defaultMaterialIndex = 0; + AZStd::vector> materials; + GetMaterials(materialSelection, materials); - if (!materialSelection.IsMaterialLibraryValid()) + outMaterials.reserve(materials.size()); + for (const auto& material : materials) { - return defaultMaterialIndex; + PhysX::Material* physxMaterial = azrtti_cast(material.get()); + AZ_Assert(physxMaterial, "Invalid physx material"); + + outMaterials.emplace_back(physxMaterial->GetPxMaterial()); } + } - auto materialAsset = AZ::Data::AssetManager::Instance().GetAsset(materialSelection.GetMaterialLibraryAssetId(), AZ::Data::AssetLoadBehavior::Default); + void MaterialsManager::UpdateMaterialSelectionFromPhysicsAsset( + const Physics::ShapeConfiguration& shapeConfiguration, + Physics::MaterialSelection& materialSelection) + { + if (shapeConfiguration.GetShapeType() != Physics::ShapeType::PhysicsAsset) + { + return; + } - materialAsset.BlockUntilLoadComplete(); + const Physics::PhysicsAssetShapeConfiguration& assetConfiguration = + static_cast(shapeConfiguration); - AZStd::vector materialList = materialAsset.Get()->GetMaterialsData(); - - const AZStd::vector& selectedMaterials = materialSelection.GetMaterialIdsAssignedToSlots(); - if (selectedMaterials.size() == 0) + if (!assetConfiguration.m_asset.GetId().IsValid()) { - return defaultMaterialIndex; + // Set the default selection if there's no physics asset. + materialSelection.SetMaterialSlots(Physics::MaterialSelection::SlotsArray()); + return; } - for (AZ::u32 i=0; i < materialList.size(); ++i) + + if (!assetConfiguration.m_asset.IsReady()) { - if (materialList[i].m_id == selectedMaterials[0]) - { - return i + 1; // Index 0 is reserved for Default material. - } + // The asset is valid but is still loading, + // Do not set the empty slots in this case to avoid the entity being in invalid state + return; } - - return defaultMaterialIndex; - } - void MaterialsManager::GetPxMaterials(const Physics::MaterialSelection& materialSelection - , AZStd::vector& outMaterials) - { - outMaterials.clear(); - if (materialSelection.GetMaterialIdsAssignedToSlots().empty()) + Pipeline::MeshAsset* meshAsset = assetConfiguration.m_asset.GetAs(); + if (!meshAsset) { - // if the materialSelection is invalid we still - // return a default material as a fallback behavior - outMaterials.push_back(GetDefaultMaterial()->GetPxMaterial()); + materialSelection.SetMaterialSlots(Physics::MaterialSelection::SlotsArray()); + AZ_Warning("PhysX", false, "UpdateMaterialSelectionFromPhysicsAsset: MeshAsset is invalid"); return; } - outMaterials.reserve(materialSelection.GetMaterialIdsAssignedToSlots().size()); - // Ensure PxMaterial instances are initialized if possible. - InitializeMaterials(materialSelection); + // Set the slots from the mesh asset + materialSelection.SetMaterialSlots(meshAsset->m_assetData.m_surfaceNames); - for (const auto& id : materialSelection.GetMaterialIdsAssignedToSlots()) + if (!assetConfiguration.m_useMaterialsFromAsset) { - Physics::MaterialFromAssetConfiguration configuration; - if (materialSelection.GetMaterialConfiguration(configuration, id)) + // Not using the materials from the asset. Nothing else to do. + return; + } + + // Update material IDs in the selection for each slot + const AZStd::vector& meshMaterialNames = meshAsset->m_assetData.m_materialNames; + for (size_t slotIndex = 0; slotIndex < meshMaterialNames.size(); ++slotIndex) + { + const AZStd::string& physicsMaterialNameFromPhysicsAsset = meshMaterialNames[slotIndex]; + if (physicsMaterialNameFromPhysicsAsset == DefaultPhysicsMaterialNameFromPhysicsAsset) + { + continue; + } + + if (auto it = FindOrCreateMaterial(physicsMaterialNameFromPhysicsAsset); + it != m_materials.end()) { - auto iterator = m_materialsFromAssets.find(id.GetUuid()); - if (iterator != m_materialsFromAssets.end()) - { - outMaterials.push_back(iterator->second->GetPxMaterial()); - } - else - { - outMaterials.push_back(GetDefaultMaterial()->GetPxMaterial()); - } + materialSelection.SetMaterialId(Physics::MaterialId::FromUUID(it->first), slotIndex); } else { - // It is important to return exactly the amount of materials specified in materialSelection - // If a number of materials different to what was cooked is assigned on a physx mesh it will lead to undefined - // behavior and subtle bugs. Unfortunately, there's no warning or assertion on physx side at the shape creation time, - // nor mention of this in the documentation - outMaterials.push_back(GetDefaultMaterial()->GetPxMaterial()); + AZ_Warning("PhysX", false, + "UpdateMaterialSelectionFromPhysicsAsset: Physics material '%s' not found in the material library. Mesh surface '%s' will use the default material.", + physicsMaterialNameFromPhysicsAsset.c_str(), + meshAsset->m_assetData.m_surfaceNames[slotIndex].c_str()); } } } @@ -390,11 +448,21 @@ namespace PhysX return GetDefaultMaterial(); } - const AZStd::shared_ptr& MaterialsManager::GetDefaultMaterial() + AZStd::shared_ptr MaterialsManager::GetDefaultMaterial() { if (!m_defaultMaterial) { - m_defaultMaterial = AZStd::make_shared(Physics::MaterialConfiguration()); + // Get default material from physics configuration + if (auto* physicsSystem = AZ::Interface::Get()) + { + m_defaultMaterialConfiguration = physicsSystem->GetConfiguration()->m_defaultMaterialConfiguration; + } + else + { + AZ_Warning("MaterialsManager", false, "Unable to get Physics System, default material will not be in sync with PhysX Configuration"); + } + + m_defaultMaterial = AZStd::make_shared(m_defaultMaterialConfiguration); } return m_defaultMaterial; @@ -403,38 +471,138 @@ namespace PhysX void MaterialsManager::ReleaseAllMaterials() { m_defaultMaterial = nullptr; - m_materialsFromAssets.clear(); + m_materials.clear(); Physics::PhysicsMaterialNotificationsBus::Broadcast(&Physics::PhysicsMaterialNotificationsBus::Events::MaterialsReleased); } - void MaterialsManager::InitializeMaterials(const Physics::MaterialSelection& materialSelection) + MaterialsManager::Materials::iterator MaterialsManager::FindOrCreateMaterial(Physics::MaterialId materialId) + { + if (materialId.IsNull()) + { + return m_materials.end(); + } + + if (auto it = m_materials.find(materialId.GetUuid()); + it != m_materials.end()) + { + return it; + } + else + { + auto* materialLibrary = GetMaterialLibrary(); + if (!materialLibrary) + { + return m_materials.end(); + } + + Physics::MaterialFromAssetConfiguration configuration; + if (!materialLibrary->GetDataForMaterialId(materialId, configuration)) + { + return m_materials.end(); + } + + auto newMaterial = AZStd::make_shared(configuration.m_configuration); + auto insertedPair = m_materials.emplace(materialId.GetUuid(), AZStd::move(newMaterial)); + return insertedPair.first; + } + } + + MaterialsManager::Materials::iterator MaterialsManager::FindOrCreateMaterial(const AZStd::string& materialName) { - const AZStd::vector& materialIds = materialSelection.GetMaterialIdsAssignedToSlots(); - for (const auto& id : materialIds) + if (materialName.empty()) { + return m_materials.end(); + } + + auto it = AZStd::find_if(m_materials.begin(), m_materials.end(), [&materialName](const auto& data) + { + return data.second->GetSurfaceTypeName() == materialName; + }); + if (it != m_materials.end()) + { + return it; + } + else + { + auto* materialLibrary = GetMaterialLibrary(); + if (!materialLibrary) + { + return m_materials.end(); + } + Physics::MaterialFromAssetConfiguration configuration; - if (!materialSelection.GetMaterialConfiguration(configuration, id)) + if (!materialLibrary->GetDataForMaterialName(materialName, configuration)) { - continue; // Default material skips code below. + return m_materials.end(); } - - auto materialId = configuration.m_id; + auto newMaterial = AZStd::make_shared(configuration.m_configuration); + auto insertedPair = m_materials.emplace(configuration.m_id.GetUuid(), AZStd::move(newMaterial)); + return insertedPair.first; + } + } + + Physics::MaterialLibraryAsset* MaterialsManager::GetMaterialLibrary() + { + if (auto* physicsSystem = AZ::Interface::Get()) + { + if (const auto* physicsConfiguration = physicsSystem->GetConfiguration()) + { + return physicsConfiguration->m_materialLibraryAsset.Get(); + } + } + return nullptr; + } + + void MaterialsManager::OnPhysicsConfigurationChanged(const AzPhysics::SystemConfiguration* config) + { + if (m_defaultMaterial && + m_defaultMaterialConfiguration != config->m_defaultMaterialConfiguration) + { + m_defaultMaterialConfiguration = config->m_defaultMaterialConfiguration; + + m_defaultMaterial->UpdateWithConfiguration(m_defaultMaterialConfiguration); + } + } + + void MaterialsManager::OnMaterialLibraryChanged([[maybe_unused]] const AZ::Data::AssetId& materialLibraryAssetId) + { + auto* materialLibrary = GetMaterialLibrary(); + if (!materialLibrary) + { + AZ_Warning("PhysX", false, "MaterialsManager: invalid material library"); + return; + } + + AZStd::vector materialsToRemove; + + for (auto& idMaterialPair : m_materials) + { + const Physics::MaterialId materialId = Physics::MaterialId::FromUUID(idMaterialPair.first); + + // Remove null materials if (materialId.IsNull()) { - materialId = Physics::MaterialId::Create(); + materialsToRemove.push_back(materialId.GetUuid()); + continue; } - auto iterator = m_materialsFromAssets.find(materialId.GetUuid()); - if (iterator != m_materialsFromAssets.end()) + Physics::MaterialFromAssetConfiguration configuration; + if (materialLibrary->GetDataForMaterialId(materialId, configuration)) { - iterator->second->UpdateWithConfiguration(configuration.m_configuration); + // Update materials found in the library. + idMaterialPair.second->UpdateWithConfiguration(configuration.m_configuration); } else { - auto newMaterial = AZStd::make_shared(configuration.m_configuration); - m_materialsFromAssets.emplace(materialId.GetUuid(), newMaterial); + // Add for removal the materials not present in the library anymore. + materialsToRemove.push_back(materialId.GetUuid()); } } + + for (const auto& id : materialsToRemove) + { + m_materials.erase(id); + } } } diff --git a/Gems/PhysX/Code/Source/Material.h b/Gems/PhysX/Code/Source/Material.h index a44c9766fc..1541d156d8 100644 --- a/Gems/PhysX/Code/Source/Material.h +++ b/Gems/PhysX/Code/Source/Material.h @@ -15,11 +15,18 @@ #include #include #include -#include +#include +#include #include namespace PhysX { + /// Name used by physx asset exporter to indicate that the default + /// physics material should be used for a mesh surface. The exporter + /// will use it as the fallback option when it's not possible to obtain + /// the surface information from the mesh material. + static const char* const DefaultPhysicsMaterialNameFromPhysicsAsset = ""; + /// PhysX implementation of Physics::Material interface /// =================================================== /// @@ -58,9 +65,9 @@ namespace PhysX // Physics::Material AZ::Crc32 GetSurfaceType() const override; - void SetSurfaceType(AZ::Crc32 surfaceType) override; - const AZStd::string& GetSurfaceTypeName() const override { return m_surfaceString; } + const AZStd::string& GetSurfaceTypeName() const override; + void SetSurfaceTypeName(const AZStd::string& surfaceTypeName) override; float GetDynamicFriction() const override; void SetDynamicFriction(float dynamicFriction) override; @@ -80,6 +87,9 @@ namespace PhysX float GetDensity() const override; void SetDensity(float density) override; + AZ::Color GetDebugColor() const override; + void SetDebugColor(const AZ::Color& debugColor) override; + AZ::u32 GetCryEngineSurfaceId() const override; void* GetNativePointer() override; @@ -92,6 +102,7 @@ namespace PhysX AZ::u32 m_cryEngineSurfaceId = -1; AZStd::string m_surfaceString; float m_density = 1000.0f; + AZ::Color m_debugColor = AZ::Colors::White; }; /// Bus with requests to MaterialsManager @@ -108,9 +119,17 @@ namespace PhysX static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + /// Returns weak pointers to physx::PxMaterial. + /// Equivalent to PhysicsMaterialRequests::GetMaterials but it returns physx::PxMaterial pointers instead. + /// @param materialSelection MaterialSelection instance to create or get materials for + /// @param outMaterials vector of pointers to physx::PxMaterial to fill with. The vector will be cleared inside the function. virtual void GetPxMaterials(const Physics::MaterialSelection& materialSelection, AZStd::vector& outMaterials) = 0; - virtual const AZStd::shared_ptr& GetDefaultMaterial() = 0; + /// Returns default material + /// @return default PhysX::Material instance + virtual AZStd::shared_ptr GetDefaultMaterial() = 0; + + /// Releases ownership of all materials created before. virtual void ReleaseAllMaterials() = 0; }; using MaterialManagerRequestsBus = AZ::EBus; @@ -120,6 +139,9 @@ namespace PhysX /// /// Material managers creates PhysX::Material instances from MaterialLibraryAsset and assumes their ownership. /// Also keeps a reference to the default material. + /// + /// Note: Materials will be created on the fly while doing queries and + /// they will be updated when the material library changes. class MaterialsManager : public MaterialManagerRequestsBus::Handler , public Physics::PhysicsMaterialRequestBus::Handler @@ -131,35 +153,19 @@ namespace PhysX MaterialsManager(); ~MaterialsManager() override; - /// Returns a vector of weak pointers to materials selected. - /// To be notified if the pointers are deleted, connect to PhysicsMaterialNotifications::MaterialsReleased(). - /// @param materialSelection MaterialSelection instance to create or get materials for. - /// @param outMaterials Collection of material weak pointers corresponding to the material selection to be returned. + // PhysicsMaterialRequestBus::Handler overrides... void GetMaterials(const Physics::MaterialSelection& materialSelection - , AZStd::vector>& outMaterials) override; - - /// Returns a weak pointer to physics material with the given name. - AZStd::weak_ptr GetMaterialByName(const AZStd::string& name) override; - - /// Returns index of selected material in its material library. 0 is the Default material. - /// @param materialSelection Selection of materials. - AZ::u32 GetFirstSelectedMaterialIndex(const Physics::MaterialSelection& materialSelection) override; - - /// Slightly faster version of GetMaterials that returns physx::PxMaterial pointers instead. \n - /// The rest is equivalent to GetMaterials function. - /// @param materialSelection MaterialSelection instance to create or get materials for - /// @param outMaterials vector of pointers to physx::PxMaterial to fill with. The vector will be cleared inside the function. - void GetPxMaterials(const Physics::MaterialSelection& materialSelection, AZStd::vector& outMaterials) override; - - /// Returns default material - /// @return default PhysX::Material instance - const AZStd::shared_ptr& GetDefaultMaterial() override; - - /// Return default material - /// @return default Physics::Material instance + , AZStd::vector>& outMaterials) override; + AZStd::shared_ptr GetMaterialById(Physics::MaterialId id) override; + AZStd::shared_ptr GetMaterialByName(const AZStd::string& name) override; + void UpdateMaterialSelectionFromPhysicsAsset( + const Physics::ShapeConfiguration& shapeConfiguration, + Physics::MaterialSelection& materialSelection) override; AZStd::shared_ptr GetGenericDefaultMaterial() override; - /// Releases ownership of all materials created before. + // MaterialManagerRequestsBus::Handler overrides... + void GetPxMaterials(const Physics::MaterialSelection& materialSelection, AZStd::vector& outMaterials) override; + AZStd::shared_ptr GetDefaultMaterial() override; void ReleaseAllMaterials() override; /// Connect to any necessary buses @@ -169,9 +175,31 @@ namespace PhysX void Disconnect(); private: - void InitializeMaterials(const Physics::MaterialSelection& materialSelection); + using Materials = AZStd::unordered_map>; + + /// Search a material by id, if it exists already it returns its iterator, + /// if it doesn't exist it tries to create it and add it to the list. + /// If the material id is null or not part of the material library then the + /// iterator returned is end of material list. + Materials::iterator FindOrCreateMaterial(Physics::MaterialId materialId); + + /// Search a material by name, if it exists already it returns its iterator, + /// if it doesn't exist it tries to create it and add it to the list. + /// If the material id is null or not part of the material library then the + /// iterator returned is end of material list. + Materials::iterator FindOrCreateMaterial(const AZStd::string& materialName); - AZStd::unordered_map> m_materialsFromAssets; + /// Returns the material library of the project. + Physics::MaterialLibraryAsset* GetMaterialLibrary(); + + void OnPhysicsConfigurationChanged(const AzPhysics::SystemConfiguration* config); + void OnMaterialLibraryChanged(const AZ::Data::AssetId& materialLibraryAssetId); + + Materials m_materials; AZStd::shared_ptr m_defaultMaterial; + Physics::MaterialConfiguration m_defaultMaterialConfiguration; + + AzPhysics::SystemEvents::OnConfigurationChangedEvent::Handler m_physicsConfigChangedHandler; + AzPhysics::SystemEvents::OnMaterialLibraryChangedEvent::Handler m_materialLibraryChangedHandler; }; } diff --git a/Gems/PhysX/Code/Source/MeshColliderComponent.cpp b/Gems/PhysX/Code/Source/MeshColliderComponent.cpp index d383ba97de..1c6fdca640 100644 --- a/Gems/PhysX/Code/Source/MeshColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/MeshColliderComponent.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include @@ -85,11 +85,6 @@ namespace PhysX UpdateMeshAsset(); } - void MeshColliderComponent::SetMaterialAsset(const AZ::Data::AssetId& id) - { - m_colliderConfiguration->m_materialSelection.SetMaterialLibrary(id); - } - void MeshColliderComponent::SetMaterialId(const Physics::MaterialId& id) { m_colliderConfiguration->m_materialSelection.SetMaterialId(id); @@ -111,8 +106,10 @@ namespace PhysX { m_shapeConfiguration->m_asset = asset; - Physics::SystemRequestBus::Broadcast(&Physics::SystemRequests::UpdateMaterialSelection, - *m_shapeConfiguration, *m_colliderConfiguration); + Physics::PhysicsMaterialRequestBus::Broadcast( + &Physics::PhysicsMaterialRequestBus::Events::UpdateMaterialSelectionFromPhysicsAsset, + *m_shapeConfiguration, + m_colliderConfiguration->m_materialSelection); } } @@ -122,8 +119,10 @@ namespace PhysX { m_shapeConfiguration->m_asset = asset; - Physics::SystemRequestBus::Broadcast(&Physics::SystemRequests::UpdateMaterialSelection, - *m_shapeConfiguration, *m_colliderConfiguration); + Physics::PhysicsMaterialRequestBus::Broadcast( + &Physics::PhysicsMaterialRequestBus::Events::UpdateMaterialSelectionFromPhysicsAsset, + *m_shapeConfiguration, + m_colliderConfiguration->m_materialSelection); } } diff --git a/Gems/PhysX/Code/Source/MeshColliderComponent.h b/Gems/PhysX/Code/Source/MeshColliderComponent.h index 4014f7ab5a..9d781fd6eb 100644 --- a/Gems/PhysX/Code/Source/MeshColliderComponent.h +++ b/Gems/PhysX/Code/Source/MeshColliderComponent.h @@ -40,7 +40,6 @@ namespace PhysX AZ::Data::Asset GetMeshAsset() const override; Physics::MaterialId GetMaterialId() const override; void SetMeshAsset(const AZ::Data::AssetId& id) override; - void SetMaterialAsset(const AZ::Data::AssetId& id) override; void SetMaterialId(const Physics::MaterialId& id) override; // BaseColliderComponent diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp index d1502a0c65..87e3304a90 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterUtils.cpp @@ -15,9 +15,7 @@ #include #include #include -#include -#include -#include +#include #include #include #include @@ -51,18 +49,32 @@ namespace PhysX static void AppendShapeIndependentProperties(physx::PxControllerDesc& controllerDesc, const Physics::CharacterConfiguration& characterConfig, CharacterControllerCallbackManager* callbackManager) { - AZStd::vector > materials; + AZStd::vector> materials; - Physics::SystemRequestBus::BroadcastResult( - materials, - &Physics::SystemRequests::CreateMaterialsFromLibrary, - characterConfig.m_materialSelection - ); - - if (materials.empty()) + if (characterConfig.m_materialSelection.GetMaterialIdsAssignedToSlots().empty()) { - AZ_Error("PhysX Character Controller", false, "Could not create character controller, material was invalid."); - return; + // If material selection has no slots, falling back to default material. + AZStd::shared_ptr defaultMaterial; + Physics::PhysicsMaterialRequestBus::BroadcastResult(defaultMaterial, + &Physics::PhysicsMaterialRequestBus::Events::GetGenericDefaultMaterial); + if (!defaultMaterial) + { + AZ_Error("PhysX Character Controller", false, "Invalid default material."); + return; + } + materials.push_back(AZStd::move(defaultMaterial)); + } + else + { + Physics::PhysicsMaterialRequestBus::Broadcast( + &Physics::PhysicsMaterialRequestBus::Events::GetMaterials, + characterConfig.m_materialSelection, + materials); + if (materials.empty()) + { + AZ_Error("PhysX Character Controller", false, "Could not create character controller, material list was empty."); + return; + } } physx::PxMaterial* pxMaterial = static_cast(materials.front()->GetNativePointer()); diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.cpp index 5249781e31..02ebe7281c 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.cpp @@ -274,7 +274,7 @@ namespace PhysX m_queuedDisableSimulation = true; } - bool Ragdoll::IsSimulated() + bool Ragdoll::IsSimulated() const { return m_simulating; } diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.h b/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.h index 7bf807bcc5..7182a3a44c 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.h +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/Ragdoll.h @@ -46,7 +46,7 @@ namespace PhysX void EnableSimulationQueued(const Physics::RagdollState& initialState) override; void DisableSimulation() override; void DisableSimulationQueued() override; - bool IsSimulated() override; + bool IsSimulated() const override; void GetState(Physics::RagdollState& ragdollState) const override; void SetState(const Physics::RagdollState& ragdollState) override; void SetStateQueued(const Physics::RagdollState& ragdollState) override; diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp index 6fa69c7a24..86c60565a0 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp @@ -105,39 +105,55 @@ namespace PhysX AZ::Vector3 CharacterControllerComponent::GetBasePosition() const { - return IsPhysicsEnabled() ? m_controller->GetBasePosition() : AZ::Vector3::CreateZero(); + if (auto* controller = GetControllerConst()) + { + return controller->GetBasePosition(); + } + return AZ::Vector3::CreateZero(); } void CharacterControllerComponent::SetBasePosition(const AZ::Vector3& position) { - if (IsPhysicsEnabled()) + if (auto* controller = GetController()) { - m_controller->SetBasePosition(position); + controller->SetBasePosition(position); AZ::TransformBus::Event(GetEntityId(), &AZ::TransformBus::Events::SetWorldTranslation, position); } } AZ::Vector3 CharacterControllerComponent::GetCenterPosition() const { - return IsPhysicsEnabled() ? m_controller->GetCenterPosition() : AZ::Vector3::CreateZero(); + if (auto* controller = GetControllerConst()) + { + return controller->GetCenterPosition(); + } + return AZ::Vector3::CreateZero(); } float CharacterControllerComponent::GetStepHeight() const { - return IsPhysicsEnabled() ? m_controller->GetStepHeight() : 0.0f; + if (auto* controller = GetControllerConst()) + { + return controller->GetStepHeight(); + } + return 0.0f; } void CharacterControllerComponent::SetStepHeight(float stepHeight) { - if (IsPhysicsEnabled()) + if (auto* controller = GetController()) { - m_controller->SetStepHeight(stepHeight); + controller->SetStepHeight(stepHeight); } } AZ::Vector3 CharacterControllerComponent::GetUpDirection() const { - return IsPhysicsEnabled() ? m_controller->GetUpDirection() : AZ::Vector3::CreateZero(); + if (auto* controller = GetControllerConst()) + { + return controller->GetUpDirection(); + } + return AZ::Vector3::CreateZero(); } void CharacterControllerComponent::SetUpDirection([[maybe_unused]] const AZ::Vector3& upDirection) @@ -147,51 +163,58 @@ namespace PhysX float CharacterControllerComponent::GetSlopeLimitDegrees() const { - return IsPhysicsEnabled() ? m_controller->GetSlopeLimitDegrees() : 0.0f; + if (auto* controller = GetControllerConst()) + { + return controller->GetSlopeLimitDegrees(); + } + return 0.0f; } void CharacterControllerComponent::SetSlopeLimitDegrees(float slopeLimitDegrees) { - if (IsPhysicsEnabled()) + if (auto* controller = GetController()) { - m_controller->SetSlopeLimitDegrees(slopeLimitDegrees); + controller->SetSlopeLimitDegrees(slopeLimitDegrees); } } float CharacterControllerComponent::GetMaximumSpeed() const { - if (IsPhysicsEnabled()) + if (auto* controller = GetControllerConst()) { - return m_controller->GetMaximumSpeed(); + return controller->GetMaximumSpeed(); } - return 0.0f; } void CharacterControllerComponent::SetMaximumSpeed(float maximumSpeed) { - if (IsPhysicsEnabled()) + if (auto* controller = GetController()) { - m_controller->SetMaximumSpeed(maximumSpeed); + controller->SetMaximumSpeed(maximumSpeed); } } AZ::Vector3 CharacterControllerComponent::GetVelocity() const { - return IsPhysicsEnabled() ? m_controller->GetVelocity() : AZ::Vector3::CreateZero(); + if (auto* controller = GetControllerConst()) + { + return controller->GetVelocity(); + } + return AZ::Vector3::CreateZero(); } void CharacterControllerComponent::AddVelocity(const AZ::Vector3& velocity) { - if (IsPhysicsEnabled()) + if (auto* controller = GetController()) { - m_controller->AddVelocity(velocity); + controller->AddVelocity(velocity); } } Physics::Character* CharacterControllerComponent::GetCharacter() { - return m_controller; + return GetController(); } void CharacterControllerComponent::EnablePhysics() @@ -206,14 +229,14 @@ namespace PhysX bool CharacterControllerComponent::IsPhysicsEnabled() const { - return m_controller != nullptr; + return GetControllerConst() != nullptr; } AZ::Aabb CharacterControllerComponent::GetAabb() const { - if (m_controller) + if (auto* controller = GetControllerConst()) { - return m_controller->GetAabb(); + return controller->GetAabb(); } return AZ::Aabb::CreateNull(); } @@ -225,94 +248,121 @@ namespace PhysX AzPhysics::SimulatedBodyHandle CharacterControllerComponent::GetSimulatedBodyHandle() const { - if (m_controller) - { - return m_controller->m_bodyHandle; - } - return AzPhysics::InvalidSimulatedBodyHandle; + return m_controllerBodyHandle; } AzPhysics::SceneQueryHit CharacterControllerComponent::RayCast(const AzPhysics::RayCastRequest& request) { - if (m_controller) + if (auto* controller = GetController()) { - return m_controller->RayCast(request); + return controller->RayCast(request); } + return AzPhysics::SceneQueryHit(); } // CharacterControllerRequestBus void CharacterControllerComponent::Resize(float height) { - return m_controller->Resize(height); + if (auto* controller = GetController()) + { + controller->Resize(height); + } } float CharacterControllerComponent::GetHeight() { - return m_controller->GetHeight(); + if (auto* controller = GetController()) + { + return controller->GetHeight(); + } + return 0.0f; } void CharacterControllerComponent::SetHeight(float height) { - return m_controller->SetHeight(height); + if (auto* controller = GetController()) + { + controller->SetHeight(height); + } } float CharacterControllerComponent::GetRadius() { - return m_controller->GetRadius(); + if (auto* controller = GetController()) + { + return controller->GetRadius(); + } + return 0.0f; } void CharacterControllerComponent::SetRadius(float radius) { - return m_controller->SetRadius(radius); + if (auto* controller = GetController()) + { + controller->SetRadius(radius); + } } float CharacterControllerComponent::GetHalfSideExtent() { - return m_controller->GetHalfSideExtent(); + if (auto* controller = GetController()) + { + return controller->GetHalfSideExtent(); + } + return 0.0f; } void CharacterControllerComponent::SetHalfSideExtent(float halfSideExtent) { - return m_controller->SetHalfSideExtent(halfSideExtent); + if (auto* controller = GetController()) + { + controller->SetHalfSideExtent(halfSideExtent); + } } float CharacterControllerComponent::GetHalfForwardExtent() { - return m_controller->GetHalfForwardExtent(); + if (auto* controller = GetController()) + { + return controller->GetHalfForwardExtent(); + } + return 0.0f; } void CharacterControllerComponent::SetHalfForwardExtent(float halfForwardExtent) { - return m_controller->SetHalfForwardExtent(halfForwardExtent); + if (auto* controller = GetController()) + { + controller->SetHalfForwardExtent(halfForwardExtent); + } } // TransformNotificationBus void CharacterControllerComponent::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world) { - if (!IsPhysicsEnabled()) + if (auto* controller = GetController()) { - return; + controller->SetBasePosition(world.GetTranslation()); } - - m_controller->SetBasePosition(world.GetTranslation()); } void CharacterControllerComponent::SetCollisionLayer(const AZStd::string& layerName, AZ::Crc32 colliderTag) { - if (!IsPhysicsEnabled()) + auto* controller = GetController(); + if (controller == nullptr) { return; } - if (Physics::Utils::FilterTag(m_controller->GetColliderTag(), colliderTag)) + if (Physics::Utils::FilterTag(controller->GetColliderTag(), colliderTag)) { bool success = false; AzPhysics::CollisionLayer collisionLayer; Physics::CollisionRequestBus::BroadcastResult(success, &Physics::CollisionRequests::TryGetCollisionLayerByName, layerName, collisionLayer); if (success) { - m_controller->SetCollisionLayer(collisionLayer); + controller->SetCollisionLayer(collisionLayer); } } } @@ -320,30 +370,33 @@ namespace PhysX AZStd::string CharacterControllerComponent::GetCollisionLayerName() { AZStd::string layerName; - if (!IsPhysicsEnabled()) + auto* controller = GetControllerConst(); + if (controller == nullptr) { return layerName; } - Physics::CollisionRequestBus::BroadcastResult(layerName, &Physics::CollisionRequests::GetCollisionLayerName, m_controller->GetCollisionLayer()); + Physics::CollisionRequestBus::BroadcastResult( + layerName, &Physics::CollisionRequests::GetCollisionLayerName, controller->GetCollisionLayer()); return layerName; } void CharacterControllerComponent::SetCollisionGroup(const AZStd::string& groupName, AZ::Crc32 colliderTag) { - if (!IsPhysicsEnabled()) + auto* controller = GetController(); + if (controller == nullptr) { return; } - if (Physics::Utils::FilterTag(m_controller->GetColliderTag(), colliderTag)) + if (Physics::Utils::FilterTag(controller->GetColliderTag(), colliderTag)) { bool success = false; AzPhysics::CollisionGroup collisionGroup; Physics::CollisionRequestBus::BroadcastResult(success, &Physics::CollisionRequests::TryGetCollisionGroupByName, groupName, collisionGroup); if (success) { - m_controller->SetCollisionGroup(collisionGroup); + controller->SetCollisionGroup(collisionGroup); } } } @@ -351,23 +404,26 @@ namespace PhysX AZStd::string CharacterControllerComponent::GetCollisionGroupName() { AZStd::string groupName; - if (!IsPhysicsEnabled()) + auto* controller = GetControllerConst(); + if (controller == nullptr) { return groupName; } - - Physics::CollisionRequestBus::BroadcastResult(groupName, &Physics::CollisionRequests::GetCollisionGroupName, m_controller->GetCollisionGroup()); + + Physics::CollisionRequestBus::BroadcastResult( + groupName, &Physics::CollisionRequests::GetCollisionGroupName, controller->GetCollisionGroup()); return groupName; } void CharacterControllerComponent::ToggleCollisionLayer(const AZStd::string& layerName, AZ::Crc32 colliderTag, bool enabled) { - if (!IsPhysicsEnabled()) + auto* controller = GetController(); + if (controller == nullptr) { return; } - if (Physics::Utils::FilterTag(m_controller->GetColliderTag(), colliderTag)) + if (Physics::Utils::FilterTag(controller->GetColliderTag(), colliderTag)) { bool success = false; AzPhysics::CollisionLayer collisionLayer; @@ -375,23 +431,43 @@ namespace PhysX if (success) { AzPhysics::CollisionLayer layer(layerName); - AzPhysics::CollisionGroup group = m_controller->GetCollisionGroup(); + AzPhysics::CollisionGroup group = controller->GetCollisionGroup(); group.SetLayer(layer, enabled); - m_controller->SetCollisionGroup(group); + controller->SetCollisionGroup(group); } } } void CharacterControllerComponent::OnPreSimulate(float deltaTime) { - if (m_controller) + if (auto* controller = GetController()) { - m_controller->ApplyRequestedVelocity(deltaTime); - const AZ::Vector3 newPosition = GetBasePosition(); + controller->ApplyRequestedVelocity(deltaTime); + const AZ::Vector3 newPosition = controller->GetBasePosition(); AZ::TransformBus::Event(GetEntityId(), &AZ::TransformBus::Events::SetWorldTranslation, newPosition); } } + const PhysX::CharacterController* CharacterControllerComponent::GetControllerConst() const + { + if (m_controllerBodyHandle == AzPhysics::InvalidSimulatedBodyHandle || m_attachedSceneHandle == AzPhysics::InvalidSceneHandle) + { + return nullptr; + } + + if (auto* sceneInterface = AZ::Interface::Get()) + { + return azdynamic_cast( + sceneInterface->GetSimulatedBodyFromHandle(m_attachedSceneHandle, m_controllerBodyHandle)); + } + return nullptr; + } + + PhysX::CharacterController* CharacterControllerComponent::GetController() + { + return const_cast(static_cast(*this).GetControllerConst()); + } + void CharacterControllerComponent::CreateController() { if (IsPhysicsEnabled()) @@ -399,9 +475,8 @@ namespace PhysX return; } - AzPhysics::SceneHandle defaultSceneHandle = AzPhysics::InvalidSceneHandle; - Physics::DefaultWorldBus::BroadcastResult(defaultSceneHandle, &Physics::DefaultWorldRequests::GetDefaultSceneHandle); - if (defaultSceneHandle == AzPhysics::InvalidSceneHandle) + Physics::DefaultWorldBus::BroadcastResult(m_attachedSceneHandle, &Physics::DefaultWorldRequests::GetDefaultSceneHandle); + if (m_attachedSceneHandle == AzPhysics::InvalidSceneHandle) { AZ_Error("PhysX Character Controller Component", false, "Failed to retrieve default scene."); return; @@ -427,11 +502,9 @@ namespace PhysX auto* sceneInterface = AZ::Interface::Get(); if (sceneInterface != nullptr) { - m_controllerBodyHandle = sceneInterface->AddSimulatedBody(defaultSceneHandle, m_characterConfig.get()); - m_controller = azdynamic_cast( - sceneInterface->GetSimulatedBodyFromHandle(defaultSceneHandle, m_controllerBodyHandle)); + m_controllerBodyHandle = sceneInterface->AddSimulatedBody(m_attachedSceneHandle, m_characterConfig.get()); } - if (m_controller == nullptr) + if (m_controllerBodyHandle == AzPhysics::InvalidSimulatedBodyHandle) { AZ_Error("PhysX Character Controller Component", false, "Failed to create character controller."); return; @@ -447,7 +520,7 @@ namespace PhysX DestroyController(); } }); - sceneInterface->RegisterSimulationBodyRemovedHandler(defaultSceneHandle, m_onSimulatedBodyRemovedHandler); + sceneInterface->RegisterSimulationBodyRemovedHandler(m_attachedSceneHandle, m_onSimulatedBodyRemovedHandler); } CharacterControllerRequestBus::Handler::BusConnect(GetEntityId()); @@ -467,24 +540,23 @@ namespace PhysX void CharacterControllerComponent::DisableController() { - if (!IsPhysicsEnabled()) + if (auto* controller = GetController()) { - return; - } + controller->DisablePhysics(); - m_controller->DisablePhysics(); + if (auto* sceneInterface = AZ::Interface::Get()) + { + sceneInterface->RemoveSimulatedBody(m_attachedSceneHandle, controller->m_bodyHandle); + } - if (auto* sceneInterface = AZ::Interface::Get()) - { - sceneInterface->RemoveSimulatedBody(m_controller->m_sceneOwner, m_controller->m_bodyHandle); + DestroyController(); } - - DestroyController(); } void CharacterControllerComponent::DestroyController() { - m_controller = nullptr; + m_controllerBodyHandle = AzPhysics::InvalidSimulatedBodyHandle; + m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; m_preSimulateHandler.Disconnect(); m_onSimulatedBodyRemovedHandler.Disconnect(); CharacterControllerRequestBus::Handler::BusDisconnect(); diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h index 7c25312b72..54513d52f4 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h @@ -131,6 +131,8 @@ namespace PhysX void ToggleCollisionLayer(const AZStd::string& layerName, AZ::Crc32 colliderTag, bool enabled) override; private: + const PhysX::CharacterController* GetControllerConst() const; + PhysX::CharacterController* GetController(); // Creates the physics character controller in the current default physics scene. // This will do nothing if the controller is already created. void CreateController(); @@ -143,8 +145,8 @@ namespace PhysX AZStd::unique_ptr m_characterConfig; AZStd::shared_ptr m_shapeConfig; - PhysX::CharacterController* m_controller = nullptr; AzPhysics::SimulatedBodyHandle m_controllerBodyHandle = AzPhysics::InvalidSimulatedBodyHandle; + AzPhysics::SceneHandle m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; AzPhysics::SystemEvents::OnPresimulateEvent::Handler m_preSimulateHandler; AzPhysics::SceneEvents::OnSimulationBodyRemoved::Handler m_onSimulatedBodyRemovedHandler; }; diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp index 4c58187fb8..8da512647f 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp @@ -170,63 +170,88 @@ namespace PhysX // RagdollPhysicsBus void RagdollComponent::EnableSimulation(const Physics::RagdollState& initialState) { - m_ragdoll->EnableSimulation(initialState); + if (auto* ragdoll = GetPhysXRagdoll()) + { + ragdoll->EnableSimulation(initialState); + } } void RagdollComponent::EnableSimulationQueued(const Physics::RagdollState& initialState) { - m_ragdoll->EnableSimulationQueued(initialState); + if (auto* ragdoll = GetPhysXRagdoll()) + { + ragdoll->EnableSimulationQueued(initialState); + } } void RagdollComponent::DisableSimulation() { - if (m_ragdoll) + if (auto* ragdoll = GetPhysXRagdoll()) { - m_ragdoll->DisableSimulation(); + ragdoll->DisableSimulation(); } } void RagdollComponent::DisableSimulationQueued() { - if (m_ragdoll) + if (auto* ragdoll = GetPhysXRagdoll()) { - m_ragdoll->DisableSimulationQueued(); + ragdoll->DisableSimulationQueued(); } } Physics::Ragdoll* RagdollComponent::GetRagdoll() { - return m_ragdoll; + return GetPhysXRagdoll(); } void RagdollComponent::GetState(Physics::RagdollState& ragdollState) const { - m_ragdoll->GetState(ragdollState); + if (const auto* ragdoll = GetPhysXRagdollConst()) + { + ragdoll->GetState(ragdollState); + } } void RagdollComponent::SetState(const Physics::RagdollState& ragdollState) { - m_ragdoll->SetState(ragdollState); + if (auto* ragdoll = GetPhysXRagdoll()) + { + ragdoll->SetState(ragdollState); + } } void RagdollComponent::SetStateQueued(const Physics::RagdollState& ragdollState) { - m_ragdoll->SetStateQueued(ragdollState); + if (auto* ragdoll = GetPhysXRagdoll()) + { + ragdoll->SetStateQueued(ragdollState); + } } void RagdollComponent::GetNodeState(size_t nodeIndex, Physics::RagdollNodeState& nodeState) const { - m_ragdoll->GetNodeState(nodeIndex, nodeState); + if (const auto* ragdoll = GetPhysXRagdollConst()) + { + ragdoll->GetNodeState(nodeIndex, nodeState); + } } void RagdollComponent::SetNodeState(size_t nodeIndex, const Physics::RagdollNodeState& nodeState) { - m_ragdoll->SetNodeState(nodeIndex, nodeState); + if (auto* ragdoll = GetPhysXRagdoll()) + { + ragdoll->SetNodeState(nodeIndex, nodeState); + } } Physics::RagdollNode* RagdollComponent::GetNode(size_t nodeIndex) const { - return m_ragdoll->GetNode(nodeIndex); + if (const auto* ragdoll = GetPhysXRagdollConst()) + { + return ragdoll->GetNode(nodeIndex); + } + return nullptr; } void RagdollComponent::EnablePhysics() @@ -245,14 +270,19 @@ namespace PhysX bool RagdollComponent::IsPhysicsEnabled() const { - return m_ragdoll && m_ragdoll->IsSimulated(); + if (const auto* ragdoll = GetPhysXRagdollConst()) + { + return ragdoll->IsSimulated(); + } + return false; + } AZ::Aabb RagdollComponent::GetAabb() const { - if (m_ragdoll) + if (const auto* ragdoll = GetPhysXRagdollConst()) { - return m_ragdoll->GetAabb(); + return ragdoll->GetAabb(); } return AZ::Aabb::CreateNull(); } @@ -264,18 +294,14 @@ namespace PhysX AzPhysics::SimulatedBodyHandle RagdollComponent::GetSimulatedBodyHandle() const { - if (m_ragdoll) - { - return m_ragdoll->m_bodyHandle; - } - return AzPhysics::InvalidSimulatedBodyHandle; + return m_ragdollHandle; } AzPhysics::SceneQueryHit RagdollComponent::RayCast(const AzPhysics::RayCastRequest& request) { - if (m_ragdoll) + if (auto* ragdoll = GetPhysXRagdoll()) { - return m_ragdoll->RayCast(request); + return ragdoll->RayCast(request); } return AzPhysics::SceneQueryHit(); } @@ -323,23 +349,24 @@ namespace PhysX AZ::TransformBus::EventResult(entityTransform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); ragdollConfiguration.m_initialState = GetBindPoseWorld(bindPose, entityTransform); - AzPhysics::SceneHandle defaultSceneHandle = AzPhysics::InvalidSceneHandle; - Physics::DefaultWorldBus::BroadcastResult(defaultSceneHandle, &Physics::DefaultWorldRequests::GetDefaultSceneHandle); + m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; + Physics::DefaultWorldBus::BroadcastResult(m_attachedSceneHandle, &Physics::DefaultWorldRequests::GetDefaultSceneHandle); if (auto* sceneInterface = AZ::Interface::Get()) { - AzPhysics::SimulatedBodyHandle bodyHandle = sceneInterface->AddSimulatedBody(defaultSceneHandle, &ragdollConfiguration); - m_ragdoll = azdynamic_cast(sceneInterface->GetSimulatedBodyFromHandle(defaultSceneHandle, bodyHandle)); + m_ragdollHandle = sceneInterface->AddSimulatedBody(m_attachedSceneHandle, &ragdollConfiguration); } - if (m_ragdoll == nullptr) + auto* ragdoll = GetPhysXRagdoll(); + if (ragdoll == nullptr || + m_ragdollHandle == AzPhysics::InvalidSimulatedBodyHandle) { AZ_Error("PhysX Ragdoll Component", false, "Failed to create ragdoll."); return; } - + for (size_t nodeIndex = 0; nodeIndex < numNodes; nodeIndex++) { - if (physx::PxRigidDynamic* pxRigidBody = m_ragdoll->GetPxRigidDynamic(nodeIndex)) + if (physx::PxRigidDynamic* pxRigidBody = ragdoll->GetPxRigidDynamic(nodeIndex)) { pxRigidBody->setSolverIterationCounts(m_positionIterations, m_velocityIterations); } @@ -352,7 +379,7 @@ namespace PhysX for (size_t nodeIndex = 0; nodeIndex < numNodes; nodeIndex++) { - if (const AZStd::shared_ptr& joint = m_ragdoll->GetNode(nodeIndex)->GetJoint()) + if (const AZStd::shared_ptr& joint = ragdoll->GetNode(nodeIndex)->GetJoint()) { if (auto* pxJoint = static_cast(joint->GetNativePointer())) { @@ -374,20 +401,41 @@ namespace PhysX void RagdollComponent::DestroyRagdoll() { - if (m_ragdoll) + if (m_ragdollHandle != AzPhysics::InvalidSimulatedBodyHandle && + m_attachedSceneHandle != AzPhysics::InvalidSceneHandle) { AzFramework::RagdollPhysicsRequestBus::Handler::BusDisconnect(); - AzFramework::RagdollPhysicsNotificationBus::Event(GetEntityId(), - &AzFramework::RagdollPhysicsNotifications::OnRagdollDeactivated); + AzFramework::RagdollPhysicsNotificationBus::Event( + GetEntityId(), &AzFramework::RagdollPhysicsNotifications::OnRagdollDeactivated); if (auto* sceneInterface = AZ::Interface::Get()) { - sceneInterface->RemoveSimulatedBody(m_ragdoll->m_sceneOwner, m_ragdoll->m_bodyHandle); + sceneInterface->RemoveSimulatedBody(m_attachedSceneHandle, m_ragdollHandle); + m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; } - m_ragdoll = nullptr; } } + Ragdoll* RagdollComponent::GetPhysXRagdoll() + { + return const_cast(static_cast(*this).GetPhysXRagdollConst()); + } + + const Ragdoll* RagdollComponent::GetPhysXRagdollConst() const + { + if (m_ragdollHandle == AzPhysics::InvalidSimulatedBodyHandle || + m_attachedSceneHandle == AzPhysics::InvalidSceneHandle) + { + return nullptr; + } + + if (auto* sceneInterface = AZ::Interface::Get()) + { + return azdynamic_cast(sceneInterface->GetSimulatedBodyFromHandle(m_attachedSceneHandle, m_ragdollHandle)); + } + return nullptr; + } + // deprecated Cry functions void RagdollComponent::EnterRagdoll() { diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.h b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.h index a02e9c47fb..2c265b0f73 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.h +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.h @@ -104,10 +104,13 @@ namespace PhysX private: void CreateRagdoll(const Physics::RagdollConfiguration& ragdollConfiguration); void DestroyRagdoll(); + Ragdoll* GetPhysXRagdoll(); + const Ragdoll* GetPhysXRagdollConst() const; bool IsJointProjectionVisible(); - Ragdoll* m_ragdoll; + AzPhysics::SimulatedBodyHandle m_ragdollHandle = AzPhysics::InvalidSimulatedBodyHandle; + AzPhysics::SceneHandle m_attachedSceneHandle = AzPhysics::InvalidSceneHandle; /// Minimum number of position iterations to perform in the PhysX solver. /// Lower iteration counts are less expensive but may behave less realistically. AZ::u32 m_positionIterations = 16; diff --git a/Gems/PhysX/Code/Source/Pipeline/MeshExporter.cpp b/Gems/PhysX/Code/Source/Pipeline/MeshExporter.cpp index 7069f9a05d..24e532e2d1 100644 --- a/Gems/PhysX/Code/Source/Pipeline/MeshExporter.cpp +++ b/Gems/PhysX/Code/Source/Pipeline/MeshExporter.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include #include @@ -153,7 +153,7 @@ namespace PhysX AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(3); + serializeContext->Class()->Version(4); } } @@ -215,7 +215,7 @@ namespace PhysX if (nameAttribute) { AZStd::string materialName = nameAttribute->value(); - AZStd::string surfaceTypeName = DefaultMaterialName; + AZStd::string surfaceTypeName = DefaultPhysicsMaterialNameFromPhysicsAsset; AZ::rapidxml::xml_attribute* surfaceTypeNode = materialNode->first_attribute("SurfaceType"); if (surfaceTypeNode && surfaceTypeNode->value_size() != 0) @@ -268,7 +268,7 @@ namespace PhysX } else { - materialName = DefaultMaterialName; + materialName = DefaultPhysicsMaterialNameFromPhysicsAsset; } materialNames.emplace_back(AZStd::move(materialName)); @@ -328,7 +328,7 @@ namespace PhysX physx::PxMeshMidPhase::Enum ret = physx::PxMeshMidPhase::eBVH34; // Fallback to 3.3 on Android and iOS platforms since they don't support SSE2, which is required for 3.4 - if (platformIdentifier == "es3" || platformIdentifier == "ios") + if (platformIdentifier == "android" || platformIdentifier == "ios") { ret = physx::PxMeshMidPhase::eBVH33; } @@ -794,6 +794,9 @@ namespace PhysX ); } + // Convex and primitive methods can only have 1 material + const bool limitToOneMaterial = pxMeshGroup.GetExportAsConvex() || pxMeshGroup.GetExportAsPrimitive(); + for (AZ::u32 faceIndex = 0; faceIndex < faceCount; ++faceIndex) { AZStd::string materialName = DefaultMaterialName; @@ -810,6 +813,14 @@ namespace PhysX } materialName = localFbxMaterialsList[materialId]; + + // Keep using the first material when it has to be limited to one. + if (limitToOneMaterial && + assetMaterialData.m_fbxMaterialNames.size() == 1 && + assetMaterialData.m_fbxMaterialNames[0] != materialName) + { + materialName = assetMaterialData.m_fbxMaterialNames[0]; + } } const AZ::SceneAPI::DataTypes::IMeshData::Face& face = nodeMesh->GetFaceInfo(faceIndex); diff --git a/Gems/PhysX/Code/Source/RigidBodyComponent.cpp b/Gems/PhysX/Code/Source/RigidBodyComponent.cpp index feb35ea07d..40cac1e19e 100644 --- a/Gems/PhysX/Code/Source/RigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/RigidBodyComponent.cpp @@ -201,7 +201,7 @@ namespace PhysX AZ::Quaternion newRotation = AZ::Quaternion::CreateIdentity(); m_interpolator->GetInterpolated(newPosition, newRotation, deltaTime); - AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetRotationQuaternion, newRotation); + AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetWorldRotationQuaternion, newRotation); AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetWorldTranslation, newPosition); } } @@ -256,7 +256,7 @@ namespace PhysX } else { - AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetRotationQuaternion, rigidBody->GetOrientation()); + AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetWorldRotationQuaternion, rigidBody->GetOrientation()); AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetWorldTranslation, rigidBody->GetPosition()); } m_isLastMovementFromKinematicSource = false; diff --git a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp index 8df9e9a86f..92f68ea13b 100644 --- a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp +++ b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp @@ -40,8 +40,8 @@ namespace PhysX } #endif - PhysXSystem::MaterialLibraryAssetHelper::MaterialLibraryAssetHelper(PhysXSystem* physXSystem) - : m_physXSystem(physXSystem) + PhysXSystem::MaterialLibraryAssetHelper::MaterialLibraryAssetHelper(OnMaterialLibraryReloadedCallback callback) + : m_onMaterialLibraryReloadedCallback(callback) { } @@ -62,16 +62,16 @@ namespace PhysX void PhysXSystem::MaterialLibraryAssetHelper::OnAssetReloaded(AZ::Data::Asset asset) { - if (m_physXSystem == nullptr || m_physXSystem->GetDefaultMaterialLibrary() != asset) - { - return; - } - m_physXSystem->UpdateDefaultMaterialLibrary(asset); + m_onMaterialLibraryReloadedCallback(asset); } PhysXSystem::PhysXSystem(PhysXSettingsRegistryManager* registryManager, const physx::PxCookingParams& cookingParams) : m_registryManager(*registryManager) - , m_materialLibraryAssetHelper(this) + , m_materialLibraryAssetHelper( + [this](const AZ::Data::Asset& materialLibrary) + { + UpdateMaterialLibrary(materialLibrary); + }) , m_sceneInterface(this) { // Start PhysX allocator @@ -127,7 +127,7 @@ namespace PhysX m_materialLibraryAssetHelper.Disconnect(); // Clear the asset reference in deactivate. The asset system is shut down before destructors are called // for system components, causing any hanging asset references to become crashes on shutdown in release builds. - m_systemConfig.m_defaultMaterialLibrary.Reset(); + m_systemConfig.m_materialLibraryAsset.Reset(); m_accumulatedTime = 0.0f; m_state = State::Shutdown; @@ -369,8 +369,18 @@ namespace PhysX void PhysXSystem::OnCatalogLoaded([[maybe_unused]]const char* catalogFile) { - //now that assets can be resolved, lets load the default material library. - LoadDefaultMaterialLibrary(); + // now that assets can be resolved, lets load the default material library. + + if (!m_systemConfig.m_materialLibraryAsset.GetId().IsValid()) + { + m_onMaterialLibraryLoadErrorEvent.Signal(AzPhysics::SystemEvents::MaterialLibraryLoadErrorType::InvalidId); + } + + bool success = LoadMaterialLibrary(); + if (!success) + { + m_onMaterialLibraryLoadErrorEvent.Signal(AzPhysics::SystemEvents::MaterialLibraryLoadErrorType::ErrorLoading); + } } void PhysXSystem::UpdateConfiguration(const AzPhysics::SystemConfiguration* newConfig, [[maybe_unused]] bool forceReinitialization /*= false*/) @@ -378,7 +388,7 @@ namespace PhysX if (const auto* physXConfig = azdynamic_cast(newConfig); m_systemConfig != (*physXConfig)) { - const bool newMaterialLibrary = m_systemConfig.m_defaultMaterialLibrary != physXConfig->m_defaultMaterialLibrary; + const bool newMaterialLibrary = m_systemConfig.m_materialLibraryAsset != physXConfig->m_materialLibraryAsset; m_systemConfig = (*physXConfig); m_configChangeEvent.Signal(physXConfig); @@ -386,9 +396,11 @@ namespace PhysX if (newMaterialLibrary) { - LoadDefaultMaterialLibrary(); - m_onDefaultMaterialLibraryChangedEvent.Signal(m_systemConfig.m_defaultMaterialLibrary.GetId()); + LoadMaterialLibrary(); + m_onMaterialLibraryChangedEvent.Signal(m_systemConfig.m_materialLibraryAsset.GetId()); } + // This function is not called from reloading the material library asset, + // which means we don't need to check if the materials inside the library have been modified. } } @@ -445,23 +457,6 @@ namespace PhysX return m_systemConfig; } - void PhysXSystem::UpdateDefaultMaterialLibrary(const AZ::Data::Asset& materialLibrary) - { - if (m_systemConfig.m_defaultMaterialLibrary == materialLibrary) - { - return; - } - m_systemConfig.m_defaultMaterialLibrary = materialLibrary; - - LoadDefaultMaterialLibrary(); - m_onDefaultMaterialLibraryChangedEvent.Signal(materialLibrary.GetId()); - } - - const AZ::Data::Asset& PhysXSystem::GetDefaultMaterialLibrary() const - { - return m_systemConfig.m_defaultMaterialLibrary; - } - void PhysXSystem::UpdateDefaultSceneConfiguration(const AzPhysics::SceneConfiguration& sceneConfiguration) { if (m_defaultSceneConfiguration != sceneConfiguration) @@ -482,9 +477,30 @@ namespace PhysX return m_registryManager; } - bool PhysXSystem::LoadDefaultMaterialLibrary() + void PhysXSystem::UpdateMaterialLibrary(const AZ::Data::Asset& materialLibrary) + { + if (m_systemConfig.m_materialLibraryAsset == materialLibrary) + { + // Same library asset, check if its data has changed. + if (m_systemConfig.m_materialLibraryAsset->GetMaterialsData() != materialLibrary->GetMaterialsData()) + { + m_systemConfig.m_materialLibraryAsset = materialLibrary; + m_onMaterialLibraryChangedEvent.Signal(materialLibrary.GetId()); + } + } + else + { + // New material library asset + m_systemConfig.m_materialLibraryAsset = materialLibrary; + + LoadMaterialLibrary(); + m_onMaterialLibraryChangedEvent.Signal(materialLibrary.GetId()); + } + } + + bool PhysXSystem::LoadMaterialLibrary() { - AZ::Data::Asset& materialLibrary = m_systemConfig.m_defaultMaterialLibrary; + AZ::Data::Asset& materialLibrary = m_systemConfig.m_materialLibraryAsset; const AZ::Data::AssetId& materialLibraryId = materialLibrary.GetId(); if (!materialLibraryId.IsValid()) { @@ -503,7 +519,7 @@ namespace PhysX AZ_Warning("PhysX", (materialLibrary.GetData() != nullptr), "LoadDefaultMaterialLibrary: Default Material Library asset data is invalid."); - return materialLibrary.GetData() != nullptr; + return materialLibrary.GetData() != nullptr && !materialLibrary.IsError(); } //TEMP -- until these are fully moved over here diff --git a/Gems/PhysX/Code/Source/System/PhysXSystem.h b/Gems/PhysX/Code/Source/System/PhysXSystem.h index 04d23ad47d..533685bbd1 100644 --- a/Gems/PhysX/Code/Source/System/PhysXSystem.h +++ b/Gems/PhysX/Code/Source/System/PhysXSystem.h @@ -64,8 +64,6 @@ namespace PhysX AZStd::pair FindAttachedBodyHandleFromEntityId(AZ::EntityId entityId) override; const AzPhysics::SystemConfiguration* GetConfiguration() const override; void UpdateConfiguration(const AzPhysics::SystemConfiguration* newConfig, bool forceReinitialization = false) override; - void UpdateDefaultMaterialLibrary(const AZ::Data::Asset& materialLibrary) override; - const AZ::Data::Asset& GetDefaultMaterialLibrary() const override; void UpdateDefaultSceneConfiguration(const AzPhysics::SceneConfiguration& sceneConfiguration) override; const AzPhysics::SceneConfiguration& GetDefaultSceneConfiguration() const override; @@ -75,6 +73,8 @@ namespace PhysX //! Accessor to get the Settings Registry Manager. const PhysXSettingsRegistryManager& GetSettingsRegistryManager() const; + void UpdateMaterialLibrary(const AZ::Data::Asset& materialLibrary); + //TEMP -- until these are fully moved over here physx::PxPhysics* GetPxPhysics() { return m_physXSdk.m_physics; } physx::PxCooking* GetPxCooking() { return m_physXSdk.m_cooking; } @@ -92,7 +92,7 @@ namespace PhysX //! @param cookingParams The cooking params to use when setting up PhysX cooking interface. void InitializePhysXSdk(const physx::PxCookingParams& cookingParams); void ShutdownPhysXSdk(); - bool LoadDefaultMaterialLibrary(); + bool LoadMaterialLibrary(); // AzFramework::AssetCatalogEventBus::Handler ... void OnCatalogLoaded(const char* catalogFile) override; @@ -133,7 +133,9 @@ namespace PhysX : private AZ::Data::AssetBus::Handler { public: - MaterialLibraryAssetHelper(PhysXSystem* physXSystem); + using OnMaterialLibraryReloadedCallback = AZStd::function& materialLibrary)>; + + MaterialLibraryAssetHelper(OnMaterialLibraryReloadedCallback callback); void Connect(const AZ::Data::AssetId& materialLibraryId); void Disconnect(); @@ -142,7 +144,7 @@ namespace PhysX // AZ::Data::AssetBus::Handler void OnAssetReloaded(AZ::Data::Asset asset) override; - PhysXSystem* m_physXSystem; + OnMaterialLibraryReloadedCallback m_onMaterialLibraryReloadedCallback; }; MaterialLibraryAssetHelper m_materialLibraryAssetHelper; }; diff --git a/Gems/PhysX/Code/Source/SystemComponent.cpp b/Gems/PhysX/Code/Source/SystemComponent.cpp index 9ef4dc5099..b4c17f672d 100644 --- a/Gems/PhysX/Code/Source/SystemComponent.cpp +++ b/Gems/PhysX/Code/Source/SystemComponent.cpp @@ -347,27 +347,6 @@ namespace PhysX return AZStd::make_shared(materialConfiguration); } - AZStd::vector> SystemComponent::CreateMaterialsFromLibrary(const Physics::MaterialSelection& materialSelection) - { - AZStd::vector pxMaterials; - m_materialManager.GetPxMaterials(materialSelection, pxMaterials); - - AZStd::vector> genericMaterials; - genericMaterials.reserve(pxMaterials.size()); - - for (physx::PxMaterial* pxMaterial : pxMaterials) - { - genericMaterials.push_back(static_cast(pxMaterial->userData)->shared_from_this()); - } - - return genericMaterials; - } - - AZStd::shared_ptr SystemComponent::GetDefaultMaterial() - { - return m_materialManager.GetDefaultMaterial(); - } - AZStd::vector SystemComponent::GetSupportedJointTypes() { return JointUtils::GetSupportedJointTypes(); @@ -484,58 +463,6 @@ namespace PhysX return m_physXSystem->GetPxCooking(); } - bool SystemComponent::UpdateMaterialSelection(const Physics::ShapeConfiguration& shapeConfiguration, - Physics::ColliderConfiguration& colliderConfiguration) - { - Physics::MaterialSelection& materialSelection = colliderConfiguration.m_materialSelection; - - // If the material library is still not set, we can't update the material selection - if (!materialSelection.IsMaterialLibraryValid()) - { - AZ_Warning("PhysX", false, - "UpdateMaterialSelection: Material Selection tried to use an invalid/non-existing Physics material library: \"%s\". " - "Please make sure the file exists or re-assign another library", materialSelection.GetMaterialLibraryAssetHint().c_str()); - return false; - } - - // If there's no material library data loaded, try to load it - if (materialSelection.GetMaterialLibraryAssetData() == nullptr) - { - AZ::Data::AssetId materialLibraryAssetId = materialSelection.GetMaterialLibraryAssetId(); - materialSelection.SetMaterialLibrary(materialLibraryAssetId); - } - - // If there's still not material library data, we can't update the material selection - if (materialSelection.GetMaterialLibraryAssetData() == nullptr) - { - AZ::Data::AssetId materialLibraryAssetId = materialSelection.GetMaterialLibraryAssetId(); - - auto materialLibraryAsset = - AZ::Data::AssetManager::Instance().GetAsset(materialLibraryAssetId, AZ::Data::AssetLoadBehavior::Default); - - materialLibraryAsset.BlockUntilLoadComplete(); - - // Log the asset path to help find out the incorrect library reference - AZStd::string assetPath = materialLibraryAsset.GetHint(); - AZ_Warning("PhysX", false, - "UpdateMaterialSelection: Unable to load the material library for a material selection." - " Please check if the asset %s exists in the asset cache.", assetPath.c_str()); - - return false; - } - - if (shapeConfiguration.GetShapeType() == Physics::ShapeType::PhysicsAsset) - { - const Physics::PhysicsAssetShapeConfiguration& assetConfiguration = - static_cast(shapeConfiguration); - - // Use the materials data from the asset to update the collider data - return UpdateMaterialSelectionFromPhysicsAsset(assetConfiguration, colliderConfiguration); - } - - return true; - } - void SystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { if (m_physXSystem) @@ -614,65 +541,4 @@ namespace PhysX m_windProvider = AZStd::make_unique(); } - - bool SystemComponent::UpdateMaterialSelectionFromPhysicsAsset( - const Physics::PhysicsAssetShapeConfiguration& assetConfiguration, - Physics::ColliderConfiguration& colliderConfiguration) - { - Physics::MaterialSelection& materialSelection = colliderConfiguration.m_materialSelection; - - if (!assetConfiguration.m_asset.GetId().IsValid()) - { - // Set the default selection if there's no physics asset. - materialSelection.SetMaterialSlots(Physics::MaterialSelection::SlotsArray()); - return false; - } - - if (!assetConfiguration.m_asset.IsReady()) - { - // The asset is valid but is still loading, - // Do not set the empty slots in this case to avoid the entity being in invalid state - return false; - } - - Pipeline::MeshAsset* meshAsset = assetConfiguration.m_asset.GetAs(); - if (!meshAsset) - { - materialSelection.SetMaterialSlots(Physics::MaterialSelection::SlotsArray()); - AZ_Warning("PhysX", false, "UpdateMaterialSelectionFromPhysicsAsset: MeshAsset is invalid"); - return false; - } - - // Set the slots from the mesh asset - materialSelection.SetMaterialSlots(meshAsset->m_assetData.m_surfaceNames); - - if (!assetConfiguration.m_useMaterialsFromAsset) - { - return false; - } - - const Physics::MaterialLibraryAsset* materialLibrary = materialSelection.GetMaterialLibraryAssetData(); - const AZStd::vector& meshMaterialNames = meshAsset->m_assetData.m_materialNames; - - // Update material IDs in the selection for each slot - int slotIndex = 0; - for (const AZStd::string& meshMaterialName : meshMaterialNames) - { - Physics::MaterialFromAssetConfiguration materialData; - bool found = materialLibrary->GetDataForMaterialName(meshMaterialName, materialData); - - AZ_Warning("PhysX", found, - "UpdateMaterialSelectionFromPhysicsAsset: No material found for surfaceType (%s) in the collider material library", - meshMaterialName.c_str()); - - if (found) - { - materialSelection.SetMaterialId(materialData.m_id, slotIndex); - } - - slotIndex++; - } - - return true; - } } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/SystemComponent.h b/Gems/PhysX/Code/Source/SystemComponent.h index 8871adfeeb..4609fde0ce 100644 --- a/Gems/PhysX/Code/Source/SystemComponent.h +++ b/Gems/PhysX/Code/Source/SystemComponent.h @@ -114,8 +114,6 @@ namespace PhysX // Physics::SystemRequestBus::Handler AZStd::shared_ptr CreateShape(const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& configuration) override; AZStd::shared_ptr CreateMaterial(const Physics::MaterialConfiguration& materialConfiguration) override; - AZStd::shared_ptr GetDefaultMaterial() override; - AZStd::vector> CreateMaterialsFromLibrary(const Physics::MaterialSelection& materialSelection) override; AZStd::vector GetSupportedJointTypes() override; AZStd::shared_ptr CreateJointLimitConfiguration(AZ::TypeId jointType) override; @@ -147,8 +145,6 @@ namespace PhysX static bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); - bool UpdateMaterialSelection(const Physics::ShapeConfiguration& shapeConfiguration, - Physics::ColliderConfiguration& colliderConfiguration) override; private: // AZ::TickBus::Handler ... void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; @@ -157,9 +153,6 @@ namespace PhysX void EnableAutoManagedPhysicsTick(bool shouldTick); void ActivatePhysXSystem(); - bool UpdateMaterialSelectionFromPhysicsAsset( - const Physics::PhysicsAssetShapeConfiguration& assetConfiguration, - Physics::ColliderConfiguration& colliderConfiguration); bool m_enabled; ///< If false, this component will not activate itself in the Activate() function. diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index 55be7c92f7..270a352fc0 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -607,48 +607,6 @@ namespace PhysX return true; } - void GetMaterialList( - AZStd::vector& pxMaterials, const AZStd::vector& terrainSurfaceIdIndexMapping, - const Physics::TerrainMaterialSurfaceIdMap& terrainMaterialsToSurfaceIds) - { - pxMaterials.reserve(terrainSurfaceIdIndexMapping.size()); - - AZStd::shared_ptr defaultMaterial; - MaterialManagerRequestsBus::BroadcastResult(defaultMaterial, &MaterialManagerRequestsBus::Events::GetDefaultMaterial); - - if (terrainSurfaceIdIndexMapping.empty()) - { - pxMaterials.push_back(defaultMaterial->GetPxMaterial()); - return; - } - - AZStd::vector materials; - - for (auto& surfaceId : terrainSurfaceIdIndexMapping) - { - const auto& userAssignedMaterials = terrainMaterialsToSurfaceIds; - const auto& matSelectionIterator = userAssignedMaterials.find(surfaceId); - if (matSelectionIterator != userAssignedMaterials.end()) - { - MaterialManagerRequestsBus::Broadcast(&MaterialManagerRequests::GetPxMaterials, matSelectionIterator->second, materials); - - if (!materials.empty()) - { - pxMaterials.push_back(materials.front()); - } - else - { - AZ_Error("PhysX", false, "Creating materials: array with materials can't be empty"); - pxMaterials.push_back(defaultMaterial->GetPxMaterial()); - } - } - else - { - pxMaterials.push_back(defaultMaterial->GetPxMaterial()); - } - } - } - AZStd::string ReplaceAll(AZStd::string str, const AZStd::string& fromString, const AZStd::string& toString) { size_t positionBegin = 0; while ((positionBegin = str.find(fromString, positionBegin)) != AZStd::string::npos) @@ -920,9 +878,9 @@ namespace PhysX AZ::Vector3 GetTransformScale(AZ::EntityId entityId) { - AZ::Vector3 worldScale = AZ::Vector3::CreateOne(); - AZ::TransformBus::EventResult(worldScale, entityId, &AZ::TransformBus::Events::GetWorldScale); - return worldScale; + float worldUniformScale = 1.0f; + AZ::TransformBus::EventResult(worldUniformScale, entityId, &AZ::TransformBus::Events::GetWorldUniformScale); + return AZ::Vector3(worldUniformScale); } AZ::Vector3 GetUniformScale(AZ::EntityId entityId) diff --git a/Gems/PhysX/Code/Source/Utils.h b/Gems/PhysX/Code/Source/Utils.h index 2885e86a09..73ce813b77 100644 --- a/Gems/PhysX/Code/Source/Utils.h +++ b/Gems/PhysX/Code/Source/Utils.h @@ -115,9 +115,6 @@ namespace PhysX bool MeshDataToPxGeometry(physx::PxBase* meshData, physx::PxGeometryHolder &pxGeometry, const AZ::Vector3& scale); - void GetMaterialList( - AZStd::vector& pxMaterials, const AZStd::vector& materialIndexMapping, - const Physics::TerrainMaterialSurfaceIdMap& terrainMaterialsToSurfaceIds); //! Returns all connected busIds of the specified type. template AZStd::vector FindConnectedBusIds() diff --git a/Gems/PhysX/Code/Tests/PhysXMaterialLibraryTest.cpp b/Gems/PhysX/Code/Tests/PhysXMaterialLibraryTest.cpp deleted file mode 100644 index 2e09ec8dca..0000000000 --- a/Gems/PhysX/Code/Tests/PhysXMaterialLibraryTest.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace PhysX -{ - class MaterialLibraryTest_MockCatalog - : public AZ::Data::AssetCatalog - , public AZ::Data::AssetCatalogRequestBus::Handler - { - - private: - AZ::Uuid m_randomUuid = AZ::Uuid::CreateRandom(); - AZStd::vector m_mockAssetIds; - - public: - AZ_CLASS_ALLOCATOR(MaterialLibraryTest_MockCatalog, AZ::SystemAllocator, 0); - - MaterialLibraryTest_MockCatalog() - { - AZ::Data::AssetCatalogRequestBus::Handler::BusConnect(); - } - - ~MaterialLibraryTest_MockCatalog() - { - AZ::Data::AssetCatalogRequestBus::Handler::BusDisconnect(); - } - - AZ::Data::AssetId GenerateMockAssetId() - { - AZ::Data::AssetId assetId = AZ::Data::AssetId(AZ::Uuid::CreateRandom(), 0); - m_mockAssetIds.push_back(assetId); - return assetId; - } - - ////////////////////////////////////////////////////////////////////////// - // AssetCatalogRequestBus - AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override - { - AZ::Data::AssetInfo result; - result.m_assetType = AZ::AzTypeInfo::Uuid(); - auto foundId = AZStd::find(m_mockAssetIds.begin(), m_mockAssetIds.end(), id); - if (foundId != m_mockAssetIds.end()) - { - result.m_assetId = *foundId; - } - - return result; - } - ////////////////////////////////////////////////////////////////////////// - - AZ::Data::AssetStreamInfo GetStreamInfoForLoad(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override - { - EXPECT_TRUE(type == AZ::AzTypeInfo::Uuid()); - AZ::Data::AssetStreamInfo info; - info.m_dataOffset = 0; - info.m_streamFlags = AZ::IO::OpenMode::ModeRead; - - for (int i = 0; i < m_mockAssetIds.size(); ++i) - { - if (m_mockAssetIds[i] == id) - { - info.m_streamName = AZStd::string::format("MaterialLibraryAssetName%d", i); - } - } - - if (!info.m_streamName.empty()) - { - // this ensures tha parallel running unit tests do not overlap their files that they use. - AZStd::string fullName = AZStd::string::format("%s-%s", m_randomUuid.ToString().c_str(), info.m_streamName.c_str()); - info.m_streamName = fullName; - info.m_dataLen = static_cast(AZ::IO::SystemFile::Length(info.m_streamName.c_str())); - } - else - { - info.m_dataLen = 0; - } - - return info; - } - - AZ::Data::AssetStreamInfo GetStreamInfoForSave(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override - { - AZ::Data::AssetStreamInfo info; - info = GetStreamInfoForLoad(id, type); - info.m_streamFlags = AZ::IO::OpenMode::ModeWrite; - return info; - } - - bool SaveAsset(AZ::Data::Asset& asset) - { - volatile bool isDone = false; - volatile bool succeeded = false; - AZ::Data::AssetBusCallbacks callbacks; - callbacks.SetCallbacks(nullptr, nullptr, nullptr, - [&isDone, &succeeded](const AZ::Data::Asset& /*asset*/, bool isSuccessful, AZ::Data::AssetBusCallbacks& /*callbacks*/) - { - isDone = true; - succeeded = isSuccessful; - }, nullptr, nullptr, nullptr); - - callbacks.BusConnect(asset.GetId()); - asset.Save(); - - while (!isDone) - { - AZ::Data::AssetManager::Instance().DispatchEvents(); - } - return succeeded; - } - }; - - class DISABLED_PhysXMaterialLibraryTest - : public ::testing::Test - { - protected: - void SetUp() override - { - m_catalog = AZStd::make_unique(); - AZ::Data::AssetManager::Instance().RegisterCatalog(m_catalog.get(), AZ::AzTypeInfo::Uuid()); - } - - void TearDown() override - { - AZ::Data::AssetManager::Instance().UnregisterCatalog(m_catalog.get()); - } - - AZStd::unique_ptr m_catalog; - }; - - TEST_F(DISABLED_PhysXMaterialLibraryTest, DISABLED_DefaultMaterialLibrary_CorrectMaterialLibraryIsInferred) - { - AZ::Data::Asset materialLibrary = AZ::Interface::Get()->GetDefaultMaterialLibrary(); - - AZ::Data::AssetId dummyAssetId = AZ::Data::AssetId(AZ::Uuid::CreateName("DummyLibrary.physmaterial")); - AZ::Data::Asset dummyMaterialLibAsset = AZ::Data::AssetManager::Instance().GetAsset(dummyAssetId, AZ::Data::AssetLoadBehavior::Default); - materialLibrary = dummyMaterialLibAsset; - AZ::Interface::Get()->UpdateDefaultMaterialLibrary(materialLibrary); - - // We must have now a default material library setup - ASSERT_TRUE(materialLibrary.GetId().IsValid()); - - AZ::Data::AssetId otherDummyAssetId = AZ::Data::AssetId(AZ::Uuid::CreateName("OtherDummyLibrary.physmaterial")); - AZ::Data::Asset otherDummyMaterialLibAsset = AZ::Data::AssetManager::Instance().GetAsset(otherDummyAssetId, AZ::Data::AssetLoadBehavior::Default); - - // Set selection's material library to a different one than default material library - Physics::MaterialSelection selectionTest; - selectionTest.SetMaterialLibrary(otherDummyAssetId); - - ASSERT_TRUE(selectionTest.GetMaterialLibraryAssetId().IsValid()); - ASSERT_EQ(selectionTest.GetMaterialLibraryAssetId(), selectionTest.GetMaterialLibraryAssetId()); - ASSERT_NE(selectionTest.GetMaterialLibraryAssetId(), materialLibrary.GetId()); - - // By reseting the selection, now it should infer to the default material library set in the global configuration - selectionTest.ResetToDefaultMaterialLibrary(); - - ASSERT_TRUE(selectionTest.GetMaterialLibraryAssetId().IsValid()); - ASSERT_EQ(selectionTest.GetMaterialLibraryAssetId(), materialLibrary.GetId()); - - // Release material library so we exit gracefully - materialLibrary = {}; - AZ::Interface::Get()->UpdateDefaultMaterialLibrary(materialLibrary); - } -} diff --git a/Gems/PhysX/Code/physx_tests_files.cmake b/Gems/PhysX/Code/physx_tests_files.cmake index 406aed64a7..79999c5f74 100644 --- a/Gems/PhysX/Code/physx_tests_files.cmake +++ b/Gems/PhysX/Code/physx_tests_files.cmake @@ -23,7 +23,6 @@ set(FILES Tests/PhysXGenericTest.cpp Tests/PhysXSpecificTest.cpp Tests/PhysXForceRegionTest.cpp - Tests/PhysXMaterialLibraryTest.cpp Tests/PhysXCollisionFilteringTest.cpp Tests/PhysXJointsTest.cpp Tests/PhysXSceneTests.cpp diff --git a/Gems/PhysXDebug/Code/CMakeLists.txt b/Gems/PhysXDebug/Code/CMakeLists.txt index f198f6f26e..b0a05ef146 100644 --- a/Gems/PhysXDebug/Code/CMakeLists.txt +++ b/Gems/PhysXDebug/Code/CMakeLists.txt @@ -44,6 +44,9 @@ ly_add_target( Gem::PhysX Gem::ImGui ) +# use the PhysXDebug module in Clients and Servers: +ly_create_alias(NAME PhysXDebug.Clients NAMESPACE Gem TARGETS Gem::PhysXDebug) +ly_create_alias(NAME PhysXDebug.Servers NAMESPACE Gem TARGETS Gem::PhysXDebug) if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( @@ -66,11 +69,15 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Legacy::CryCommon Legacy::Editor.Headers AZ::AzToolsFramework - Gem::PhysX + Gem::PhysX.Editor Gem::ImGui.imguilib - Gem::ImGui + Gem::ImGui.Editor RUNTIME_DEPENDENCIES Gem::PhysX.Editor Gem::ImGui.Editor ) + # use the PhysXDebug.Editor module in dev tools: + ly_create_alias(NAME PhysXDebug.Builders NAMESPACE Gem TARGETS Gem::PhysXDebug.Editor) + ly_create_alias(NAME PhysXDebug.Tools NAMESPACE Gem TARGETS Gem::PhysXDebug.Editor) + endif() diff --git a/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_ddna.tif.imagesettings b/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_ddna.tif.imagesettings index c2fe2400cb..9da169c456 100644 --- a/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_ddna.tif.imagesettings +++ b/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_ddna.tif.imagesettings @@ -17,7 +17,7 @@ - + @@ -81,7 +81,7 @@ - + diff --git a/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_spec.tif.imagesettings b/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_spec.tif.imagesettings index c3632028c2..43410a50df 100644 --- a/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_spec.tif.imagesettings +++ b/Gems/PhysXSamples/Assets/Characters/Cowboy/Actor/Textures/Cowboy_01_spec.tif.imagesettings @@ -17,7 +17,7 @@ - + @@ -81,7 +81,7 @@ - + diff --git a/AutomatedTesting/Gem/Code/Platform/iOS/runtime_dependencies.cmake b/Gems/PhysXSamples/CMakeLists.txt similarity index 100% rename from AutomatedTesting/Gem/Code/Platform/iOS/runtime_dependencies.cmake rename to Gems/PhysXSamples/CMakeLists.txt diff --git a/AutomatedTesting/Gem/Code/Platform/iOS/tool_dependencies.cmake b/Gems/PhysicsEntities/CMakeLists.txt similarity index 100% rename from AutomatedTesting/Gem/Code/Platform/iOS/tool_dependencies.cmake rename to Gems/PhysicsEntities/CMakeLists.txt diff --git a/Gems/Prefab/PrefabBuilder/CMakeLists.txt b/Gems/Prefab/PrefabBuilder/CMakeLists.txt index 22b89287ca..dbd3c2281b 100644 --- a/Gems/Prefab/PrefabBuilder/CMakeLists.txt +++ b/Gems/Prefab/PrefabBuilder/CMakeLists.txt @@ -38,14 +38,16 @@ ly_add_target( Gem::PrefabBuilder.Static ) -ly_add_target_dependencies( - TARGETS - AssetBuilder - AssetProcessor - AssetProcessorBatch - DEPENDENT_TARGETS - Gem::PrefabBuilder -) +# the prefab builder only needs to be active in builders +# use the PrefabBuilder module in Clients and Servers: +ly_create_alias(NAME PrefabBuilder.Builders NAMESPACE Gem TARGETS Gem::PrefabBuilder) + +# we automatically add this gem, if it is present, to all our known set of builder applications: +ly_enable_gems(GEMS PrefabBuilder VARIANTS Builders TARGETS AssetProcessor AssetProcessorBatch AssetBuilder) + +# if you have a custom builder application in your project, then use ly_enable_gems() to +# add it to that application for your project, like this to make YOUR_TARGET_NAME load it automatically +# ly_enable_gems(PROJECT (YOUR_PROJECT_NAME) GEMS PrefabBuilder VARIANTS Builders TARGETS (YOUR_TARGET_NAME) ) if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_add_target( diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp index 2bb0d8aaf3..ace089b9bd 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace UnitTest @@ -172,6 +173,12 @@ namespace UnitTest void PrefabBuilderTests::SetUp() { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + AZ::ComponentApplication::Descriptor desc; m_app.Start(desc); m_app.CreateReflectionManager(); diff --git a/Gems/Presence/Code/CMakeLists.txt b/Gems/Presence/Code/CMakeLists.txt index 07780fc8eb..b9db324996 100644 --- a/Gems/Presence/Code/CMakeLists.txt +++ b/Gems/Presence/Code/CMakeLists.txt @@ -44,3 +44,6 @@ ly_add_target( AZ::AzFramework Gem::Presence.Headers ) + +# we activate the presence gem (if enabled) only on client applications such as the launcher: +ly_create_alias(NAME Presence.Clients NAMESPACE Gem TARGETS Gem::Presence) diff --git a/Gems/PrimitiveAssets/CMakeLists.txt b/Gems/PrimitiveAssets/CMakeLists.txt new file mode 100644 index 0000000000..4d5680a30d --- /dev/null +++ b/Gems/PrimitiveAssets/CMakeLists.txt @@ -0,0 +1,10 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# diff --git a/Gems/PythonAssetBuilder/Code/CMakeLists.txt b/Gems/PythonAssetBuilder/Code/CMakeLists.txt index 60af675bc6..73fa90ab67 100644 --- a/Gems/PythonAssetBuilder/Code/CMakeLists.txt +++ b/Gems/PythonAssetBuilder/Code/CMakeLists.txt @@ -13,24 +13,11 @@ if(NOT PAL_TRAIT_BUILD_HOST_TOOLS) return() endif() -set(static_files pythonassetbuilder_common_files.cmake) -set(editor_files pythonassetbuilder_editor_files.cmake) -set(shared_files pythonassetbuilder_shared_files.cmake) -set(static_dependencies - 3rdParty::Python - Gem::EditorPythonBindings.Static - AZ::AssetBuilderSDK -) -set(editor_dependencies - Gem::EditorPythonBindings.Static - AZ::AssetBuilderSDK -) - ly_add_target( NAME PythonAssetBuilder.Static STATIC NAMESPACE Gem FILES_CMAKE - ${static_files} + pythonassetbuilder_common_files.cmake PLATFORM_INCLUDE_FILES Source/Platform/Common/${PAL_TRAIT_COMPILER_ID}/pythonassetbuilder_static_${PAL_TRAIT_COMPILER_ID_LOWERCASE}.cmake INCLUDE_DIRECTORIES @@ -43,7 +30,9 @@ ly_add_target( PRIVATE AZ::AzCore PUBLIC - ${static_dependencies} + 3rdParty::Python + Gem::EditorPythonBindings.Static + AZ::AssetBuilderSDK AZ::AzToolsFramework ) @@ -52,8 +41,8 @@ ly_add_target( NAMESPACE Gem FILES_CMAKE - ${editor_files} - ${shared_files} + pythonassetbuilder_editor_files.cmake + pythonassetbuilder_shared_files.cmake PLATFORM_INCLUDE_FILES Source/Platform/Common/${PAL_TRAIT_COMPILER_ID}/pythonassetbuilder_static_${PAL_TRAIT_COMPILER_ID_LOWERCASE}.cmake INCLUDE_DIRECTORIES @@ -64,11 +53,17 @@ ly_add_target( Include BUILD_DEPENDENCIES PRIVATE - ${editor_dependencies} + Gem::EditorPythonBindings.Static + AZ::AssetBuilderSDK RUNTIME_DEPENDENCIES Gem::EditorPythonBindings.Editor ) +# the above target is used in both builders like AssetProcessor and Tools like the Editor +# but is not used in clients or servers +ly_create_alias(NAME PythonAssetBuilder.Tools NAMESPACE Gem TARGETS Gem::PythonAssetBuilder.Editor) +ly_create_alias(NAME PythonAssetBuilder.Builders NAMESPACE Gem TARGETS Gem::PythonAssetBuilder.Editor) + ################################################################################ # Tests ################################################################################ diff --git a/Gems/QtForPython/Code/CMakeLists.txt b/Gems/QtForPython/Code/CMakeLists.txt index 74c660043f..c11d93634e 100644 --- a/Gems/QtForPython/Code/CMakeLists.txt +++ b/Gems/QtForPython/Code/CMakeLists.txt @@ -55,3 +55,9 @@ ly_add_target( RUNTIME_DEPENDENCIES Gem::EditorPythonBindings.Editor ) + +# the above target is used in both builders like AssetProcessor and Tools like the Editor +# but is not used in clients or servers +ly_create_alias(NAME QtForPython.Tools NAMESPACE Gem TARGETS Gem::QtForPython.Editor) +ly_create_alias(NAME QtForPython.Builders NAMESPACE Gem TARGETS Gem::QtForPython.Editor) + diff --git a/Gems/RADTelemetry/Code/CMakeLists.txt b/Gems/RADTelemetry/Code/CMakeLists.txt index 78a5561b7c..8b3cc70570 100644 --- a/Gems/RADTelemetry/Code/CMakeLists.txt +++ b/Gems/RADTelemetry/Code/CMakeLists.txt @@ -42,3 +42,8 @@ ly_add_target( PRIVATE Gem::RADTelemetry.Static ) + +# the RADTelemetry module above can be used in all kinds of applications, but we don't enable it in asset builders +ly_create_alias(NAME RADTelemetry.Clients NAMESPACE Gem TARGETS Gem::RADTelemetry) +ly_create_alias(NAME RADTelemetry.Tools NAMESPACE Gem TARGETS Gem::RADTelemetry) +ly_create_alias(NAME RADTelemetry.Servers NAMESPACE Gem TARGETS Gem::RADTelemetry) diff --git a/Gems/SaveData/Code/CMakeLists.txt b/Gems/SaveData/Code/CMakeLists.txt index 46c6e91f58..d9dc62ef03 100644 --- a/Gems/SaveData/Code/CMakeLists.txt +++ b/Gems/SaveData/Code/CMakeLists.txt @@ -46,6 +46,9 @@ ly_add_target( Gem::SaveData.Static ) +# the SaveData module above is only used in Clients by default. +ly_create_alias(NAME SaveData.Clients NAMESPACE Gem TARGETS Gem::SaveData) + ################################################################################ # Tests ################################################################################ diff --git a/Gems/SceneLoggingExample/Code/CMakeLists.txt b/Gems/SceneLoggingExample/Code/CMakeLists.txt index 8cd012c4c2..6370420928 100644 --- a/Gems/SceneLoggingExample/Code/CMakeLists.txt +++ b/Gems/SceneLoggingExample/Code/CMakeLists.txt @@ -40,3 +40,7 @@ ly_add_target( PRIVATE Gem::SceneLoggingExample.Static ) + +# the SceneLoggingExample module above is only used in Builders and Tools by default. +ly_create_alias(NAME SceneLoggingExample.Builders NAMESPACE Gem TARGETS Gem::SceneLoggingExample) +ly_create_alias(NAME SceneLoggingExample.Tools NAMESPACE Gem TARGETS Gem::SceneLoggingExample) diff --git a/Gems/SceneProcessing/Code/CMakeLists.txt b/Gems/SceneProcessing/Code/CMakeLists.txt index 67124a74d5..4b0dfbab27 100644 --- a/Gems/SceneProcessing/Code/CMakeLists.txt +++ b/Gems/SceneProcessing/Code/CMakeLists.txt @@ -66,6 +66,10 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) AZ::SceneCore AZ::SceneData ) + # the SceneProcessing.Editor module above is only used in Builders and Tools. + ly_create_alias(NAME SceneProcessing.Builders NAMESPACE Gem TARGETS Gem::SceneProcessing.Editor) + ly_create_alias(NAME SceneProcessing.Tools NAMESPACE Gem TARGETS Gem::SceneProcessing.Editor) + endif() ################################################################################ @@ -84,7 +88,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) BUILD_DEPENDENCIES PRIVATE AZ::AzTest - Gem::SceneProcessing + RUNTIME_DEPENDENCIES + Gem::SceneProcessing ) ly_add_googletest( NAME Gem::SceneProcessing.Tests diff --git a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.cpp b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.cpp index e71a5207d0..25faca3667 100644 --- a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.cpp +++ b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.cpp @@ -72,6 +72,11 @@ namespace SceneBuilder m_sceneBuilder.BusDisconnect(); } + void BuilderPluginComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.emplace_back(AZ_CRC_CE("AssetImportRequestHandler")); + } + void BuilderPluginComponent::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); @@ -81,5 +86,4 @@ namespace SceneBuilder ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector({ AssetBuilderSDK::ComponentTags::AssetBuilder })); } } - } // namespace SceneBuilder diff --git a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.h b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.h index c1fc6ebb36..aed5e1b026 100644 --- a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.h +++ b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.h @@ -32,6 +32,8 @@ namespace SceneBuilder void Activate() override; void Deactivate() override; + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + private: SceneBuilderWorker m_sceneBuilder; }; diff --git a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp index 5f96353996..2a89911fda 100644 --- a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp +++ b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -139,6 +140,12 @@ class SceneBuilderPhasesFixture public: void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(AZ::ComponentApplication::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is diff --git a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp index 66fd717508..2287077c89 100644 --- a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp +++ b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,12 @@ class SceneBuilderTests protected: void SetUp() override { + AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); + auto projectPathKey = + AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; + registry->Set(projectPathKey, "AutomatedTesting"); + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + m_app.Start(AZ::ComponentApplication::Descriptor()); AZ::Debug::TraceMessageBus::Handler::BusConnect(); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is diff --git a/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilderWorker.h b/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilderWorker.h index 1d1ea1d4aa..fc9613c3a7 100644 --- a/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilderWorker.h +++ b/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilderWorker.h @@ -59,6 +59,7 @@ namespace ScriptCanvasBuilder QuantumLeap, DependencyArguments, DependencyRequirementsData, + AddAssetDependencySearch, // add new entries above Current, }; diff --git a/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilderWorkerUtility.cpp b/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilderWorkerUtility.cpp index ab59ddd840..ba22789cd1 100644 --- a/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilderWorkerUtility.cpp +++ b/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilderWorkerUtility.cpp @@ -681,6 +681,19 @@ namespace ScriptCanvasBuilder } AssetBuilderSDK::JobProduct jobProduct; + + // Scan our runtime input for any asset references + // Store them as product dependencies + AssetBuilderSDK::OutputObject(&runtimeData.m_input, + azrtti_typeid(), + input.runtimeScriptCanvasOutputPath, + azrtti_typeid(), + AZ_CRC("RuntimeData", 0x163310ae), jobProduct); + + // Output Object marks dependencies as handled. + // We still have more to evaluate + jobProduct.m_dependenciesHandled = false; + jobProduct.m_dependencies.push_back({ runtimeData.m_script.GetId(), {} }); for (const auto& assetDependency : runtimeData.m_requiredAssets) @@ -713,9 +726,6 @@ namespace ScriptCanvasBuilder } jobProduct.m_dependenciesHandled = true; - jobProduct.m_productFileName = input.runtimeScriptCanvasOutputPath; - jobProduct.m_productAssetType = azrtti_typeid(); - jobProduct.m_productSubID = AZ_CRC("RuntimeData", 0x163310ae); input.response->m_outputProducts.push_back(AZStd::move(jobProduct)); return AZ::Success(); } diff --git a/Gems/ScriptCanvas/Code/CMakeLists.txt b/Gems/ScriptCanvas/Code/CMakeLists.txt index 32efa74520..f9165192f0 100644 --- a/Gems/ScriptCanvas/Code/CMakeLists.txt +++ b/Gems/ScriptCanvas/Code/CMakeLists.txt @@ -49,6 +49,14 @@ ly_add_target( Gem::ScriptEvents.Static ) +# the script canvas debugger is an optional gem module +# To Enable it: ly_enable_gems( ... TARGETS xxxyyzzz GEMS ScriptCanvasDebugger ...) +# in any particular target. +ly_create_alias(NAME ScriptCanvasDebugger.Builders NAMESPACE Gem TARGETS Gem::ScriptCanvasDebugger) +ly_create_alias(NAME ScriptCanvasDebugger.Tools NAMESPACE Gem TARGETS Gem::ScriptCanvasDebugger) +ly_create_alias(NAME ScriptCanvasDebugger.Clients NAMESPACE Gem TARGETS Gem::ScriptCanvasDebugger) +ly_create_alias(NAME ScriptCanvasDebugger.Servers NAMESPACE Gem TARGETS Gem::ScriptCanvasDebugger) + ly_add_target( NAME ScriptCanvas.Static STATIC NAMESPACE Gem @@ -81,6 +89,8 @@ ly_add_target( *.ScriptCanvasGrammar.xml,ScriptCanvasGrammar_Source.jinja,$path/$fileprefix.generated.cpp *.ScriptCanvasNodeable.xml,ScriptCanvasNodeable_Header.jinja,$path/$fileprefix.generated.h *.ScriptCanvasNodeable.xml,ScriptCanvasNodeable_Source.jinja,$path/$fileprefix.generated.cpp + RUNTIME_DEPENDENCIES + Gem::ScriptCanvasDebugger ) ly_add_target( @@ -109,6 +119,10 @@ ly_add_target( Gem::ExpressionEvaluation ) +# the "ScriptCanvas" target is active in Clients and Servers +ly_create_alias(NAME ScriptCanvas.Clients NAMESPACE Gem TARGETS Gem::ScriptCanvas) +ly_create_alias(NAME ScriptCanvas.Servers NAMESPACE Gem TARGETS Gem::ScriptCanvas) + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME ScriptCanvasEditor STATIC @@ -170,6 +184,8 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::ExpressionEvaluation.Static PRIVATE Legacy::EditorCore + RUNTIME_DEPENDENCIES + Gem::ScriptCanvas ) ly_add_target( @@ -204,6 +220,12 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::ScriptEvents.Editor Gem::ExpressionEvaluation ) + + # the "ScriptCanvas.Editor" target is active in all dev tools: + ly_create_alias(NAME ScriptCanvas.Builders NAMESPACE Gem TARGETS Gem::ScriptCanvas.Editor) + ly_create_alias(NAME ScriptCanvas.Tools NAMESPACE Gem TARGETS Gem::ScriptCanvas.Editor) + + endif() ################################################################################ @@ -228,7 +250,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) PRIVATE AZ::AzTest AZ::AzFramework - Gem::ScriptCanvas + RUNTIME_DEPENDENCIES + Gem::ScriptCanvas ) ly_add_googletest( NAME Gem::ScriptCanvas.Tests diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp index a0db02c3a8..30e55cee49 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp @@ -2527,15 +2527,15 @@ namespace ScriptCanvas { Data::TransformType copy(source); AZ::Vector3 pos = copy.GetTranslation(); - AZ::Vector3 scale = copy.ExtractScale(); + float scale = copy.ExtractUniformScale(); AZ::Vector3 rotation = AZ::ConvertTransformToEulerDegrees(copy); return AZStd::string::format ( "(Position: X: %f, Y: %f, Z: %f," " Rotation: X: %f, Y: %f, Z: %f," - " Scale: X: %f, Y: %f, Z: %f)" + " Scale: %f)" , static_cast(pos.GetX()), static_cast(pos.GetY()), static_cast(pos.GetZ()) , static_cast(rotation.GetX()), static_cast(rotation.GetY()), static_cast(rotation.GetZ()) - , static_cast(scale.GetX()), static_cast(scale.GetY()), static_cast(scale.GetZ())); + , scale); } AZStd::string Datum::ToStringVector2(const AZ::Vector2& source) const diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Libraries.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Libraries.cpp index 641bdca8f7..8c2671ac6d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Libraries.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Libraries.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ namespace ScriptCanvas Entity::InitNodeRegistry(*g_nodeRegistry); Comparison::InitNodeRegistry(*g_nodeRegistry); Time::InitNodeRegistry(*g_nodeRegistry); + Spawning::InitNodeRegistry(*g_nodeRegistry); String::InitNodeRegistry(*g_nodeRegistry); Operators::InitNodeRegistry(*g_nodeRegistry); @@ -61,6 +63,7 @@ namespace ScriptCanvas Entity::Reflect(reflectContext); Comparison::Reflect(reflectContext); Time::Reflect(reflectContext); + Spawning::Reflect(reflectContext); String::Reflect(reflectContext); Operators::Reflect(reflectContext); @@ -90,6 +93,9 @@ namespace ScriptCanvas componentDescriptors = Time::GetComponentDescriptors(); libraryDescriptors.insert(libraryDescriptors.end(), componentDescriptors.begin(), componentDescriptors.end()); + componentDescriptors = Spawning::GetComponentDescriptors(); + libraryDescriptors.insert(libraryDescriptors.end(), componentDescriptors.begin(), componentDescriptors.end()); + componentDescriptors = String::GetComponentDescriptors(); libraryDescriptors.insert(libraryDescriptors.end(), componentDescriptors.begin(), componentDescriptors.end()); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Libraries.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Libraries.h index 3388a029ac..67094f4db8 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Libraries.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Libraries.h @@ -143,6 +143,17 @@ namespace ScriptCanvas }; + struct Spawning : public LibraryDefinition + { + AZ_RTTI(Spawning, "{41E910AE-FBD2-41AD-9173-5105141F0466}", LibraryDefinition); + + static void Reflect(AZ::ReflectContext*); + static void InitNodeRegistry(NodeRegistry& nodeRegistry); + static AZStd::vector GetComponentDescriptors(); + + ~Spawning() override = default; + }; + struct String : public LibraryDefinition { AZ_RTTI(String, "{5B700838-21A2-4579-9303-F4A4822AFEF4}", LibraryDefinition); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Math/TransformNodes.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Math/TransformNodes.h index 6a0f082272..9e66c6c6fc 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Math/TransformNodes.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Math/TransformNodes.h @@ -26,12 +26,12 @@ namespace ScriptCanvas using namespace MathNodeUtilities; static const char* k_categoryName = "Math/Transform"; - AZ_INLINE std::tuple ExtractScale(TransformType source) + AZ_INLINE std::tuple ExtractUniformScale(TransformType source) { - auto scale(source.ExtractScale()); + auto scale(source.ExtractUniformScale()); return std::make_tuple( scale, source ); } - SCRIPT_CANVAS_GENERIC_FUNCTION_MULTI_RESULTS_NODE(ExtractScale, k_categoryName, "{8DFE5247-0950-4CD1-87E6-0CAAD42F1637}", "returns a vector which is the length of the scale components, and a transform with the scale extracted ", "Source", "Scale", "Extracted"); + SCRIPT_CANVAS_GENERIC_FUNCTION_MULTI_RESULTS_NODE(ExtractUniformScale, k_categoryName, "{8DFE5247-0950-4CD1-87E6-0CAAD42F1637}", "returns the uniform scale as a float, and a transform with the scale extracted ", "Source", "Uniform Scale", "Extracted"); AZ_INLINE TransformType FromMatrix3x3(Matrix3x3Type source) { @@ -57,11 +57,11 @@ namespace ScriptCanvas } SCRIPT_CANVAS_GENERIC_FUNCTION_NODE(FromRotationAndTranslation, k_categoryName, "{99A4D55D-6EFB-4E24-8113-F5B46DE3A194}", "returns a transform from the rotation and the translation", "Rotation", "Translation"); - AZ_INLINE TransformType FromScale(Vector3Type scale) + AZ_INLINE TransformType FromScale(NumberType scale) { - return TransformType::CreateScale(scale); + return TransformType::CreateUniformScale(scale); } - SCRIPT_CANVAS_GENERIC_FUNCTION_NODE(FromScale, k_categoryName, "{4B6454BC-015C-41BB-9C78-34ADBCF70187}", "returns a scale matrix and the translation set to zero", "Scale"); + SCRIPT_CANVAS_GENERIC_FUNCTION_NODE(FromScale, k_categoryName, "{4B6454BC-015C-41BB-9C78-34ADBCF70187}", "returns a transform which applies the specified uniform Scale, but no rotation or translation", "Scale"); AZ_INLINE TransformType FromTranslation(Vector3Type translation) { @@ -145,12 +145,12 @@ namespace ScriptCanvas } SCRIPT_CANVAS_GENERIC_FUNCTION_NODE(Multiply3x3ByVector3, k_categoryName, "{4F2ABFC6-2E93-4A9D-8639-C7967DB318DB}", "returns Source's 3x3 upper matrix post multiplied by Multiplier", "Source", "Multiplier"); - AZ_INLINE TransformType MultiplyByScale(TransformType source, Vector3Type scale) + AZ_INLINE TransformType MultiplyByUniformScale(TransformType source, NumberType scale) { - source.MultiplyByScale(scale); + source.MultiplyByUniformScale(scale); return source; } - SCRIPT_CANVAS_GENERIC_FUNCTION_NODE(MultiplyByScale, k_categoryName, "{90472D62-65A8-40C1-AB08-FA66D793F689}", "returns Source multiplied by the scale matrix produced by Scale", "Source", "Scale"); + SCRIPT_CANVAS_GENERIC_FUNCTION_NODE(MultiplyByUniformScale, k_categoryName, "{90472D62-65A8-40C1-AB08-FA66D793F689}", "returns Source multiplied uniformly by Scale", "Source", "Scale"); AZ_INLINE TransformType MultiplyByTransform(const TransformType& a, const TransformType& b) { @@ -194,16 +194,16 @@ namespace ScriptCanvas } SCRIPT_CANVAS_GENERIC_FUNCTION_NODE(RotationZDegrees, k_categoryName, "{F848306A-C07C-4586-B52F-BEEE489045D2}", "returns a transform representing a rotation Degrees around the Z-Axis", "Degrees"); - AZ_INLINE Vector3Type ToScale(const TransformType& source) + AZ_INLINE NumberType ToScale(const TransformType& source) { - return source.GetScale(); + return source.GetUniformScale(); } - SCRIPT_CANVAS_GENERIC_FUNCTION_NODE(ToScale, k_categoryName, "{063C58AD-F567-464D-A432-F298FE3953A6}", "returns the scale part of the Source, the length of the scale components", "Source"); + SCRIPT_CANVAS_GENERIC_FUNCTION_NODE(ToScale, k_categoryName, "{063C58AD-F567-464D-A432-F298FE3953A6}", "returns the uniform scale of the Source", "Source"); using Registrar = RegistrarGeneric < #if ENABLE_EXTENDED_MATH_SUPPORT - ExtractScaleNode , + ExtractUniformScaleNode , #endif FromMatrix3x3AndTranslationNode , FromMatrix3x3Node @@ -230,7 +230,7 @@ namespace ScriptCanvas , Multiply3x3ByVector3Node #endif - , MultiplyByScaleNode + , MultiplyByUniformScaleNode , MultiplyByTransformNode , MultiplyByVector3Node , MultiplyByVector4Node diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.ScriptCanvasNodeable.xml b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.ScriptCanvasNodeable.xml new file mode 100644 index 0000000000..e9b1ce9f4e --- /dev/null +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.ScriptCanvasNodeable.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + /> + + + + + + + + + + + + diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp new file mode 100644 index 0000000000..ad53236108 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp @@ -0,0 +1,140 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +#include +#include + +namespace ScriptCanvas::Nodeables::Spawning +{ + SpawnNodeable::SpawnNodeable(const SpawnNodeable& rhs) + : m_spawnableAsset(rhs.m_spawnableAsset) + {} + + SpawnNodeable& SpawnNodeable::operator=(SpawnNodeable& rhs) + { + m_spawnableAsset = rhs.m_spawnableAsset; + return *this; + } + + void SpawnNodeable::OnInitializeExecutionState() + { + if (!AZ::TickBus::Handler::BusIsConnected()) + { + AZ::TickBus::Handler::BusConnect(); + } + + m_spawnTicket = AzFramework::EntitySpawnTicket(m_spawnableAsset); + } + + void SpawnNodeable::OnDeactivate() + { + AZ::TickBus::Handler::BusDisconnect(); + + m_spawnTicket = AzFramework::EntitySpawnTicket(); + } + + void SpawnNodeable::OnTick([[maybe_unused]] float delta, [[maybe_unused]] AZ::ScriptTimePoint timePoint) + { + AZStd::vector swappedSpawnedEntityList; + AZStd::vector swappedSpawnBatchSizes; + { + AZStd::lock_guard lock(m_idBatchMutex); + + swappedSpawnedEntityList.swap(m_spawnedEntityList); + swappedSpawnBatchSizes.swap(m_spawnBatchSizes); + } + + AZ::EntityId* batchBegin = swappedSpawnedEntityList.data(); + for (size_t batchSize : swappedSpawnBatchSizes) + { + if (batchSize == 0) + { + continue; + } + + AZStd::vector spawnedEntitiesBatch( + batchBegin, batchBegin + batchSize); + + CallOnSpawn(AZStd::move(spawnedEntitiesBatch)); + + batchBegin += batchSize; + } + } + + void SpawnNodeable::OnSpawnAssetChanged() + { + if (m_spawnableAsset.GetId().IsValid()) + { + AZStd::string rootSpawnableFile; + AzFramework::StringFunc::Path::GetFileName(m_spawnableAsset.GetHint().c_str(), rootSpawnableFile); + + rootSpawnableFile += AzFramework::Spawnable::DotFileExtension; + + AZ::u32 rootSubId = AzFramework::SpawnableAssetHandler::BuildSubId(AZStd::move(rootSpawnableFile)); + + if (m_spawnableAsset.GetId().m_subId != rootSubId) + { + AZ::Data::AssetId rootAssetId = m_spawnableAsset.GetId(); + rootAssetId.m_subId = rootSubId; + + m_spawnableAsset = AZ::Data::AssetManager::Instance(). + FindOrCreateAsset(rootAssetId, AZ::Data::AssetLoadBehavior::Default); + } + else + { + m_spawnableAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::Default); + } + } + } + + void SpawnNodeable::RequestSpawn(Data::Vector3Type translation, Data::Vector3Type rotation, Data::NumberType scale) + { + if (m_spawnableAsset.GetAutoLoadBehavior() == AZ::Data::AssetLoadBehavior::NoLoad) + { + return; + } + + auto preSpawnCB = [this, translation, rotation, scale]([[maybe_unused]] AzFramework::EntitySpawnTicket::Id ticketId, + AzFramework::SpawnableEntityContainerView view) + { + AZ::Entity* rootEntity = *view.begin(); + + AzFramework::TransformComponent* entityTransform = + rootEntity->FindComponent(); + + if (entityTransform) + { + AZ::Vector3 rotationCopy = rotation; + AZ::Quaternion rotationQuat = AZ::Quaternion::CreateFromEulerAnglesDegrees(rotationCopy); + + entityTransform->SetWorldTM(AZ::Transform(translation, rotationQuat, scale)); + } + }; + + auto spawnCompleteCB = [this]([[maybe_unused]] AzFramework::EntitySpawnTicket::Id ticketId, + AzFramework::SpawnableConstEntityContainerView view) + { + AZStd::lock_guard lock(m_idBatchMutex); + m_spawnedEntityList.reserve(m_spawnedEntityList.size() + view.size()); + for (const AZ::Entity* entity : view) + { + m_spawnedEntityList.emplace_back(entity->GetId()); + } + m_spawnBatchSizes.push_back(view.size()); + }; + + AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities( + m_spawnTicket, AzFramework::SpawnablePriority_Default, preSpawnCB, spawnCompleteCB); + } +} diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.h new file mode 100644 index 0000000000..0f3a27d2ea --- /dev/null +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.h @@ -0,0 +1,52 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +#include + +#include + +#include +#include +#include + +namespace ScriptCanvas::Nodeables::Spawning +{ + class SpawnNodeable + : public ScriptCanvas::Nodeable, + public AZ::TickBus::Handler + { + SCRIPTCANVAS_NODE(SpawnNodeable); + public: + SpawnNodeable() = default; + SpawnNodeable(const SpawnNodeable& rhs); + SpawnNodeable& operator=(SpawnNodeable& rhs); + + void OnInitializeExecutionState() override; + void OnDeactivate() override; + + //TickBus + void OnTick(float delta, AZ::ScriptTimePoint timePoint) override; + + void OnSpawnAssetChanged(); + + private: + AzFramework::EntitySpawnTicket m_spawnTicket; + + AZStd::vector m_spawnedEntityList; + AZStd::vector m_spawnBatchSizes; + AZStd::recursive_mutex m_idBatchMutex; + }; +} diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/Spawning.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/Spawning.cpp new file mode 100644 index 0000000000..6591950b77 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/Spawning.cpp @@ -0,0 +1,50 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +namespace ScriptCanvas +{ + namespace Library + { + void Spawning::Reflect(AZ::ReflectContext* reflection) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(reflection)) + { + serializeContext->Class() + ->Version(1) + ; + + AZ::EditContext* editContext = serializeContext->GetEditContext(); + if (editContext) + { + editContext->Class("Spawning", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Icon, "Icons/ScriptCanvas/Libraries/Entity.png"); + } + } + } + + void Spawning::InitNodeRegistry(NodeRegistry& nodeRegistry) + { + AddNodeToRegistry(nodeRegistry); + } + + AZStd::vector Spawning::GetComponentDescriptors() + { + return AZStd::vector({ + ScriptCanvas::Nodes::SpawnNodeableNode::CreateDescriptor(), + }); + } + } +} diff --git a/Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/Spawning.h similarity index 76% rename from Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.cpp rename to Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/Spawning.h index 6fdd7bdc45..fa92ee97a9 100644 --- a/Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/Spawning.h @@ -10,4 +10,8 @@ * */ -#include "precompiled.h" +#pragma once + +// This header is only meant to include the nodes and should not contain +// shared code +#include diff --git a/Gems/ScriptCanvas/Code/scriptcanvasgem_common_files.cmake b/Gems/ScriptCanvas/Code/scriptcanvasgem_common_files.cmake index 84391021df..f0be1accc9 100644 --- a/Gems/ScriptCanvas/Code/scriptcanvasgem_common_files.cmake +++ b/Gems/ScriptCanvas/Code/scriptcanvasgem_common_files.cmake @@ -454,6 +454,11 @@ set(FILES Include/ScriptCanvas/Libraries/Time/TimerNodeable.h Include/ScriptCanvas/Libraries/Time/TimerNodeable.cpp Include/ScriptCanvas/Libraries/Time/TimerNodeable.ScriptCanvasNodeable.xml + Include/ScriptCanvas/Libraries/Spawning/Spawning.cpp + Include/ScriptCanvas/Libraries/Spawning/Spawning.h + Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp + Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.h + Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.ScriptCanvasNodeable.xml Include/ScriptCanvas/Libraries/String/Contains.cpp Include/ScriptCanvas/Libraries/String/Contains.h Include/ScriptCanvas/Libraries/String/Contains.ScriptCanvasGrammar.xml diff --git a/Gems/ScriptCanvasDeveloper/Code/CMakeLists.txt b/Gems/ScriptCanvasDeveloper/Code/CMakeLists.txt index d9ce9004d3..8f2f6d3fc0 100644 --- a/Gems/ScriptCanvasDeveloper/Code/CMakeLists.txt +++ b/Gems/ScriptCanvasDeveloper/Code/CMakeLists.txt @@ -54,6 +54,11 @@ ly_add_target( Gem::ScriptCanvas ) +# By default, the above module is the Client/Server module +ly_create_alias(NAME ScriptCanvasDeveloper.Clients NAMESPACE Gem TARGETS Gem::ScriptCanvasDeveloper) +ly_create_alias(NAME ScriptCanvasDeveloper.Servers NAMESPACE Gem TARGETS Gem::ScriptCanvasDeveloper) + + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME ScriptCanvasDeveloper.Editor GEM_MODULE @@ -81,5 +86,10 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::GraphCanvasWidgets RUNTIME_DEPENDENCIES Gem::ScriptCanvas.Editor + Gem::GraphCanvasWidgets ) + # By Default the above module is the dev tools module + ly_create_alias(NAME ScriptCanvasDeveloper.Builders NAMESPACE Gem TARGETS Gem::ScriptCanvasDeveloper.Editor) + ly_create_alias(NAME ScriptCanvasDeveloper.Tools NAMESPACE Gem TARGETS Gem::ScriptCanvasDeveloper.Editor) + endif() diff --git a/Gems/ScriptCanvasDiagnosticLibrary/Code/Tests/ScriptCanvasDiagnosticLibraryTest.cpp b/Gems/ScriptCanvasDiagnosticLibrary/Code/Tests/ScriptCanvasDiagnosticLibraryTest.cpp deleted file mode 100644 index 4052b9dee4..0000000000 --- a/Gems/ScriptCanvasDiagnosticLibrary/Code/Tests/ScriptCanvasDiagnosticLibraryTest.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ -#include "precompiled.h" - -#include - -class ScriptCanvasDiagnosticLibraryTest - : public ::testing::Test -{ -protected: - static void SetUpTestCase() - { - } - - static void TearDownTestCase() - { - } - - void SetUp() override - { - } - - void TearDown() override - { - } - -}; - -TEST_F(ScriptCanvasDiagnosticLibraryTest, Sanity_Pass) -{ - EXPECT_TRUE(true); -} - - -AZ_UNIT_TEST_HOOK(); diff --git a/Gems/ScriptCanvasPhysics/Code/CMakeLists.txt b/Gems/ScriptCanvasPhysics/Code/CMakeLists.txt index 23ee6937c7..107db38f5e 100644 --- a/Gems/ScriptCanvasPhysics/Code/CMakeLists.txt +++ b/Gems/ScriptCanvasPhysics/Code/CMakeLists.txt @@ -18,10 +18,9 @@ ly_add_target( PRIVATE Source BUILD_DEPENDENCIES - PUBLIC - Gem::ScriptCanvas PRIVATE Legacy::CryCommon + Gem::ScriptCanvas ) ly_add_target( @@ -36,8 +35,16 @@ ly_add_target( PRIVATE Legacy::CryCommon Gem::ScriptCanvasPhysics.Static + Gem::ScriptCanvas ) +# By default, the above module is used by all application types, however, the module depends at runtime to ScriptCanvas +# and the dependency needs to be different per application type +ly_create_alias(NAME ScriptCanvasPhysics.Clients NAMESPACE Gem TARGETS Gem::ScriptCanvasPhysics Gem::ScriptCanvas) +ly_create_alias(NAME ScriptCanvasPhysics.Servers NAMESPACE Gem TARGETS Gem::ScriptCanvasPhysics Gem::ScriptCanvas) +ly_create_alias(NAME ScriptCanvasPhysics.Tools NAMESPACE Gem TARGETS Gem::ScriptCanvasPhysics Gem::ScriptCanvas.Editor) +ly_create_alias(NAME ScriptCanvasPhysics.Builders NAMESPACE Gem TARGETS Gem::ScriptCanvasPhysics Gem::ScriptCanvas.Editor) + ################################################################################ # Tests ################################################################################ @@ -57,6 +64,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest Legacy::CryCommon Gem::ScriptCanvasPhysics.Static + Gem::ScriptCanvas ) ly_add_googletest( NAME Gem::ScriptCanvasPhysics.Tests diff --git a/Gems/ScriptCanvasPhysics/Code/Tests/ScriptCanvasPhysicsTest.cpp b/Gems/ScriptCanvasPhysics/Code/Tests/ScriptCanvasPhysicsTest.cpp index 4b04434e13..07ccdc5011 100644 --- a/Gems/ScriptCanvasPhysics/Code/Tests/ScriptCanvasPhysicsTest.cpp +++ b/Gems/ScriptCanvasPhysics/Code/Tests/ScriptCanvasPhysicsTest.cpp @@ -207,8 +207,8 @@ namespace ScriptCanvasPhysicsTests { public: MOCK_CONST_METHOD0(GetSurfaceType, AZ::Crc32()); - MOCK_METHOD1(SetSurfaceType, void(AZ::Crc32)); MOCK_CONST_METHOD0(GetSurfaceTypeName, const AZStd::string&()); + MOCK_METHOD1(SetSurfaceTypeName, void(const AZStd::string&)); MOCK_CONST_METHOD0(GetDynamicFriction, float()); MOCK_METHOD1(SetDynamicFriction, void(float)); MOCK_CONST_METHOD0(GetStaticFriction, float()); @@ -223,6 +223,8 @@ namespace ScriptCanvasPhysicsTests MOCK_METHOD0(GetNativePointer, void*()); MOCK_CONST_METHOD0(GetDensity, float()); MOCK_METHOD1(SetDensity, void(float)); + MOCK_CONST_METHOD0(GetDebugColor, AZ::Color()); + MOCK_METHOD1(SetDebugColor, void(const AZ::Color&)); }; class ScriptCanvasPhysicsTestEnvironment diff --git a/Gems/ScriptCanvasTesting/Code/CMakeLists.txt b/Gems/ScriptCanvasTesting/Code/CMakeLists.txt index 639ef114fc..76a549d6b9 100644 --- a/Gems/ScriptCanvasTesting/Code/CMakeLists.txt +++ b/Gems/ScriptCanvasTesting/Code/CMakeLists.txt @@ -34,7 +34,7 @@ ly_add_target( Gem::ScriptCanvas Gem::ScriptCanvasEditor Gem::GraphCanvasWidgets - Gem::ScriptEvents + Gem::ScriptEvents.Editor PRIVATE AZ::AzCore AZ::AzFramework @@ -45,6 +45,11 @@ ly_add_target( *.ScriptCanvasGrammar.xml,ScriptCanvasGrammar_Source.jinja,$path/$fileprefix.generated.cpp *.ScriptCanvasNodeable.xml,ScriptCanvasNodeable_Header.jinja,$path/$fileprefix.generated.h *.ScriptCanvasNodeable.xml,ScriptCanvasNodeable_Source.jinja,$path/$fileprefix.generated.cpp + RUNTIME_DEPENDENCIES + Gem::ScriptCanvas.Editor + Gem::ScriptCanvasEditor + Gem::GraphCanvasWidgets + Gem::ScriptEvents ) ly_add_target( @@ -73,6 +78,9 @@ ly_add_target( Gem::ScriptCanvas.Editor ) +# By default, the above module is used only in tools: +ly_create_alias(NAME ScriptCanvasTesting.Tools NAMESPACE Gem TARGETS Gem::ScriptCanvasTesting.Editor) + ################################################################################ # Tests ################################################################################ @@ -101,6 +109,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzFramework AZ::AzToolsFramework Gem::ScriptCanvasTesting.Editor.Static + Gem::ScriptCanvas.Editor RUNTIME_DEPENDENCIES Gem::GraphCanvas.Editor Gem::ScriptCanvas.Editor diff --git a/Gems/ScriptEvents/Code/CMakeLists.txt b/Gems/ScriptEvents/Code/CMakeLists.txt index 12f8cfd7b6..6f75e35d71 100644 --- a/Gems/ScriptEvents/Code/CMakeLists.txt +++ b/Gems/ScriptEvents/Code/CMakeLists.txt @@ -40,6 +40,11 @@ ly_add_target( Gem::ScriptEvents.Static ) +# the above module is for use in clients and servers +ly_create_alias(NAME ScriptEvents.Clients NAMESPACE Gem TARGETS Gem::ScriptEvents) +ly_create_alias(NAME ScriptEvents.Servers NAMESPACE Gem TARGETS Gem::ScriptEvents) + + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME ScriptEvents.Editor GEM_MODULE @@ -61,6 +66,10 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AssetBuilderSDK Gem::ScriptEvents.Static ) + + # the above module is for use in dev tools. + ly_create_alias(NAME ScriptEvents.Tools NAMESPACE Gem TARGETS Gem::ScriptEvents.Editor) + ly_create_alias(NAME ScriptEvents.Builders NAMESPACE Gem TARGETS Gem::ScriptEvents.Editor) endif() ################################################################################ diff --git a/Gems/ScriptedEntityTweener/Code/CMakeLists.txt b/Gems/ScriptedEntityTweener/Code/CMakeLists.txt index c5062c84b7..2488057f8c 100644 --- a/Gems/ScriptedEntityTweener/Code/CMakeLists.txt +++ b/Gems/ScriptedEntityTweener/Code/CMakeLists.txt @@ -40,3 +40,9 @@ ly_add_target( AZ::AzCore Legacy::CryCommon ) + +# the above module is for use in all application types: +ly_create_alias(NAME ScriptedEntityTweener.Tools NAMESPACE Gem TARGETS Gem::ScriptedEntityTweener) +ly_create_alias(NAME ScriptedEntityTweener.Clients NAMESPACE Gem TARGETS Gem::ScriptedEntityTweener) +ly_create_alias(NAME ScriptedEntityTweener.Builders NAMESPACE Gem TARGETS Gem::ScriptedEntityTweener) +ly_create_alias(NAME ScriptedEntityTweener.Servers NAMESPACE Gem TARGETS Gem::ScriptedEntityTweener) \ No newline at end of file diff --git a/Gems/SliceFavorites/Code/CMakeLists.txt b/Gems/SliceFavorites/Code/CMakeLists.txt index 4ad89ad6c3..35349c777c 100644 --- a/Gems/SliceFavorites/Code/CMakeLists.txt +++ b/Gems/SliceFavorites/Code/CMakeLists.txt @@ -51,3 +51,6 @@ ly_add_target( 3rdParty::Qt::Core Gem::SliceFavorites.Editor.Static ) + +# the above module is for use in Tools only (no need to load it in builders) +ly_create_alias(NAME SliceFavorites.Tools NAMESPACE Gem TARGETS Gem::SliceFavorites.Editor) \ No newline at end of file diff --git a/Gems/StartingPointCamera/Code/CMakeLists.txt b/Gems/StartingPointCamera/Code/CMakeLists.txt index d6dd1a7038..7bc57476a5 100644 --- a/Gems/StartingPointCamera/Code/CMakeLists.txt +++ b/Gems/StartingPointCamera/Code/CMakeLists.txt @@ -48,3 +48,9 @@ ly_add_target( RUNTIME_DEPENDENCIES Gem::CameraFramework ) + +# the above module is for use in all kinds of applications +ly_create_alias(NAME StartingPointCamera.Servers NAMESPACE Gem TARGETS Gem::StartingPointCamera) +ly_create_alias(NAME StartingPointCamera.Clients NAMESPACE Gem TARGETS Gem::StartingPointCamera) +ly_create_alias(NAME StartingPointCamera.Builders NAMESPACE Gem TARGETS Gem::StartingPointCamera) +ly_create_alias(NAME StartingPointCamera.Tools NAMESPACE Gem TARGETS Gem::StartingPointCamera) diff --git a/Gems/StartingPointInput/Code/CMakeLists.txt b/Gems/StartingPointInput/Code/CMakeLists.txt index 1372a79a78..c6e7fdcb52 100644 --- a/Gems/StartingPointInput/Code/CMakeLists.txt +++ b/Gems/StartingPointInput/Code/CMakeLists.txt @@ -56,6 +56,10 @@ ly_add_source_properties( VALUES ${LY_PAL_TOOLS_DEFINES} ) +# the above module is for use in clients and servers +ly_create_alias(NAME StartingPointInput.Servers NAMESPACE Gem TARGETS Gem::StartingPointInput) +ly_create_alias(NAME StartingPointInput.Clients NAMESPACE Gem TARGETS Gem::StartingPointInput) + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME StartingPointInput.Editor GEM_MODULE @@ -74,6 +78,11 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AzFramework Gem::StartingPointInput.Static ) + + # by default, activate the ab ove module in builders and tools: + ly_create_alias(NAME StartingPointInput.Builders NAMESPACE Gem TARGETS Gem::StartingPointInput.Editor) + ly_create_alias(NAME StartingPointInput.Tools NAMESPACE Gem TARGETS Gem::StartingPointInput.Editor) + endif() ################################################################################ diff --git a/Gems/StartingPointMovement/Code/CMakeLists.txt b/Gems/StartingPointMovement/Code/CMakeLists.txt index d6434ccf78..417dfe01ee 100644 --- a/Gems/StartingPointMovement/Code/CMakeLists.txt +++ b/Gems/StartingPointMovement/Code/CMakeLists.txt @@ -40,3 +40,9 @@ ly_add_target( AZ::AzCore AZ::AzFramework ) + +# the above module is for use in all application types (there is no tool specialization) +ly_create_alias(NAME StartingPointMovement.Servers NAMESPACE Gem TARGETS Gem::StartingPointMovement) +ly_create_alias(NAME StartingPointMovement.Clients NAMESPACE Gem TARGETS Gem::StartingPointMovement) +ly_create_alias(NAME StartingPointMovement.Builders NAMESPACE Gem TARGETS Gem::StartingPointMovement) +ly_create_alias(NAME StartingPointMovement.Tools NAMESPACE Gem TARGETS Gem::StartingPointMovement) \ No newline at end of file diff --git a/Gems/SurfaceData/Code/CMakeLists.txt b/Gems/SurfaceData/Code/CMakeLists.txt index de1aa51938..642849675c 100644 --- a/Gems/SurfaceData/Code/CMakeLists.txt +++ b/Gems/SurfaceData/Code/CMakeLists.txt @@ -22,10 +22,10 @@ ly_add_target( BUILD_DEPENDENCIES PRIVATE Legacy::CryCommon + Gem::LmbrCentral PUBLIC Gem::Atom_RPI.Public Gem::Atom_Feature_Common.Static - Gem::LmbrCentral ) ly_add_target( @@ -42,10 +42,15 @@ ly_add_target( PRIVATE Legacy::CryCommon Gem::SurfaceData.Static + Gem::LmbrCentral RUNTIME_DEPENDENCIES Gem::LmbrCentral ) +# the above module is for use in all client/server types +ly_create_alias(NAME SurfaceData.Servers NAMESPACE Gem TARGETS Gem::SurfaceData) +ly_create_alias(NAME SurfaceData.Clients NAMESPACE Gem TARGETS Gem::SurfaceData) + if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( @@ -67,9 +72,13 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Legacy::CryCommon AZ::AzToolsFramework Gem::SurfaceData.Static + Gem::LmbrCentral.Editor RUNTIME_DEPENDENCIES Gem::LmbrCentral.Editor ) + # the above module is for use in dev tool situations + ly_create_alias(NAME SurfaceData.Builders NAMESPACE Gem TARGETS Gem::SurfaceData.Editor) + ly_create_alias(NAME SurfaceData.Tools NAMESPACE Gem TARGETS Gem::SurfaceData.Editor) endif() @@ -93,6 +102,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest Legacy::CryCommon Gem::SurfaceData.Static + Gem::LmbrCentral ) ly_add_googletest( NAME Gem::SurfaceData.Tests diff --git a/Gems/TestAssetBuilder/Code/CMakeLists.txt b/Gems/TestAssetBuilder/Code/CMakeLists.txt index dbd2907033..ebd34140aa 100644 --- a/Gems/TestAssetBuilder/Code/CMakeLists.txt +++ b/Gems/TestAssetBuilder/Code/CMakeLists.txt @@ -40,3 +40,6 @@ ly_add_target( PRIVATE Gem::TestAssetBuilder.Static ) + +# the above module is for use in builders only +ly_create_alias(NAME TestAssetBuilder.Builders NAMESPACE Gem TARGETS Gem::TestAssetBuilder.Editor) diff --git a/Gems/TextureAtlas/Code/CMakeLists.txt b/Gems/TextureAtlas/Code/CMakeLists.txt index 5e29a7ea65..d67601235d 100644 --- a/Gems/TextureAtlas/Code/CMakeLists.txt +++ b/Gems/TextureAtlas/Code/CMakeLists.txt @@ -62,5 +62,10 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::TextureAtlas.Static Gem::ImageProcessingAtom.Headers ) + ly_create_alias(NAME TextureAtlas.Builders NAMESPACE Gem TARGETS Gem::TextureAtlas.Editor) + ly_create_alias(NAME TextureAtlas.Tools NAMESPACE Gem TARGETS Gem::TextureAtlas.Editor) endif() +ly_create_alias(NAME TextureAtlas.Servers NAMESPACE Gem TARGETS Gem::TextureAtlas) +ly_create_alias(NAME TextureAtlas.Clients NAMESPACE Gem TARGETS Gem::TextureAtlas) + diff --git a/Gems/TickBusOrderViewer/Code/CMakeLists.txt b/Gems/TickBusOrderViewer/Code/CMakeLists.txt index 3f56a0d554..551c62c64c 100644 --- a/Gems/TickBusOrderViewer/Code/CMakeLists.txt +++ b/Gems/TickBusOrderViewer/Code/CMakeLists.txt @@ -38,3 +38,9 @@ ly_add_target( PRIVATE Gem::TickBusOrderViewer.Static ) + + +# the above module is for use in all application types except builders +ly_create_alias(NAME TickBusOrderViewer.Servers NAMESPACE Gem TARGETS Gem::TickBusOrderViewer) +ly_create_alias(NAME TickBusOrderViewer.Clients NAMESPACE Gem TARGETS Gem::TickBusOrderViewer) +ly_create_alias(NAME TickBusOrderViewer.Tools NAMESPACE Gem TARGETS Gem::TickBusOrderViewer) diff --git a/Gems/Twitch/Code/CMakeLists.txt b/Gems/Twitch/Code/CMakeLists.txt index 14d7a41532..b5c1751003 100644 --- a/Gems/Twitch/Code/CMakeLists.txt +++ b/Gems/Twitch/Code/CMakeLists.txt @@ -29,6 +29,8 @@ ly_add_target( AZ::AzCore Gem::HttpRequestor 3rdParty::AWSNativeSDK::Core + RUNTIME_DEPENDENCIES + Gem::HttpRequestor ) ly_add_target( @@ -47,3 +49,9 @@ ly_add_target( RUNTIME_DEPENDENCIES Gem::HttpRequestor ) + +# the above module is for use in all application types except builders +ly_create_alias(NAME Twitch.Servers NAMESPACE Gem TARGETS Gem::Twitch) +ly_create_alias(NAME Twitch.Clients NAMESPACE Gem TARGETS Gem::Twitch) +ly_create_alias(NAME Twitch.Tools NAMESPACE Gem TARGETS Gem::Twitch) + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Normal.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Pressed.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Pressed.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Pressed.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Pressed.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Selected.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Selected.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Selected.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Button_Sliced_Selected.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Normal.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Pressed.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Pressed.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Pressed.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Pressed.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Selected.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Selected.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Selected.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Button_Stretched_Selected.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check_Background.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check_Background.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check_Background.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Check_Background.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Cross.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Cross.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Cross.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Cross.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Off.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Off.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Off.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_Off.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_On.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_On.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/CheckBox_On.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/CheckBox_On.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Disabled.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Disabled.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Hover.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Hover.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Hover.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Hover.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Normal.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Checkbox_Background_Normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Checkered.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Checkered.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Checkered.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Checkered.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Disabled.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Disabled.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Hover.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Hover.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Hover.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Hover.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Normal.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Background_Normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Sliced.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Sliced.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Sliced.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Sliced.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Stretch.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Stretch.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Stretch.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Fill_Stretch.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Manipulator.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Manipulator.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Manipulator.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Manipulator.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Sliced.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Sliced.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Sliced.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Sliced.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Stretch.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Stretch.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Stretch.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Slider_Track_Stretch.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Normal.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Pressed.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Pressed.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Pressed.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Pressed.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Selected.tif.assetinfo b/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Selected.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Selected.tif.assetinfo +++ b/Gems/UiBasics/Assets/Textures/Basic/Text_Input_Sliced_Selected.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Arrow.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Arrow.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Arrow.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Arrow.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowL.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowL.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowL.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowL.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowR.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowR.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowR.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowR.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowU.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowU.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowU.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_ArrowU.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Button.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Button.tif.assetinfo index 95b548a2eb..f808dda121 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Button.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Button.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Menu.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Menu.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Menu.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/Dropdown_Menu.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Disabled.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Hover.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Hover.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Hover.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Hover.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Background_Normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Dot.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Dot.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Dot.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/RadioButton_Dot.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/button_disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/button_disabled.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/button_disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/button_disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/button_normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/button_normal.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/button_normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/button_normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_disabled.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_hover.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_hover.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_hover.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_hover.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_normal.tif.assetinfo index 2eb5be8e93..54e075dd32 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_box_normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -56,7 +56,7 @@ - + @@ -64,7 +64,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_check.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_check.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_check.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/checkbox_check.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_handle.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_handle.tif.assetinfo index 95b548a2eb..f808dda121 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_handle.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_handle.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_horiz_track.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_horiz_track.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_horiz_track.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_horiz_track.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_vert_track.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_vert_track.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_vert_track.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/scrollbar_vert_track.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_disabled.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_fill_normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_disabled.tif.assetinfo index 95b548a2eb..f808dda121 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_handle_normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_disabled.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_normal.tif.assetinfo index 61b2832ff3..47b628d60a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/slider_track_normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_disabled.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_disabled.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_disabled.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_disabled.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_hover.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_hover.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_hover.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_hover.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_normal.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_normal.tif.assetinfo index c6f6ca1e9c..c8704c4e0d 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_normal.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/textinput_normal.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/Assets/UI/Textures/Prefab/tooltip_sliced.tif.assetinfo b/Gems/UiBasics/Assets/UI/Textures/Prefab/tooltip_sliced.tif.assetinfo index dd1d22706e..e508d7465a 100644 --- a/Gems/UiBasics/Assets/UI/Textures/Prefab/tooltip_sliced.tif.assetinfo +++ b/Gems/UiBasics/Assets/UI/Textures/Prefab/tooltip_sliced.tif.assetinfo @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/Gems/UiBasics/CMakeLists.txt b/Gems/UiBasics/CMakeLists.txt new file mode 100644 index 0000000000..4d5680a30d --- /dev/null +++ b/Gems/UiBasics/CMakeLists.txt @@ -0,0 +1,10 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# diff --git a/Gems/Vegetation/Code/CMakeLists.txt b/Gems/Vegetation/Code/CMakeLists.txt index c4a003bb4a..7283f53c97 100644 --- a/Gems/Vegetation/Code/CMakeLists.txt +++ b/Gems/Vegetation/Code/CMakeLists.txt @@ -24,12 +24,14 @@ ly_add_target( PUBLIC Include BUILD_DEPENDENCIES + PRIVATE + Gem::LmbrCentral + Gem::SurfaceData PUBLIC Legacy::CryCommon - Gem::LmbrCentral - Gem::GradientSignal - Gem::SurfaceData.Static Gem::AtomLyIntegration_CommonFeatures.Static + RUNTIME_DEPENDENCIES + Gem::GradientSignal ) ly_add_target( @@ -51,6 +53,10 @@ ly_add_target( Gem::SurfaceData ) +# the above module is for use in clients and server type applications +ly_create_alias(NAME Vegetation.Servers NAMESPACE Gem TARGETS Gem::Vegetation) +ly_create_alias(NAME Vegetation.Clients NAMESPACE Gem TARGETS Gem::Vegetation) + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME Vegetation.Editor GEM_MODULE @@ -75,6 +81,10 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::GradientSignal.Editor Gem::SurfaceData.Editor ) + # the above module is for use in dev tools + ly_create_alias(NAME Vegetation.Builders NAMESPACE Gem TARGETS Gem::Vegetation.Editor) + ly_create_alias(NAME Vegetation.Tools NAMESPACE Gem TARGETS Gem::Vegetation.Editor) + endif() ################################################################################ diff --git a/Gems/Vegetation_Gem_Assets/CMakeLists.txt b/Gems/Vegetation_Gem_Assets/CMakeLists.txt new file mode 100644 index 0000000000..4d5680a30d --- /dev/null +++ b/Gems/Vegetation_Gem_Assets/CMakeLists.txt @@ -0,0 +1,10 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# diff --git a/Gems/VideoPlaybackFramework/Code/CMakeLists.txt b/Gems/VideoPlaybackFramework/Code/CMakeLists.txt index 297f4cfaac..b29fe53216 100644 --- a/Gems/VideoPlaybackFramework/Code/CMakeLists.txt +++ b/Gems/VideoPlaybackFramework/Code/CMakeLists.txt @@ -42,6 +42,11 @@ ly_add_target( Gem::VideoPlaybackFramework.Static ) +# the video playback framework makes sense in everything but servers: +ly_create_alias(NAME VideoPlaybackFramework.Clients NAMESPACE Gem TARGETS Gem::VideoPlaybackFramework) +ly_create_alias(NAME VideoPlaybackFramework.Tools NAMESPACE Gem TARGETS Gem::VideoPlaybackFramework) +ly_create_alias(NAME VideoPlaybackFramework.Builders NAMESPACE Gem TARGETS Gem::VideoPlaybackFramework) + ################################################################################ # Tests ################################################################################ diff --git a/Gems/VirtualGamepad/Code/CMakeLists.txt b/Gems/VirtualGamepad/Code/CMakeLists.txt index 99a33db70b..4311796b57 100644 --- a/Gems/VirtualGamepad/Code/CMakeLists.txt +++ b/Gems/VirtualGamepad/Code/CMakeLists.txt @@ -40,3 +40,8 @@ ly_add_target( PRIVATE Gem::VirtualGamepad.Static ) + +# the virtual gamepad is needed everywhere except servers: +ly_create_alias(NAME VirtualGamepad.Clients NAMESPACE Gem TARGETS Gem::VirtualGamepad) +ly_create_alias(NAME VirtualGamepad.Tools NAMESPACE Gem TARGETS Gem::VirtualGamepad) +ly_create_alias(NAME VirtualGamepad.Builders NAMESPACE Gem TARGETS Gem::VirtualGamepad) diff --git a/Gems/WhiteBox/Code/CMakeLists.txt b/Gems/WhiteBox/Code/CMakeLists.txt index a15a4e150c..5985ff26a3 100644 --- a/Gems/WhiteBox/Code/CMakeLists.txt +++ b/Gems/WhiteBox/Code/CMakeLists.txt @@ -86,6 +86,10 @@ ly_add_target( Gem::WhiteBox.Static ) +# use the above WhiteBox module in runtimes: +ly_create_alias(NAME WhiteBox.Clients NAMESPACE Gem TARGETS Gem::WhiteBox) +ly_create_alias(NAME WhiteBox.Servers NAMESPACE Gem TARGETS Gem::WhiteBox) + if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( NAME WhiteBox.Editor.Static STATIC @@ -129,6 +133,12 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) PRIVATE Gem::WhiteBox.Editor.Static ) + + # use the above WhiteBox.Editor module in dev tools: + ly_create_alias(NAME WhiteBox.Tools NAMESPACE Gem TARGETS Gem::WhiteBox.Editor) + ly_create_alias(NAME WhiteBox.Builders NAMESPACE Gem TARGETS Gem::WhiteBox.Editor) + + endif() ################################################################################ diff --git a/README.md b/README.md index e0442adc16..0c59837a62 100644 --- a/README.md +++ b/README.md @@ -118,11 +118,6 @@ If you have the Git credential manager core or other credential helpers installe ``` python\get_python.bat ``` - -1. While still within the repo folder, register the engine with this command: - ``` - scripts\o3de.bat register --this-engine - ``` 1. Configure the source into a solution using this command line, replacing and <3rdParty cache path> to a path you've created: ``` @@ -146,7 +141,11 @@ If you have the Git credential manager core or other credential helpers installe 1. This will compile after some time and binaries will be available in the build path you've specified -### Setting up new projects +### Setting up new projects +1. While still within the repo folder, register the engine with this command: + ``` + scripts\o3de.bat register --this-engine + ``` 1. Setup new projects using the `o3de create-project` command. In the 0.5 branch, the project directory must be a subdirectory in the repo folder. ``` \scripts\o3de.bat create-project --project-path @@ -160,10 +159,10 @@ If you have the Git credential manager core or other credential helpers installe cmake -B -S -G "Visual Studio 16" -DLY_3RDPARTY_PATH=<3rdParty cache path> // For the 0.5 branch, you must build a new Editor for each project: - cmake --build --target Editor --config profile -- /m + cmake --build --target .GameLauncher Editor --config profile -- /m // For all other branches, just build the project: - cmake --build --target --config profile -- /m + cmake --build --target .GameLauncher --config profile -- /m ``` For a tutorial on project configuration, see [Creating Projects Using the Command Line](https://docs.o3de.org/docs/welcome-guide/get-started/project-config/creating-projects-using-cli) in the documentation. diff --git a/Registry/AssetProcessorPlatformConfig.setreg b/Registry/AssetProcessorPlatformConfig.setreg index 5f397db06b..2147842da7 100644 --- a/Registry/AssetProcessorPlatformConfig.setreg +++ b/Registry/AssetProcessorPlatformConfig.setreg @@ -24,13 +24,13 @@ "Platform pc": { "tags": "tools,renderer,dx12,vulkan,null" }, - "Platform es3": { + "Platform android": { "tags": "android,mobile,renderer,vulkan" }, "Platform ios": { "tags": "mobile,renderer,metal" }, - "Platform osx_gl": { + "Platform mac": { "tags": "tools,renderer,metal,null" }, // this is an example of a headless platform that has no renderer. @@ -43,9 +43,9 @@ // To enable any additional platform, just uncomment the appropriate line below. "Platforms": { //"pc": "enabled", - //"es3": "enabled", + //"android": "enabled", //"ios": "enabled", - //"osx_gl": "enabled", + //"mac": "enabled", //"server": "enabled" }, // ---- The number of worker jobs, 0 means use the number of Logical Cores @@ -95,11 +95,11 @@ // "exclude": "(comma seperated platform tags or identifiers)" // } // For example if you want to include a scan folder only for platforms that have the platform tags tools and renderer - // but omit it for platform osx_gl, you will have a scanfolder rule like + // but omit it for platform mac, you will have a scanfolder rule like // "ScanFolder (unique identifier)": { // "watch": "@ROOT@/foo", // "include": "tools, renderer", - // "exclude": "osx_gl" + // "exclude": "mac" // } "ScanFolder Game": { diff --git a/Registry/bootstrap.setreg b/Registry/bootstrap.setreg index b0c954a127..ccbf744232 100644 --- a/Registry/bootstrap.setreg +++ b/Registry/bootstrap.setreg @@ -8,9 +8,9 @@ "ios_remote_filesystem": 0, "mac_remote_filesystem": 0, "assets": "pc", - "android_assets": "es3", + "android_assets": "android", "ios_assets": "ios", - "mac_assets": "osx_gl", + "mac_assets": "mac", "allowed_list": "", "remote_ip": "127.0.0.1", "remote_port": 45643, diff --git a/Registry/prefab.setreg b/Registry/prefab.setreg new file mode 100644 index 0000000000..903dc0c4f8 --- /dev/null +++ b/Registry/prefab.setreg @@ -0,0 +1,14 @@ +{ + "O3DE": + { + "AzFramework": + { + "Spawnables": + { + // Any requests with a priorty value equal or smaller than this will be considered a high priority request. + // The range for this value is between 0 and 255. + "HighPriorityThreshold" : 64 + } + } + } +} \ No newline at end of file diff --git a/Registry/sceneassetimporter.setreg b/Registry/sceneassetimporter.setreg new file mode 100644 index 0000000000..bd7c4d0705 --- /dev/null +++ b/Registry/sceneassetimporter.setreg @@ -0,0 +1,16 @@ +{ + "O3DE": + { + "SceneAPI": + { + "AssetImporter": + { + "SupportedFileTypeExtensions": + [ + ".fbx", + ".stl" + ] + } + } + } +} \ No newline at end of file diff --git a/Templates/DefaultGem/Template/CMakeLists.txt b/Templates/DefaultGem/Template/CMakeLists.txt index fb63008782..1a24bd488f 100644 --- a/Templates/DefaultGem/Template/CMakeLists.txt +++ b/Templates/DefaultGem/Template/CMakeLists.txt @@ -11,7 +11,7 @@ set(o3de_gem_path ${CMAKE_CURRENT_LIST_DIR}) set(o3de_gem_json ${o3de_gem_path}/gem.json) -o3de_gem_name(${o3de_gem_json} o3de_gem_name) +o3de_read_json_key(o3de_gem_name ${o3de_gem_json} "gem_name") o3de_restricted_path(${o3de_gem_json} o3de_gem_restricted_path) # Currently we are in the DefaultProjectSource folder: ${CMAKE_CURRENT_LIST_DIR} diff --git a/Templates/DefaultGem/Template/Code/CMakeLists.txt b/Templates/DefaultGem/Template/Code/CMakeLists.txt index 3511f8ff83..b0e52dd79f 100644 --- a/Templates/DefaultGem/Template/Code/CMakeLists.txt +++ b/Templates/DefaultGem/Template/Code/CMakeLists.txt @@ -59,6 +59,12 @@ ly_add_target( Gem::${Name}.Static ) +# By default, we will specify that the above target ${Name} would be used by +# Client and Server type targets when this gem is enabled. If you don't want it +# active in Clients or Servers by default, delete one of both of the following lines: +ly_create_alias(NAME ${Name}.Clients NAMESPACE Gem TARGETS Gem::${Name}) +ly_create_alias(NAME ${Name}.Servers NAMESPACE Gem TARGETS Gem::${Name}) + # If we are on a host platform, we want to add the host tools targets like the ${Name}.Editor target which # will also depend on ${Name}.Static if(PAL_TRAIT_BUILD_HOST_TOOLS) @@ -94,6 +100,14 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) PUBLIC Gem::${Name}.Editor.Static ) + + # By default, we will specify that the above target ${Name} would be used by + # Tool and Builder type targets when this gem is enabled. If you don't want it + # active in Tools or Builders by default, delete one of both of the following lines: + ly_create_alias(NAME ${Name}.Tools NAMESPACE Gem TARGETS Gem::${Name}.Editor) + ly_create_alias(NAME ${Name}.Builders NAMESPACE Gem TARGETS Gem::${Name}.Editor) + + endif() ################################################################################ diff --git a/Templates/DefaultGem/template.json b/Templates/DefaultGem/template.json index 22d4eb27e6..b653718095 100644 --- a/Templates/DefaultGem/template.json +++ b/Templates/DefaultGem/template.json @@ -1,6 +1,6 @@ { "template_name": "DefaultGem", - "restricted": "o3de", + "restricted_name": "o3de", "restricted_platform_relative_path": "Templates", "origin": "The primary repo for DefaultGem goes here: i.e. http://www.mydomain.com", "license": "What license DefaultGem uses goes here: i.e. https://opensource.org/licenses/MIT", diff --git a/Templates/DefaultProject/Template/CMakeLists.txt b/Templates/DefaultProject/Template/CMakeLists.txt index ad0a4c869d..b5b8692059 100644 --- a/Templates/DefaultProject/Template/CMakeLists.txt +++ b/Templates/DefaultProject/Template/CMakeLists.txt @@ -23,64 +23,26 @@ function(add_vs_debugger_arguments) endforeach() endfunction() -set(o3de_project_path ${CMAKE_CURRENT_LIST_DIR}) -set(o3de_project_json ${o3de_project_path}/project.json) - if(NOT PROJECT_NAME) cmake_minimum_required(VERSION 3.19) project(${Name} LANGUAGES C CXX VERSION 1.0.0.0 ) - - # set this project as the only project - set(LY_PROJECTS ${CMAKE_CURRENT_LIST_DIR}) - - # o3de manifest - include(o3de_manifest.cmake) - - ################################################################################ - # Set the engine_path and resolve this engines restricted path if it has one - ################################################################################ - o3de_engine_path(${o3de_project_json} o3de_engine_path) - o3de_project_name(${o3de_project_json} o3de_project_name) - o3de_restricted_path(${o3de_project_json} o3de_project_restricted_path) - message(STATUS "O3DE Project Name: ${o3de_project_name}") - message(STATUS "O3DE Project Path: ${o3de_project_path}") - if(o3de_project_restricted_path) - message(STATUS "O3DE Project Restricted Path: ${o3de_project_restricted_path}") - endif() - - # add the engines cmake folder to the CMAKE_MODULE_PATH - list(APPEND CMAKE_MODULE_PATH "${o3de_engine_path}/cmake") - - # add subdirectory on the engine path for this project - add_subdirectory(${o3de_engine_path} o3de) - - # add this --project-path arguments to visual studio debugger + include(EngineFinder.cmake OPTIONAL) + find_package(o3de REQUIRED) + o3de_initialize() add_vs_debugger_arguments() - else() - ###################################################### - # the engine is calling add sub_directory() on us - ###################################################### - o3de_project_name(${o3de_project_json} o3de_project_name) - o3de_restricted_path(${o3de_project_json} o3de_project_restricted_path) - - # Currently we are in the folder: ${CMAKE_CURRENT_LIST_DIR} - # Get the platform specific folder ${pal_dir} for the folder: ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} - # Note: ly_get_list_relative_pal_filename will take care of the details for us, as this may be a restricted platform - # in which case it will see if that platform is present here or in the restricted folder. - # i.e. It could here: TestDP/Platform/ or - # //TestDP - ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${o3de_project_restricted_path} ${o3de_project_path} ${o3de_project_name}) + # Add the project_name to global LY_PROJECTS_TARGET_NAME property + file(READ "${CMAKE_CURRENT_LIST_DIR}/project.json" project_json) - # Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the - # project cmake for this platform. - include(${pal_dir}/${PAL_PLATFORM_NAME_LOWERCASE}_project.cmake) + string(JSON project_target_name ERROR_VARIABLE json_error GET ${project_json} "project_name") + if(json_error) + message(FATAL_ERROR "Unable to read key 'project_name' from 'project.json'") + endif() - # Add the project_name to global LY_PROJECTS_TARGET_NAME property - set_property(GLOBAL APPEND PROPERTY LY_PROJECTS_TARGET_NAME ${o3de_project_name}) + set_property(GLOBAL APPEND PROPERTY LY_PROJECTS_TARGET_NAME ${project_target_name}) add_subdirectory(Code) endif() diff --git a/Templates/DefaultProject/Template/Code/${NameLower}_files.cmake b/Templates/DefaultProject/Template/Code/${NameLower}_files.cmake index 459e33f547..f77348395b 100644 --- a/Templates/DefaultProject/Template/Code/${NameLower}_files.cmake +++ b/Templates/DefaultProject/Template/Code/${NameLower}_files.cmake @@ -13,7 +13,5 @@ set(FILES Include/${Name}/${Name}Bus.h Source/${Name}SystemComponent.cpp Source/${Name}SystemComponent.h - runtime_dependencies.cmake - tool_dependencies.cmake - server_dependencies.cmake + enabled_gems.cmake ) diff --git a/Templates/DefaultProject/Template/Code/CMakeLists.txt b/Templates/DefaultProject/Template/Code/CMakeLists.txt index 38999c031c..43459b1606 100644 --- a/Templates/DefaultProject/Template/Code/CMakeLists.txt +++ b/Templates/DefaultProject/Template/Code/CMakeLists.txt @@ -33,7 +33,7 @@ endif() # in ${pal_dir}/${NameLower}_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake ly_add_target( NAME ${Name}.Static STATIC - NAMESPACE Project + NAMESPACE Gem FILES_CMAKE ${NameLower}_files.cmake ${pal_dir}/${NameLower}_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake @@ -48,7 +48,7 @@ ly_add_target( ly_add_target( NAME ${Name} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} - NAMESPACE Project + NAMESPACE Gem FILES_CMAKE ${NameLower}_shared_files.cmake ${pal_dir}/${NameLower}_shared_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake @@ -57,48 +57,60 @@ ly_add_target( Include BUILD_DEPENDENCIES PRIVATE - Project::${Name}.Static + Gem::${Name}.Static AZ::AzCore ) +# if enabled, ${Name} is used by all kinds of applications +ly_create_alias(NAME ${Name}.Builders NAMESPACE Gem TARGETS Gem::${Name}) +ly_create_alias(NAME ${Name}.Tools NAMESPACE Gem TARGETS Gem::${Name}) +ly_create_alias(NAME ${Name}.Clients NAMESPACE Gem TARGETS Gem::${Name}) +ly_create_alias(NAME ${Name}.Servers NAMESPACE Gem TARGETS Gem::${Name}) + ################################################################################ # Gem dependencies ################################################################################ -ly_add_project_dependencies( - PROJECT_NAME - ${Name} + +# The GameLauncher uses "Clients" gem variants: +ly_enable_gems( + PROJECT_NAME ${Name} GEM_FILE enabled_gems.cmake TARGETS ${Name}.GameLauncher - DEPENDENCIES_FILES - runtime_dependencies.cmake - ${pal_dir}/${PAL_PLATFORM_NAME_LOWERCASE}_runtime_dependencies.cmake -) + VARIANTS + Clients) if(PAL_TRAIT_BUILD_HOST_TOOLS) - ly_add_project_dependencies( - PROJECT_NAME - ${Name} + + # the builder type applications use the "Builders" variants of the enabled gems. + ly_enable_gems( + PROJECT_NAME ${Name} GEM_FILE enabled_gems.cmake TARGETS AssetBuilder AssetProcessor AssetProcessorBatch + VARIANTS + Builders) + + # the Editor applications use the "Tools" variants of the enabled gems. + ly_enable_gems( + PROJECT_NAME ${Name} GEM_FILE enabled_gems.cmake + TARGETS Editor - DEPENDENCIES_FILES - tool_dependencies.cmake - ${pal_dir}/${PAL_PLATFORM_NAME_LOWERCASE}_tool_dependencies.cmake - ) + VARIANTS + Tools) endif() if(PAL_TRAIT_BUILD_SERVER_SUPPORTED) - ly_add_project_dependencies( - PROJECT_NAME - ${Name} + # this property causes it to actually make a ServerLauncher. + # if you don't want a Server application, you can remove this and the + # following ly_enable_gems lines. + set_property(GLOBAL APPEND PROPERTY LY_LAUNCHER_SERVER_PROJECTS ${Name}) + + # The ServerLauncher uses the "Servers" variants of enabled gems: + ly_enable_gems( + PROJECT_NAME ${Name} GEM_FILE enabled_gems.cmake TARGETS ${Name}.ServerLauncher - DEPENDENCIES_FILES - server_dependencies.cmake - ${pal_dir}/${PAL_PLATFORM_NAME_LOWERCASE}_server_dependencies.cmake - ) - set_property(GLOBAL APPEND PROPERTY LY_LAUNCHER_SERVER_PROJECTS ${Name}) - + VARIANTS + Servers) endif() diff --git a/Templates/DefaultProject/Template/Code/Platform/Android/${NameLower}_android_files.cmake b/Templates/DefaultProject/Template/Code/Platform/Android/${NameLower}_android_files.cmake index b774cd944f..78fd98ba6c 100644 --- a/Templates/DefaultProject/Template/Code/Platform/Android/${NameLower}_android_files.cmake +++ b/Templates/DefaultProject/Template/Code/Platform/Android/${NameLower}_android_files.cmake @@ -11,7 +11,4 @@ set(FILES PAL_android.cmake - android_runtime_dependencies.cmake - android_tool_dependencies.cmake - android_server_dependencies.cmake ) diff --git a/Templates/DefaultProject/Template/Code/Platform/Android/android_runtime_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Android/android_runtime_dependencies.cmake deleted file mode 100644 index a1ebd6e455..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/Android/android_runtime_dependencies.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Gem::Atom_RHI_Vulkan.Private -) diff --git a/Templates/DefaultProject/Template/Code/Platform/Android/android_server_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Android/android_server_dependencies.cmake deleted file mode 100644 index fe28e294e9..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/Android/android_server_dependencies.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES -) diff --git a/Templates/DefaultProject/Template/Code/Platform/Android/android_tool_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Android/android_tool_dependencies.cmake deleted file mode 100644 index 14e6f1aa4c..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/Android/android_tool_dependencies.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES -) - diff --git a/Templates/DefaultProject/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake b/Templates/DefaultProject/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake index 58fc59d265..ee0b06efc4 100644 --- a/Templates/DefaultProject/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake +++ b/Templates/DefaultProject/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake @@ -11,7 +11,4 @@ set(FILES PAL_linux.cmake - linux_runtime_dependencies.cmake - linux_tool_dependencies.cmake - linux_server_dependencies.cmake ) diff --git a/Templates/DefaultProject/Template/Code/Platform/Linux/linux_runtime_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Linux/linux_runtime_dependencies.cmake deleted file mode 100644 index a54c22de8c..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/Linux/linux_runtime_dependencies.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Gem::Atom_RHI_Vulkan.Private - Gem::Atom_RHI_Vulkan.Builders -) diff --git a/Templates/DefaultProject/Template/Code/Platform/Linux/linux_server_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Linux/linux_server_dependencies.cmake deleted file mode 100644 index fe28e294e9..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/Linux/linux_server_dependencies.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES -) diff --git a/Templates/DefaultProject/Template/Code/Platform/Linux/linux_tool_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Linux/linux_tool_dependencies.cmake deleted file mode 100644 index a1ebd6e455..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/Linux/linux_tool_dependencies.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Gem::Atom_RHI_Vulkan.Private -) diff --git a/Templates/DefaultProject/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake b/Templates/DefaultProject/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake index 7eb776e3a6..e14e028c88 100644 --- a/Templates/DefaultProject/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake +++ b/Templates/DefaultProject/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake @@ -12,7 +12,4 @@ set(FILES ../../../Resources/Platform/Mac/Info.plist PAL_mac.cmake - mac_runtime_dependencies.cmake - mac_tool_dependencies.cmake - mac_server_dependencies.cmake ) diff --git a/Templates/DefaultProject/Template/Code/Platform/Mac/mac_runtime_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Mac/mac_runtime_dependencies.cmake deleted file mode 100644 index 2821493346..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/Mac/mac_runtime_dependencies.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Gem::Atom_RHI_Metal.Private - Gem::Atom_RHI_Null.Private -) diff --git a/Templates/DefaultProject/Template/Code/Platform/Mac/mac_server_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Mac/mac_server_dependencies.cmake deleted file mode 100644 index fe28e294e9..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/Mac/mac_server_dependencies.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES -) diff --git a/Templates/DefaultProject/Template/Code/Platform/Mac/mac_tool_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Mac/mac_tool_dependencies.cmake deleted file mode 100644 index adf5485ed4..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/Mac/mac_tool_dependencies.cmake +++ /dev/null @@ -1,18 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Gem::Atom_RHI_Metal.Private - Gem::Atom_RHI_Metal.Builders - Gem::Atom_RHI_Vulkan.Builders - Gem::Atom_RHI_DX12.Builders - Gem::Atom_RHI_Null.Builders -) diff --git a/Templates/DefaultProject/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake b/Templates/DefaultProject/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake index 8fee85a163..b6eb718a05 100644 --- a/Templates/DefaultProject/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake +++ b/Templates/DefaultProject/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake @@ -11,7 +11,4 @@ set(FILES PAL_windows.cmake - windows_runtime_dependencies.cmake - windows_tool_dependencies.cmake - windows_server_dependencies.cmake ) diff --git a/Templates/DefaultProject/Template/Code/Platform/Windows/windows_runtime_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Windows/windows_runtime_dependencies.cmake deleted file mode 100644 index 514a61aa57..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/Windows/windows_runtime_dependencies.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Gem::Atom_RHI_Vulkan.Private - Gem::Atom_RHI_DX12.Private - Gem::Atom_RHI_Null.Private -) diff --git a/Templates/DefaultProject/Template/Code/Platform/Windows/windows_server_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Windows/windows_server_dependencies.cmake deleted file mode 100644 index fe28e294e9..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/Windows/windows_server_dependencies.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES -) diff --git a/Templates/DefaultProject/Template/Code/Platform/Windows/windows_tool_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/Windows/windows_tool_dependencies.cmake deleted file mode 100644 index b7f4b82126..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/Windows/windows_tool_dependencies.cmake +++ /dev/null @@ -1,19 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Gem::Atom_RHI_Vulkan.Private - Gem::Atom_RHI_Vulkan.Builders - Gem::Atom_RHI_DX12.Private - Gem::Atom_RHI_DX12.Builders - Gem::Atom_RHI_Null.Private - Gem::Atom_RHI_Null.Builders -) diff --git a/Templates/DefaultProject/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake b/Templates/DefaultProject/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake index 41a6d13884..44f15538c8 100644 --- a/Templates/DefaultProject/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake +++ b/Templates/DefaultProject/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake @@ -12,7 +12,4 @@ set(FILES ../Resources/Platform/iOS/Info.plist PAL_ios.cmake - ios_runtime_dependencies.cmake - ios_tool_dependencies.cmake - ios_server_dependencies.cmake ) diff --git a/Templates/DefaultProject/Template/Code/Platform/iOS/ios_runtime_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/iOS/ios_runtime_dependencies.cmake deleted file mode 100644 index e49929c6e1..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/iOS/ios_runtime_dependencies.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Gem::Atom_RHI_Metal.Private -) diff --git a/Templates/DefaultProject/Template/Code/Platform/iOS/ios_server_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/iOS/ios_server_dependencies.cmake deleted file mode 100644 index fe28e294e9..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/iOS/ios_server_dependencies.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES -) diff --git a/Templates/DefaultProject/Template/Code/Platform/iOS/ios_tool_dependencies.cmake b/Templates/DefaultProject/Template/Code/Platform/iOS/ios_tool_dependencies.cmake deleted file mode 100644 index 14e6f1aa4c..0000000000 --- a/Templates/DefaultProject/Template/Code/Platform/iOS/ios_tool_dependencies.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES -) - diff --git a/Templates/DefaultProject/Template/Code/Source/${Name}Module.cpp b/Templates/DefaultProject/Template/Code/Source/${Name}Module.cpp index 003a984dd6..4f11828366 100644 --- a/Templates/DefaultProject/Template/Code/Source/${Name}Module.cpp +++ b/Templates/DefaultProject/Template/Code/Source/${Name}Module.cpp @@ -47,4 +47,4 @@ namespace ${Name} }; }// namespace ${Name} -AZ_DECLARE_MODULE_CLASS(Project_${Name}, ${Name}::${Name}Module) +AZ_DECLARE_MODULE_CLASS(Gem_${Name}, ${Name}::${Name}Module) diff --git a/Templates/DefaultProject/Template/Code/server_dependencies.cmake b/Templates/DefaultProject/Template/Code/enabled_gems.cmake similarity index 70% rename from Templates/DefaultProject/Template/Code/server_dependencies.cmake rename to Templates/DefaultProject/Template/Code/enabled_gems.cmake index 3982bbb166..dfb7d93233 100644 --- a/Templates/DefaultProject/Template/Code/server_dependencies.cmake +++ b/Templates/DefaultProject/Template/Code/enabled_gems.cmake @@ -9,8 +9,20 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # {END_LICENSE} -set(GEM_DEPENDENCIES +set(ENABLED_GEMS Project::${Name} - Gem::Maestro - Gem::LmbrCentral + Atom_AtomBridge + Camera + CameraFramework + EditorPythonBindings + EMotionFX + GradientSignal + ImGui + LmbrCentral + LyShine + Maestro + NvCloth + SceneProcessing + TextureAtlas + WhiteBox ) diff --git a/Templates/DefaultProject/Template/Code/gem.json b/Templates/DefaultProject/Template/Code/gem.json new file mode 100644 index 0000000000..5b8fb3fde0 --- /dev/null +++ b/Templates/DefaultProject/Template/Code/gem.json @@ -0,0 +1,14 @@ +{ + "gem_name": "${Name}", + "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", + "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "display_name": "${Name}", + "summary": "A short description of ${Name}.", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "${Name}" + ], + "icon_path": "preview.png" +} diff --git a/Templates/DefaultProject/Template/Code/runtime_dependencies.cmake b/Templates/DefaultProject/Template/Code/runtime_dependencies.cmake deleted file mode 100644 index ce8df8152d..0000000000 --- a/Templates/DefaultProject/Template/Code/runtime_dependencies.cmake +++ /dev/null @@ -1,36 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Project::${Name} - Gem::Maestro - Gem::TextureAtlas - Gem::LmbrCentral - Gem::NvCloth - Gem::LyShine - Gem::Camera - Gem::CameraFramework - Gem::Atom_RHI.Private - Gem::EMotionFX - Gem::Atom_RPI.Private - Gem::Atom_Feature_Common - Gem::ImGui - Gem::Atom_Bootstrap - Gem::Atom_Component_DebugCamera - Gem::AtomImGuiTools - Gem::AtomLyIntegration_CommonFeatures - Gem::EMotionFX_Atom - Gem::ImguiAtom - Gem::Atom_AtomBridge - Gem::GradientSignal - Gem::AtomFont - Gem::WhiteBox -) diff --git a/Templates/DefaultProject/Template/Code/tool_dependencies.cmake b/Templates/DefaultProject/Template/Code/tool_dependencies.cmake deleted file mode 100644 index 010d45bd0f..0000000000 --- a/Templates/DefaultProject/Template/Code/tool_dependencies.cmake +++ /dev/null @@ -1,43 +0,0 @@ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Project::${Name} - Gem::Maestro.Editor - Gem::TextureAtlas - Gem::LmbrCentral.Editor - Gem::NvCloth.Editor - Gem::LyShine.Editor - Gem::SceneProcessing.Editor - Gem::EditorPythonBindings.Editor - Gem::Camera.Editor - Gem::CameraFramework - Gem::Atom_RHI.Private - Gem::EMotionFX.Editor - Gem::Atom_RPI.Builders - Gem::Atom_RPI.Editor - Gem::Atom_Feature_Common.Builders - Gem::Atom_Feature_Common.Editor - Gem::ImGui.Editor - Gem::Atom_Bootstrap - Gem::Atom_Asset_Shader.Builders - Gem::Atom_Component_DebugCamera - Gem::AtomImGuiTools - Gem::AtomLyIntegration_CommonFeatures.Editor - Gem::EMotionFX_Atom.Editor - Gem::ImageProcessingAtom.Editor - Gem::Atom_AtomBridge.Editor - Gem::ImguiAtom - Gem::AtomFont - Gem::AtomToolsFramework.Editor - Gem::GradientSignal.Editor - Gem::WhiteBox.Editor -) diff --git a/Templates/DefaultProject/Template/EngineFinder.cmake b/Templates/DefaultProject/Template/EngineFinder.cmake new file mode 100644 index 0000000000..fbbe3d8cfe --- /dev/null +++ b/Templates/DefaultProject/Template/EngineFinder.cmake @@ -0,0 +1,68 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +# This file is copied during engine registration. Edits to this file will be lost next +# time a registration happens. + +include_guard() + +# Read the engine name from the project_json file +file(READ ${CMAKE_CURRENT_LIST_DIR}/project.json project_json) +string(JSON LY_ENGINE_NAME_TO_USE ERROR_VARIABLE json_error GET ${project_json} engine) +if(json_error) + message(FATAL_ERROR "Unable to read key 'engine' from 'project.json', error: ${json_error}") +endif() + +if(DEFINED ENV{USERPROFILE} AND EXISTS $ENV{USERPROFILE}) + set(manifest_path $ENV{USERPROFILE}/.o3de/o3de_manifest.json) # Windows +else() + set(manifest_path $ENV{HOME}/.o3de/o3de_manifest.json) # Unix +endif() + +# Read the ~/.o3de/o3de_manifest.json file and look through the 'engines_path' object. +# Find a key that matches LY_ENGINE_NAME_TO_USE and use that as the engine path. +if(EXISTS ${manifest_path}) + file(READ ${manifest_path} manifest_json) + + string(JSON engines_path_count ERROR_VARIABLE json_error LENGTH ${manifest_json} engines_path) + if(json_error) + message(FATAL_ERROR "Unable to read key 'engines_path' from '${manifest_path}', error: ${json_error}") + endif() + + string(JSON engines_path_type ERROR_VARIABLE json_error TYPE ${manifest_json} engines_path) + if(json_error OR NOT ${engines_path_type} STREQUAL "OBJECT") + message(FATAL_ERROR "Type of 'engines_path' in '${manifest_path}' is not a JSON Object, error: ${json_error}") + endif() + + math(EXPR engines_path_count "${engines_path_count}-1") + foreach(engine_path_index RANGE ${engines_path_count}) + string(JSON engine_name ERROR_VARIABLE json_error MEMBER ${manifest_json} engines_path ${engine_path_index}) + if(json_error) + message(FATAL_ERROR "Unable to read 'engines_path/${engine_path_index}' from '${manifest_path}', error: ${json_error}") + endif() + + if(LY_ENGINE_NAME_TO_USE STREQUAL engine_name) + string(JSON engine_path ERROR_VARIABLE json_error GET ${manifest_json} engines_path ${engine_name}) + if(json_error) + message(FATAL_ERROR "Unable to read value from 'engines_path/${engine_name}', error: ${json_error}") + endif() + + if(engine_path) + list(APPEND CMAKE_MODULE_PATH "${engine_path}/cmake") + break() + endif() + endif() + endforeach() +else() + # If the user is passing CMAKE_MODULE_PATH we assume thats where we will find the engine + if(NOT CMAKE_MODULE_PATH) + message(FATAL_ERROR "Engine registration is required before configuring a project. Please register an engine by running 'scripts/o3de register --this-engine'") + endif() +endif() diff --git a/Templates/DefaultProject/Template/ShaderLib/README.md b/Templates/DefaultProject/Template/ShaderLib/README.md new file mode 100644 index 0000000000..034550163d --- /dev/null +++ b/Templates/DefaultProject/Template/ShaderLib/README.md @@ -0,0 +1,5 @@ +# Customizing Shader Resource Groups + +Please read: +*\/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/README.md* +for details on how to customize scenesrg.srgi and viewsrg.srgi. diff --git a/Templates/DefaultProject/Template/ShaderLib/raytracingscenesrg.srgi b/Templates/DefaultProject/Template/ShaderLib/raytracingscenesrg.srgi deleted file mode 100644 index ac27571828..0000000000 --- a/Templates/DefaultProject/Template/ShaderLib/raytracingscenesrg.srgi +++ /dev/null @@ -1,30 +0,0 @@ -// {BEGIN_LICENSE} -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ -// {END_LICENSE} - -#pragma once - -// Please read README.md for an explanation on why scenesrg.srgi and viewsrg.srgi are -// located in this folder (And how you can optionally customize your own scenesrg.srgi -// and viewsrg.srgi in your game project). - -#include - -partial ShaderResourceGroup RayTracingSceneSrg : SRG_RayTracingScene -{ -/* Intentionally Empty. Helps define the SrgSemantic for RayTracingSceneSrg once.*/ -}; - -#define AZ_COLLECTING_PARTIAL_SRGS -#include -#undef AZ_COLLECTING_PARTIAL_SRGS diff --git a/Templates/DefaultProject/Template/ShaderLib/scenesrg.srgi b/Templates/DefaultProject/Template/ShaderLib/scenesrg.srgi index 9b4803b7dc..0a8cec5963 100644 --- a/Templates/DefaultProject/Template/ShaderLib/scenesrg.srgi +++ b/Templates/DefaultProject/Template/ShaderLib/scenesrg.srgi @@ -26,5 +26,6 @@ partial ShaderResourceGroup SceneSrg : SRG_PerScene }; #define AZ_COLLECTING_PARTIAL_SRGS -#include +#include +#include #undef AZ_COLLECTING_PARTIAL_SRGS diff --git a/Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.h b/Templates/DefaultProject/Template/Shaders/ShaderResourceGroups/SceneSrg.azsli similarity index 62% rename from Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.h rename to Templates/DefaultProject/Template/Shaders/ShaderResourceGroups/SceneSrg.azsli index 01688d8dc7..4c962fbbcd 100644 --- a/Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.h +++ b/Templates/DefaultProject/Template/Shaders/ShaderResourceGroups/SceneSrg.azsli @@ -1,3 +1,4 @@ +// {BEGIN_LICENSE} /* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. @@ -9,20 +10,15 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ +// {END_LICENSE} -#pragma once - -#include -#include -#include -#include -#include -#include - -#if !defined(SCRIPTCANVASDIAGNOSTICSLIBRARY_EDITOR) - -#include +#ifndef AZ_COLLECTING_PARTIAL_SRGS +#error Do not include this file directly. Include the main .srgi file instead. +#endif -#else +partial ShaderResourceGroup SceneSrg +{ + float m_time; + float m_deltaTime; +} -#endif diff --git a/Templates/DefaultProject/Template/preview.png b/Templates/DefaultProject/Template/preview.png index 2191a0ebc2..a3e13481c9 100644 --- a/Templates/DefaultProject/Template/preview.png +++ b/Templates/DefaultProject/Template/preview.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a18fae4040a22d2bb359a8ca642b97bb8f6468eeb52e2826b3b029bd8f1350b6 -size 5466 +oid sha256:4a5881b8d6cfbc4ceefb14ab96844484fe19407ee030824768f9fcce2f729d35 +size 2949 diff --git a/Templates/DefaultProject/template.json b/Templates/DefaultProject/template.json index 56278a6b04..26b868d315 100644 --- a/Templates/DefaultProject/template.json +++ b/Templates/DefaultProject/template.json @@ -1,6 +1,6 @@ { "template_name": "DefaultProject", - "restricted": "o3de", + "restricted_name": "o3de", "restricted_platform_relative_path": "Templates", "origin": "The primary repo for DefaultProject goes here: i.e. http://www.mydomain.com", "license": "What license DefaultProject uses goes here: i.e. https://opensource.org/licenses/MIT", @@ -24,6 +24,12 @@ "isTemplated": true, "isOptional": false }, + { + "file": "EngineFinder.cmake", + "origin": "EngineFinder.cmake", + "isTemplated": false, + "isOptional": false + }, { "file": "Code/${NameLower}_files.cmake", "origin": "Code/${NameLower}_files.cmake", @@ -42,6 +48,12 @@ "isTemplated": true, "isOptional": false }, + { + "file": "Code/gem.json", + "origin": "Code/gem.json", + "isTemplated": true, + "isOptional": true + }, { "file": "Code/Include/${Name}/${Name}Bus.h", "origin": "Code/Include/${Name}/${Name}Bus.h", @@ -66,24 +78,6 @@ "isTemplated": true, "isOptional": false }, - { - "file": "Code/Platform/Android/android_runtime_dependencies.cmake", - "origin": "Code/Platform/Android/android_runtime_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Code/Platform/Android/android_server_dependencies.cmake", - "origin": "Code/Platform/Android/android_server_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Code/Platform/Android/android_tool_dependencies.cmake", - "origin": "Code/Platform/Android/android_tool_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, { "file": "Code/Platform/Linux/${NameLower}_linux_files.cmake", "origin": "Code/Platform/Linux/${NameLower}_linux_files.cmake", @@ -102,24 +96,6 @@ "isTemplated": true, "isOptional": false }, - { - "file": "Code/Platform/Linux/linux_runtime_dependencies.cmake", - "origin": "Code/Platform/Linux/linux_runtime_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Code/Platform/Linux/linux_server_dependencies.cmake", - "origin": "Code/Platform/Linux/linux_server_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Code/Platform/Linux/linux_tool_dependencies.cmake", - "origin": "Code/Platform/Linux/linux_tool_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, { "file": "Code/Platform/Mac/${NameLower}_mac_files.cmake", "origin": "Code/Platform/Mac/${NameLower}_mac_files.cmake", @@ -138,24 +114,6 @@ "isTemplated": true, "isOptional": false }, - { - "file": "Code/Platform/Mac/mac_runtime_dependencies.cmake", - "origin": "Code/Platform/Mac/mac_runtime_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Code/Platform/Mac/mac_server_dependencies.cmake", - "origin": "Code/Platform/Mac/mac_server_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Code/Platform/Mac/mac_tool_dependencies.cmake", - "origin": "Code/Platform/Mac/mac_tool_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, { "file": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", "origin": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", @@ -174,24 +132,6 @@ "isTemplated": true, "isOptional": false }, - { - "file": "Code/Platform/Windows/windows_runtime_dependencies.cmake", - "origin": "Code/Platform/Windows/windows_runtime_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Code/Platform/Windows/windows_server_dependencies.cmake", - "origin": "Code/Platform/Windows/windows_server_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Code/Platform/Windows/windows_tool_dependencies.cmake", - "origin": "Code/Platform/Windows/windows_tool_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, { "file": "Code/Platform/iOS/${NameLower}_ios_files.cmake", "origin": "Code/Platform/iOS/${NameLower}_ios_files.cmake", @@ -210,24 +150,6 @@ "isTemplated": true, "isOptional": false }, - { - "file": "Code/Platform/iOS/ios_runtime_dependencies.cmake", - "origin": "Code/Platform/iOS/ios_runtime_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Code/Platform/iOS/ios_server_dependencies.cmake", - "origin": "Code/Platform/iOS/ios_server_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Code/Platform/iOS/ios_tool_dependencies.cmake", - "origin": "Code/Platform/iOS/ios_tool_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, { "file": "Code/Source/${Name}Module.cpp", "origin": "Code/Source/${Name}Module.cpp", @@ -247,20 +169,8 @@ "isOptional": false }, { - "file": "Code/runtime_dependencies.cmake", - "origin": "Code/runtime_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Code/server_dependencies.cmake", - "origin": "Code/server_dependencies.cmake", - "isTemplated": true, - "isOptional": false - }, - { - "file": "Code/tool_dependencies.cmake", - "origin": "Code/tool_dependencies.cmake", + "file": "Code/enabled_gems.cmake", + "origin": "Code/enabled_gems.cmake", "isTemplated": true, "isOptional": false }, @@ -565,10 +475,10 @@ "isOptional": false }, { - "file": "ShaderLib/raytracingscenesrg.srgi", - "origin": "ShaderLib/raytracingscenesrg.srgi", + "file": "ShaderLib/README.md", + "origin": "ShaderLib/README.md", "isTemplated": true, - "isOptional": false + "isOptional": true }, { "file": "ShaderLib/scenesrg.srgi", @@ -588,6 +498,12 @@ "isTemplated": true, "isOptional": false }, + { + "file": "Shaders/ShaderResourceGroups/SceneSrg.azsli", + "origin": "Shaders/ShaderResourceGroups/SceneSrg.azsli", + "isTemplated": true, + "isOptional": false + }, { "file": "autoexec.cfg", "origin": "autoexec.cfg", diff --git a/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py b/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py index 5f6db7ac05..ec4b43b023 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/managers/abstract_resource_locator.py @@ -16,8 +16,10 @@ import pathlib import warnings from abc import ABCMeta, abstractmethod +import ly_test_tools._internal.pytest_plugin from ly_test_tools.environment.file_system import find_ancestor_file + def _find_engine_root(initial_path): # type: (str) -> str """ @@ -34,11 +36,9 @@ def _find_engine_root(initial_path): # Assumes folder structure similar to: engine_root/dev/Tools/.../ly_test_tools/builtin for _ in range(15): if os.path.exists(os.path.join(current_dir, root_file)): - # The parent of the directory containing the engineroot.txt is the root directory - engine_root = current_dir - return engine_root - # Using an explicit else to avoid aberrant behavior from following filesystem links - else: + # parent of the directory containing root_file + return current_dir + else: # explicit else avoids aberrant behavior from following filesystem links current_dir = os.path.abspath(os.path.join(current_dir, os.path.pardir)) raise OSError(f"Unable to find engine root directory. Verify root file '{root_file}' exists") @@ -50,8 +50,10 @@ def _find_project_json(engine_root, project): Find the project.json file for this project. :return: Full path to the project.json file """ - project_json = find_ancestor_file('project.json') - if not project_json: + # First check relative to defined build directory, for external projects which configure through SDK settings + project_json = find_ancestor_file(target_file_name='project.json', + start_path=ly_test_tools._internal.pytest_plugin.build_directory) + if not project_json: # check internally for a project bundled with the engine project_json = os.path.join(engine_root, project, 'project.json') return project_json @@ -250,9 +252,6 @@ class AbstractResourceLocator(object): """ return os.path.join(self.build_directory(), 'CrySCompileServer') - def bootstrap_config_file(self): - return os.path.join(self.engine_root(), 'bootstrap.cfg') - def asset_processor_config_file(self): return os.path.join(self.engine_root(), 'Registry', 'AssetProcessorPlatformConfig.setreg') diff --git a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/mac.py b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/mac.py index 453e80ac41..4e19a3955a 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/mac.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/mac.py @@ -21,8 +21,8 @@ from ly_test_tools._internal.managers.abstract_resource_locator import AbstractR logger = logging.getLogger(__name__) -CACHE_DIR = 'osx_gl' -CONFIG_FILE = 'system_osx_osx_gl.cfg' +CACHE_DIR = 'mac' +CONFIG_FILE = 'system_osx_mac.cfg' class _MacResourceLocator(AbstractResourceLocator): @@ -33,7 +33,7 @@ class _MacResourceLocator(AbstractResourceLocator): def platform_config_file(self): """ Return the path to the platform config file. - ex. engine_root/dev/system_osx_osx_gl.cfg + ex. engine_root/dev/system_osx_mac.cfg :return: path to the platform config file """ return os.path.join(self.engine_root(), CONFIG_FILE) diff --git a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/windows.py b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/windows.py index 80ac413dbb..db6e7d713d 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/windows.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/managers/platforms/windows.py @@ -39,7 +39,7 @@ class _WindowsResourceLocator(AbstractResourceLocator): def platform_config_file(self): """ Return the path to the platform config file. - ex. engine_root/dev/system_osx_osx_gl.cfg + ex. engine_root/dev/system_osx_mac.cfg :return: path to the platform config file """ return os.path.join(self.engine_root(), CONFIG_FILE) diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/base.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/base.py index 1b0afb3efe..386de62048 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/platforms/base.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/base.py @@ -123,7 +123,6 @@ class Launcher(object): """ backup_path = self.workspace.settings.get_temp_path() log.debug(f"Performing automatic backup of bootstrap, platform and user settings in path {backup_path}") - self.workspace.settings.backup_bootstrap_settings(backup_path) self.workspace.settings.backup_platform_settings(backup_path) self.workspace.settings.backup_shader_compiler_settings(backup_path) diff --git a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py index 5507588ae3..8e2b93c20a 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/asset_processor.py @@ -36,10 +36,10 @@ DEFAULT_TIMEOUT_HOURS = 8 DEFAULT_TIMEOUT_SECONDS = 300 ASSET_PROCESSOR_PLATFORM_MAP = { - 'android': 'es3', + 'android': 'android', 'ios': 'ios', 'linux': 'linux', # Not fully implemented, see SPEC-2501 - 'mac': 'osx_gl', + 'mac': 'mac', 'windows': 'pc', } @@ -664,8 +664,7 @@ class AssetProcessor(object): make_dir = os.path.join(self._temp_asset_root, copy_dir) if not os.path.isdir(make_dir): os.makedirs(make_dir) - for copyfile_name in ['bootstrap.cfg', - 'Registry/AssetProcessorPlatformConfig.setreg', + for copyfile_name in ['Registry/AssetProcessorPlatformConfig.setreg', os.path.join(self._workspace.project, "project.json"), os.path.join('Assets', 'Engine', 'exclude.filetag')]: shutil.copyfile(os.path.join(self._workspace.paths.engine_root(), copyfile_name), diff --git a/Tools/LyTestTools/ly_test_tools/o3de/settings.py b/Tools/LyTestTools/ly_test_tools/o3de/settings.py index 9677a4d3a3..a1e83abe51 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/settings.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/settings.py @@ -57,14 +57,6 @@ class LySettings(object): """ self._backup_settings(self._resource_locator.platform_config_file(), backup_path) - def backup_bootstrap_settings(self, backup_path=None): - """ - Creates a backup of the bootstrap settings file (~/dev/bootstrap.cfg) in the backup_path. If no path is - provided, it will store in the workspace temp path (the contents of the workspace temp directory are removed - during workspace teardown) - """ - self._backup_settings(self._resource_locator.bootstrap_config_file(), backup_path) - def backup_shader_compiler_settings(self, backup_path=None): self._backup_settings(self._resource_locator.shader_compiler_config_file(), backup_path) @@ -79,14 +71,6 @@ class LySettings(object): """ self._restore_settings(self._resource_locator.platform_config_file(), backup_path) - def restore_bootstrap_settings(self, backup_path=None): - """ - Restores the bootstrap settings file (~/dev/bootstrap.cfg) from its backup. - The backup is stored in the backup_path. - If no backup_path is provided, it will attempt to retrieve the backup from the workspace temp path. - """ - self._restore_settings(self._resource_locator.bootstrap_config_file(), backup_path) - def restore_shader_compiler_settings(self, backup_path=None): self._restore_settings(self._resource_locator.shader_compiler_config_file(), backup_path) diff --git a/Tools/LyTestTools/tests/unit/test_abstract_resource_locator.py b/Tools/LyTestTools/tests/unit/test_abstract_resource_locator.py index e46fefa461..12286b3dd7 100755 --- a/Tools/LyTestTools/tests/unit/test_abstract_resource_locator.py +++ b/Tools/LyTestTools/tests/unit/test_abstract_resource_locator.py @@ -158,13 +158,6 @@ class TestAbstractResourceLocator(object): assert mock_abstract_resource_locator.shader_cache() == expected_path - def test_BootstrapConfigFile_IsCalled_ReturnBootstrapConfigFilePath(self): - mock_abstract_resource_locator = abstract_resource_locator.AbstractResourceLocator( - mock_build_directory, mock_project) - expected_path = os.path.join(mock_abstract_resource_locator.engine_root(), 'bootstrap.cfg') - - assert mock_abstract_resource_locator.bootstrap_config_file() == expected_path - def test_AssetProcessorConfigFile_IsCalled_ReturnsAssetProcessorConfigFilePath(self): mock_abstract_resource_locator = abstract_resource_locator.AbstractResourceLocator( mock_build_directory, mock_project) diff --git a/bootstrap.cfg b/bootstrap.cfg deleted file mode 100644 index 858e9093f5..0000000000 --- a/bootstrap.cfg +++ /dev/null @@ -1,12 +0,0 @@ -; This file is deprecated and is only use currently for setting the path when running O3DE in an engine-centric manner -; By engine-centric, what is meant is using CMake to configure from the directory and passing in the LY_PROJECTS value - -project_path=AutomatedTesting - -; The Asset Processor Specific settings are now the /Engine/Registry/bootstrap.setreg settings -; The Engine specific settings can be overridden in order of least precedence to most -; 1. Override the settings in a "/Registry/*.setreg(patch)" file (Shared per Gem Settings) -; 2. Override the settings in a "/Registry/*.setreg(patch)" file (Shared per Project Settings) -; 3. Override the settings in a "/user/Registry/*.setreg(patch)" file (User per Project Settings) -; 4. Override the settings in a "~/.o3de/Registry/*.setreg(patch)" file (User Global Settings) -; Where "~" is %USERPROFILE% on Windows and $HOME on Unix like platforms diff --git a/cmake/3rdParty.cmake b/cmake/3rdParty.cmake index 7385f34e5a..eb11404237 100644 --- a/cmake/3rdParty.cmake +++ b/cmake/3rdParty.cmake @@ -313,6 +313,9 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/3rdParty) ly_get_absolute_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/3rdParty/Platform/${PAL_PLATFORM_NAME}) list(APPEND CMAKE_MODULE_PATH ${pal_dir}) -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) +if(NOT INSTALLED_ENGINE) + # Add the 3rdParty cmake files to the IDE + 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 diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index 3cd453b943..3220271b42 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -10,40 +10,41 @@ # # shared by other platforms: -ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARGETS zlib PACKAGE_HASH e6f34b8ac16acf881e3d666ef9fd0c1aee94c3f69283fb6524d35d6f858eebbb) -ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) -ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) -ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) -ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev9-multiplatform TARGETS assimplib PACKAGE_HASH 448530277b51b145ca43b96becd0266e29ae210fc9e2b45f5afe85f301a040e7) -ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) -ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) -ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) -ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-multiplatform TARGETS RapidJSON PACKAGE_HASH 18b0aef4e6e849389916ff6de6682ab9c591ebe15af6ea6017014453c1119ea1) -ly_associate_package(PACKAGE_NAME RapidXML-1.13-multiplatform TARGETS RapidXML PACKAGE_HASH 510b3c12f8872c54b34733e34f2f69dd21837feafa55bfefa445c98318d96ebf) -ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev2-multiplatform TARGETS pybind11 PACKAGE_HASH d8012f907b6c54ac990b899a0788280857e7c93a9595405a28114b48c354eb1b) -ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform TARGETS lz4 PACKAGE_HASH d7b1d5651191db2c339827ad24f669d9d37754143e9173abc986184532f57c9d) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) -ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) -ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) -ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) -ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) -ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) -ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) +ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARGETS zlib PACKAGE_HASH e6f34b8ac16acf881e3d666ef9fd0c1aee94c3f69283fb6524d35d6f858eebbb) +ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) +ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) +ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) +ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev9-multiplatform TARGETS assimplib PACKAGE_HASH 448530277b51b145ca43b96becd0266e29ae210fc9e2b45f5afe85f301a040e7) +ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) +ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) +ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) +ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-multiplatform TARGETS RapidJSON PACKAGE_HASH 18b0aef4e6e849389916ff6de6682ab9c591ebe15af6ea6017014453c1119ea1) +ly_associate_package(PACKAGE_NAME RapidXML-1.13-multiplatform TARGETS RapidXML PACKAGE_HASH 510b3c12f8872c54b34733e34f2f69dd21837feafa55bfefa445c98318d96ebf) +ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev2-multiplatform TARGETS pybind11 PACKAGE_HASH d8012f907b6c54ac990b899a0788280857e7c93a9595405a28114b48c354eb1b) +ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) +ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform TARGETS lz4 PACKAGE_HASH d7b1d5651191db2c339827ad24f669d9d37754143e9173abc986184532f57c9d) +ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) +ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) +ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) +ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) +ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) +ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) # platform-specific: -ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-linux TARGETS AWSGameLiftServerSDK PACKAGE_HASH a8149a95bd100384af6ade97e2b21a56173740d921e6c3da8188cd51554d39af) -ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-linux TARGETS freetype PACKAGE_HASH 9ad246873067717962c6b780d28a5ce3cef3321b73c9aea746a039c798f52e93) -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-linux TARGETS tiff PACKAGE_HASH ae92b4d3b189c42ef644abc5cac865d1fb2eb7cb5622ec17e35642b00d1a0a76) -ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev4-linux TARGETS AWSNativeSDK PACKAGE_HASH b4db38de49d35a5f7500aed7f4aee5ec511dd3b584ee06fe9097885690191a5d) -ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-linux TARGETS Lua PACKAGE_HASH 1adc812abe3dd0dbb2ca9756f81d8f0e0ba45779ac85bf1d8455b25c531a38b0) -ly_associate_package(PACKAGE_NAME PhysX-4.1.0.25992954-rev1-linux TARGETS PhysX PACKAGE_HASH e3ca36106a8dbf1524709f8bb82d520920ebd3ff3a92672d382efff406c75ee3) -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 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) -ly_associate_package(PACKAGE_NAME unwind-1.2.1-linux TARGETS unwind PACKAGE_HASH 3453265fb056e25432f611a61546a25f60388e315515ad39007b5925dd054a77) -ly_associate_package(PACKAGE_NAME qt-5.15.2-rev3-linux TARGETS Qt PACKAGE_HASH b7d9932647f4b138b3f0b124d70debd250d2a8a6dca52b04dcbe82c6369d48ca) -ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-linux TARGETS libsamplerate PACKAGE_HASH 41643c31bc6b7d037f895f89d8d8d6369e906b92eff42b0fe05ee6a100f06261) -ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-linux TARGETS OpenSSL PACKAGE_HASH b779426d1e9c5ddf71160d5ae2e639c3b956e0fb5e9fcaf9ce97c4526024e3bc) +ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-linux TARGETS AWSGameLiftServerSDK PACKAGE_HASH a8149a95bd100384af6ade97e2b21a56173740d921e6c3da8188cd51554d39af) +ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-linux TARGETS freetype PACKAGE_HASH 9ad246873067717962c6b780d28a5ce3cef3321b73c9aea746a039c798f52e93) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-linux TARGETS tiff PACKAGE_HASH ae92b4d3b189c42ef644abc5cac865d1fb2eb7cb5622ec17e35642b00d1a0a76) +ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev4-linux TARGETS AWSNativeSDK PACKAGE_HASH b4db38de49d35a5f7500aed7f4aee5ec511dd3b584ee06fe9097885690191a5d) +ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-linux TARGETS Lua PACKAGE_HASH 1adc812abe3dd0dbb2ca9756f81d8f0e0ba45779ac85bf1d8455b25c531a38b0) +ly_associate_package(PACKAGE_NAME PhysX-4.1.0.25992954-rev1-linux TARGETS PhysX PACKAGE_HASH e3ca36106a8dbf1524709f8bb82d520920ebd3ff3a92672d382efff406c75ee3) +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 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) +ly_associate_package(PACKAGE_NAME unwind-1.2.1-linux TARGETS unwind PACKAGE_HASH 3453265fb056e25432f611a61546a25f60388e315515ad39007b5925dd054a77) +ly_associate_package(PACKAGE_NAME qt-5.15.2-rev3-linux TARGETS Qt PACKAGE_HASH b7d9932647f4b138b3f0b124d70debd250d2a8a6dca52b04dcbe82c6369d48ca) +ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-linux TARGETS libsamplerate PACKAGE_HASH 41643c31bc6b7d037f895f89d8d8d6369e906b92eff42b0fe05ee6a100f06261) +ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-linux TARGETS OpenSSL PACKAGE_HASH b779426d1e9c5ddf71160d5ae2e639c3b956e0fb5e9fcaf9ce97c4526024e3bc) +ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev2-linux TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 235606f98512c076a1ba84a8402ad24ac21945998abcea264e8e204678efc0ba) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index cf5ecaa15b..f85048d13e 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -10,41 +10,41 @@ # # shared by other platforms: -ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARGETS zlib PACKAGE_HASH e6f34b8ac16acf881e3d666ef9fd0c1aee94c3f69283fb6524d35d6f858eebbb) -ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) -ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) -ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) -ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev9-multiplatform TARGETS assimplib PACKAGE_HASH 448530277b51b145ca43b96becd0266e29ae210fc9e2b45f5afe85f301a040e7) -ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) -ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) -ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) -ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-multiplatform TARGETS RapidJSON PACKAGE_HASH 18b0aef4e6e849389916ff6de6682ab9c591ebe15af6ea6017014453c1119ea1) -ly_associate_package(PACKAGE_NAME RapidXML-1.13-multiplatform TARGETS RapidXML PACKAGE_HASH 510b3c12f8872c54b34733e34f2f69dd21837feafa55bfefa445c98318d96ebf) -ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev2-multiplatform TARGETS pybind11 PACKAGE_HASH d8012f907b6c54ac990b899a0788280857e7c93a9595405a28114b48c354eb1b) -ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform TARGETS lz4 PACKAGE_HASH d7b1d5651191db2c339827ad24f669d9d37754143e9173abc986184532f57c9d) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) -ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) -ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) -ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-mac TARGETS SPIRVCross PACKAGE_HASH 78c6376ed2fd195b9b1f5fb2b56e5267a32c3aa21fb399e905308de470eb4515) -ly_associate_package(PACKAGE_NAME DirectXShaderCompiler-1.6.2104-o3de-rev1-mac TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 4e97484f8fcf73fc39f22fc85ae86933a8f2e3ba0748fcec128bce05795035a6) -ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform TARGETS azslc PACKAGE_HASH 772b7a2d9cc68aa1da4f0ee7db57ee1b4e7a8f20b81961fc5849af779582f4df) -ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) -ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) -ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) -ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) +ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARGETS zlib PACKAGE_HASH e6f34b8ac16acf881e3d666ef9fd0c1aee94c3f69283fb6524d35d6f858eebbb) +ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) +ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) +ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) +ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev9-multiplatform TARGETS assimplib PACKAGE_HASH 448530277b51b145ca43b96becd0266e29ae210fc9e2b45f5afe85f301a040e7) +ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) +ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) +ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) +ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-multiplatform TARGETS RapidJSON PACKAGE_HASH 18b0aef4e6e849389916ff6de6682ab9c591ebe15af6ea6017014453c1119ea1) +ly_associate_package(PACKAGE_NAME RapidXML-1.13-multiplatform TARGETS RapidXML PACKAGE_HASH 510b3c12f8872c54b34733e34f2f69dd21837feafa55bfefa445c98318d96ebf) +ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev2-multiplatform TARGETS pybind11 PACKAGE_HASH d8012f907b6c54ac990b899a0788280857e7c93a9595405a28114b48c354eb1b) +ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) +ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform TARGETS lz4 PACKAGE_HASH d7b1d5651191db2c339827ad24f669d9d37754143e9173abc986184532f57c9d) +ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) +ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) +ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform TARGETS azslc PACKAGE_HASH 772b7a2d9cc68aa1da4f0ee7db57ee1b4e7a8f20b81961fc5849af779582f4df) +ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) +ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) +ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) +ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) # platform-specific: -ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-mac-ios TARGETS freetype PACKAGE_HASH 67b4f57aed92082d3fd7c16aa244a7d908d90122c296b0a63f73e0a0b8761977) -ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-mac-ios TARGETS tiff PACKAGE_HASH a23ae1f8991a29f8e5df09d6d5b00d7768a740f90752cef465558c1768343709) -ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev3-mac TARGETS AWSNativeSDK PACKAGE_HASH 21920372e90355407578b45ac19580df1463a39a25a867bcd0ffd8b385c8254a) -ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev6-mac TARGETS Lua PACKAGE_HASH b9079fd35634774c9269028447562c6b712dbc83b9c64975c095fd423ff04c08) -ly_associate_package(PACKAGE_NAME PhysX-4.1.0.25992954-rev1-mac TARGETS PhysX PACKAGE_HASH 149f5e9b44bd27291b1c4772f5e89a1e0efa88eef73c7e0b188935ed4d0c4a70) -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 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) -ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev1-mac TARGETS OpenSSL PACKAGE_HASH 28adc1c0616ac0482b2a9d7b4a3a3635a1020e87b163f8aba687c501cf35f96c) -ly_associate_package(PACKAGE_NAME qt-5.15.2-rev3-mac TARGETS Qt PACKAGE_HASH 4723ac43b19d4633c3fa4b9642f27c992d30cdc689f769f82869786f1c22a728) -ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-mac TARGETS libsamplerate PACKAGE_HASH b912af40c0ac197af9c43d85004395ba92a6a859a24b7eacd920fed5854a97fe) +ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev2-mac TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 2bede9a7ef3573027c005e38139237559eebf845c13ffb54c33c5b8675f962e2) +ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-mac TARGETS SPIRVCross PACKAGE_HASH 78c6376ed2fd195b9b1f5fb2b56e5267a32c3aa21fb399e905308de470eb4515) +ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-mac-ios TARGETS freetype PACKAGE_HASH 67b4f57aed92082d3fd7c16aa244a7d908d90122c296b0a63f73e0a0b8761977) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.15-mac-ios TARGETS tiff PACKAGE_HASH a23ae1f8991a29f8e5df09d6d5b00d7768a740f90752cef465558c1768343709) +ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev3-mac TARGETS AWSNativeSDK PACKAGE_HASH 21920372e90355407578b45ac19580df1463a39a25a867bcd0ffd8b385c8254a) +ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev6-mac TARGETS Lua PACKAGE_HASH b9079fd35634774c9269028447562c6b712dbc83b9c64975c095fd423ff04c08) +ly_associate_package(PACKAGE_NAME PhysX-4.1.0.25992954-rev1-mac TARGETS PhysX PACKAGE_HASH 149f5e9b44bd27291b1c4772f5e89a1e0efa88eef73c7e0b188935ed4d0c4a70) +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 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) +ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev1-mac TARGETS OpenSSL PACKAGE_HASH 28adc1c0616ac0482b2a9d7b4a3a3635a1020e87b163f8aba687c501cf35f96c) +ly_associate_package(PACKAGE_NAME qt-5.15.2-rev3-mac TARGETS Qt PACKAGE_HASH 4723ac43b19d4633c3fa4b9642f27c992d30cdc689f769f82869786f1c22a728) +ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-mac TARGETS libsamplerate PACKAGE_HASH b912af40c0ac197af9c43d85004395ba92a6a859a24b7eacd920fed5854a97fe) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 8fc009c601..fa1326b63d 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -10,49 +10,49 @@ # # shared by other platforms: -ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARGETS zlib PACKAGE_HASH e6f34b8ac16acf881e3d666ef9fd0c1aee94c3f69283fb6524d35d6f858eebbb) -ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) -ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) -ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) -ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev9-multiplatform TARGETS assimplib PACKAGE_HASH 448530277b51b145ca43b96becd0266e29ae210fc9e2b45f5afe85f301a040e7) -ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) -ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) -ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) -ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-multiplatform TARGETS RapidJSON PACKAGE_HASH 18b0aef4e6e849389916ff6de6682ab9c591ebe15af6ea6017014453c1119ea1) -ly_associate_package(PACKAGE_NAME RapidXML-1.13-multiplatform TARGETS RapidXML PACKAGE_HASH 510b3c12f8872c54b34733e34f2f69dd21837feafa55bfefa445c98318d96ebf) -ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev2-multiplatform TARGETS pybind11 PACKAGE_HASH d8012f907b6c54ac990b899a0788280857e7c93a9595405a28114b48c354eb1b) -ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) -ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform TARGETS lz4 PACKAGE_HASH d7b1d5651191db2c339827ad24f669d9d37754143e9173abc986184532f57c9d) -ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) -ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) -ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) -ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-windows TARGETS SPIRVCross PACKAGE_HASH 7d601ea9d625b1d509d38bd132a1f433d7e895b16adab76bac6103567a7a6817) -ly_associate_package(PACKAGE_NAME DirectXShaderCompiler-1.6.2104-o3de-rev1-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 2c60297758d73f7833911e5ae3006fe0b10ced6e0b1b54764b33ae2b86e0d41d) -ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform TARGETS azslc PACKAGE_HASH 772b7a2d9cc68aa1da4f0ee7db57ee1b4e7a8f20b81961fc5849af779582f4df) -ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) -ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) -ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) -ly_associate_package(PACKAGE_NAME Blast-1.1.7-rev1-multiplatform TARGETS Blast PACKAGE_HASH 36b8f393bcd25d0f85cfc7a831ebbdac881e6054c4f0735649966aa6aa86e6f0) -ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) +ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARGETS zlib PACKAGE_HASH e6f34b8ac16acf881e3d666ef9fd0c1aee94c3f69283fb6524d35d6f858eebbb) +ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) +ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) +ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) +ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev9-multiplatform TARGETS assimplib PACKAGE_HASH 448530277b51b145ca43b96becd0266e29ae210fc9e2b45f5afe85f301a040e7) +ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) +ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) +ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) +ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-multiplatform TARGETS RapidJSON PACKAGE_HASH 18b0aef4e6e849389916ff6de6682ab9c591ebe15af6ea6017014453c1119ea1) +ly_associate_package(PACKAGE_NAME RapidXML-1.13-multiplatform TARGETS RapidXML PACKAGE_HASH 510b3c12f8872c54b34733e34f2f69dd21837feafa55bfefa445c98318d96ebf) +ly_associate_package(PACKAGE_NAME pybind11-2.4.3-rev2-multiplatform TARGETS pybind11 PACKAGE_HASH d8012f907b6c54ac990b899a0788280857e7c93a9595405a28114b48c354eb1b) +ly_associate_package(PACKAGE_NAME cityhash-1.1-multiplatform TARGETS cityhash PACKAGE_HASH 0ace9e6f0b2438c5837510032d2d4109125845c0efd7d807f4561ec905512dd2) +ly_associate_package(PACKAGE_NAME lz4-r128-multiplatform TARGETS lz4 PACKAGE_HASH d7b1d5651191db2c339827ad24f669d9d37754143e9173abc986184532f57c9d) +ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform TARGETS expat PACKAGE_HASH 452256acd1fd699cef24162575b3524fccfb712f5321c83f1df1ce878de5b418) +ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) +ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) +ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform TARGETS azslc PACKAGE_HASH 772b7a2d9cc68aa1da4f0ee7db57ee1b4e7a8f20b81961fc5849af779582f4df) +ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) +ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) +ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) +ly_associate_package(PACKAGE_NAME Blast-1.1.7-rev1-multiplatform TARGETS Blast PACKAGE_HASH 36b8f393bcd25d0f85cfc7a831ebbdac881e6054c4f0735649966aa6aa86e6f0) +ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) # platform-specific: -ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-windows TARGETS AWSGameLiftServerSDK PACKAGE_HASH a0586b006e4def65cc25f388de17dc475e417dc1e6f9d96749777c88aa8271b0) -ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-windows TARGETS freetype PACKAGE_HASH 88dedc86ccb8c92f14c2c033e51ee7d828fa08eafd6475c6aa963938a99f4bf3) -ly_associate_package(PACKAGE_NAME tiff-4.2.0.14-windows TARGETS tiff PACKAGE_HASH ab60d1398e4e1e375ec0f1a00cdb1d812a07c0096d827db575ce52dd6d714207) -ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev3-windows TARGETS AWSNativeSDK PACKAGE_HASH 929873d4252c464620a9d288e41bd5d47c0bd22750aeb3a1caa68a3da8247c48) -ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-windows TARGETS Lua PACKAGE_HASH 136faccf1f73891e3fa3b95f908523187792e56f5b92c63c6a6d7e72d1158d40) -ly_associate_package(PACKAGE_NAME PhysX-4.1.0.25992954-rev1-windows TARGETS PhysX PACKAGE_HASH 198bed89d1aae7caaf5dadba24cee56235fe41725d004b64040d4e50d0f3aa1a) -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 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) -ly_associate_package(PACKAGE_NAME d3dx12-headers-rev1-windows TARGETS d3dx12 PACKAGE_HASH 088c637159fba4a3e4c0cf08fb4921906fd4cca498939bd239db7c54b5b2f804) -ly_associate_package(PACKAGE_NAME pyside2-qt-5.15.1-rev2-windows TARGETS pyside2 PACKAGE_HASH c90f3efcc7c10e79b22a33467855ad861f9dbd2e909df27a5cba9db9fa3edd0f) -ly_associate_package(PACKAGE_NAME openimageio-2.1.16.0-rev2-windows TARGETS OpenImageIO PACKAGE_HASH 85a2a6cf35cbc4c967c56ca8074babf0955c5b490c90c6e6fd23c78db99fc282) -ly_associate_package(PACKAGE_NAME qt-5.15.2-rev2-windows TARGETS Qt PACKAGE_HASH 29966f22ec253dc9904e88ad48fe6b6a669302b2dc7049f2e2bbd4949e79e595) -ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-windows TARGETS libsamplerate PACKAGE_HASH dcf3c11a96f212a52e2c9241abde5c364ee90b0f32fe6eeb6dcdca01d491829f) -ly_associate_package(PACKAGE_NAME OpenMesh-8.1-rev1-windows TARGETS OpenMesh PACKAGE_HASH 1c1df639358526c368e790dfce40c45cbdfcfb1c9a041b9d7054a8949d88ee77) -ly_associate_package(PACKAGE_NAME civetweb-1.8-rev1-windows TARGETS civetweb PACKAGE_HASH 36d0e58a59bcdb4dd70493fb1b177aa0354c945b06c30416348fd326cf323dd4) -ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-windows TARGETS OpenSSL PACKAGE_HASH 9af1c50343f89146b4053101a7aeb20513319a3fe2f007e356d7ce25f9241040) -ly_associate_package(PACKAGE_NAME Crashpad-0.8.0-rev1-windows TARGETS Crashpad PACKAGE_HASH d162aa3070147bc0130a44caab02c5fe58606910252caf7f90472bd48d4e31e2) +ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-windows TARGETS AWSGameLiftServerSDK PACKAGE_HASH a0586b006e4def65cc25f388de17dc475e417dc1e6f9d96749777c88aa8271b0) +ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev2-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH decc53e97c7ddda9c7f853a30af7808a7b652a912f59ad2cd4bca5d308aae2c4) +ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-windows TARGETS SPIRVCross PACKAGE_HASH 7d601ea9d625b1d509d38bd132a1f433d7e895b16adab76bac6103567a7a6817) +ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-windows TARGETS freetype PACKAGE_HASH 88dedc86ccb8c92f14c2c033e51ee7d828fa08eafd6475c6aa963938a99f4bf3) +ly_associate_package(PACKAGE_NAME tiff-4.2.0.14-windows TARGETS tiff PACKAGE_HASH ab60d1398e4e1e375ec0f1a00cdb1d812a07c0096d827db575ce52dd6d714207) +ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev3-windows TARGETS AWSNativeSDK PACKAGE_HASH 929873d4252c464620a9d288e41bd5d47c0bd22750aeb3a1caa68a3da8247c48) +ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-windows TARGETS Lua PACKAGE_HASH 136faccf1f73891e3fa3b95f908523187792e56f5b92c63c6a6d7e72d1158d40) +ly_associate_package(PACKAGE_NAME PhysX-4.1.0.25992954-rev1-windows TARGETS PhysX PACKAGE_HASH 198bed89d1aae7caaf5dadba24cee56235fe41725d004b64040d4e50d0f3aa1a) +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 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) +ly_associate_package(PACKAGE_NAME d3dx12-headers-rev1-windows TARGETS d3dx12 PACKAGE_HASH 088c637159fba4a3e4c0cf08fb4921906fd4cca498939bd239db7c54b5b2f804) +ly_associate_package(PACKAGE_NAME pyside2-qt-5.15.1-rev2-windows TARGETS pyside2 PACKAGE_HASH c90f3efcc7c10e79b22a33467855ad861f9dbd2e909df27a5cba9db9fa3edd0f) +ly_associate_package(PACKAGE_NAME openimageio-2.1.16.0-rev2-windows TARGETS OpenImageIO PACKAGE_HASH 85a2a6cf35cbc4c967c56ca8074babf0955c5b490c90c6e6fd23c78db99fc282) +ly_associate_package(PACKAGE_NAME qt-5.15.2-rev2-windows TARGETS Qt PACKAGE_HASH 29966f22ec253dc9904e88ad48fe6b6a669302b2dc7049f2e2bbd4949e79e595) +ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-windows TARGETS libsamplerate PACKAGE_HASH dcf3c11a96f212a52e2c9241abde5c364ee90b0f32fe6eeb6dcdca01d491829f) +ly_associate_package(PACKAGE_NAME OpenMesh-8.1-rev1-windows TARGETS OpenMesh PACKAGE_HASH 1c1df639358526c368e790dfce40c45cbdfcfb1c9a041b9d7054a8949d88ee77) +ly_associate_package(PACKAGE_NAME civetweb-1.8-rev1-windows TARGETS civetweb PACKAGE_HASH 36d0e58a59bcdb4dd70493fb1b177aa0354c945b06c30416348fd326cf323dd4) +ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-windows TARGETS OpenSSL PACKAGE_HASH 9af1c50343f89146b4053101a7aeb20513319a3fe2f007e356d7ce25f9241040) +ly_associate_package(PACKAGE_NAME Crashpad-0.8.0-rev1-windows TARGETS Crashpad PACKAGE_HASH d162aa3070147bc0130a44caab02c5fe58606910252caf7f90472bd48d4e31e2) diff --git a/cmake/CMakeFiles.cmake b/cmake/CMakeFiles.cmake index 952f9b5eb8..77c2eb75e1 100644 --- a/cmake/CMakeFiles.cmake +++ b/cmake/CMakeFiles.cmake @@ -9,8 +9,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# Add all cmake files in a project so they can be handled from within the IDE -ly_include_cmake_file_list(cmake/cmake_files.cmake) -add_custom_target(CMakeFiles SOURCES ${ALLFILES}) -ly_source_groups_from_folders("${ALLFILES}") -unset(ALLFILES) \ No newline at end of file +if(NOT INSTALLED_ENGINE) + # Add all cmake files in a project so they can be handled from within the IDE + ly_include_cmake_file_list(cmake/cmake_files.cmake) + add_custom_target(CMakeFiles SOURCES ${ALLFILES}) + ly_source_groups_from_folders("${ALLFILES}") + unset(ALLFILES) +endif() \ No newline at end of file diff --git a/cmake/EngineFinder.cmake b/cmake/EngineFinder.cmake deleted file mode 100644 index 9ff8ce4d66..0000000000 --- a/cmake/EngineFinder.cmake +++ /dev/null @@ -1,52 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# -# This file is copied during engine registration. Edits to this file will be lost next -# time a registration happens. - -include_guard() - -# Read the engine name from the project_json file -file(READ ${CMAKE_CURRENT_LIST_DIR}/project.json project_json) -string(JSON LY_ENGINE_NAME_TO_USE ERROR_VARIABLE json_error GET ${project_json} engine) -if(json_error) - message(FATAL_ERROR "Unable to read key 'engine' from 'project.json', error: ${json_error}") -endif() - -# Read the list of paths from ~.o3de/o3de_manifest.json -file(TO_CMAKE_PATH "$ENV{USERPROFILE}" home_directory) # Windows -if((NOT home_directory) OR (NOT EXISTS ${home_directory})) - file(TO_CMAKE_PATH "$ENV{HOME}" home_directory)# Unix -endif() - -if (NOT home_directory) - message(FATAL_ERROR "Cannot find user home directory, the o3de manifest cannot be found") -endif() -# Set manifest path to path in the user home directory -set(manifest_path ${home_directory}/.o3de/o3de_manifest.json) - -if(EXISTS ${manifest_path}) - file(READ ${manifest_path} manifest_json) - string(JSON engines_count ERROR_VARIABLE json_error LENGTH ${manifest_json} engines) - if(json_error) - message(FATAL_ERROR "Unable to read key 'engines' from '${manifest_path}', error: ${json_error}") - endif() - - math(EXPR engines_count "${engines_count}-1") - foreach(engine_path_index RANGE ${engines_count}) - string(JSON engine_path ERROR_VARIABLE json_error GET ${manifest_json} engines ${engine_path_index}) - if(${json_error}) - message(FATAL_ERROR "Unable to read engines[${engine_path_index}] '${manifest_path}', error: ${json_error}") - endif() - if(engine_path) - list(APPEND CMAKE_MODULE_PATH "${engine_path}/cmake") - endif() - endforeach() -endif() diff --git a/cmake/EngineJson.cmake b/cmake/EngineJson.cmake new file mode 100644 index 0000000000..c3ab29d09e --- /dev/null +++ b/cmake/EngineJson.cmake @@ -0,0 +1,47 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +# This file is copied during engine registration. Edits to this file will be lost next +# time a registration happens. + +include_guard() + +set(LY_EXTERNAL_SUBDIRS "" CACHE STRING "List of subdirectories to recurse into when running cmake against the engine's CMakeLists.txt") + +#! read_engine_external_subdirs +# Read the external subdirectories from the engine.json file +# External subdirectories are any folders with CMakeLists.txt in them +# This could be regular subdirectories, Gems(contains an additional gem.json), +# Restricted folders(contains an additional restricted.json), etc... +# \arg:output_external_subdirs name of output variable to store external subdirectories into +function(read_engine_external_subdirs output_external_subdirs) + file(READ ${LY_ROOT_FOLDER}/engine.json engine_json_data) + string(JSON external_subdirs_count ERROR_VARIABLE engine_json_error + LENGTH ${engine_json_data} "external_subdirectories") + if(engine_json_error) + message(FATAL_ERROR "Error querying number of elements in JSON array \"external_subdirectories\": ${engine_json_error}") + endif() + + if(external_subdirs_count GREATER 0) + math(EXPR external_subdir_range "${external_subdirs_count}-1") + # Convert the paths the relative paths to absolute paths using the engine root + # as the base directory + foreach(external_subdir_index RANGE ${external_subdir_range}) + string(JSON external_subdir ERROR_VARIABLE engine_json_error + GET ${engine_json_data} "external_subdirectories" "${external_subdir_index}") + if(engine_json_error) + message(FATAL_ERROR "Error reading field at index ${external_subdir_index} in \"external_subdirectories\" JSON array: ${engine_json_error}") + endif() + file(REAL_PATH ${external_subdir} real_external_subdir BASE_DIRECTORY ${CMAKE_SOURCE_DIR}) + list(APPEND external_subdirs ${real_external_subdir}) + endforeach() + endif() + set(${output_external_subdirs} ${external_subdirs} PARENT_SCOPE) +endfunction() diff --git a/cmake/FindTarget.cmake.in b/cmake/FindTarget.cmake.in deleted file mode 100644 index 8ad9822dae..0000000000 --- a/cmake/FindTarget.cmake.in +++ /dev/null @@ -1,34 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -# Generated by O3DE - -include(FindPackageHandleStandardArgs) - -ly_add_target( - NAME @NAME_PLACEHOLDER@ UNKNOWN IMPORTED - @NAMESPACE_PLACEHOLDER@ - COMPILE_DEFINITIONS - INTERFACE -@COMPILE_DEFINITIONS_PLACEHOLDER@ - INCLUDE_DIRECTORIES - INTERFACE -@INCLUDE_DIRECTORIES_PLACEHOLDER@ - BUILD_DEPENDENCIES - INTERFACE -@BUILD_DEPENDENCIES_PLACEHOLDER@ - RUNTIME_DEPENDENCIES -@RUNTIME_DEPENDENCIES_PLACEHOLDER@ -) - -foreach(config @CMAKE_CONFIGURATION_TYPES@) - include("${LY_ROOT_FOLDER}/cmake_autogen/@NAME_PLACEHOLDER@/@NAME_PLACEHOLDER@_${config}.cmake" OPTIONAL) -endforeach() diff --git a/cmake/Gems.cmake b/cmake/Gems.cmake new file mode 100644 index 0000000000..d418d5dcd1 --- /dev/null +++ b/cmake/Gems.cmake @@ -0,0 +1,217 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +# This file contains utility wrappers for dealing with the Gems system. + +# ly_create_alias +# given an alias to create, and a list of one or more targets, +# this creates an alias that depends on all of the given targets. +function(ly_create_alias) + set(options) + set(oneValueArgs NAME NAMESPACE) + set(multiValueArgs TARGETS) + + cmake_parse_arguments(ly_create_alias "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT ly_create_alias_NAME) + message(FATAL_ERROR "Provide the name of the alias to create using the NAME keyword") + endif() + + if (NOT ly_create_alias_NAMESPACE) + message(FATAL_ERROR "Provide the namespace of the alias to create using the NAMESPACE keyword") + endif() + + if (NOT ly_create_alias_TARGETS) + message(FATAL_ERROR "Provide the name of the targets the alias be associated with, using the TARGETS keyword") + endif() + + if(TARGET ${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME}) + message(FATAL_ERROR "Target already exists, cannot create an alias for it: ${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME}\n" + "Make sure the target wasn't copy and pasted here or elsewhere.") + endif() + + # easy version - if its juts one target, we can directly get the target, and make both aliases, + # the namespaced and non namespaced one, point at it. + list(LENGTH ly_create_alias_TARGETS number_of_targets) + if (number_of_targets EQUAL 1) + ly_de_alias_target(${ly_create_alias_TARGETS} de_aliased_target_name) + add_library(${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME} ALIAS ${de_aliased_target_name}) + if (NOT TARGET ${ly_create_alias_NAME}) + add_library(${ly_create_alias_NAME} ALIAS ${de_aliased_target_name}) + endif() + # Store off the arguments needed used ly_create_alias into a DIRECTORY property + # This will be used to re-create the calls in the generated CMakeLists.txt in the INSTALL step + string(REPLACE ";" " " create_alias_args "${ly_create_alias_NAME},${ly_create_alias_NAMESPACE},${ly_create_alias_TARGETS}") + set_property(DIRECTORY APPEND PROPERTY LY_CREATE_ALIAS_ARGUMENTS "${ly_create_alias_NAME},${ly_create_alias_NAMESPACE},${ly_create_alias_TARGETS}") + return() + endif() + + # more complex version - one alias to multiple targets. To actually achieve this + # we have to create an interface library with those dependencies, then we have to create an alias to that target. + # by convention we create one without a namespace then alias the namespaced one. + + if(TARGET ${ly_create_alias_NAME}) + message(FATAL_ERROR "Internal alias target already exists, cannot create an alias for it: ${ly_create_alias_NAME}\n" + "This could be a copy-paste error, where some part of the ly_create_alias call was changed but the other") + endif() + + add_library(${ly_create_alias_NAME} INTERFACE IMPORTED GLOBAL) + set_target_properties(${ly_create_alias_NAME} PROPERTIES GEM_MODULE TRUE) + + foreach(target_name ${ly_create_alias_TARGETS}) + if(TARGET ${target_name}) + ly_de_alias_target(${target_name} de_aliased_target_name) + if(NOT de_aliased_target_name) + message(FATAL_ERROR "Target not found in ly_create_alias call: ${target_name} - check your spelling of the target name") + endif() + else() + set(de_aliased_target_name ${target_name}) + endif() + list(APPEND final_targets ${de_aliased_target_name}) + endforeach() + + ly_parse_third_party_dependencies("${final_targets}") + ly_add_dependencies(${ly_create_alias_NAME} ${final_targets}) + + # now add the final alias: + add_library(${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME} ALIAS ${ly_create_alias_NAME}) + + # Store off the arguments needed used ly_create_alias into a DIRECTORY property + # This will be used to re-create the calls in the generated CMakeLists.txt in the INSTALL step + + # Replace the CMake list separator with a space to replicate the space separated TARGETS arguments + string(REPLACE ";" " " create_alias_args "${ly_create_alias_NAME},${ly_create_alias_NAMESPACE},${ly_create_alias_TARGETS}") + set_property(DIRECTORY APPEND PROPERTY LY_CREATE_ALIAS_ARGUMENTS "${create_alias_args}") +endfunction() + +# ly_enable_gems +# this function makes sure that the given gems, or gems listed in the variable ENABLED_GEMS +# in the GEM_FILE name, are set as runtime dependencies (and thus loaded) for the given targets +# in the context of the given project. +# note that it can't do this immediately, so it saves the data for later processing. +# Note: If you don't supply a project name, it will apply it across the board to all projects. +# this is useful in the case of "ly_add_gems being called for so called 'mandatory gems' inside the engine. +# if you specify a gem name with a namespace, it will be used, otherwise it will assume Gem:: +function(ly_enable_gems) + set(options) + set(oneValueArgs PROJECT_NAME GEM_FILE) + set(multiValueArgs GEMS TARGETS VARIANTS) + + cmake_parse_arguments(ly_enable_gems "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT ly_enable_gems_TARGETS) + message(FATAL_ERROR "You must provide the targets to add gems to using the TARGETS keyword") + endif() + + + if (NOT ly_enable_gems_PROJECT_NAME) + message(VERBOSE "Note: ly_enable_gems called with no PROJECT_NAME name, applying to all projects: \n" + " - VARIANTS ${ly_enable_gems_VARIANTS} \n" + " - GEMS ${ly_enable_gems_GEMS} \n" + " - TARGETS ${ly_enable_gems_TARGETS} \n" + " - GEM_FILE ${ly_enable_gems_GEM_FILE}") + set(ly_enable_gems_PROJECT_NAME "__NOPROJECT__") # so that the token is not blank + endif() + + if (NOT ly_enable_gems_VARIANTS) + message(FATAL_ERROR "You must provide at least 1 variant of the gem modules (Editor, Server, Client, Builder) to " + "add to your targets, using the VARIANTS keyword") + endif() + + if ((NOT ly_enable_gems_GEMS AND NOT ly_enable_gems_GEM_FILE) OR (ly_enable_gems_GEMS AND ly_enable_gems_GEM_FILE)) + message(FATAL_ERROR "Provide exactly one of either GEM_FILE (filename) or GEMS (list of gems) keywords.") + endif() + + if (ly_enable_gems_GEM_FILE) + set(store_temp ${ENABLED_GEMS}) + include(${ly_enable_gems_GEM_FILE} RESULT_VARIABLE was_able_to_load_the_file) + if(NOT was_able_to_load_the_file) + message(FATAL_ERROR "could not load the GEM_FILE ${ly_enable_gems_GEM_FILE}") + endif() + if(NOT ENABLED_GEMS) + message(FATAL_ERROR "GEM_FILE ${ly_enable_gems_GEM_FILE} did not set the value of ENABLED_GEMS.\n" + "Gem Files should contain set(ENABLED_GEMS ... )") + endif() + set(ly_enable_gems_GEMS ${ENABLED_GEMS}) + set(ENABLED_GEMS ${store_temp}) # restore value of ENABLED_GEMS just in case... + endif() + + # all the actual work has to be done later. + foreach(target_name ${ly_enable_gems_TARGETS}) + foreach(variant_name ${ly_enable_gems_VARIANTS}) + set_property(GLOBAL APPEND PROPERTY LY_DELAYED_ENABLE_GEMS "${ly_enable_gems_PROJECT_NAME},${target_name},${variant_name}") + set_property(GLOBAL APPEND PROPERTY LY_DELAYED_ENABLE_GEMS_"${ly_enable_gems_PROJECT_NAME},${target_name},${variant_name}" ${ly_enable_gems_GEMS}) + endforeach() + endforeach() +endfunction() + +# call this before runtime dependencies are used to add any relevant targets +# saved by the above function +function(ly_enable_gems_delayed) + get_property(ly_delayed_enable_gems GLOBAL PROPERTY LY_DELAYED_ENABLE_GEMS) + foreach(project_target_variant ${ly_delayed_enable_gems}) + # we expect a colon separated list of + # PROJECT_NAME,target_name,variant_name + string(REPLACE "," ";" project_target_variant_list "${project_target_variant}") + list(LENGTH project_target_variant_list project_target_variant_length) + if(project_target_variant_length EQUAL 0) + continue() + endif() + + if(NOT project_target_variant_length EQUAL 3) + message(FATAL_ERROR "Invalid specification of gems, expected 'project','target','variant' and got ${project_target_variant}") + endif() + + list(POP_BACK project_target_variant_list variant) + list(POP_BACK project_target_variant_list target) + list(POP_BACK project_target_variant_list project) + + get_property(gem_dependencies GLOBAL PROPERTY LY_DELAYED_ENABLE_GEMS_"${project_target_variant}") + if (NOT gem_dependencies) + continue() + endif() + + if(${project} STREQUAL "__NOPROJECT__") + # special case, apply to all + unset(PREFIX_CLAUSE) + else() + set(PREFIX_CLAUSE "PREFIX;${project}") + endif() + + if (NOT TARGET ${target}) + message(FATAL_ERROR "ly_enable_gems specified TARGET '${target}' but no such target was found.") + endif() + + # apply the list of gem targets. Adding a gem really just means adding the appropriate dependency. + foreach(gem_name ${gem_dependencies}) + # the gem name may already have a namespace. If it does, we use that one + ly_strip_target_namespace(TARGET ${gem_name} OUTPUT_VARIABLE unaliased_gem_name) + if (${unaliased_gem_name} STREQUAL ${gem_name}) + # if stripping a namespace had no effect, it had no namespace + # and we supply the default Gem:: namespace. + set(gem_name_with_namespace Gem::${gem_name}) + else() + # if stripping the namespace had an effect then we use the original + # with the namespace, instead of assuming Gem:: + set(gem_name_with_namespace ${gem_name}) + endif() + + # if the target exists, add it. + if (TARGET ${gem_name_with_namespace}.${variant}) + ly_add_target_dependencies( + ${PREFIX_CLAUSE} + TARGETS ${target} + DEPENDENT_TARGETS ${gem_name_with_namespace}.${variant} + ) + endif() + endforeach() + endforeach() +endfunction() \ No newline at end of file diff --git a/cmake/Install.cmake b/cmake/Install.cmake index b56f5ced85..205277f0e5 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -9,5 +9,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -ly_get_absolute_pal_filename(pal_dir ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}) -include(${pal_dir}/Install_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) \ No newline at end of file +if(NOT INSTALLED_ENGINE) + ly_get_absolute_pal_filename(pal_dir ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}) + include(${pal_dir}/Install_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) +endif() \ No newline at end of file diff --git a/cmake/LYPython.cmake b/cmake/LYPython.cmake index ff5132097c..238889d829 100644 --- a/cmake/LYPython.cmake +++ b/cmake/LYPython.cmake @@ -81,7 +81,7 @@ function(update_pip_requirements requirements_file_path unique_name) set(ENV{PYTHONNOUSERSITE} 1) execute_process(COMMAND - ${LY_PYTHON_CMD} -m pip install --no-deps -r "${requirements_file_path}" --disable-pip-version-check --no-warn-script-location + ${LY_PYTHON_CMD} -m pip install -r "${requirements_file_path}" --disable-pip-version-check --no-warn-script-location WORKING_DIRECTORY ${Python_BINFOLDER} RESULT_VARIABLE PIP_RESULT OUTPUT_VARIABLE PIP_OUT @@ -265,10 +265,13 @@ if (NOT CMAKE_SCRIPT_MODE_FILE) # we also need to make sure any custom packages are installed. # this costs a moment of time though, so we'll only do it based on stamp files. + if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND NOT INSTALLED_ENGINE) + ly_pip_install_local_package_editable(${LY_ROOT_FOLDER}/Tools/LyTestTools ly-test-tools) + ly_pip_install_local_package_editable(${LY_ROOT_FOLDER}/Tools/RemoteConsole/ly_remote_console ly-remote-console) + ly_pip_install_local_package_editable(${LY_ROOT_FOLDER}/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools editor-python-test-tools) + endif() - ly_pip_install_local_package_editable(${LY_ROOT_FOLDER}/Tools/LyTestTools ly-test-tools) - ly_pip_install_local_package_editable(${LY_ROOT_FOLDER}/Tools/RemoteConsole/ly_remote_console ly-remote-console) - ly_pip_install_local_package_editable(${LY_ROOT_FOLDER}/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools editor-python-test-tools) + ly_pip_install_local_package_editable(${LY_ROOT_FOLDER}/scripts/o3de o3de) endif() endif() diff --git a/cmake/LYWrappers.cmake b/cmake/LYWrappers.cmake index f6a36afc89..d7f88f12ec 100644 --- a/cmake/LYWrappers.cmake +++ b/cmake/LYWrappers.cmake @@ -46,13 +46,13 @@ define_property(TARGET PROPERTY GEM_MODULE # # \arg:NAME name of the target # \arg:STATIC (bool) defines this target to be a static library +# \arg:GEM_STATIC (bool) defines this target to be a static library while also setting the GEM_MODULE property # \arg:SHARED (bool) defines this target to be a dynamic library # \arg:MODULE (bool) defines this target to be a module library # \arg:GEM_MODULE (bool) defines this target to be a module library while also marking the target as a "Gem" via the GEM_MODULE property # \arg:HEADERONLY (bool) defines this target to be a header only library. A ${NAME}_HEADERS project will be created for the IDE # \arg:EXECUTABLE (bool) defines this target to be an executable # \arg:APPLICATION (bool) defines this target to be an application (executable that is not a console) -# \arg:UNKNOWN (bool) defines this target to be unknown. This is used when importing installed targets from Find files # \arg:IMPORTED (bool) defines this target to be imported. # \arg:NAMESPACE namespace declaration for this target. It will be used for IDE and dependencies # \arg:OUTPUT_NAME (optional) overrides the name of the output target. If not specified, the name will be used. @@ -76,7 +76,7 @@ define_property(TARGET PROPERTY GEM_MODULE # \arg:AUTOGEN_RULES a set of AutoGeneration rules to be passed to the AzAutoGen expansion system function(ly_add_target) - set(options STATIC SHARED MODULE GEM_MODULE HEADERONLY EXECUTABLE APPLICATION UNKNOWN IMPORTED AUTOMOC AUTOUIC AUTORCC NO_UNITY) + set(options STATIC SHARED MODULE GEM_STATIC GEM_MODULE HEADERONLY EXECUTABLE APPLICATION IMPORTED AUTOMOC AUTOUIC AUTORCC NO_UNITY) set(oneValueArgs NAME NAMESPACE OUTPUT_SUBDIRECTORY OUTPUT_NAME) set(multiValueArgs FILES_CMAKE GENERATED_FILES INCLUDE_DIRECTORIES COMPILE_DEFINITIONS BUILD_DEPENDENCIES RUNTIME_DEPENDENCIES PLATFORM_INCLUDE_FILES TARGET_PROPERTIES AUTOGEN_RULES) @@ -86,7 +86,7 @@ function(ly_add_target) if(NOT ly_add_target_NAME) message(FATAL_ERROR "You must provide a name for the target") endif() - if(NOT ly_add_target_IMPORTED) + if(NOT ly_add_target_IMPORTED AND NOT ly_add_target_HEADERONLY) if(NOT ly_add_target_FILES_CMAKE) message(FATAL_ERROR "You must provide a list of _files.cmake files for the target") endif() @@ -96,28 +96,36 @@ function(ly_add_target) if(ly_add_target_GEM_MODULE) set(ly_add_target_MODULE ${ly_add_target_GEM_MODULE}) endif() + # If the GEM_STATIC tag is passed mark the target as STATIC + if(ly_add_target_GEM_STATIC) + set(ly_add_target_STATIC ${ly_add_target_GEM_STATIC}) + endif() foreach(file_cmake ${ly_add_target_FILES_CMAKE}) ly_include_cmake_file_list(${file_cmake}) endforeach() - set(linking_options) - set(linking_count) + unset(linking_options) + unset(linking_count) + unset(target_type_options) if(ly_add_target_STATIC) set(linking_options STATIC) + set(target_type_options STATIC) set(linking_count "${linking_count}1") endif() if(ly_add_target_SHARED) set(linking_options SHARED) + set(target_type_options SHARED) set(linking_count "${linking_count}1") endif() if(ly_add_target_MODULE) set(linking_options ${PAL_LINKOPTION_MODULE}) + set(target_type_options ${PAL_LINKOPTION_MODULE}) set(linking_count "${linking_count}1") endif() - if(ly_add_target_HEADERONLY) set(linking_options INTERFACE) + set(target_type_options INTERFACE) set(linking_count "${linking_count}1") endif() if(ly_add_target_EXECUTABLE) @@ -128,12 +136,11 @@ function(ly_add_target) set(linking_options APPLICATION) set(linking_count "${linking_count}1") endif() - if(ly_add_target_UNKNOWN) - set(linking_options UNKNOWN) - set(linking_count "${linking_count}1") - endif() if(NOT ("${linking_count}" STREQUAL "1")) - message(FATAL_ERROR "More than one of the following options [STATIC | SHARED | MODULE | HEADERONLY | EXECUTABLE | APPLICATION | UNKNOWN] was specified and they are mutually exclusive") + message(FATAL_ERROR "More than one of the following options [STATIC | SHARED | MODULE | HEADERONLY | EXECUTABLE | APPLICATION ] was specified and they are mutually exclusive") + endif() + if(ly_add_target_IMPORTED) + list(APPEND target_type_options IMPORTED GLOBAL) endif() if(ly_add_target_NAMESPACE) @@ -144,29 +151,32 @@ function(ly_add_target) set(project_NAME ${ly_add_target_NAME}) if(ly_add_target_EXECUTABLE) - add_executable(${ly_add_target_NAME} + add_executable(${ly_add_target_NAME} + ${target_type_options} ${ALLFILES} ${ly_add_target_GENERATED_FILES} ) ly_apply_platform_properties(${ly_add_target_NAME}) + if(ly_add_target_IMPORTED) + set_target_properties(${ly_add_target_NAME} PROPERTIES LINKER_LANGUAGE CXX) + endif() elseif(ly_add_target_APPLICATION) - add_executable(${ly_add_target_NAME} + add_executable(${ly_add_target_NAME} + ${target_type_options} ${PAL_EXECUTABLE_APPLICATION_FLAG} ${ALLFILES} ${ly_add_target_GENERATED_FILES} ) ly_apply_platform_properties(${ly_add_target_NAME}) + if(ly_add_target_IMPORTED) + set_target_properties(${ly_add_target_NAME} PROPERTIES LINKER_LANGUAGE CXX) + endif() elseif(ly_add_target_HEADERONLY) add_library(${ly_add_target_NAME} - ${linking_options} + ${target_type_options} ${ALLFILES} ${ly_add_target_GENERATED_FILES} ) - elseif(ly_add_target_UNKNOWN) - add_library(${ly_add_target_NAME} - ${linking_options} - IMPORTED - ) else() add_library(${ly_add_target_NAME} - ${linking_options} + ${target_type_options} ${ALLFILES} ${ly_add_target_GENERATED_FILES} ) ly_apply_platform_properties(${ly_add_target_NAME}) @@ -199,12 +209,12 @@ function(ly_add_target) endif() - if(ly_add_target_GEM_MODULE) + if(ly_add_target_GEM_MODULE OR ly_add_target_GEM_STATIC) set_target_properties(${ly_add_target_NAME} PROPERTIES GEM_MODULE TRUE) endif() if (ly_add_target_INCLUDE_DIRECTORIES) - ly_target_include_directories(${ly_add_target_NAME} + target_include_directories(${ly_add_target_NAME} ${ly_add_target_INCLUDE_DIRECTORIES} ) endif() @@ -301,10 +311,19 @@ function(ly_add_target) endif() # Store the target so we can walk through all of them in LocationDependencies.cmake - set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGETS ${ly_add_target_NAME}) + set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGETS ${interface_name}) + + # Store the aliased target into a DIRECTORY property + set_property(DIRECTORY APPEND PROPERTY LY_DIRECTORY_TARGETS ${interface_name}) + # 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() set(runtime_dependencies_list SHARED MODULE EXECUTABLE APPLICATION) - if(linking_options IN_LIST runtime_dependencies_list) + if(NOT ly_add_target_IMPORTED AND linking_options IN_LIST runtime_dependencies_list) add_custom_command(TARGET ${ly_add_target_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/runtime_dependencies/$/${ly_add_target_NAME}.cmake @@ -332,22 +351,6 @@ function(ly_add_target) ) endif() - if(NOT ly_add_target_IMPORTED) - if(NOT ly_add_target_INSTALL_COMPONENT) - set(ly_add_target_INSTALL_COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT}) - endif() - - ly_install_target( - ${ly_add_target_NAME} - NAMESPACE ${ly_add_target_NAMESPACE} - INCLUDE_DIRECTORIES ${ly_add_target_INCLUDE_DIRECTORIES} - BUILD_DEPENDENCIES ${ly_add_target_BUILD_DEPENDENCIES} - RUNTIME_DEPENDENCIES ${ly_add_target_RUNTIME_DEPENDENCIES} - COMPILE_DEFINITIONS ${ly_add_target_COMPILE_DEFINITIONS} - COMPONENT ${ly_add_target_INSTALL_COMPONENT} - ) - endif() - endfunction() #! ly_target_link_libraries: wraps target_link_libraries handling also MODULE linkage. @@ -407,12 +410,10 @@ function(ly_delayed_target_link_libraries) endif() if(item_type STREQUAL MODULE_LIBRARY) - ly_target_include_directories(${target} ${visibility} $) + target_include_directories(${target} ${visibility} $) target_link_libraries(${target} ${visibility} $) target_compile_definitions(${target} ${visibility} $) target_compile_options(${target} ${visibility} $) - # Add it also as a manual dependency so runtime_dependencies walks it through - ly_add_dependencies(${target} ${item}) else() ly_parse_third_party_dependencies(${item}) target_link_libraries(${target} ${visibility} ${item}) @@ -508,7 +509,7 @@ endfunction() # Looks at the the following variables within the platform include file to set the equivalent target properties # LY_FILES_CMAKE -> extract list of files -> target_sources # LY_FILES -> target_source -# LY_INCLUDE_DIRECTORIES -> ly_target_include_directories +# LY_INCLUDE_DIRECTORIES -> target_include_directories # LY_COMPILE_DEFINITIONS -> target_compile_definitions # LY_COMPILE_OPTIONS -> target_compile_options # LY_LINK_OPTIONS -> target_link_options @@ -534,7 +535,11 @@ macro(ly_configure_target_platform_properties) message(FATAL_ERROR "The supplied PLATFORM_INCLUDE_FILE(${platform_include_file}) cannot be included.\ Parsing of target will halt") endif() - target_sources(${ly_add_target_NAME} PRIVATE ${platform_include_file}) + if(ly_add_target_HEADERONLY) + target_sources(${ly_add_target_NAME} INTERFACE ${platform_include_file}) + else() + target_sources(${ly_add_target_NAME} PRIVATE ${platform_include_file}) + endif() ly_source_groups_from_folders("${platform_include_file}") if(LY_FILES_CMAKE) @@ -550,7 +555,7 @@ macro(ly_configure_target_platform_properties) target_sources(${ly_add_target_NAME} PRIVATE ${LY_FILES}) endif() if (LY_INCLUDE_DIRECTORIES) - ly_target_include_directories(${ly_add_target_NAME} ${LY_INCLUDE_DIRECTORIES}) + target_include_directories(${ly_add_target_NAME} ${LY_INCLUDE_DIRECTORIES}) endif() if(LY_COMPILE_DEFINITIONS) target_compile_definitions(${ly_add_target_NAME} ${LY_COMPILE_DEFINITIONS}) @@ -653,42 +658,6 @@ function(ly_add_source_properties) endfunction() -function(ly_target_include_directories TARGET) - - # Add the includes to the build and install interface - set(reserved_keywords PRIVATE PUBLIC INTERFACE) - unset(last_keyword) - foreach(include ${ARGN}) - if(${include} IN_LIST reserved_keywords) - list(APPEND adapted_includes ${include}) - elseif(IS_ABSOLUTE ${include}) - list(APPEND adapted_includes - $ - ) - else() - string(GENEX_STRIP ${include} include_genex_expr) - if(include_genex_expr STREQUAL include) # only for cases where there are no generation expressions - # We will be installing the includes using the same directory structure used in our source tree. - # The INSTALL_INTERFACE path tells CMake the location of the includes relative to the install prefix. - # When the target is imported into an external project, cmake will find these includes at /include/ - # where is the location of the lumberyard install on disk. - file(REAL_PATH ${include} include_real) - file(RELATIVE_PATH install_dir ${CMAKE_SOURCE_DIR} ${include_real}) - list(APPEND adapted_includes - $ - $ - ) - else() - list(APPEND adapted_includes - ${include} - ) - endif() - endif() - endforeach() - target_include_directories(${TARGET} ${adapted_includes}) - -endfunction() - #! ly_project_add_subdirectory: calls add_subdirectory() if the project name is in the project list # @@ -719,3 +688,22 @@ function(ly_project_add_subdirectory project_name) endif() endif() endfunction() + +# given a target name, returns the "real" name of the target if its an alias. +# this function recursively de-aliases +function(ly_de_alias_target target_name output_variable_name) + # its not okay to call get_target_property on a non-existent target + if (NOT TARGET ${target_name}) + message(FATAL_ERROR "ly_de_alias_target called on non-existent target: ${target_name}") + endif() + + while(target_name) + set(de_aliased_target_name ${target_name}) + get_target_property(target_name ${target_name} ALIASED_TARGET) + endwhile() + + if(NOT de_aliased_target_name) + message(FATAL_ERROR "Empty de_aliased for ${target_name}") + endif() + set(${output_variable_name} ${de_aliased_target_name} PARENT_SCOPE) +endfunction() diff --git a/cmake/LyAutoGen.cmake b/cmake/LyAutoGen.cmake index 16a8a8de55..aa0e7f8d5a 100644 --- a/cmake/LyAutoGen.cmake +++ b/cmake/LyAutoGen.cmake @@ -26,7 +26,7 @@ function(ly_add_autogen) if(ly_add_autogen_AUTOGEN_RULES) set(AZCG_INPUTFILES ${ly_add_autogen_ALLFILES}) list(FILTER AZCG_INPUTFILES INCLUDE REGEX ".*\.(xml|json|jinja)$") - ly_target_include_directories(${ly_add_autogen_NAME} PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/Azcg/Generated") + target_include_directories(${ly_add_autogen_NAME} PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/Azcg/Generated") execute_process( COMMAND ${LY_PYTHON_CMD} "${LY_ROOT_FOLDER}/Code/Framework/AzAutoGen/AzAutoGen.py" "${CMAKE_BINARY_DIR}/Azcg/TemplateCache/" "${CMAKE_CURRENT_BINARY_DIR}/Azcg/Generated/" "${CMAKE_CURRENT_SOURCE_DIR}" "${AZCG_INPUTFILES}" "${ly_add_autogen_AUTOGEN_RULES}" "-n" OUTPUT_VARIABLE AUTOGEN_OUTPUTS diff --git a/cmake/Monolithic.cmake b/cmake/Monolithic.cmake index db45c182fd..dfd6816dde 100644 --- a/cmake/Monolithic.cmake +++ b/cmake/Monolithic.cmake @@ -14,7 +14,7 @@ set(LY_MONOLITHIC_GAME FALSE CACHE BOOL "Indicates if the game will be built mon if(LY_MONOLITHIC_GAME) add_compile_definitions(AZ_MONOLITHIC_BUILD) ly_set(PAL_TRAIT_MONOLITHIC_DRIVEN_LIBRARY_TYPE STATIC) - ly_set(PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE STATIC) + ly_set(PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE GEM_STATIC) # Disable targets that are not supported with monolithic ly_set(PAL_TRAIT_BUILD_HOST_TOOLS FALSE) ly_set(PAL_TRAIT_BUILD_HOST_GUI_TOOLS FALSE) diff --git a/cmake/O3DEJson.cmake b/cmake/O3DEJson.cmake new file mode 100644 index 0000000000..ab5f95bc8c --- /dev/null +++ b/cmake/O3DEJson.cmake @@ -0,0 +1,62 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +include_guard() + +set(LY_EXTERNAL_SUBDIRS "" CACHE STRING "List of subdirectories to recurse into when running cmake against the engine's CMakeLists.txt") + +#! read_json_external_subdirs +# Read the "external_subdirectories" array from a *.json file +# External subdirectories are any folders with CMakeLists.txt in them +# This could be regular subdirectories, Gems(contains an additional gem.json), +# Restricted folders(contains an additional restricted.json), etc... +# +# \arg:output_external_subdirs name of output variable to store external subdirectories into +# \arg:input_json_path path to the *.json file to load and read the external subdirectories from +# \return: external subdirectories as is from the json file. +function(read_json_external_subdirs output_external_subdirs input_json_path) + o3de_read_json_array(json_array ${input_json_path} "external_subdirectories") + set(${output_external_subdirs} ${json_array} PARENT_SCOPE) +endfunction() + +#! read_json_array +# Reads the a json array field into a cmake list variable +function(o3de_read_json_array read_output_array input_json_path array_key) + file(READ ${input_json_path} manifest_json_data) + string(JSON array_count ERROR_VARIABLE manifest_json_error + LENGTH ${manifest_json_data} ${array_key}) + if(manifest_json_error) + # There is no key, return + return() + endif() + + if(array_count GREATER 0) + math(EXPR array_range "${array_count}-1") + foreach(array_index RANGE ${array_range}) + string(JSON array_element ERROR_VARIABLE manifest_json_error + GET ${manifest_json_data} ${array_key} "${array_index}") + if(manifest_json_error) + message(FATAL_ERROR "Error reading field at index ${array_index} in \"${array_key}\" JSON array: ${manifest_json_error}") + endif() + list(APPEND array_elements ${array_element}) + endforeach() + endif() + set(${read_output_array} ${array_elements} PARENT_SCOPE) +endfunction() + +function(o3de_read_json_key output_value input_json_path key) + file(READ ${input_json_path} manifest_json_data) + string(JSON value ERROR_VARIABLE manifest_json_error GET ${manifest_json_data} ${key}) + if(manifest_json_error) + message(FATAL_ERROR "Error reading field at key ${key} in file \"${input_json_path}\" : ${manifest_json_error}") + endif() + set(${output_value} ${value} PARENT_SCOPE) +endfunction() diff --git a/cmake/OutputDirectory.cmake b/cmake/OutputDirectory.cmake index 9055802d39..5fe5c7a957 100644 --- a/cmake/OutputDirectory.cmake +++ b/cmake/OutputDirectory.cmake @@ -13,4 +13,8 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib CACHE PATH "Build directory for static libraries and import libraries") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin CACHE PATH "Build directory for shared libraries") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin CACHE PATH "Build directory for executables") -set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install CACHE PATH "Installation prefix") + +# We install outside of the binary dir because our install support muliple platforms to +# be installed together. We also have an exclusion rule in the AP that filters out the +# "build" folder which is a common binary dir +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install CACHE PATH "Installation prefix") diff --git a/cmake/PAL.cmake b/cmake/PAL.cmake index 7802ac8c58..dca54e4731 100644 --- a/cmake/PAL.cmake +++ b/cmake/PAL.cmake @@ -22,7 +22,105 @@ file(GLOB detection_files "cmake/Platform/*/PALDetection_*.cmake") foreach(detection_file ${detection_files}) include(${detection_file}) endforeach() -file(GLOB detection_files ${o3de_engine_restricted_path}/*/cmake/PALDetection_*.cmake) + + +#! o3de_restricted_id: Reads the "restricted" key from the o3de manifest +# +# \arg:o3de_json_file name of the o3de json file to read the "restricted_name" key from +# \arg:restricted returns the restricted association element from an o3de json, otherwise engine 'o3de' is assumed +# \arg:o3de_json_file name of the o3de json file +function(o3de_restricted_id o3de_json_file restricted) + file(READ ${o3de_json_file} json_data) + string(JSON restricted_entry ERROR_VARIABLE json_error GET ${json_data} "restricted_name") + if(json_error) + message(WARNING "Unable to read restricted from '${o3de_json_file}', error: ${json_error}") + endif() + if(restricted_entry) + set(${restricted} ${restricted_entry} PARENT_SCOPE) + endif() +endfunction() + +#! o3de_find_restricted_folder: +# +# \arg:restricted_path returns the path of the o3de restricted folder with name restricted_name +# \arg:restricted_name name of the restricted +function(o3de_find_restricted_folder restricted_name restricted_path) + # Read the restricted path from engine.json if one EXISTS + file(READ ${LY_ROOT_FOLDER}/engine.json engine_json_data) + string(JSON restricted_subdirs_count ERROR_VARIABLE engine_json_error LENGTH ${engine_json_data} "restricted") + if(restricted_subdirs_count GREATER 0) + string(JSON restricted_subdir ERROR_VARIABLE engine_json_error GET ${engine_json_data} "restricted" "0") + set(${restricted_path} ${restricted_subdir} PARENT_SCOPE) + return() + endif() + + + file(TO_CMAKE_PATH "$ENV{USERPROFILE}" home_directory) # Windows + if(NOT EXISTS ${home_directory}) + file(TO_CMAKE_PATH "$ENV{HOME}" home_directory) # Unix + if (NOT EXISTS ${home_directory}) + return() + endif() + endif() + + # Examine the o3de manifest file for the list of restricted directories + set(o3de_manifest_path ${home_directory}/.o3de/o3de_manifest.json) + if(EXISTS ${o3de_manifest_path}) + file(READ ${o3de_manifest_path} o3de_manifest_json_data) + string(JSON restricted_subdirs_count ERROR_VARIABLE engine_json_error LENGTH ${o3de_manifest_json_data} "restricted") + if(restricted_subdirs_count GREATER 0) + math(EXPR restricted_subdirs_range "${restricted_subdirs_count}-1") + foreach(restricted_subdir_index RANGE ${restricted_subdirs_range}) + string(JSON restricted_subdir ERROR_VARIABLE engine_json_error GET ${o3de_manifest_json_data} "restricted" "${restricted_subdir_index}") + list(APPEND restricted_subdirs ${restricted_subdir}) + endforeach() + endif() + endif() + # Iterate over the restricted directories from the manifest file + foreach(restricted_entry ${restricted_subdirs}) + set(restricted_json_file ${restricted_entry}/restricted.json) + file(READ ${restricted_json_file} restricted_json) + string(JSON this_restricted_name ERROR_VARIABLE json_error GET ${restricted_json} "restricted_name") + if(json_error) + message(WARNING "Unable to read restricted_name from '${restricted_json_file}', error: ${json_error}") + else() + if(this_restricted_name STREQUAL restricted_name) + set(${restricted_path} ${restricted_entry} PARENT_SCOPE) + return() + endif() + endif() + endforeach() +endfunction() + + +#! o3de_restricted_path: +# +# \arg:o3de_json_file json file to read restricted id from +# \arg:restricted_name name of the restricted object +function(o3de_restricted_path o3de_json_file restricted_path) + o3de_restricted_id(${o3de_json_file} restricted_name) + if(restricted_name) + o3de_find_restricted_folder(${restricted_name} restricted_folder) + if(restricted_folder) + set(${restricted_path} ${restricted_folder} PARENT_SCOPE) + endif() + endif() +endfunction() + +#! read_engine_restricted_path: Locates the restricted path within the engine from a json file +# +# \arg:output_restricted_path returns the path of the o3de restricted folder with name restricted_name +function(read_engine_restricted_path output_restricted_path) + # Set manifest path to path in the user home directory + set(manifest_path ${LY_ROOT_FOLDER}/engine.json) + if(EXISTS ${manifest_path}) + o3de_restricted_path(${manifest_path} output_restricted_path) + endif() +endfunction() + +read_engine_restricted_path(O3DE_ENGINE_RESTRICTED_PATH) + +file(GLOB detection_files ${O3DE_ENGINE_RESTRICTED_PATH}/*/cmake/PALDetection_*.cmake) foreach(detection_file ${detection_files}) include(${detection_file}) endforeach() @@ -37,8 +135,8 @@ ly_set(PAL_HOST_PLATFORM_NAME_LOWERCASE ${PAL_HOST_PLATFORM_NAME_LOWERCASE}) set(PAL_RESTRICTED_PLATFORMS) -string(LENGTH ${o3de_engine_restricted_path} engine_restricted_length) -file(GLOB pal_restricted_files ${o3de_engine_restricted_path}/*/cmake/PAL_*.cmake) +string(LENGTH "${O3DE_ENGINE_RESTRICTED_PATH}" engine_restricted_length) +file(GLOB pal_restricted_files ${O3DE_ENGINE_RESTRICTED_PATH}/*/cmake/PAL_*.cmake) foreach(pal_restricted_file ${pal_restricted_files}) string(FIND ${pal_restricted_file} "/cmake/PAL" end) if(${end} GREATER -1) @@ -109,18 +207,18 @@ function(ly_get_absolute_pal_filename out_name in_name) else() # The user has not supplied any path so we must assume it is the o3de engine restricted and o3de engine path # if the file is not in the o3de engine path then we cannot determine a PAL file for it - file(RELATIVE_PATH relative_path ${o3de_engine_path} ${full_name}) + file(RELATIVE_PATH relative_path ${LY_ROOT_FOLDER} ${full_name}) if (NOT (IS_ABSOLUTE relative_path OR relative_path MATCHES [[^(\.\./)+(.*)]])) if (NOT EXISTS ${full_name}) - string(REGEX MATCH "${o3de_engine_path}/(.*)/Platform/([^/]*)/?(.*)$" match ${full_name}) + string(REGEX MATCH "${LY_ROOT_FOLDER}/(.*)/Platform/([^/]*)/?(.*)$" match ${full_name}) if(NOT CMAKE_MATCH_1) - string(REGEX MATCH "${o3de_engine_path}/Platform/([^/]*)/?(.*)$" match ${full_name}) - set(full_name ${o3de_engine_restricted_path}/${CMAKE_MATCH_1}) + string(REGEX MATCH "${LY_ROOT_FOLDER}/Platform/([^/]*)/?(.*)$" match ${full_name}) + set(full_name ${O3DE_ENGINE_RESTRICTED_PATH}/${CMAKE_MATCH_1}) if(CMAKE_MATCH_2) string(APPEND full_name "/" ${CMAKE_MATCH_2}) endif() elseif("${CMAKE_MATCH_2}" IN_LIST PAL_RESTRICTED_PLATFORMS) - set(full_name ${o3de_engine_restricted_path}/${CMAKE_MATCH_2}/${CMAKE_MATCH_1}) + set(full_name ${O3DE_ENGINE_RESTRICTED_PATH}/${CMAKE_MATCH_2}/${CMAKE_MATCH_1}) if(CMAKE_MATCH_3) string(APPEND full_name "/" ${CMAKE_MATCH_3}) endif() @@ -149,25 +247,3 @@ set(LY_DISABLE_TEST_MODULES FALSE CACHE BOOL "Option to forcibly disable the inc if(LY_DISABLE_TEST_MODULES) ly_set(PAL_TRAIT_BUILD_TESTS_SUPPORTED FALSE) endif() - -################################################################################ -# Add each restricted platform in the engines restricted folder -# If the enabled restricted platform does not have a folder add one. -# If the restricted platform folder does not have a CMakeLists.txt, create one -# so the add_subdirectory on the external folder does not fail. -################################################################################ -function(o3de_add_engine_restricted_platform_external_subdirs) - foreach(restricted_platform ${PAL_RESTRICTED_PLATFORMS}) - if(restricted_platform IN_LIST enabled_platforms) - set(o3de_engine_restricted_platform_folder ${o3de_engine_restricted_path}/${restricted_platform}) - if(NOT EXISTS ${o3de_engine_restricted_platform_folder}) - file(MAKE_DIRECTORY ${o3de_engine_restricted_platform_folder}) - endif() - set(o3de_engine_restricted_platform_folder_cmakelists ${o3de_engine_restricted_platform_folder}/CMakeLists.txt) - if(NOT EXISTS ${o3de_engine_restricted_platform_folder_cmakelists}) - file(TOUCH ${o3de_engine_restricted_platform_folder_cmakelists}) - endif() - list(APPEND LY_EXTERNAL_SUBDIRS ${o3de_engine_restricted_platform_folder}) - endif() - endforeach() -endfunction() diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index fbeffa94eb..84bad13687 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -17,6 +17,8 @@ endif() set(LY_INSTALLER_DOWNLOAD_URL "" CACHE STRING "URL embedded into the installer to download additional artifacts") set(LY_INSTALLER_LICENSE_URL "" CACHE STRING "Optionally embed a link to the license instead of raw text") +set(CPACK_DESIRED_CMAKE_VERSION 3.20.2) + # set all common cpack variable overrides first so they can be accessible via configure_file # when the platform specific settings are applied below. additionally, any variable with # the "CPACK_" prefix will automatically be cached for use in any phase of cpack namely @@ -38,6 +40,7 @@ set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_VENDOR}/${CPACK_PACKAGE_VER # neither of the SOURCE_DIR variables equate to anything during execution of pre/post build scripts set(CPACK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +set(CPACK_BINARY_DIR ${CMAKE_BINARY_DIR}/_CPack) # to match other CPack out dirs # attempt to apply platform specific settings ly_get_absolute_pal_filename(pal_dir ${CPACK_SOURCE_DIR}/Platform/${PAL_HOST_PLATFORM_NAME}) @@ -48,6 +51,53 @@ if(NOT CPACK_GENERATOR) return() endif() +# pull down the desired copy of CMake so it can be included in the package +if(NOT (CPACK_CMAKE_PACKAGE_FILE AND CPACK_CMAKE_PACKAGE_HASH)) + message(FATAL_ERROR + "Packaging is missing one or more following properties required to include CMake: " + " CPACK_CMAKE_PACKAGE_FILE, CPACK_CMAKE_PACKAGE_HASH") +endif() + +set(_cmake_package_dest ${CPACK_BINARY_DIR}/${CPACK_CMAKE_PACKAGE_FILE}) + +string(REPLACE "." ";" _version_componets "${CPACK_DESIRED_CMAKE_VERSION}") +list(GET _version_componets 0 _major_version) +list(GET _version_componets 1 _minor_version) + +set(_url_version_tag "v${_major_version}.${_minor_version}") +set(_package_url "https://cmake.org/files/${_url_version_tag}/${CPACK_CMAKE_PACKAGE_FILE}") + +message(STATUS "Ensuring CMake ${CPACK_DESIRED_CMAKE_VERSION} is available for packaging...") +download_file( + URL ${_package_url} + TARGET_FILE ${_cmake_package_dest} + EXPECTED_HASH ${CPACK_CMAKE_PACKAGE_HASH} + RESULTS _results +) +list(GET _results 0 _status_code) + +if (${_status_code} EQUAL 0 AND EXISTS ${_cmake_package_dest}) + message(STATUS "-> Package found and verified!") +else() + file(REMOVE ${_cmake_package_dest}) + list(REMOVE_AT _results 0) + + set(_error_message "An error occurred, code ${_status_code}. URL ${_package_url} - ${_results}") + + if(${_status_code} EQUAL 1) + string(APPEND _error_message + " Please double check the CPACK_CMAKE_PACKAGE_FILE and " + "CPACK_CMAKE_PACKAGE_HASH properties before trying again.") + endif() + + message(FATAL_ERROR ${_error_message}) +endif() + +install(FILES ${_cmake_package_dest} + DESTINATION ./Tools/Redistributables/CMake + COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} +) + # IMPORTANT: required to be included AFTER setting all property overrides include(CPack REQUIRED) diff --git a/cmake/Platform/Android/PAL_android.cmake b/cmake/Platform/Android/PAL_android.cmake index bb4ac5f32b..5f35767f98 100644 --- a/cmake/Platform/Android/PAL_android.cmake +++ b/cmake/Platform/Android/PAL_android.cmake @@ -35,7 +35,7 @@ else() endif() # Set the default asset type for deployment -set(LY_ASSET_DEPLOY_ASSET_TYPE "es3" CACHE STRING "Set the asset type for deployment.") +set(LY_ASSET_DEPLOY_ASSET_TYPE "android" CACHE STRING "Set the asset type for deployment.") # Set the python cmd tool if(PAL_HOST_PLATFORM_NAME_LOWERCASE STREQUAL "windows") diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 8fe2fe2c1c..b18aed6fb4 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -11,52 +11,57 @@ set(CMAKE_INSTALL_MESSAGE NEVER) # Simplify messages to reduce output noise -ly_set(LY_DEFAULT_INSTALL_COMPONENT "Core") +ly_set(LY_DEFAULT_INSTALL_COMPONENT Core) -#! ly_install_target: registers the target to be installed by cmake install. -# -# \arg:NAME name of the target -# \arg:COMPONENT the grouping string of the target used for splitting up the install -# into smaller packages. -# All other parameters are forwarded to ly_generate_target_find_file -function(ly_install_target ly_install_target_NAME) +file(RELATIVE_PATH runtime_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +file(RELATIVE_PATH library_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) +set(install_output_folder "${CMAKE_INSTALL_PREFIX}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$") - set(options) - set(oneValueArgs NAMESPACE COMPONENT) - set(multiValueArgs INCLUDE_DIRECTORIES BUILD_DEPENDENCIES RUNTIME_DEPENDENCIES COMPILE_DEFINITIONS) - cmake_parse_arguments(ly_install_target "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) +#! 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) + # De-alias target name + ly_de_alias_target(${ALIAS_TARGET_NAME} TARGET_NAME) - # All include directories marked PUBLIC or INTERFACE will be installed + # All include directories marked PUBLIC or INTERFACE will be installed. We dont use PUBLIC_HEADER because in order to do that + # we need to set the PUBLIC_HEADER property of the target for all the headers we are exporting. After doing that, installing the + # headers end up in one folder instead of duplicating the folder structure of the public/interface include directory. + # Instead, we install them with install(DIRECTORY) set(include_location "include") - get_target_property(include_directories ${ly_install_target_NAME} INTERFACE_INCLUDE_DIRECTORIES) - + get_target_property(include_directories ${TARGET_NAME} INTERFACE_INCLUDE_DIRECTORIES) if (include_directories) - set_target_properties(${ly_install_target_NAME} PROPERTIES PUBLIC_HEADER "${include_directories}") - # The include directories are specified relative to the CMakeLists.txt file that adds the target. - # We need to install the includes relative to our source tree root because that's where INSTALL_INTERFACE - # will point CMake when it looks for headers - file(RELATIVE_PATH relative_path ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) - string(APPEND include_location "/${relative_path}") + unset(public_headers) + foreach(include_directory ${include_directories}) + string(GENEX_STRIP ${include_directory} include_genex_expr) + if(include_genex_expr STREQUAL include_directory) # only for cases where there are no generation expressions + unset(current_public_headers) + install(DIRECTORY ${include_directory} + DESTINATION ${include_location}/${target_source_dir} + COMPONENT ${ly_install_target_COMPONENT} + FILES_MATCHING + PATTERN *.h + PATTERN *.hpp + PATTERN *.inl + ) + endif() + endforeach() endif() # Get the output folders, archive is always the same, but runtime/library can be in subfolders defined per target file(RELATIVE_PATH archive_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}) - get_target_property(target_runtime_output_directory ${ly_install_target_NAME} RUNTIME_OUTPUT_DIRECTORY) + get_target_property(target_runtime_output_directory ${TARGET_NAME} RUNTIME_OUTPUT_DIRECTORY) if(target_runtime_output_directory) file(RELATIVE_PATH target_runtime_output_subdirectory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${target_runtime_output_directory}) endif() - file(RELATIVE_PATH runtime_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - get_target_property(target_library_output_directory ${ly_install_target_NAME} LIBRARY_OUTPUT_DIRECTORY) + get_target_property(target_library_output_directory ${TARGET_NAME} LIBRARY_OUTPUT_DIRECTORY) if(target_library_output_directory) file(RELATIVE_PATH target_library_output_subdirectory ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${target_library_output_directory}) endif() - file(RELATIVE_PATH library_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) install( - TARGETS ${ly_install_target_NAME} + TARGETS ${TARGET_NAME} ARCHIVE DESTINATION ${archive_output_directory}/${PAL_PLATFORM_NAME}/$ COMPONENT ${ly_install_target_COMPONENT} @@ -66,151 +71,184 @@ function(ly_install_target ly_install_target_NAME) RUNTIME DESTINATION ${runtime_output_directory}/${PAL_PLATFORM_NAME}/$/${target_runtime_output_subdirectory} COMPONENT ${ly_install_target_COMPONENT} - PUBLIC_HEADER - DESTINATION ${include_location} - COMPONENT ${ly_install_target_COMPONENT} - ) - - ly_generate_target_find_file( - NAME ${ly_install_target_NAME} - ${ARGN} - ) - ly_generate_target_config_file(${ly_install_target_NAME}) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${ly_install_target_NAME}_$.cmake" - DESTINATION cmake_autogen/${ly_install_target_NAME} - COMPONENT ${ly_install_target_COMPONENT} - ) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/Find${ly_install_target_NAME}.cmake" - DESTINATION cmake - COMPONENT ${ly_install_target_COMPONENT} ) -endfunction() - + # CMakeLists.txt file + string(REGEX MATCH "(.*)::(.*)$" match ${ALIAS_TARGET_NAME}) + if(match) + set(NAMESPACE_PLACEHOLDER "NAMESPACE ${CMAKE_MATCH_1}") + set(NAME_PLACEHOLDER ${CMAKE_MATCH_2}) + else() + set(NAMESPACE_PLACEHOLDER "") + set(NAME_PLACEHOLDER ${TARGET_NAME}) + endif() -#! ly_generate_target_find_file: generates the Find${target}.cmake file which is used when importing installed packages. -# -# \arg:NAME name of the target -# \arg:NAMESPACE namespace declaration for this target. It will be used for IDE and dependencies -# \arg:INCLUDE_DIRECTORIES paths to the include directories -# \arg:BUILD_DEPENDENCIES list of interfaces this target depends on (could be a compilation dependency -# if the dependency is only exposing an include path, or could be a linking -# dependency is exposing a lib) -# \arg:RUNTIME_DEPENDENCIES list of dependencies this target depends on at runtime -# \arg:COMPILE_DEFINITIONS list of compilation definitions this target will use to compile -function(ly_generate_target_find_file) - - set(options) - set(oneValueArgs NAME NAMESPACE) - set(multiValueArgs INCLUDE_DIRECTORIES COMPILE_DEFINITIONS BUILD_DEPENDENCIES RUNTIME_DEPENDENCIES) - cmake_parse_arguments(ly_generate_target_find_file "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - set(NAME_PLACEHOLDER ${ly_generate_target_find_file_NAME}) - unset(NAMESPACE_PLACEHOLDER) - unset(COMPILE_DEFINITIONS_PLACEHOLDER) - unset(include_directories_interface_props) - unset(INCLUDE_DIRECTORIES_PLACEHOLDER) - set(RUNTIME_DEPENDENCIES_PLACEHOLDER ${ly_generate_target_find_file_RUNTIME_DEPENDENCIES}) - - # These targets will be imported. We will expose PUBLIC and INTERFACE properties as INTERFACE properties since - # only INTERFACE properties can be exposed on imported targets - ly_strip_private_properties(COMPILE_DEFINITIONS_PLACEHOLDER ${ly_generate_target_find_file_COMPILE_DEFINITIONS}) - ly_strip_private_properties(include_directories_interface_props ${ly_generate_target_find_file_INCLUDE_DIRECTORIES}) - ly_strip_private_properties(BUILD_DEPENDENCIES_PLACEHOLDER ${ly_generate_target_find_file_BUILD_DEPENDENCIES}) - - if(ly_generate_target_find_file_NAMESPACE) - set(NAMESPACE_PLACEHOLDER "NAMESPACE ${ly_generate_target_find_file_NAMESPACE}") + set(TARGET_TYPE_PLACEHOLDER "") + get_target_property(target_type ${NAME_PLACEHOLDER} TYPE) + # Remove the _LIBRARY since we dont need to pass that to ly_add_targets + string(REPLACE "_LIBRARY" "" TARGET_TYPE_PLACEHOLDER ${target_type}) + # For HEADER_ONLY libs we end up generating "INTERFACE" libraries, need to specify HEADERONLY instead + string(REPLACE "INTERFACE" "HEADERONLY" TARGET_TYPE_PLACEHOLDER ${TARGET_TYPE_PLACEHOLDER}) + if(TARGET_TYPE_PLACEHOLDER STREQUAL "MODULE") + get_target_property(gem_module ${NAME_PLACEHOLDER} GEM_MODULE) + if(gem_module) + set(TARGET_TYPE_PLACEHOLDER "GEM_MODULE") + endif() endif() - string(REPLACE ";" "\n" COMPILE_DEFINITIONS_PLACEHOLDER "${COMPILE_DEFINITIONS_PLACEHOLDER}") + get_target_property(COMPILE_DEFINITIONS_PLACEHOLDER ${TARGET_NAME} INTERFACE_COMPILE_DEFINITIONS) + if(COMPILE_DEFINITIONS_PLACEHOLDER) + string(REPLACE ";" "\n" COMPILE_DEFINITIONS_PLACEHOLDER "${COMPILE_DEFINITIONS_PLACEHOLDER}") + else() + unset(COMPILE_DEFINITIONS_PLACEHOLDER) + endif() # Includes need additional processing to add the install root - foreach(include ${include_directories_interface_props}) - set(installed_include_prefix "\${LY_ROOT_FOLDER}/include/") - file(RELATIVE_PATH relative_path ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${include}) - list(APPEND INCLUDE_DIRECTORIES_PLACEHOLDER "include/${relative_path}") - endforeach() - string(REPLACE ";" "\n" INCLUDE_DIRECTORIES_PLACEHOLDER "${INCLUDE_DIRECTORIES_PLACEHOLDER}") - - string(REPLACE ";" "\n" BUILD_DEPENDENCIES_PLACEHOLDER "${BUILD_DEPENDENCIES_PLACEHOLDER}") - string(REPLACE ";" "\n" RUNTIME_DEPENDENCIES_PLACEHOLDER "${RUNTIME_DEPENDENCIES_PLACEHOLDER}") - - configure_file(${LY_ROOT_FOLDER}/cmake/FindTarget.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/Find${ly_generate_target_find_file_NAME}.cmake @ONLY) - -endfunction() - + if(include_directories) + foreach(include ${include_directories}) + string(GENEX_STRIP ${include} include_genex_expr) + if(include_genex_expr STREQUAL include) # only for cases where there are no generation expressions + file(RELATIVE_PATH relative_include ${absolute_target_source_dir} ${include}) + string(APPEND INCLUDE_DIRECTORIES_PLACEHOLDER "\${LY_ROOT_FOLDER}/include/${target_source_dir}/${relative_include}\n") + endif() + endforeach() + endif() -#! ly_generate_target_config_file: generates the ${target}_$.cmake files for a target -# -# The generated file will set the location of the target binary per configuration -# These per config files will be included by the target's find file to set the location of the binary/ -# \arg:NAME name of the target -function(ly_generate_target_config_file NAME) + get_target_property(RUNTIME_DEPENDENCIES_PLACEHOLDER ${TARGET_NAME} MANUALLY_ADDED_DEPENDENCIES) + if(RUNTIME_DEPENDENCIES_PLACEHOLDER) # not found properties return the name of the variable with a "-NOTFOUND" at the end, here we set it to empty if not found + string(REPLACE ";" "\n" RUNTIME_DEPENDENCIES_PLACEHOLDER "${RUNTIME_DEPENDENCIES_PLACEHOLDER}") + else() + unset(RUNTIME_DEPENDENCIES_PLACEHOLDER) + endif() - get_target_property(target_type ${NAME} TYPE) + get_target_property(inteface_build_dependencies_props ${TARGET_NAME} INTERFACE_LINK_LIBRARIES) + unset(INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER) + if(inteface_build_dependencies_props) + foreach(build_dependency ${inteface_build_dependencies_props}) + # Skip wrapping produced when targets are not created in the same directory + if(NOT ${build_dependency} MATCHES "^::@") + list(APPEND INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${build_dependency}") + endif() + endforeach() + endif() + # We also need to pass the private link libraries since we will use that to generate the runtime dependencies + get_target_property(private_build_dependencies_props ${TARGET_NAME} LINK_LIBRARIES) + if(private_build_dependencies_props) + foreach(build_dependency ${private_build_dependencies_props}) + # Skip wrapping produced when targets are not created in the same directory + if(NOT ${build_dependency} MATCHES "^::@") + list(APPEND INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${build_dependency}") + endif() + endforeach() + endif() + list(REMOVE_DUPLICATES INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER) + string(REPLACE ";" "\n" INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER}") + # Config file set(target_file_contents "# Generated by O3DE install\n\n") if(NOT target_type STREQUAL INTERFACE_LIBRARY) unset(target_location) set(runtime_types EXECUTABLE APPLICATION) if(target_type IN_LIST runtime_types) - string(APPEND target_location "\"\${LY_ROOT_FOLDER}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$/${target_runtime_output_subdirectory}/$\"") + set(target_location "\${LY_ROOT_FOLDER}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$/${target_runtime_output_subdirectory}/$") elseif(target_type STREQUAL MODULE_LIBRARY) - string(APPEND target_location "\"\${LY_ROOT_FOLDER}/${library_output_directory}/${PAL_PLATFORM_NAME}/$/${target_library_output_subdirectory}/$\"") + set(target_location "\${LY_ROOT_FOLDER}/${library_output_directory}/${PAL_PLATFORM_NAME}/$/${target_library_output_subdirectory}/$") elseif(target_type STREQUAL SHARED_LIBRARY) - string(APPEND target_location "\"\${LY_ROOT_FOLDER}/${archive_output_directory}/${PAL_PLATFORM_NAME}/$/$\"") - string(APPEND target_file_contents "ly_add_dependencies(${NAME} \"\${LY_ROOT_FOLDER}/${library_output_directory}/${PAL_PLATFORM_NAME}/$/${target_library_output_subdirectory}/$\")\n") + string(APPEND target_file_contents "set_property(TARGET ${TARGET_NAME} PROPERTY IMPORTED_IMPLIB_$ \"\${LY_ROOT_FOLDER}/${archive_output_directory}/${PAL_PLATFORM_NAME}/$/$\")\n") + set(target_location "\${LY_ROOT_FOLDER}/${library_output_directory}/${PAL_PLATFORM_NAME}/$/${target_library_output_subdirectory}/$") else() # STATIC_LIBRARY, OBJECT_LIBRARY, INTERFACE_LIBRARY - string(APPEND target_location "\"\${LY_ROOT_FOLDER}/${archive_output_directory}/${PAL_PLATFORM_NAME}/$/$\"") + set(target_location "\${LY_ROOT_FOLDER}/${archive_output_directory}/${PAL_PLATFORM_NAME}/$/$") endif() - string(APPEND target_file_contents -"set(target_location ${target_location}) -set_target_properties(${NAME} - PROPERTIES - $<$:IMPORTED_LOCATION \"\${target_location}\"> - IMPORTED_LOCATION_$> \"\${target_location}\" + if(target_location) + string(APPEND target_file_contents +"set_property(TARGET ${TARGET_NAME} + APPEND_STRING PROPERTY IMPORTED_LOCATION + $<$$:${target_location}$ +) +set_property(TARGET ${TARGET_NAME} + PROPERTY IMPORTED_LOCATION_$> + ${target_location} ) -if(EXISTS \"\${target_location}\") - set(${NAME}_$_FOUND TRUE) -else() - set(${NAME}_$_FOUND FALSE) -endif() ") + endif() endif() - file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${NAME}_$.cmake" CONTENT "${target_file_contents}") + file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" CONTENT "${target_file_contents}") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" + DESTINATION ${target_source_dir} + COMPONENT ${ly_install_target_COMPONENT} + ) + # Since a CMakeLists.txt could contain multiple targets, we generate it in a folder per target + file(READ ${LY_ROOT_FOLDER}/cmake/install/InstalledTarget.in target_cmakelists_template) + string(CONFIGURE ${target_cmakelists_template} output_cmakelists_data @ONLY) + set(${OUTPUT_CONFIGURED_TARGET} ${output_cmakelists_data} PARENT_SCOPE) endfunction() +#! ly_setup_subdirectories: setups all targets on a per directory basis +function(ly_setup_subdirectories) + get_property(all_subdirectories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES) + foreach(target IN LISTS all_subdirectories) + ly_setup_subdirectory(${target}) + endforeach() +endfunction() -#! ly_strip_private_properties: strips private properties since we're exporting an interface target -# -# \arg:INTERFACE_PROPERTIES list of interface properties to be returned -function(ly_strip_private_properties INTERFACE_PROPERTIES) - set(reserved_keywords PRIVATE PUBLIC INTERFACE) - unset(last_keyword) - unset(stripped_props) - foreach(prop ${ARGN}) - if(${prop} IN_LIST reserved_keywords) - set(last_keyword ${prop}) - else() - if (NOT last_keyword STREQUAL "PRIVATE") - list(APPEND stripped_props ${prop}) - endif() - endif() + +#! ly_setup_subdirectory: setup all targets in the subdirectory +function(ly_setup_subdirectory absolute_target_source_dir) + + # The builtin BUILDSYSTEM_TARGETS property isn't being used here as that returns the de-alised + # TARGET and we need the alias namespace for recreating the CMakeLists.txt in the install layout + get_property(ALIAS_TARGETS_NAME DIRECTORY ${absolute_target_source_dir} PROPERTY LY_DIRECTORY_TARGETS) + file(RELATIVE_PATH target_source_dir ${LY_ROOT_FOLDER} ${absolute_target_source_dir}) + foreach(ALIAS_TARGET_NAME IN LISTS ALIAS_TARGETS_NAME) + ly_setup_target(configured_target ${ALIAS_TARGET_NAME}) + string(APPEND all_configured_targets "${configured_target}") endforeach() - set(${INTERFACE_PROPERTIES} ${stripped_props} PARENT_SCOPE) -endfunction() + # Replicate the ly_create_alias() calls based on the SOURCE_DIR for each target that generates an installed CMakeLists.txt + string(JOIN "\n" create_alias_template + "if(NOT TARGET @ALIAS_NAME@)" + " ly_create_alias(NAME @ALIAS_NAME@ NAMESPACE @ALIAS_NAMESPACE@ TARGETS @ALIAS_TARGETS@)" + "endif()" + "" + ) + get_property(create_alias_commands_arg_list DIRECTORY ${absolute_target_source_dir} PROPERTY LY_CREATE_ALIAS_ARGUMENTS) + foreach(create_alias_single_command_arg_list ${create_alias_commands_arg_list}) + # Split the ly_create_alias arguments back out based on commas + string(REPLACE "," ";" create_alias_single_command_arg_list "${create_alias_single_command_arg_list}") + list(POP_FRONT create_alias_single_command_arg_list ALIAS_NAME) + list(POP_FRONT create_alias_single_command_arg_list ALIAS_NAMESPACE) + # The rest of the list are the target dependencies + set(ALIAS_TARGETS ${create_alias_single_command_arg_list}) + string(CONFIGURE "${create_alias_template}" create_alias_command @ONLY) + string(APPEND CREATE_ALIASES_PLACEHOLDER ${create_alias_command}) + endforeach() + file(READ ${LY_ROOT_FOLDER}/cmake/install/Copyright.in cmake_copyright_comment) + # Write out all the agreegated ly_add_target function calls and the final ly_create_alias() calls to the target CMakeList.txt + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/CMakeLists.txt + "${cmake_copyright_comment}" + "${all_configured_targets}" + "\n" + "${CREATE_ALIASES_PLACEHOLDER}" + ) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/CMakeLists.txt" + DESTINATION ${target_source_dir} + COMPONENT ${ly_install_target_COMPONENT} + ) + +endfunction() #! ly_setup_o3de_install: orchestrates the installation of the different parts. This is the entry point from the root CMakeLists.txt function(ly_setup_o3de_install) + ly_setup_subdirectories() ly_setup_cmake_install() ly_setup_target_generator() + ly_setup_runtime_dependencies() ly_setup_others() endfunction() @@ -218,16 +256,35 @@ endfunction() #! ly_setup_cmake_install: install the "cmake" folder function(ly_setup_cmake_install) - install(DIRECTORY "${CMAKE_SOURCE_DIR}/cmake" + install(DIRECTORY "${LY_ROOT_FOLDER}/cmake" DESTINATION . COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} + PATTERN "__pycache__" EXCLUDE REGEX "Findo3de.cmake" EXCLUDE REGEX "Platform\/.*\/BuiltInPackages_.*\.cmake" EXCLUDE ) + + # Transform the LY_EXTERNAL_SUBDIRS list into a json array + set(indent " ") + foreach(external_subdir ${LY_EXTERNAL_SUBDIRS}) + file(RELATIVE_PATH engine_rel_external_subdir ${LY_ROOT_FOLDER} ${external_subdir}) + list(APPEND relative_external_subdirs "\"${engine_rel_external_subdir}\"") + endforeach() + list(JOIN relative_external_subdirs ",\n${indent}" LY_INSTALL_EXTERNAL_SUBDIRS) + + # Read the "templates" key from the source engine.json + o3de_read_json_array(engine_templates ${LY_ROOT_FOLDER}/engine.json "templates") + foreach(template_path ${engine_templates}) + list(APPEND relative_templates "\"${template_path}\"") + endforeach() + list(JOIN relative_templates ",\n${indent}" LY_INSTALL_TEMPLATES) + + configure_file(${LY_ROOT_FOLDER}/cmake/install/engine.json.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/engine.json @ONLY) + install( FILES - "${CMAKE_SOURCE_DIR}/CMakeLists.txt" - "${CMAKE_SOURCE_DIR}/engine.json" + "${LY_ROOT_FOLDER}/CMakeLists.txt" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/engine.json" DESTINATION . COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) @@ -247,14 +304,16 @@ function(ly_setup_cmake_install) # Findo3de.cmake file: we generate a different Findo3de.camke file than the one we have in cmake. This one is going to expose all # targets that are pre-built - get_property(all_targets GLOBAL PROPERTY LY_ALL_TARGETS) unset(FIND_PACKAGES_PLACEHOLDER) - foreach(target IN LISTS all_targets) - string(APPEND FIND_PACKAGES_PLACEHOLDER " find_package(${target})\n") - endforeach() - configure_file(${LY_ROOT_FOLDER}/cmake/Findo3de.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake @ONLY) + # Add to the FIND_PACKAGES_PLACEHOLDER all directories in which ly_add_target were called in + get_property(all_subdirectories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES) + foreach(target_subdirectory IN LISTS all_subdirectories) + file(RELATIVE_PATH target_source_dir_relative ${LY_ROOT_FOLDER} ${target_subdirectory}) + string(APPEND FIND_PACKAGES_PLACEHOLDER " add_subdirectory(${target_source_dir_relative})\n") + endforeach() + configure_file(${LY_ROOT_FOLDER}/cmake/install/Findo3de.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake" DESTINATION cmake COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} @@ -282,6 +341,74 @@ function(ly_setup_cmake_install) endfunction() +#! ly_setup_runtime_dependencies: install runtime dependencies +function(ly_setup_runtime_dependencies) + + # Common functions used by the bellow code + install(CODE +"function(ly_deploy_qt_install target_output) + execute_process(COMMAND \"${WINDEPLOYQT_EXECUTABLE}\" --verbose 0 --no-compiler-runtime \"\${target_output}\" ERROR_VARIABLE deploy_error RESULT_VARIABLE deploy_result) + if (NOT \${deploy_result} EQUAL 0) + if(NOT deploy_error MATCHES \"does not seem to be a Qt executable\" ) + message(SEND_ERROR \"Deploying qt for \${target_output} returned \${deploy_result}: \${deploy_error}\") + endif() + endif() +endfunction() + +function(ly_copy source_file target_directory) + file(COPY \"\${source_file}\" DESTINATION \"\${target_directory}\" FILE_PERMISSIONS ${LY_COPY_PERMISSIONS}) +endfunction()" + COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} + ) + + unset(runtime_commands) + 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() + + get_target_property(target_runtime_output_directory ${target} RUNTIME_OUTPUT_DIRECTORY) + if(target_runtime_output_directory) + file(RELATIVE_PATH target_runtime_output_subdirectory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${target_runtime_output_directory}) + endif() + + # Qt + get_property(has_qt_dependency GLOBAL PROPERTY LY_DETECT_QT_DEPENDENCY_${target}) + if(has_qt_dependency) + # Qt deploy needs to be done after the binary is copied to the output, so we do a install(CODE) which effectively + # puts it as a postbuild step of the "install" target. Binaries are copied at that point. + if(NOT EXISTS ${WINDEPLOYQT_EXECUTABLE}) + message(FATAL_ERROR "Qt deploy executable not found: ${WINDEPLOYQT_EXECUTABLE}") + endif() + set(target_output "${install_output_folder}/${target_runtime_output_subdirectory}/$") + list(APPEND runtime_commands "ly_deploy_qt_install(\"${target_output}\")\n") + endif() + + # runtime dependencies that need to be copied to the output + set(target_file_dir "${install_output_folder}/${target_runtime_output_subdirectory}") + ly_get_runtime_dependencies(runtime_dependencies ${target}) + foreach(runtime_dependency ${runtime_dependencies}) + unset(runtime_command) + ly_get_runtime_dependency_command(runtime_command ${runtime_dependency}) + string(CONFIGURE "${runtime_command}" runtime_command @ONLY) + list(APPEND runtime_commands ${runtime_command}) + endforeach() + + endforeach() + + list(REMOVE_DUPLICATES runtime_commands) + list(JOIN runtime_commands " " runtime_commands_str) # the spaces are just to see the right identation in the cmake_install.cmake file + install(CODE "${runtime_commands_str}" + COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} + ) + +endfunction() + #! ly_setup_others: install directories required by the engine function(ly_setup_others) @@ -294,15 +421,16 @@ function(ly_setup_others) set(install_path .) endif() - install(DIRECTORY "${CMAKE_SOURCE_DIR}/${dir}" + install(DIRECTORY "${LY_ROOT_FOLDER}/${dir}" DESTINATION ${install_path} COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} + PATTERN "__pycache__" EXCLUDE ) endforeach() # Scripts - file(GLOB o3de_scripts "${CMAKE_SOURCE_DIR}/scripts/o3de.*") + file(GLOB o3de_scripts "${LY_ROOT_FOLDER}/scripts/o3de.*") install(FILES ${o3de_scripts} DESTINATION ./scripts @@ -310,8 +438,9 @@ function(ly_setup_others) ) install(DIRECTORY - ${CMAKE_SOURCE_DIR}/scripts/bundler - ${CMAKE_SOURCE_DIR}/scripts/project_manager + ${LY_ROOT_FOLDER}/scripts/bundler + ${LY_ROOT_FOLDER}/scripts/project_manager + ${LY_ROOT_FOLDER}/scripts/o3de DESTINATION ./scripts COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} PATTERN "__pycache__" EXCLUDE @@ -319,7 +448,7 @@ function(ly_setup_others) PATTERN "tests" EXCLUDE ) - install(DIRECTORY "${CMAKE_SOURCE_DIR}/python" + install(DIRECTORY "${LY_ROOT_FOLDER}/python" DESTINATION . COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} REGEX "downloaded_packages" EXCLUDE @@ -328,36 +457,35 @@ function(ly_setup_others) # Registry install(DIRECTORY - ${CMAKE_CURRENT_BINARY_DIR}/bin/$/Registry - DESTINATION ./bin/${PAL_PLATFORM_NAME}/$ + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/Registry + DESTINATION ./${runtime_output_directory}/${PAL_PLATFORM_NAME}/$ COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) install(DIRECTORY - ${CMAKE_SOURCE_DIR}/Registry + ${LY_ROOT_FOLDER}/Registry DESTINATION . COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Engine Source Assets install(DIRECTORY - ${CMAKE_SOURCE_DIR}/Assets + ${LY_ROOT_FOLDER}/Assets DESTINATION . COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Gem Source Assets and Registry # Find all gem directories relative to the CMake Source Dir - file( - GLOB_RECURSE + file(GLOB_RECURSE gems_assets_path LIST_DIRECTORIES TRUE - RELATIVE "${CMAKE_SOURCE_DIR}/" + RELATIVE "${LY_ROOT_FOLDER}/" "Gems/*" ) list(FILTER gems_assets_path INCLUDE REGEX "/(Assets|Registry)$") foreach (gem_assets_path ${gems_assets_path}) - set(gem_abs_assets_path ${CMAKE_SOURCE_DIR}/${gem_assets_path}/) + set(gem_abs_assets_path ${LY_ROOT_FOLDER}/${gem_assets_path}/) if (EXISTS ${gem_abs_assets_path}) # The trailing slash is IMPORTANT here as that is needed to prevent # the "Assets" folder from being copied underneath the /Assets folder @@ -368,50 +496,62 @@ function(ly_setup_others) endif() endforeach() - # Qt Binaries - set(QT_DIRS bearer iconengines imageformats platforms styles translations) - list(TRANSFORM QT_DIRS PREPEND "${CMAKE_CURRENT_BINARY_DIR}/bin/$/" OUTPUT_VARIABLE QT_BIN_DIRS) + # gem.json files + file(GLOB_RECURSE + gems_json_path + LIST_DIRECTORIES FALSE + RELATIVE "${LY_ROOT_FOLDER}" + "Gems/*/gem.json" + ) + foreach(gem_json_path ${gems_json_path}) + get_filename_component(gem_relative_path ${gem_json_path} DIRECTORY) + install(FILES ${gem_json_path} + DESTINATION ${gem_relative_path} + COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} + ) + endforeach() + + # Additional files needed by gems install(DIRECTORY - ${QT_BIN_DIRS} - DESTINATION ./bin/${PAL_PLATFORM_NAME}/$ + ${LY_ROOT_FOLDER}/Gems/Atom/Asset/ImageProcessingAtom/Config + DESTINATION Gems/Atom/Asset/ImageProcessingAtom COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Templates install(DIRECTORY - ${CMAKE_SOURCE_DIR}/Templates + ${LY_ROOT_FOLDER}/Templates DESTINATION . COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Misc install(FILES - ${CMAKE_SOURCE_DIR}/ctest_pytest.ini - ${CMAKE_SOURCE_DIR}/LICENSE.txt - ${CMAKE_SOURCE_DIR}/README.md + ${LY_ROOT_FOLDER}/ctest_pytest.ini + ${LY_ROOT_FOLDER}/LICENSE.txt + ${LY_ROOT_FOLDER}/README.md DESTINATION . COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endfunction() - #! ly_setup_target_generator: install source files needed for project launcher generation function(ly_setup_target_generator) install(FILES - ${CMAKE_SOURCE_DIR}/Code/LauncherUnified/launcher_generator.cmake - ${CMAKE_SOURCE_DIR}/Code/LauncherUnified/launcher_project_files.cmake - ${CMAKE_SOURCE_DIR}/Code/LauncherUnified/LauncherProject.cpp - ${CMAKE_SOURCE_DIR}/Code/LauncherUnified/StaticModules.in + ${LY_ROOT_FOLDER}/Code/LauncherUnified/launcher_generator.cmake + ${LY_ROOT_FOLDER}/Code/LauncherUnified/launcher_project_files.cmake + ${LY_ROOT_FOLDER}/Code/LauncherUnified/LauncherProject.cpp + ${LY_ROOT_FOLDER}/Code/LauncherUnified/StaticModules.in DESTINATION LauncherGenerator COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) - install(DIRECTORY ${CMAKE_SOURCE_DIR}/Code/LauncherUnified/Platform + install(DIRECTORY ${LY_ROOT_FOLDER}/Code/LauncherUnified/Platform DESTINATION LauncherGenerator COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) - install(FILES ${CMAKE_SOURCE_DIR}/Code/LauncherUnified/FindLauncherGenerator.cmake + install(FILES ${LY_ROOT_FOLDER}/Code/LauncherUnified/FindLauncherGenerator.cmake DESTINATION cmake COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) diff --git a/cmake/Platform/Common/RuntimeDependencies_common.cmake b/cmake/Platform/Common/RuntimeDependencies_common.cmake index 6ac5215a01..4ae914744f 100644 --- a/cmake/Platform/Common/RuntimeDependencies_common.cmake +++ b/cmake/Platform/Common/RuntimeDependencies_common.cmake @@ -10,7 +10,7 @@ # set(LY_COPY_PERMISSIONS "OWNER_READ OWNER_WRITE OWNER_EXECUTE") -set(LY_TARGET_TYPES_WITH_RUNTIME_OUTPUTS MODULE_LIBRARY SHARED_LIBRARY EXECUTABLE) +set(LY_TARGET_TYPES_WITH_RUNTIME_OUTPUTS MODULE_LIBRARY SHARED_LIBRARY EXECUTABLE APPLICATION) # There are several runtime dependencies to handle: # 1. Dependencies to 3rdparty libraries. This involves copying IMPORTED_LOCATION to the folder where the target is. @@ -61,7 +61,7 @@ function(ly_get_runtime_dependencies ly_RUNTIME_DEPENDENCIES ly_TARGET) if(dependencies) list(APPEND link_dependencies ${dependencies}) endif() - if(NOT target_type MATCHES "INTERFACE") + if(NOT target_type STREQUAL "INTERFACE_LIBRARY") unset(dependencies) get_target_property(dependencies ${ly_TARGET} LINK_LIBRARIES) if(dependencies) @@ -105,11 +105,15 @@ function(ly_get_runtime_dependencies ly_RUNTIME_DEPENDENCIES ly_TARGET) set(skip_imported TRUE) endif() endif() + if(target_type MATCHES "(STATIC_LIBRARY)") + # No need to copy these dependencies since the outputs are not used at runtime + set(skip_imported TRUE) + endif() if(NOT skip_imported) # Add imported locations - if(target_type MATCHES "INTERFACE") + if(target_type STREQUAL "INTERFACE_LIBRARY") set(imported_property INTERFACE_IMPORTED_LOCATION) else() set(imported_property IMPORTED_LOCATION) @@ -183,7 +187,7 @@ function(ly_get_runtime_dependency_command ly_RUNTIME_COMMAND ly_TARGET) # To support platforms where the binaries end in different places, we are going to assume that all dependencies, # including the ones we are building, need to be copied over. However, we add a check to prevent copying something # over itself. This detection cannot happen now because the target we are copying for varies. - set(runtime_command "ly_copy(\"${source_file}\" \"$${target_directory}\")\n") + set(runtime_command "ly_copy(\"${source_file}\" \"@target_file_dir@${target_directory}\")\n") # Tentative optimization: this is an attempt to solve the first "if" at generation time, making the runtime_dependencies # file smaller and faster to run. In platforms where the built target and the dependencies targets end up in the same @@ -206,20 +210,25 @@ function(ly_get_runtime_dependency_command ly_RUNTIME_COMMAND ly_TARGET) endfunction() -get_property(additional_module_paths GLOBAL PROPERTY LY_ADDITIONAL_MODULE_PATH) -list(APPEND CMAKE_MODULE_PATH ${additional_module_paths}) +function(ly_delayed_generate_runtime_dependencies) -get_property(all_targets GLOBAL PROPERTY LY_ALL_TARGETS) -foreach(target IN LISTS all_targets) + get_property(additional_module_paths GLOBAL PROPERTY LY_ADDITIONAL_MODULE_PATH) + list(APPEND CMAKE_MODULE_PATH ${additional_module_paths}) - # 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() + get_property(all_targets GLOBAL PROPERTY LY_ALL_TARGETS) + foreach(aliased_target IN LISTS all_targets) + + unset(target) + ly_de_alias_target(${aliased_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() - unset(runtime_dependencies) - set(runtime_commands " + unset(runtime_dependencies) + set(runtime_commands " function(ly_copy source_file target_directory) get_filename_component(target_filename \"\${source_file}\" NAME) if(NOT \"\${source_file}\" STREQUAL \"\${target_directory}/\${target_filename}\") @@ -232,21 +241,23 @@ function(ly_copy source_file target_directory) endif() endif() endfunction() -\n") + \n") - ly_get_runtime_dependencies(runtime_dependencies ${target}) - foreach(runtime_dependency ${runtime_dependencies}) - unset(runtime_command) - ly_get_runtime_dependency_command(runtime_command ${runtime_dependency}) - string(APPEND runtime_commands ${runtime_command}) - endforeach() - - # Generate the output file - string(CONFIGURE "${runtime_commands}" generated_commands @ONLY) - file(GENERATE - OUTPUT ${CMAKE_BINARY_DIR}/runtime_dependencies/$/${target}.cmake - CONTENT "${generated_commands}" - ) + ly_get_runtime_dependencies(runtime_dependencies ${target}) + foreach(runtime_dependency ${runtime_dependencies}) + unset(runtime_command) + ly_get_runtime_dependency_command(runtime_command ${runtime_dependency}) + string(APPEND runtime_commands ${runtime_command}) + endforeach() + + # Generate the output file + set(target_file_dir "$") + string(CONFIGURE "${runtime_commands}" generated_commands @ONLY) + file(GENERATE + OUTPUT ${CMAKE_BINARY_DIR}/runtime_dependencies/$/${target}.cmake + CONTENT "${generated_commands}" + ) -endforeach() + endforeach() +endfunction() diff --git a/cmake/Platform/Mac/Install_mac.cmake b/cmake/Platform/Mac/Install_mac.cmake index 8c96c199de..5c7959bf77 100644 --- a/cmake/Platform/Mac/Install_mac.cmake +++ b/cmake/Platform/Mac/Install_mac.cmake @@ -11,11 +11,5 @@ # Empty implementations for untested platforms to fix build errors. -function(ly_install_target ly_install_target_NAME) - -endfunction() - - function(ly_setup_o3de_install) - endfunction() \ No newline at end of file diff --git a/cmake/Platform/Mac/PAL_mac.cmake b/cmake/Platform/Mac/PAL_mac.cmake index f6cf034312..988d36af14 100644 --- a/cmake/Platform/Mac/PAL_mac.cmake +++ b/cmake/Platform/Mac/PAL_mac.cmake @@ -35,7 +35,7 @@ else() endif() # Set the default asset type for deployment -set(LY_ASSET_DEPLOY_ASSET_TYPE "osx_gl" CACHE STRING "Set the asset type for deployment.") +set(LY_ASSET_DEPLOY_ASSET_TYPE "mac" 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/Windows/Configurations_windows.cmake b/cmake/Platform/Windows/Configurations_windows.cmake index 6ab376ed0b..9ef535e455 100644 --- a/cmake/Platform/Windows/Configurations_windows.cmake +++ b/cmake/Platform/Windows/Configurations_windows.cmake @@ -106,7 +106,7 @@ if(NOT CMAKE_GENERATOR MATCHES "Visual Studio") endforeach() if(NOT version VERSION_EQUAL CMAKE_SYSTEM_VERSION) - message(STATUS "Selecting Windows SDK version ${version} to target Windows ${CMAKE_SYSTEM_VERSION}.") + message(STATUS "Using Windows SDK version ${version} to target Windows ${CMAKE_SYSTEM_VERSION}") endif() ly_set(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION "${version}") @@ -116,4 +116,3 @@ endif() if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION MATCHES "10.0") message(FATAL_ERROR "Unsupported version of Windows SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}, specify \"-DCMAKE_SYSTEM_VERSION=10.0\" when invoking cmake") endif() -message(STATUS "Using Windows target SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") diff --git a/cmake/Platform/Windows/Packaging/PostInstallSetup.wxs b/cmake/Platform/Windows/Packaging/PostInstallSetup.wxs new file mode 100644 index 0000000000..d4f6c181dd --- /dev/null +++ b/cmake/Platform/Windows/Packaging/PostInstallSetup.wxs @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cmake/Platform/Windows/Packaging/Template.wxs.in b/cmake/Platform/Windows/Packaging/Template.wxs.in index 2900b96f41..e14e064fbc 100644 --- a/cmake/Platform/Windows/Packaging/Template.wxs.in +++ b/cmake/Platform/Windows/Packaging/Template.wxs.in @@ -17,8 +17,7 @@ - @@ -41,8 +40,24 @@ + + + + + + + NOT Installed Or REINSTALL + + + NOT Installed Or REINSTALL + + + NOT Installed Or REINSTALL + + + diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index ce73e9a07b..db9c7fc906 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -28,6 +28,10 @@ endif() set(CPACK_GENERATOR "WIX") +set(_cmake_package_name "cmake-${CPACK_DESIRED_CMAKE_VERSION}-windows-x86_64") +set(CPACK_CMAKE_PACKAGE_FILE "${_cmake_package_name}.zip") +set(CPACK_CMAKE_PACKAGE_HASH "15a49e2ab81c1822d75b1b1a92f7863f58e31f6d6aac1c4103eef2b071be3112") + # CPack will generate the WiX product/upgrade GUIDs further down the chain if they weren't supplied # however, they are unique for each run. instead, let's do the auto generation here and add it to # the cache for run persistence and have the ability to detect if they are still being used. @@ -83,9 +87,14 @@ set(CPACK_WIX_PRODUCT_ICON ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/produc set(CPACK_WIX_TEMPLATE "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/Template.wxs.in") set(CPACK_WIX_EXTRA_SOURCES + "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/PostInstallSetup.wxs" "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/Shortcuts.wxs" ) +set(CPACK_WIX_EXTENSIONS + WixUtilExtension +) + set(_embed_artifacts "yes") if(LY_INSTALLER_DOWNLOAD_URL) @@ -101,4 +110,5 @@ endif() set(CPACK_WIX_CANDLE_EXTRA_FLAGS -dCPACK_EMBED_ARTIFACTS=${_embed_artifacts} + -dCPACK_CMAKE_PACKAGE_NAME=${_cmake_package_name} ) diff --git a/cmake/Platform/iOS/Install_ios.cmake b/cmake/Platform/iOS/Install_ios.cmake index 8c96c199de..5c7959bf77 100644 --- a/cmake/Platform/iOS/Install_ios.cmake +++ b/cmake/Platform/iOS/Install_ios.cmake @@ -11,11 +11,5 @@ # Empty implementations for untested platforms to fix build errors. -function(ly_install_target ly_install_target_NAME) - -endfunction() - - function(ly_setup_o3de_install) - endfunction() \ No newline at end of file diff --git a/cmake/Platform/iOS/RuntimeDependencies_ios.cmake b/cmake/Platform/iOS/RuntimeDependencies_ios.cmake index d558c6f12a..a2a6d30593 100644 --- a/cmake/Platform/iOS/RuntimeDependencies_ios.cmake +++ b/cmake/Platform/iOS/RuntimeDependencies_ios.cmake @@ -118,57 +118,65 @@ function(ios_get_dependencies_recursive ios_DEPENDENCIES ly_TARGET) endfunction() -# For each (non-monolithic) game project, find runtime dependencies and tell XCode to embed/sign them -if(NOT LY_MONOLITHIC_GAME) +function(ly_delayed_generate_runtime_dependencies) + + # For each (non-monolithic) game project, find runtime dependencies and tell XCode to embed/sign them + if(NOT LY_MONOLITHIC_GAME) + + foreach(game_project ${LY_PROJECTS}) + + # Recursively get all dependent frameworks for the game project. + unset(dependencies) + ios_get_dependencies_recursive(dependencies ${game_project}.GameLauncher) + if(dependencies) + set_target_properties(${game_project}.GameLauncher + PROPERTIES + XCODE_EMBED_FRAMEWORKS "${dependencies}" + XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY TRUE + XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks" + ) + endif() - foreach(game_project ${LY_PROJECTS}) + endforeach() - # Recursively get all dependent frameworks for the game project. - unset(dependencies) - ios_get_dependencies_recursive(dependencies ${game_project}.GameLauncher) - if(dependencies) - set_target_properties(${game_project}.GameLauncher - PROPERTIES - XCODE_EMBED_FRAMEWORKS "${dependencies}" - XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY TRUE - XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks" - ) - endif() + endif() - endforeach() + get_property(all_targets GLOBAL PROPERTY LY_ALL_TARGETS) + unset(test_runner_dependencies) + foreach(aliased_target IN LISTS all_targets) -endif() + unset(target) + ly_de_alias_target(${aliased_target} target) -get_property(all_targets GLOBAL PROPERTY LY_ALL_TARGETS) -unset(test_runner_dependencies) -foreach(target IN LISTS all_targets) - # 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() - - file(GENERATE - OUTPUT ${CMAKE_BINARY_DIR}/runtime_dependencies/$/${target}.cmake - CONTENT "" - ) - - if(target_type IN_LIST IOS_FRAMEWORK_TARGET_TYPES) - list(APPEND test_runner_dependencies ${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() + + file(GENERATE + OUTPUT ${CMAKE_BINARY_DIR}/runtime_dependencies/$/${target}.cmake + CONTENT "" + ) + + if(target_type IN_LIST IOS_FRAMEWORK_TARGET_TYPES) + list(APPEND test_runner_dependencies ${target}) + endif() + endforeach() + + if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + add_dependencies("AzTestRunner" ${test_runner_dependencies}) + + # We still need to add indirect dependencies(eg. 3rdParty) + unset(all_dependencies) + ios_get_dependencies_recursive(all_dependencies AzTestRunner) + + set_target_properties("AzTestRunner" + PROPERTIES + XCODE_EMBED_FRAMEWORKS "${all_dependencies}" + XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY TRUE + XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks" + ) endif() -endforeach() -if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) - add_dependencies("AzTestRunner" ${test_runner_dependencies}) - - # We still need to add indirect dependencies(eg. 3rdParty) - unset(all_dependencies) - ios_get_dependencies_recursive(all_dependencies AzTestRunner) - - set_target_properties("AzTestRunner" - PROPERTIES - XCODE_EMBED_FRAMEWORKS "${all_dependencies}" - XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY TRUE - XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks" - ) -endif() \ No newline at end of file +endfunction() \ No newline at end of file diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index 781fee3711..297dad4ddf 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -105,6 +105,7 @@ function(ly_add_project_dependencies) ) endfunction() + #template for generating the project build_path setreg set(project_build_path_template [[ { @@ -120,7 +121,6 @@ set(project_build_path_template [[ }]] ) - #! ly_generate_project_build_path_setreg: Generates a .setreg file that contains an absolute path to the ${CMAKE_BINARY_DIR} # This allows locate the directory where the project it's binaries are built to be located within the engine. # Which are the shared libraries and launcher executables @@ -136,18 +136,32 @@ set(project_build_path_template [[ # can only run on the host platform # \arg:project_real_path Full path to the o3de project directory function(ly_generate_project_build_path_setreg project_real_path) - # The build path isn't needed on non-monolithic platforms - # Nor on any non-host platforms - if (LY_MONOLITHIC_GAME OR NOT PAL_TRAIT_BUILD_HOST_TOOLS) - return() - endif() + # The build path isn't needed on non-monolithic platforms + # Nor on any non-host platforms + if (LY_MONOLITHIC_GAME OR NOT PAL_TRAIT_BUILD_HOST_TOOLS) + return() + endif() + + # Set the project_bin_path to the ${CMAKE_BINARY_DIR} to provide the configure template + # with the project build directory + set(project_bin_path ${CMAKE_BINARY_DIR}) + string(CONFIGURE ${project_build_path_template} project_build_path_setreg_content @ONLY) + set(project_user_build_path_setreg_file ${project_real_path}/user/Registry/Platform/${PAL_PLATFORM_NAME}/build_path.setreg) + file(GENERATE OUTPUT ${project_user_build_path_setreg_file} CONTENT ${project_build_path_setreg_content}) +endfunction() + - # Set the project_bin_path to the ${CMAKE_BINARY_DIR} to provide the configure template - # with the project build directory - set(project_bin_path ${CMAKE_BINARY_DIR}) - string(CONFIGURE ${project_build_path_template} project_build_path_setreg_content @ONLY) - set(project_user_build_path_setreg_file ${project_real_path}/user/Registry/Platform/${PAL_PLATFORM_NAME}/build_path.setreg) - file(GENERATE OUTPUT ${project_user_build_path_setreg_file} CONTENT ${project_build_path_setreg_content}) +function(add_project_json_external_subdirectories project_path) + set(project_json_path ${project_path}/project.json) + if(EXISTS ${project_json_path}) + read_json_external_subdirs(external_subdirs ${project_path}/project.json) + foreach(external_subdir ${external_subdirs}) + file(REAL_PATH ${external_subdir} real_external_subdir BASE_DIRECTORY ${project_path}) + list(APPEND project_external_subdirs ${real_external_subdir}) + endforeach() + + set_property(GLOBAL APPEND PROPERTY LY_EXTERNAL_SUBDIRS ${project_external_subdirs}) + endif() endfunction() # Add the projects here so the above function is found @@ -163,5 +177,6 @@ foreach(project ${LY_PROJECTS}) list(APPEND LY_PROJECTS_FOLDER_NAME ${project_folder_name}) add_subdirectory(${project} "${project_folder_name}-${full_directory_hash}") ly_generate_project_build_path_setreg(${full_directory_path}) + add_project_json_external_subdirectories(${full_directory_path}) endforeach() ly_set(LY_PROJECTS_FOLDER_NAME ${LY_PROJECTS_FOLDER_NAME}) diff --git a/cmake/SettingsRegistry.cmake b/cmake/SettingsRegistry.cmake index fd5985a5a1..4d932601b4 100644 --- a/cmake/SettingsRegistry.cmake +++ b/cmake/SettingsRegistry.cmake @@ -31,7 +31,7 @@ set(gem_module_template [[ "@stripped_gem_target@": { "Modules":["$"], - "SourcePaths":["@gem_relative_source_dir@"] + "SourcePaths":["@gem_module_root_relative_to_engine_root@"] }]] ) @@ -64,18 +64,16 @@ function(ly_get_gem_load_dependencies ly_GEM_LOAD_DEPENDENCIES ly_TARGET) get_target_property(load_dependencies ${ly_TARGET} MANUALLY_ADDED_DEPENDENCIES) if(load_dependencies) foreach(load_dependency ${load_dependencies}) - # Skip wrapping produced when targets are not created in the same directory - if(NOT ${load_dependency} MATCHES "^::@") - get_property(dependency_type TARGET ${load_dependency} PROPERTY TYPE) - get_property(is_gem_target TARGET ${load_dependency} PROPERTY GEM_MODULE SET) - # If the dependency is a "gem module" then add it as a load dependencies - # and recurse into its manually added dependencies - if (is_gem_target) - unset(dependencies) - ly_get_gem_load_dependencies(dependencies ${load_dependency}) - list(APPEND all_gem_load_dependencies ${load_dependency}) - list(APPEND all_gem_load_dependencies ${dependencies}) - endif() + # Skip wrapping produced when targets are not created in the same directory + ly_de_alias_target(${load_dependency} dealias_load_dependency) + get_property(is_gem_target TARGET ${dealias_load_dependency} PROPERTY GEM_MODULE SET) + # If the dependency is a "gem module" then add it as a load dependencies + # and recurse into its manually added dependencies + if (is_gem_target) + unset(dependencies) + ly_get_gem_load_dependencies(dependencies ${dealias_load_dependency}) + list(APPEND all_gem_load_dependencies ${dependencies}) + list(APPEND all_gem_load_dependencies ${dealias_load_dependency}) endif() endforeach() endif() @@ -83,37 +81,76 @@ function(ly_get_gem_load_dependencies ly_GEM_LOAD_DEPENDENCIES ly_TARGET) list(REMOVE_DUPLICATES all_gem_load_dependencies) set_property(GLOBAL PROPERTY LY_GEM_LOAD_DEPENDENCIES_${ly_TARGET} "${all_gem_load_dependencies}") set(${ly_GEM_LOAD_DEPENDENCIES} ${all_gem_load_dependencies} PARENT_SCOPE) + message(VERBOSE "Gem Target \"${ly_TARGET}\" has load dependencies of: ${all_gem_load_dependencies}") + +endfunction() + +#!ly_get_gem_module_root: Uses the supplied gem_target to lookup the nearest gem.json file above the SOURCE_DIR +# +# \arg:gem_target(TARGET) - Target to look upwards from using its SOURCE_DIR property +function(ly_get_gem_module_root output_gem_module_root gem_target) + unset(gem_module_roots) + get_property(gem_source_dir TARGET ${gem_target} PROPERTY SOURCE_DIR) + + if(gem_source_dir) + set(candidate_gem_dir ${gem_source_dir}) + # Locate the root of the gem by finding the gem.json location + while(NOT EXISTS ${candidate_gem_dir}/gem.json) + get_filename_component(parent_dir ${candidate_gem_dir} DIRECTORY) + if (${parent_dir} STREQUAL ${candidate_gem_dir}) + message(WARNING "Did not find a gem.json while processing GEM_MODULE target ${gem_target}!") + break() + endif() + set(candidate_gem_dir ${parent_dir}) + endwhile() + endif() + + if (EXISTS ${candidate_gem_dir}/gem.json) + set(gem_source_dir ${candidate_gem_dir}) + endif() + + # Set the gem module root output directory to the location with the gem.json file within it or + # the supplied gem_target SOURCE_DIR location if no gem.json file was found + set(${output_gem_module_root} ${gem_source_dir} PARENT_SCOPE) endfunction() + #! ly_delayed_generate_settings_registry: Generates a .setreg file for each target with dependencies # added to it via ly_add_target_dependencies # The generated file contains the file to the each dependent targets # This can be used for example to determine which list of gems to load with an application function(ly_delayed_generate_settings_registry) get_property(ly_delayed_load_targets GLOBAL PROPERTY LY_DELAYED_LOAD_DEPENDENCIES) - foreach(prefix_target ${ly_delayed_load_targets}) string(REPLACE "," ";" prefix_target_list "${prefix_target}") list(LENGTH prefix_target_list prefix_target_length) if(prefix_target_length EQUAL 0) - message(SEND_ERROR "Delayed load target is missing target name") continue() endif() # Retrieve the target name from the back of the list list(POP_BACK prefix_target_list target) - # Retreives the prefix if available from the remaining element of the list + # Retrieves the prefix if available from the remaining element of the list list(POP_BACK prefix_target_list prefix) # Get the gem dependencies for the given project and target combination get_property(gem_dependencies GLOBAL PROPERTY LY_DELAYED_LOAD_"${prefix_target}") list(REMOVE_DUPLICATES gem_dependencies) # Strip out any duplicate gem targets - set(all_gem_dependencies ${gem_dependencies}) + unset(all_gem_dependencies) foreach(gem_target ${gem_dependencies}) ly_get_gem_load_dependencies(gem_load_gem_dependencies ${gem_target}) - list(APPEND all_gem_dependencies ${gem_load_gem_dependencies}) + list(APPEND all_gem_dependencies ${gem_load_gem_dependencies} ${gem_target}) + endforeach() + list(REMOVE_DUPLICATES all_gem_dependencies) + + # de-namespace them + unset(new_gem_dependencies) + foreach(gem_target ${all_gem_dependencies}) + ly_de_alias_target(${gem_target} stripped_gem_target) + list(APPEND new_gem_dependencies ${stripped_gem_target}) endforeach() + set(all_gem_dependencies ${new_gem_dependencies}) list(REMOVE_DUPLICATES all_gem_dependencies) unset(target_gem_dependencies_names) @@ -123,18 +160,20 @@ function(ly_delayed_generate_settings_registry) if (NOT TARGET ${gem_target}) message(FATAL_ERROR "Dependency ${gem_target} from ${target} does not exist") endif() - get_property(gem_relative_source_dir TARGET ${gem_target} PROPERTY SOURCE_DIR) - if(gem_relative_source_dir) - # Most gems CMakeLists.txt files reside in the /Code/ so remove "Code/" from the path - if(gem_relative_source_dir MATCHES ".*/Code$") - get_filename_component(gem_relative_source_dir ${gem_relative_source_dir} DIRECTORY) - endif() - file(TO_CMAKE_PATH ${LY_ROOT_FOLDER} ly_root_folder_cmake) - file(RELATIVE_PATH gem_relative_source_dir ${ly_root_folder_cmake} ${gem_relative_source_dir}) + + get_target_property(target_type ${gem_target} TYPE) + if (target_type STREQUAL "INTERFACE_LIBRARY") + # don't use interface libraries here, we only want ones which produce actual binaries. + # we have still already recursed into their dependencies - they'll show up later. + continue() endif() - # Strip target namespace from gem targets before configuring them into the json template - ly_strip_target_namespace(TARGET ${gem_target} OUTPUT_VARIABLE stripped_gem_target) + + ly_get_gem_module_root(gem_module_root ${gem_target}) + file(RELATIVE_PATH gem_module_root_relative_to_engine_root ${LY_ROOT_FOLDER} ${gem_module_root}) + + # De-alias namespace from gem targets before configuring them into the json template + ly_de_alias_target(${gem_target} stripped_gem_target) string(CONFIGURE ${gem_module_template} gem_module_json @ONLY) list(APPEND target_gem_dependencies_names ${gem_module_json}) endforeach() @@ -146,7 +185,12 @@ function(ly_delayed_generate_settings_registry) list(JOIN target_gem_dependencies_names ",\n" target_gem_dependencies_names) string(CONFIGURE ${gems_json_template} gem_json @ONLY) - set(dependencies_setreg $/Registry/cmake_dependencies.${specialization_name}.setreg) + if(prefix) + set(target_dir $) + else() + set(target_dir $) + endif() + set(dependencies_setreg ${target_dir}/Registry/cmake_dependencies.${specialization_name}.setreg) file(GENERATE OUTPUT ${dependencies_setreg} CONTENT ${gem_json}) set_property(TARGET ${target} APPEND PROPERTY INTERFACE_LY_TARGET_FILES "${dependencies_setreg}\nRegistry") diff --git a/cmake/TestImpactFramework/LYTestImpactFramework.cmake b/cmake/TestImpactFramework/LYTestImpactFramework.cmake index c10c5bf637..d46b16bca5 100644 --- a/cmake/TestImpactFramework/LYTestImpactFramework.cmake +++ b/cmake/TestImpactFramework/LYTestImpactFramework.cmake @@ -204,7 +204,10 @@ function(ly_test_impact_export_source_target_mappings MAPPING_TEMPLATE_FILE) get_property(LY_ALL_TARGETS GLOBAL PROPERTY LY_ALL_TARGETS) # Walk the build targets - foreach(target ${LY_ALL_TARGETS}) + foreach(aliased_target ${LY_ALL_TARGETS}) + + unset(target) + ly_de_alias_target(${aliased_target} target) message(TRACE "Exporting static source file mappings for ${target}") # Target name and path relative to root diff --git a/cmake/Tools/Platform/Android/android_deployment.py b/cmake/Tools/Platform/Android/android_deployment.py index 8a76270df1..eb8361ec18 100755 --- a/cmake/Tools/Platform/Android/android_deployment.py +++ b/cmake/Tools/Platform/Android/android_deployment.py @@ -62,7 +62,7 @@ class AndroidDeployment(object): :param deployment_type: The type of deployment (DEPLOY_APK_ONLY, DEPLOY_ASSETS_ONLY, or DEPLOY_BOTH) :param game_name: The name of the game whose assets are being deployed. None if is_test_project is True :param asset_mode: The asset mode of deployment (LOOSE, PAK, VFS). None if is_test_project is True - :param asset_type: The asset type (for android, 'es3'). None if is_test_project is True + :param asset_type: The asset type. None if is_test_project is True :param embedded_assets: Boolean to indicate if the assets are embedded in the APK or not :param is_unit_test: Boolean to indicate if this is a unit test deployment """ diff --git a/cmake/Tools/Platform/Android/android_support.py b/cmake/Tools/Platform/Android/android_support.py index 6443077457..75e0cd2970 100755 --- a/cmake/Tools/Platform/Android/android_support.py +++ b/cmake/Tools/Platform/Android/android_support.py @@ -10,7 +10,9 @@ # import imghdr +import configparser import datetime +import fnmatch import logging import os import json @@ -33,6 +35,13 @@ if ROOT_DEV_PATH not in sys.path: from cmake.Tools import common +ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP = { + '4.2.0': {'min_gradle_version': '6.7.1', + 'sdk_build': '30.0.2', + 'default_ndk': '21.4.7075529', + 'min_cmake_version': '3.20'} +} + APP_NAME = 'app' ANDROID_MANIFEST_FILE = 'AndroidManifest.xml' ANDROID_LIBRARIES_JSON_FILE = 'android_libraries.json' @@ -86,83 +95,93 @@ PYTHON_SCRIPT = 'python.cmd' if platform.system() == 'Windows' else 'python.sh' ANDROID_LAUNCHER_NAME_PATTERN = "{project_name}.GameLauncher" + class AndroidProjectManifestEnvironment(object): """ - This class manages the environment for the AndroidManifiest.xml template file, based on project settings and environments + This class manages the environment for the AndroidManifest.xml template file, based on project settings and environments that were passed in or calculated from the command line arguments. """ - def __init__(self, engine_root, project_path, android_sdk_version_number, android_ndk_platform_number, is_test:bool): + def __init__(self, engine_root, project_path, android_sdk_version_number, is_test:bool): """ Initialize the object with the project specific parameters and values for the game project :param engine_root: The path where the engine is located :param project_path: The path were the project is located :param android_sdk_version_number: The android SDK platform version - :param android_ndk_platform_number: The android NDK platform version :param is_test: Indicates if theAzTestRunner application should be run """ - if is_test: - # The AzTestRunner project.json is located under {engine_root}/Code/Tools/AzTestRunner/Platform/Android/android_project.json - project_properties_path = engine_root / 'Code' / 'Tools' / 'AzTestRunner' / 'Platform' / 'Android' / 'android_project.json' - else: - # The project.json file is located under the game name folder - project_properties_path = project_path / 'project.json' - # Read and parse the project.json file into a dictionary to process the specific attributes needed for the manifest template - project_properties_content = project_properties_path.resolve(strict=True)\ - .read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING, - errors=common.ENCODING_ERROR_HANDLINGS) - self.project_path = project_path + try: + if is_test: + # The AzTestRunner project.json is located under {engine_root}/Code/Tools/AzTestRunner/Platform/Android/android_project.json + project_properties_path = engine_root / 'Code' / 'Tools' / 'AzTestRunner' / 'Platform' / 'Android' / 'android_project.json' + assert project_properties_path.is_file(), f'Missing required android settings file {project_properties_path.resolve()}' + project_properties_content = project_properties_path.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING, + errors=common.ENCODING_ERROR_HANDLINGS) + project_json = json.loads(project_properties_content) - # Extract the key attributes we need to process and build up our environment table - project_json = json.loads(project_properties_content) - - project_name = project_json.get('project_name') - if not project_name: - raise common.LmbrCmdError(f"Missing required 'project_name' from project.json for project at '{str(project_path)}'") - product_name = project_json.get('product_name', project_name) - - game_project_android_settings = project_json['android_settings'] - - package_name = game_project_android_settings["package_name"] - - package_path = package_name.replace('.', '/') - - project_activity = f'{TEST_RUNNER_PROJECT}Activity' if is_test else f'{project_name}Activity' - - # Multiview options require special processing - multi_window_options = AndroidProjectManifestEnvironment.process_android_multi_window_options(game_project_android_settings) - - self.internal_dict = { - 'ANDROID_PACKAGE': package_name, - 'ANDROID_PACKAGE_PATH': package_path, - 'ANDROID_VERSION_NUMBER': game_project_android_settings["version_number"], - "ANDROID_VERSION_NAME": game_project_android_settings["version_name"], - "ANDROID_SCREEN_ORIENTATION": game_project_android_settings["orientation"], - 'ANDROID_APP_NAME': TEST_RUNNER_PROJECT if is_test else product_name, # external facing name - 'ANDROID_PROJECT_NAME': TEST_RUNNER_PROJECT if is_test else project_name, # internal facing name - 'ANDROID_PROJECT_ACTIVITY': project_activity, - 'ANDROID_LAUNCHER_NAME': TEST_RUNNER_PROJECT if is_test else ANDROID_LAUNCHER_NAME_PATTERN.format(project_name=project_name), - 'ANDROID_CONFIG_CHANGES': multi_window_options['ANDROID_CONFIG_CHANGES'], - 'ANDROID_APP_PUBLIC_KEY': game_project_android_settings.get('app_public_key', 'NoKey'), - 'ANDROID_APP_OBFUSCATOR_SALT': game_project_android_settings.get('app_obfuscator_salt', ''), - 'ANDROID_USE_MAIN_OBB': game_project_android_settings.get('use_main_obb', 'false'), - 'ANDROID_USE_PATCH_OBB': game_project_android_settings.get('use_patch_obb', 'false'), - 'ANDROID_ENABLE_KEEP_SCREEN_ON': game_project_android_settings.get('enable_keep_screen_on', 'false'), - 'ANDROID_DISABLE_IMMERSIVE_MODE': game_project_android_settings.get('disable_immersive_mode', 'false'), - 'ANDROID_MIN_SDK_VERSION': android_ndk_platform_number, - 'ANDROID_TARGET_SDK_VERSION': android_sdk_version_number, - 'ICONS': game_project_android_settings.get('icons', None), - 'SPLASH_SCREEN': game_project_android_settings.get('splash_screen', None), - - 'ANDROID_MULTI_WINDOW': multi_window_options['ANDROID_MULTI_WINDOW'], - 'ANDROID_MULTI_WINDOW_PROPERTIES': multi_window_options['ANDROID_MULTI_WINDOW_PROPERTIES'], - - 'SAMSUNG_DEX_KEEP_ALIVE': multi_window_options['SAMSUNG_DEX_KEEP_ALIVE'], - 'SAMSUNG_DEX_LAUNCH_WIDTH': multi_window_options['SAMSUNG_DEX_LAUNCH_WIDTH'], - 'SAMSUNG_DEX_LAUNCH_HEIGHT': multi_window_options['SAMSUNG_DEX_LAUNCH_HEIGHT'] - } + android_settings = project_json['android_settings'] + + else: + # O3DE projects have both a project.json and an android_project.json files (unless its internal) + project_properties_path = project_path / 'project.json' + assert project_properties_path.is_file(), f'Missing required project settings file {project_properties_path.resolve()}' + project_properties_content = project_properties_path.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING, + errors=common.ENCODING_ERROR_HANDLINGS) + project_json = json.loads(project_properties_content) + + android_project_properties_path = project_path / 'Platform' / 'Android' / 'android_project.json' + if android_project_properties_path.is_file(): + android_project_properties_content = android_project_properties_path.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING, + errors=common.ENCODING_ERROR_HANDLINGS) + android_project_json = json.loads(android_project_properties_content) + android_settings = android_project_json['android_settings'] + else: + android_settings = project_json['android_settings'] + + self.project_path = project_path + + project_name = project_json['project_name'] + product_name = project_json.get('product_name', project_name) + package_name = android_settings["package_name"] + package_path = package_name.replace('.', '/') + + project_activity = f'{TEST_RUNNER_PROJECT}Activity' if is_test else f'{project_name}Activity' + + # Multiview options require special processing + multi_window_options = AndroidProjectManifestEnvironment.process_android_multi_window_options(android_settings) + + self.internal_dict = { + 'ANDROID_PACKAGE': package_name, + 'ANDROID_PACKAGE_PATH': package_path, + 'ANDROID_VERSION_NUMBER': android_settings["version_number"], + "ANDROID_VERSION_NAME": android_settings["version_name"], + "ANDROID_SCREEN_ORIENTATION": android_settings["orientation"], + 'ANDROID_APP_NAME': TEST_RUNNER_PROJECT if is_test else product_name, # external facing name + 'ANDROID_PROJECT_NAME': TEST_RUNNER_PROJECT if is_test else project_name, # internal facing name + 'ANDROID_PROJECT_ACTIVITY': project_activity, + 'ANDROID_LAUNCHER_NAME': TEST_RUNNER_PROJECT if is_test else ANDROID_LAUNCHER_NAME_PATTERN.format(project_name=project_name), + 'ANDROID_CONFIG_CHANGES': multi_window_options['ANDROID_CONFIG_CHANGES'], + 'ANDROID_APP_PUBLIC_KEY': android_settings.get('app_public_key', 'NoKey'), + 'ANDROID_APP_OBFUSCATOR_SALT': android_settings.get('app_obfuscator_salt', ''), + 'ANDROID_USE_MAIN_OBB': android_settings.get('use_main_obb', 'false'), + 'ANDROID_USE_PATCH_OBB': android_settings.get('use_patch_obb', 'false'), + 'ANDROID_ENABLE_KEEP_SCREEN_ON': android_settings.get('enable_keep_screen_on', 'false'), + 'ANDROID_DISABLE_IMMERSIVE_MODE': android_settings.get('disable_immersive_mode', 'false'), + 'ANDROID_TARGET_SDK_VERSION': android_sdk_version_number, + 'ICONS': android_settings.get('icons', None), + 'SPLASH_SCREEN': android_settings.get('splash_screen', None), + + 'ANDROID_MULTI_WINDOW': multi_window_options['ANDROID_MULTI_WINDOW'], + 'ANDROID_MULTI_WINDOW_PROPERTIES': multi_window_options['ANDROID_MULTI_WINDOW_PROPERTIES'], + + 'SAMSUNG_DEX_KEEP_ALIVE': multi_window_options['SAMSUNG_DEX_KEEP_ALIVE'], + 'SAMSUNG_DEX_LAUNCH_WIDTH': multi_window_options['SAMSUNG_DEX_LAUNCH_WIDTH'], + 'SAMSUNG_DEX_LAUNCH_HEIGHT': multi_window_options['SAMSUNG_DEX_LAUNCH_HEIGHT'] + } + except KeyError as e: + raise common.LmbrCmdError(f"Missing key from android project settings for project at {project_path}:'{e}' ") def __getitem__(self, item): return self.internal_dict.get(item) @@ -306,6 +325,7 @@ asset_deploy_type={asset_type} android_sdk_path={android_sdk_path} embed_assets_in_apk={embed_assets_in_apk} is_unit_test={is_unit_test} +android_gradle_plugin={android_gradle_plugin_version} """ NATIVE_CMAKE_SECTION_ANDROID_FORMAT = """ @@ -425,26 +445,28 @@ class AndroidProjectGenerator(object): Class the manages the process to generate an android project folder in order to build with gradle/android studio """ - def __init__(self, engine_root, build_dir, android_ndk_path, android_sdk_path, android_sdk_version, android_ndk_platform, - project_path, third_party_path, cmake_version, override_cmake_path, override_gradle_path, override_ninja_path, - android_sdk_build_tool_version, include_assets_in_apk, asset_mode, asset_type, signing_config, is_test_project=False, + def __init__(self, engine_root, build_dir, android_sdk_path, build_tool, android_sdk_platform, android_native_api_level, android_ndk, + project_path, third_party_path, cmake_version, override_cmake_path, override_gradle_path, gradle_version, gradle_plugin_version, + override_ninja_path, include_assets_in_apk, asset_mode, asset_type, signing_config, is_test_project=False, overwrite_existing=True): """ Initialize the object with all the required parameters needed to create an Android Project. The parameters should be verified before initializing this object - + :param engine_root: The engine root that contains the engine :param build_dir: The target folder under the where the android project folder will be created - :param android_ndk_path: The path to the ANDROID_NDK used for building the native android code :param android_sdk_path: The path to the ANDROID_SDK used for building the android java code - :param android_sdk_version: The android platform version number to use for the Android SDK related builds - :param android_ndk_platform: The android platform version number to use for the Android NDK related builds + :param build_tool: The android SDK build-tool version. + :param android_sdk_platform: The android sdk platform version number to use for the Android SDK related builds + :param android_native_api_level:The android native API level (ANDROID_NATIVE_API_LEVEL) to set + :param android_ndk: The android ndk version number to use for the native builds :param project_path: The path to the project :param third_party_path: The required path to the lumberyard 3rd party path :param cmake_version: The version number of cmake that will be used by gradle :param override_cmake_path: The override path to cmake if it does not exists in the system path :param override_gradle_path: The override path to gradle if it does not exists in the system path + :param gradle_version: The detected version of gradle being used + :param gradle_plugin_version: The android gradle plugin version :param override_ninja_path: The override path to ninja if it does not exists in the system path - :param android_sdk_build_tool_version: The preferred android SDK build-tool version. Will default to the first one detected in the android sdk path :param include_assets_in_apk: :param asset_mode: :param asset_type: @@ -458,17 +480,16 @@ class AndroidProjectGenerator(object): self.build_dir = build_dir - self.android_ndk_path = android_ndk_path - self.android_sdk_path = android_sdk_path self.android_project_builder_path = self.engine_root / 'Code/Tools/Android/ProjectBuilder' - self.android_sdk_version = android_sdk_version - - self.android_sdk_build_tool_version = android_sdk_build_tool_version + self.android_sdk_platform = android_sdk_platform + self.android_sdk_build_tool_version = build_tool.version - self.android_ndk_platform = android_ndk_platform + self.android_ndk = android_ndk + self.android_ndk_version = android_ndk.version + self.android_native_api_level = android_native_api_level self.project_path = project_path @@ -480,6 +501,10 @@ class AndroidProjectGenerator(object): self.override_gradle_path = override_gradle_path + self.gradle_version = gradle_version + + self.gradle_plugin_version = gradle_plugin_version + self.override_ninja_path = override_ninja_path self.include_assets_in_apk = include_assets_in_apk @@ -511,8 +536,10 @@ class AndroidProjectGenerator(object): project_names.extend(self.create_lumberyard_app(project_names)) root_gradle_env = { - 'SDK_VER': self.android_sdk_version, - 'NDK_PLATFORM_VER': self.android_ndk_platform, + 'ANDROID_GRADLE_PLUGIN_VERSION': str(self.gradle_plugin_version), + 'SDK_VER': self.android_sdk_platform, + 'MIN_SDK_VER': self.android_sdk_platform, + 'NDK_VERSION': self.android_ndk_version, 'SDK_BUILD_TOOL_VER': self.android_sdk_build_tool_version, 'LY_ENGINE_ROOT': common.normalize_path_for_settings(self.engine_root) } @@ -557,7 +584,7 @@ class AndroidProjectGenerator(object): if self.override_gradle_path: gradle_wrapper_cmd = [self.override_gradle_path] else: - gradle_wrapper_cmd = ['gradle.bat' if platform.system() == 'Windows' else 'gradle'] + gradle_wrapper_cmd = ['gradle'] gradle_wrapper_cmd.extend(['wrapper', '-p', str(self.build_dir.resolve())]) @@ -580,7 +607,8 @@ class AndroidProjectGenerator(object): asset_type='', android_sdk_path=str(self.android_sdk_path), embed_assets_in_apk=True, - is_unit_test=True) + is_unit_test=True, + android_gradle_plugin_version=self.gradle_plugin_version) else: platform_settings_content = PLATFORM_SETTINGS_FORMAT.format(generation_timestamp=str(datetime.datetime.now().strftime("%c")), platform='android', @@ -589,16 +617,28 @@ class AndroidProjectGenerator(object): asset_type=self.asset_type, android_sdk_path=str(self.android_sdk_path), embed_assets_in_apk=str(self.include_assets_in_apk), - is_unit_test=False) + is_unit_test=False, + android_gradle_plugin_version=self.gradle_plugin_version) platform_settings_file = self.build_dir / 'platform.settings' + + # Check if there already exists the build folder and a 'platform.settings' file. If there is an android gradle + # plugin version set and it is different than the one configured here, we will always overwrite it since + # there could be significant differences from one plug-in to the next + if platform_settings_file.is_file(): + config = configparser.ConfigParser() + config.read([str(platform_settings_file.resolve(strict=True))]) + if config.has_option('android', 'android_gradle_plugin'): + exist_agp_version = config.get('android', 'android_gradle_plugin') + if exist_agp_version != self.gradle_plugin_version: + self.overwrite_existing = True + platform_settings_file.open('w').write(platform_settings_content) def create_default_local_properties(self): """ Create the default 'local.properties' file in the build folder """ - template_android_ndk_path = common.normalize_path_for_settings(self.android_ndk_path, True) template_android_sdk_path = common.normalize_path_for_settings(self.android_sdk_path, True) if self.override_cmake_path: # The cmake dir references the base cmake folder, not the executable path itself, so resolve to the base folder @@ -608,7 +648,6 @@ class AndroidProjectGenerator(object): local_properties_env = { "GENERATION_TIMESTAMP": str(datetime.datetime.now().strftime("%c")), - "ANDROID_NDK_PATH": template_android_ndk_path, "ANDROID_SDK_PATH": template_android_sdk_path, "CMAKE_DIR_LINE": f'cmake.dir={template_cmake_path}' if template_cmake_path else '' } @@ -626,8 +665,7 @@ class AndroidProjectGenerator(object): # before we can process it. android_libraries_substitution_table = { "ANDROID_SDK_HOME": common.normalize_path_for_settings(self.android_sdk_path, False), - "ANDROID_NDK_HOME": common.normalize_path_for_settings(self.android_ndk_path, False), - "ANDROID_SDK_VERSION": "android-".format(self.android_sdk_version) + "ANDROID_SDK_VERSION": f"android-{self.android_sdk_platform}" } android_libraries_template_json_path = self.android_project_builder_path / ANDROID_LIBRARIES_JSON_FILE @@ -717,7 +755,7 @@ class AndroidProjectGenerator(object): template_engine_root = common.normalize_path_for_settings(self.engine_root) template_third_party_path = common.normalize_path_for_settings(self.third_party_path) - template_ndk_path = common.normalize_path_for_settings(self.android_ndk_path) + template_ndk_path = common.normalize_path_for_settings(os.path.join(self.android_sdk_path, self.android_ndk.location)) gradle_build_env = dict() @@ -733,7 +771,6 @@ class AndroidProjectGenerator(object): gradle_build_env['OVERRIDE_JAVA_SOURCESET'] = OVERRIDE_JAVA_SOURCESET_STR.format(absolute_azandroid_path=absolute_azandroid_path) - gradle_build_env['OPTIONAL_JNI_SRC_LIB_SET'] = ', "outputs/native-lib"' for native_config in BUILD_CONFIGURATIONS: @@ -755,7 +792,7 @@ class AndroidProjectGenerator(object): cmake_argument_list.append('"-DLY_TEST_PROJECT=1"') cmake_argument_list.extend([ - f'"-DANDROID_NATIVE_API_LEVEL={self.android_ndk_platform}"', + f'"-DANDROID_NATIVE_API_LEVEL={self.android_native_api_level}"', f'"-DLY_NDK_DIR={template_ndk_path}"', '"-DANDROID_STL=c++_shared"', '"-Wno-deprecated"', @@ -835,8 +872,7 @@ class AndroidProjectGenerator(object): dest_src_main_path.mkdir(parents=True) az_android_package_env = AndroidProjectManifestEnvironment(engine_root=self.engine_root, project_path=self.project_path, - android_sdk_version_number=self.android_sdk_version, - android_ndk_platform_number=self.android_ndk_platform, + android_sdk_version_number=self.android_sdk_platform, is_test=self.is_test_project) self.create_file_from_project_template(src_template_file=ANDROID_MANIFEST_FILE, template_env=az_android_package_env, @@ -1304,218 +1340,7 @@ class AndroidProjectGenerator(object): self.new = new -ANDROID_PLATFORM_PATTERN = re.compile(r'([\w\d]*-)?(\d+\d*)') # Regex to handle android platform naming for both SDKs and NDKs - - -def validate_android_platform_input(input_android_platform, platform_variable_type, min_version, max_version): - """ - Helper tool to support android platform number inputs and perform min/max version validation - - :param input_android_platform: The inpuit argument to evaluate - :param platform_variable_type: The type of platform version to validate (android sdk / android ndk) - :param min_version: The minimum version to validate against - :param max_version: The maximum version to validate against - :return: The int version of the extracted platform number from the input - """ - # Validate the platform number's format and against the supported versions - platform_number_match = ANDROID_PLATFORM_PATTERN.search(input_android_platform) - if not platform_number_match or not platform_number_match.group(2) or (platform_number_match.group(1) and platform_number_match.group(1) != 'android-'): - raise common.LmbrCmdError(f"Invalid {platform_variable_type} version value ({input_android_platform}). It must be " - f"either 'XX' or android-'XX' where 'XX' is a platform number.", - common.ERROR_CODE_INVALID_PARAMETER) - - android_platform_number = int(platform_number_match.group(2)) - if android_platform_number < min_version: - raise common.LmbrCmdError(f"Invalid {platform_variable_type} version value ({input_android_platform}) is less than the minimum " - f"supported version ({min_version}).", - common.ERROR_CODE_INVALID_PARAMETER) - if android_platform_number > max_version: - raise common.LmbrCmdError(f"Invalid {platform_variable_type} version value ({input_android_platform}) is greater than the maximum " - f"supported version ({max_version}).", - common.ERROR_CODE_INVALID_PARAMETER) - return android_platform_number - - ANDROID_SDK_ENV_NAME = 'ANDROID_SDK' -ANDROID_SDK_MIN_PLATFORM = 28 -ANDROID_SDK_MAX_PLATFORM = 29 - - -def verify_android_sdk(android_sdk_platform, argument_name, override_android_sdk_path=None, preferred_sdk_build_tools_ver=None): - """ - Verify the android sdk and the requested platform platform against the android sdk path - - :param android_sdk_platform: The android sdk platform to use (e.g. '28' or 'android-28') - :param argument_name: The name of the argument for descriptive errors to present - :param override_android_sdk_path: The location of the android SDK path if not set through the environment variable - :param preferred_sdk_build_tools_ver: Option prefered built tool version under the android SDK if available. Will fallback to the first one discovered - :returns tuple of the verified android sdk platform number, path to the Android SDK path and the build tool version - """ - android_sdk_platform_number = validate_android_platform_input(input_android_platform=android_sdk_platform, - platform_variable_type='android sdk', - min_version=ANDROID_SDK_MIN_PLATFORM, - max_version=ANDROID_SDK_MAX_PLATFORM) - - # Get the candidate android sdk path from either the override argument or the system environment variable - if override_android_sdk_path: - check_android_sdk_path = override_android_sdk_path - else: - check_android_sdk_path = os.environ.get(ANDROID_SDK_ENV_NAME) - if not check_android_sdk_path: - raise common.LmbrCmdError(f"Android SDK path not set. Make sure that either the '{ANDROID_SDK_ENV_NAME}' environment is " - f"set or it is passed in through the {argument_name} argument") - - # The android sdk folder structure is expected to have a 'platforms' sub folder based on the android sdk-platform number - check_android_sdk_path = pathlib.Path(check_android_sdk_path) - android_sdk_platforms_path = check_android_sdk_path / 'platforms' - if not android_sdk_platforms_path.is_dir(): - raise common.LmbrCmdError(f"Invalid Android SDK path '{str(check_android_sdk_path)}': Missing 'platforms' directory.") - - # Collect the available platform numbers from the platforms subdirectory - validated_android_platforms = [] - for dir_item in android_sdk_platforms_path.iterdir(): - if not dir_item.is_dir(): - continue - check_file = dir_item / 'package.xml' - if check_file.is_file(): - validated_android_platforms.append(dir_item.name) - - if not validated_android_platforms: - raise common.LmbrCmdError(f"Invalid Android SDK path '{str(check_android_sdk_path)}': Unable to find any android platforms.") - - # Normalize the android_sdk argument to fit the same folder name pattern - android_sdk_platform_name = f'android-{android_sdk_platform_number}' - if android_sdk_platform_name not in validated_android_platforms: - raise common.LmbrCmdError(f"Android SDK platform {android_sdk_platform_name} is not a valid for the android SDK located under '{str(check_android_sdk_path)}'") - - # Enumerate through the build tools under android sdk - android_sdk_build_tools_dir = check_android_sdk_path / 'build-tools' - if not android_sdk_build_tools_dir.is_dir(): - raise common.LmbrCmdError(f"Invalid Android SDK path '{str(check_android_sdk_path)}': Unable to find any built-tools folder.") - supported_build_tools = [str(build_tool.name) for build_tool in android_sdk_build_tools_dir.iterdir() if build_tool.is_dir()] - if not supported_build_tools: - raise common.LmbrCmdError(f"Invalid Android SDK path '{str(check_android_sdk_path)}': Unable to find any built-tools.") - if preferred_sdk_build_tools_ver: - if preferred_sdk_build_tools_ver in supported_build_tools: - validated_build_tool = preferred_sdk_build_tools_ver - else: - validated_build_tool = supported_build_tools[0] - logging.warning("Unable to locate android sdk build tool version {preferred_sdk_build_tools_ver}. Defaulting to version {validated_build_tool}") - - else: - validated_build_tool = supported_build_tools[0] - - return android_sdk_platform_number, check_android_sdk_path, validated_build_tool - - -ANDROID_NDK_ENV_NAME = 'ANDROID_NDK' -ANDROID_NDK_MIN_PLATFORM = 21 -ANDROID_NDK_MAX_PLATFORM = 29 -ANDROID_NDK_SOURCE_PROPERTIES_REVISION_PATTERN = re.compile(r'Pkg.Revision\s*=\s*(\d+.\d+.\d+)') - - -def verify_android_ndk(android_ndk_platform, argument_name, override_android_ndk_path=None): - """ - Verify the android ndk and requested platform against the android ndk path - - :param android_ndk_platform: The android ndk platform to use (e.g. '21' or 'android-21') - :param argument_name: The name of the argument for descriptive errors to present - :param override_android_ndk_path: The location of the android NDK path if not set through the environment variable - :returns tuple of the verified android ndk platform number and the Path to the Android SDK path and the - """ - - android_ndk_platform_number = validate_android_platform_input(input_android_platform=android_ndk_platform, - platform_variable_type='android ndk', - min_version=ANDROID_NDK_MIN_PLATFORM, - max_version=ANDROID_NDK_MAX_PLATFORM) - - # Get the candidate android ndk path from either the override argument or the system environment variable - if override_android_ndk_path: - check_android_ndk_path = str(override_android_ndk_path) - else: - check_android_ndk_path = os.environ.get(ANDROID_NDK_ENV_NAME) - if not check_android_ndk_path: - raise common.LmbrCmdError(f"Android NDK path not set. Make sure that either the {ANDROID_NDK_ENV_NAME} environment " - f"is set or it is passed in through the {argument_name} argument") - check_android_ndk_path = pathlib.Path(check_android_ndk_path) - - # Validate the android ndk path - - # Determine the NDK revision by reading the source.properties file - ndk_source_properties_file = check_android_ndk_path / 'source.properties' - if not ndk_source_properties_file.is_file(): - raise common.LmbrCmdError(f"Invalid Android NDK path '{str(check_android_ndk_path)}'. Missing 'source.properties' file.", - common.ERROR_CODE_INVALID_PARAMETER) - ndk_source_properties_file_content = ndk_source_properties_file.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING, - errors=common.ENCODING_ERROR_HANDLINGS) - - ndk_revision_match = ANDROID_NDK_SOURCE_PROPERTIES_REVISION_PATTERN.search(ndk_source_properties_file_content) - if not ndk_revision_match: - raise common.LmbrCmdError(f"Invalid Android NDK path '{str(check_android_ndk_path)}'. Unable to extract version from 'source.properties' file.", - common.ERROR_CODE_INVALID_PARAMETER) - ndk_revision_number = LooseVersion(ndk_revision_match.group(1)) - logging.info(f"Detected Android NDK Revision {str(ndk_revision_number)}") - - # Collect the supported android platforms from the required 'platforms' folder under the ndk path - android_ndk_platforms_path = check_android_ndk_path / 'platforms' - if not android_ndk_platforms_path.is_dir(): - raise common.LmbrCmdError(f"Invalid Android NDK path '{str(check_android_ndk_path)}'. Missing 'platforms' folder.", - common.ERROR_CODE_INVALID_PARAMETER) - - validated_android_platforms = [] - for dir_item in android_ndk_platforms_path.iterdir(): - if not dir_item.is_dir(): - continue - api_version_match = ANDROID_PLATFORM_PATTERN.search(dir_item.name) - if not api_version_match or api_version_match.group(1) != 'android-': - continue - - check_lib_path = dir_item / 'arch-arm64/usr/lib' - if check_lib_path.is_dir(): - validated_android_platforms.append(dir_item.name) - - # For NDK revisions 19 and up, there is a mapping file for version numbers that map to other version. - platforms_map_aliases = {} - if ndk_revision_number >= LooseVersion('19.0.0'): - platforms_map_file = check_android_ndk_path / 'meta/platforms.json' - if platforms_map_file.exists(): - with open(platforms_map_file, 'r') as platforms_map_file_handle: - platforms_map_file_json = json.load(platforms_map_file_handle) - platforms_map_aliases = platforms_map_file_json['aliases'] - elif validated_android_platforms: - # Revisions before 19 does not have a mapping file for API versions, they fall back to the previous one - # So we need to make a mapping file that does the same - platforms_map_aliases = {} - validated_android_platforms.sort() - max_supported_api_number = int(ANDROID_PLATFORM_PATTERN.search(validated_android_platforms[-1]).group(2)) - for validated_android_platform in validated_android_platforms: - current_api_version = int(ANDROID_PLATFORM_PATTERN.search(validated_android_platform).group(2)) - next_api_version = current_api_version + 1 - while f'android-{next_api_version}' not in validated_android_platforms and next_api_version <= max_supported_api_number: - platforms_map_aliases[str(next_api_version)] = current_api_version - next_api_version += 1 - - # Go through the aliases and add to the validated platforms if it is mapped to an existing platform - for alias_key, alias_value in platforms_map_aliases.items(): - if not ANDROID_PLATFORM_PATTERN.search(f'android-{alias_key}'): - # Skip any non android-XX (XX = number) aliases - continue - aliased_platform_key = f'android-{alias_value}' - if aliased_platform_key in validated_android_platforms: - validated_android_platforms.append(f'android-{alias_key}') - - if not validated_android_platforms: - raise common.LmbrCmdError(f"Invalid Android NDK path {str(check_android_ndk_path)}") - - # Verify the ndk platform against the ndk path - android_ndk_platform_name = f'android-{android_ndk_platform_number}' - if android_ndk_platform_name not in validated_android_platforms: - raise common.LmbrCmdError(f"Android NDK platform {android_ndk_platform_name} is not a valid for the Android NDK located under '{str(check_android_ndk_path)}'") - - return android_ndk_platform_number, check_android_ndk_path - - -ADB_TARGET = 'adb.exe' if platform.system() == 'Windows' else 'adb' def resolve_adb_tool(android_sdk_path): @@ -1528,9 +1353,16 @@ def resolve_adb_tool(android_sdk_path): if isinstance(android_sdk_path, str): android_sdk_path = pathlib.Path(android_sdk_path) - check_adb_target = android_sdk_path / 'platform-tools' / ADB_TARGET - if not check_adb_target.exists(): - raise common.LmbrCmdError(f"Invalid Android SDK path '{str(android_sdk_path)}': Unable to locate '{ADB_TARGET}'.") + file_found = False + for executable_path_ext in common.PLATFORM_EXECUTABLE_EXTENSIONS: + check_adb_target = android_sdk_path / 'platform-tools' / f'adb{executable_path_ext}' + if check_adb_target.is_file(): + file_found = True + break + + if not file_found: + raise common.LmbrCmdError(f"Invalid Android SDK path '{str(android_sdk_path)}': Unable to locate 'adb'.") + return check_adb_target @@ -1633,3 +1465,196 @@ class AdbTool(common.CommandLineExec): else: adb_params = arguments return super().popen(adb_params, cwd) + + +class AndroidGradlePluginInfo(object): + + def __init__(self, android_gradle_plugin_version): + + if android_gradle_plugin_version not in ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP.keys(): + raise common.LmbrCmdError(f"Android Gradle Plugin version {android_gradle_plugin_version} is not supported. " + f"Only the following version(s) are supported: {','.join(ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP.keys())}") + + details = ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP[android_gradle_plugin_version] + self.default_sdk_build_tools_version = LooseVersion(details.get('sdk_build')) + + self.default_ndk_version = LooseVersion(details.get('default_ndk')) + + self.min_gradle_version = LooseVersion(details.get('min_gradle_version')) + + self.min_cmake_version = LooseVersion(details.get('min_cmake_version')) + + max_cmake_version_number = details.get('max_cmake_version') + self.max_cmake_version = None if max_cmake_version_number is None else LooseVersion(max_cmake_version_number) + + +class AndroidSDKResolver(object): + """ + Class that manages the Android SDK tool to validate, install packages (e.g. built tools, sdk platforms, ndk, etc) + """ + + class InstalledPackage(object): + def __init__(self, installed_package_components): + assert len(installed_package_components) == 4, '4 sections expected for installed package components (path, version, description, location)' + self.path = installed_package_components[0] + self.version = LooseVersion(installed_package_components[1]) + self.description = installed_package_components[2] + self.location = installed_package_components[3] + + class AvailablePackage(object): + def __init__(self, available_package_components): + assert len(available_package_components) == 3, '3 sections expected for installed package components (path, version, description)' + self.path = available_package_components[0] + self.version = LooseVersion(available_package_components[1]) + self.description = available_package_components[2] + + class AvailableUpdate(object): + def __init__(self, available_update_components): + assert len(available_update_components) == 3, '3 sections expected for installed package components (path, version, available)' + self.path = available_update_components[0] + self.version = LooseVersion(available_update_components[1]) + self.available = available_update_components[2] + + def __init__(self, android_sdk_path): + + self.android_sdk_path = android_sdk_path or os.environ.get(ANDROID_SDK_ENV_NAME) + if not self.android_sdk_path: + raise common.LmbrCmdError(f"Android SDK path not set or it was not passed into the command to generate the android project") + if not os.path.isdir(self.android_sdk_path): + raise common.LmbrCmdError(f"Android SDK path {self.android_sdk_path} is not valid") + if platform.system() == 'Windows': + self.sdk_manager_path = pathlib.Path(self.android_sdk_path) / 'tools' / 'bin' / 'sdkmanager.bat' + else: + raise common.LmbrCmdError(f"This tool is not supported on the current platform {platform.system()}") + if not self.sdk_manager_path.is_file(): + raise common.LmbrCmdError(f"Android SDK path {self.android_sdk_path} is not valid or complete. Missing {self.sdk_manager_path}") + + self.sdk_manager = common.CommandLineExec(str(self.sdk_manager_path.resolve())) + + self.installed_packages = {} + self.available_packages = {} + self.available_updates = {} + self.refresh_sdk_installation() + + def refresh_sdk_installation(self): + """ + Utilize the sdk_manager command line tool from the Android SDK to collect / refresh the list of + installed, available, and updateable packages that are managed by the android SDK. + """ + self.installed_packages = {} + self.available_packages = {} + self.available_updates = {} + + def _factory_installed_package(package_map, item_components): + package_map[item_components[0]] = AndroidSDKResolver.InstalledPackage(item_components) + + def _factory_available_package(package_map, item_components): + package_map[item_components[0]] = AndroidSDKResolver.AvailablePackage(item_components) + + def _factory_available_update(package_map, item_components): + package_map[item_components[0]] = AndroidSDKResolver.AvailableUpdate(item_components) + + # Use the SDK manager to collect the available and installed packages + result_code, result_stdout, result_stderr = self.sdk_manager.exec(['--list'], capture_stdout=True, suppress_stderr=True) + + current_append_map = None + current_item_factory = None + for package_item in result_stdout.split('\n'): + package_item_stripped = package_item.strip() + if not package_item_stripped: + continue + if '|' not in package_item_stripped: + if package_item_stripped.upper() == 'INSTALLED PACKAGES:': + current_append_map = self.installed_packages + current_item_factory = _factory_installed_package + elif package_item_stripped.upper() == 'AVAILABLE PACKAGES:': + current_append_map = self.available_packages + current_item_factory = _factory_available_package + elif package_item_stripped.upper() == 'AVAILABLE UPDATES:': + current_append_map = self.available_updates + current_item_factory = _factory_available_update + else: + current_append_map = None + current_item_factory = None + continue + item_parts = [split.strip() for split in package_item_stripped.split('|')] + if len(item_parts) < 3: + continue + elif item_parts[1].upper() in ('VERSION', 'INSTALLED', '-------'): + continue + elif current_append_map is None: + continue + if current_append_map is not None and current_item_factory is not None: + current_item_factory(current_append_map, item_parts) + + def is_package_installed(self, search_package_path): + """ + Check if a package path to see if its a package that is installed. The path can use wildcard '*'s + The function will return a list of the results that match the package paths, ordered by the newest version first + """ + def _package_sort(package): + return package.version + package_detail_result_list = [] + for installed_package_path, installed_package_details in self.installed_packages.items(): + if fnmatch.fnmatch(installed_package_path, search_package_path): + package_detail_result_list.append(installed_package_details) + package_detail_result_list.sort(reverse=True, key=_package_sort) + return package_detail_result_list + + def is_package_available(self, search_package_path): + """ + Check if a package path to see if its an available package to install. The path can use wildcard '*'s + The function will return a list of the results that match the package paths, ordered by the newest version first + """ + def _package_sort(package): + return package.version + package_detail_result_list = [] + for available_package_path, available_package_details in self.available_packages.items(): + if fnmatch.fnmatch(available_package_path, search_package_path): + package_detail_result_list.append(available_package_details) + package_detail_result_list.sort(reverse=True, key=_package_sort) + return package_detail_result_list + + def install_package(self, package_install_path, package_description): + """ + Install a package based on the path of an available android sdk package + """ + + # Skip installation if the package is already installed + package_result_list = self.is_package_installed(package_install_path) + if package_result_list: + installed_package_detail = package_result_list[0] + logging.info(f"{installed_package_detail.description} (version {installed_package_detail.version}) Detected") + return installed_package_detail + + # Make sure the package name is available + package_result_list = self.is_package_available(package_install_path) + if not package_result_list: + raise common.LmbrCmdError(f"Invalid Android SDK Package {package_description}: Bad package path {package_install_path}") + + # Reverse sort and pick the first item, which should be the latest (if the install path contains wildcards) + def _available_sort(item): + return item.path + + package_result_list.sort(reverse=True, key=_available_sort) + + available_package_to_install = package_result_list[0] # For multiple hits, resolve to the first item which will be the latest version + + # Perform the package installation + logging.info(f"Installing {available_package_to_install.description} ...") + result_code, result_stdout, result_stderr = self.sdk_manager.exec(['--install', available_package_to_install.path], capture_stdout=True, suppress_stderr=True) + if result_code != 0: + raise common.LmbrCmdError(f"Error installing package {available_package_to_install.path}: \n{result_stderr}") + + # Refresh the tracked SDK Contents + self.refresh_sdk_installation() + + # Get the package details to verify + package_result_list = self.is_package_installed(package_install_path) + if package_result_list: + installed_package_detail = package_result_list[0] + logging.info(f"{installed_package_detail.description} (version {installed_package_detail.version}) Installed") + return installed_package_detail + else: + raise common.LmbrCmdError(f"Error installing package {available_package_to_install.path}: \n{result_stderr}") + diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index f3f6a3acda..d25b62dde8 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -27,7 +27,7 @@ from cmake.Tools import common from cmake.Tools.Platform.Android import android_support GRADLE_ARGUMENT_NAME = '--gradle-install-path' -GRADLE_MIN_VERSION = LooseVersion('4.10.1') +GRADLE_MIN_VERSION = LooseVersion('6.5') GRADLE_MAX_VERSION = LooseVersion('7.0.0') GRADLE_VERSION_REGEX = re.compile(r"Gradle\s(\d+.\d+.?\d*)") GRADLE_EXECUTABLE = 'gradle.bat' if platform.system() == 'Windows' else 'gradle' @@ -48,9 +48,9 @@ def verify_gradle(override_gradle_path=None): CMAKE_ARGUMENT_NAME = '--cmake-install-path' -CMAKE_MIN_VERSION = LooseVersion('3.17.0') +CMAKE_MIN_VERSION = LooseVersion('3.19.0') CMAKE_VERSION_REGEX = re.compile(r'cmake version (\d+.\d+.?\d*)') -CMAKE_EXECUTABLE = 'cmake.exe' if platform.system() == 'Windows' else 'cmake' +CMAKE_EXECUTABLE = 'cmake' def verify_cmake(override_cmake_path=None): @@ -69,7 +69,7 @@ def verify_cmake(override_cmake_path=None): NINJA_ARGUMENT_NAME = '--ninja-install-path' NINJA_VERSION_REGEX = re.compile(r'(\d+.\d+.?\d*)') -NINJA_EXECUTABLE = 'ninja.exe' if platform.system() == 'Windows' else 'ninja' +NINJA_EXECUTABLE = 'ninja' def verify_ninja(override_ninja_path=None): @@ -78,7 +78,7 @@ def verify_ninja(override_ninja_path=None): """ return common.verify_tool(override_tool_path=override_ninja_path, tool_name='ninja', - tool_filename='ninja.exe' if platform.system() == 'Windows' else 'ninja', + tool_filename='ninja', argument_name=NINJA_ARGUMENT_NAME, tool_version_argument='--version', tool_version_regex=NINJA_VERSION_REGEX, @@ -103,13 +103,21 @@ def build_optional_signing_profile(store_file, store_password, key_alias, key_pa ANDROID_SDK_ARGUMENT_NAME = '--android-sdk-path' -ANDROID_SDK_PLATFORM_ARGUMENT_NAME = '--android-sdk-version' +ANDROID_SDK_PLATFORM_ARGUMENT_NAME = '--android-sdk-platform' ANDROID_SDK_PREFERRED_TOOL_VER = '--android-sdk-build-tool-version' +ANDROID_NATIVE_API_LEVEL = '--android-native-api-level' + + +MIN_ANDROID_SDK_PLATFORM = 28 # The minimum platform/api level that is supported for the SDK Platform +MIN_NATIVE_API_LEVEL = 24 # The minimum Native API level that is supported for the NDK + -ANDROID_NDK_ARGUMENT_NAME = '--android-ndk-path' ANDROID_NDK_PLATFORM_ARGUMENT_NAME = '--android-ndk-version' +ANDROID_GRADLE_PLUGIN_ARGUMENT_NAME = '--gradle-plugin-version' +ANDROID_GRADLE_MIN_PLUGIN_VERSION = LooseVersion("4.2.0") + # Constants for asset-related options for APK generation INCLUDE_APK_ASSETS_ARGUMENT_NAME = "--include-apk-assets" ASSET_MODE_ARGUMENT_NAME = "--asset-mode" @@ -118,7 +126,7 @@ ASSET_MODE_LOOSE = 'LOOSE' ASSET_MODE_VFS = 'VFS' ALL_ASSET_MODES = [ASSET_MODE_PAK, ASSET_MODE_LOOSE, ASSET_MODE_VFS] ASSET_TYPE_ARGUMENT_NAME = '--asset-type' -DEFAULT_ASSET_TYPE = 'es3' +DEFAULT_ASSET_TYPE = 'android' def wrap_parsed_args(parsed_args): @@ -147,6 +155,7 @@ def main(args): parser = argparse.ArgumentParser(description="Prepare the android studio subfolder") + # Required Arguments parser.add_argument('--engine-root', help='The path to the engine root. Defaults to the current working directory.', default=os.getcwd()) @@ -160,32 +169,42 @@ def main(args): help='The path to the 3rd Party root directory', required=True) - parser.add_argument(ANDROID_NDK_ARGUMENT_NAME, - help='The path to the android NDK', - required=True) - parser.add_argument(ANDROID_SDK_ARGUMENT_NAME, help='The path to the android SDK', required=True) - parser.add_argument(ANDROID_SDK_PLATFORM_ARGUMENT_NAME, - help='The android SDK version', + parser.add_argument('-g', '--project-path', + help='The project path to generate an android project', required=True) + parser.add_argument(ANDROID_SDK_PLATFORM_ARGUMENT_NAME, + help=f'The android SDK platform number version to use for the APK. (Minimum {MIN_ANDROID_SDK_PLATFORM})', + type=int, + default=-1) + + parser.add_argument(ANDROID_NATIVE_API_LEVEL, + help=f'The android native API level to use for the APK. If not set, this will default to the android SDK platform. (Minimum {MIN_ANDROID_SDK_PLATFORM})', + type=int, + default=-1) + + # Override arguments parser.add_argument(ANDROID_SDK_PREFERRED_TOOL_VER, - help='The preferred android sdk build version (i.e. 28.0.3). Will default to the first one detected under the android sdk', - default=None, + help='The android SDK build tools version.', required=False) parser.add_argument(ANDROID_NDK_PLATFORM_ARGUMENT_NAME, help='The android NDK version', - required=True) + required=False) parser.add_argument(GRADLE_ARGUMENT_NAME, help=f'The path to installed gradle. The version of gradle must fall in between {str(GRADLE_MIN_VERSION)} and {str(GRADLE_MAX_VERSION)}.', default=None, required=False) + parser.add_argument(ANDROID_GRADLE_PLUGIN_ARGUMENT_NAME, + help=f'The version of the android gradle plugin to use. Defaults to the minimum version ({ANDROID_GRADLE_MIN_PLUGIN_VERSION})', + default=str(ANDROID_GRADLE_MIN_PLUGIN_VERSION)) + parser.add_argument(CMAKE_ARGUMENT_NAME, help=f'The path to cmake build tool if not installed on the system path. The version of cmake must be at least version {str(CMAKE_MIN_VERSION)}.', default=None, @@ -196,9 +215,6 @@ def main(args): default=None, required=False) - parser.add_argument('-g', '--project-path', - help='The project path to generate an android project') - # Asset Options parser.add_argument(INCLUDE_APK_ASSETS_ARGUMENT_NAME, action='store_true', @@ -207,11 +223,11 @@ def main(args): parser.add_argument(ASSET_MODE_ARGUMENT_NAME, choices=ALL_ASSET_MODES, default=ASSET_MODE_LOOSE, - help='Asset Mode (vfs|pak|loose) to use when including assets into the APK') + help=f'Asset Mode (vfs|pak|loose) to use when including assets into the APK. (Defaults to {ASSET_MODE_LOOSE})') parser.add_argument(ASSET_TYPE_ARGUMENT_NAME, default=DEFAULT_ASSET_TYPE, - help='Asset Type to use when including assets into the APK') + help=f'Asset Type to use when including assets into the APK. (Defaults to {DEFAULT_ASSET_TYPE})') parser.add_argument('--debug', action='store_true', @@ -260,16 +276,81 @@ def main(args): ninja_version, override_ninja_path = verify_ninja(override_ninja_path=parsed_args.get_argument(NINJA_ARGUMENT_NAME)) logging.info("Detected Ninja version %s", str(ninja_version)) - # Verify the android sdk path and sdk version - verified_android_sdk_platform, verified_android_sdk_path, android_sdk_build_tool_ver = android_support.verify_android_sdk(android_sdk_platform=parsed_args.get_argument(ANDROID_SDK_PLATFORM_ARGUMENT_NAME), - argument_name=ANDROID_SDK_ARGUMENT_NAME, - override_android_sdk_path=parsed_args.get_argument(ANDROID_SDK_ARGUMENT_NAME), - preferred_sdk_build_tools_ver=parsed_args.get_argument(ANDROID_SDK_PREFERRED_TOOL_VER)) - - # Verify the android ndk path and ndk version - verified_android_ndk_platform, verified_android_ndk_path = android_support.verify_android_ndk(android_ndk_platform=parsed_args.get_argument(ANDROID_NDK_PLATFORM_ARGUMENT_NAME), - argument_name=ANDROID_NDK_ARGUMENT_NAME, - override_android_ndk_path=parsed_args.get_argument(ANDROID_NDK_ARGUMENT_NAME)) + # Get the android sdk platform version to use from the arguments, but also handle the deprecated argument name + android_sdk_platform_version = parsed_args.get_argument(ANDROID_SDK_PLATFORM_ARGUMENT_NAME) + + # Get the gradle plugin details and validate against the current environment + android_gradle_plugin_version = parsed_args.get_argument(ANDROID_GRADLE_PLUGIN_ARGUMENT_NAME) + android_gradle_plugin = android_support.AndroidGradlePluginInfo(android_gradle_plugin_version) + logging.info(f"Generating Android Gradle Plugin version {android_gradle_plugin_version} based project") + + if gradle_version < android_gradle_plugin.min_gradle_version: + raise common.LmbrCmdError(f"The current version of gradle ({gradle_version}) does not satisfy the minimum version " + f"({android_gradle_plugin.min_gradle_version}) needed for the android gradle plugin " + f"({android_gradle_plugin_version}). Please upgrade your gradle.") + if cmake_version < android_gradle_plugin.min_cmake_version: + raise common.LmbrCmdError(f"The current version of cmake ({cmake_version}) does not satisfy the minimum version " + f"({android_gradle_plugin.min_cmake_version}) needed for the android gradle plugin " + f"({android_gradle_plugin_version}). Please upgrade your cmake.") + if android_gradle_plugin.max_cmake_version and cmake_version > android_gradle_plugin.max_cmake_version: + raise common.LmbrCmdError(f"The current version of cmake ({cmake_version}) exceeds the maximum version " + f"({android_gradle_plugin.max_cmake_version}) of the android gradle plugin " + f"({android_gradle_plugin_version}).") + + # Use the SDK Resolver to make sure the build tools and ndk + android_sdk = android_support.AndroidSDKResolver(android_sdk_path=parsed_args.get_argument(ANDROID_SDK_ARGUMENT_NAME)) + + # If no SDK platform is provided, check for any installed one + if android_sdk_platform_version < 0: + android_sdk_platform_version = MIN_ANDROID_SDK_PLATFORM + installed_android_sdk_platforms = android_sdk.is_package_installed('platforms;*') + if installed_android_sdk_platforms: + # If there are installed platforms, check the most recent one + latest_platform_version = -1 + for installed_android_sdk_platform in installed_android_sdk_platforms: + platform_number_match = re.match(r'platforms;android-([0-9]*)', installed_android_sdk_platform.path) + if not platform_number_match: + continue + check_platform_version = int(platform_number_match.group(1)) + if check_platform_version > latest_platform_version: + latest_platform_version = check_platform_version + if latest_platform_version >= MIN_ANDROID_SDK_PLATFORM: + android_sdk_platform_version = latest_platform_version + else: + if android_sdk_platform_version < MIN_ANDROID_SDK_PLATFORM: + raise common.LmbrCmdError(f"Invalid argument for {ANDROID_SDK_PLATFORM_ARGUMENT_NAME} ({android_sdk_platform_version}). Must be greater than the minimum value supported {MIN_ANDROID_SDK_PLATFORM}.") + + # Get the android native api level from the arguments. Default to the sdk platform version if not provided + android_native_api_level = parsed_args.get_argument(ANDROID_NATIVE_API_LEVEL) + if android_native_api_level < 0: + android_native_api_level = android_sdk_platform_version + else: + if android_native_api_level < MIN_NATIVE_API_LEVEL: + raise common.LmbrCmdError(f"Invalid argument for {ANDROID_NATIVE_API_LEVEL} ({android_native_api_level}). Must be greater than the minimum value supported {MIN_NATIVE_API_LEVEL}.") + + # Check and make sure that the requested sdk platform exists, download if necessary + platform_package_name = f"platforms;android-{android_sdk_platform_version}" + android_sdk.install_package(package_install_path=platform_package_name, + package_description=f'Android SDK Platform {android_sdk_platform_version}') + + # Make sure we have the extra android packages "market_apk_expansion" and "market_licensing" which is needed by the APK + android_sdk.install_package(package_install_path='extras;google;market_apk_expansion', + package_description='Google APK Expansion Library') + + android_sdk.install_package(package_install_path='extras;google;market_licensing', + package_description='Google Play Licensing Library') + + # Install either the requested SDK build tools or the default one for the android gradle plugin version + build_tools_version = parsed_args.get_argument(ANDROID_SDK_PREFERRED_TOOL_VER) or android_gradle_plugin.default_sdk_build_tools_version + build_tools_package_name = f'build-tools;{build_tools_version}' + build_tools_package = android_sdk.install_package(package_install_path=build_tools_package_name, + package_description='Android SDK Build Tools') + + # Install either the requested NDK version or the default one for the android gradle plugin version + android_ndk_version = parsed_args.get_argument(ANDROID_NDK_PLATFORM_ARGUMENT_NAME) or android_gradle_plugin.default_ndk_version + android_ndk_package_name = f'ndk;{android_ndk_version}' + android_ndk_package = android_sdk.install_package(package_install_path=android_ndk_package_name, + package_description='Android NDK') # Verify the engine root path and project path verified_project_path, verified_engine_root = common.verify_project_and_engine_root(project_root=parsed_args.project_path, @@ -277,10 +358,9 @@ def main(args): is_test_project = parsed_args.unit_test # Verify the 3rd Party Root Path - third_party_path = pathlib.Path(parsed_args.third_party_path) / '3rdParty.txt' - if not third_party_path.is_file(): - raise common.LmbrCmdError("Invalid --third-party-path '{}'. Make sure it exists and contains " - "3rdParty.txt".format(parsed_args.third_party_path), + third_party_path = pathlib.Path(parsed_args.third_party_path) + if not third_party_path.is_dir(): + raise common.LmbrCmdError(f"Invalid --third-party-path '{parsed_args.third_party_path}'.", common.ERROR_CODE_INVALID_PARAMETER) third_party_path = third_party_path.parent @@ -293,23 +373,23 @@ def main(args): logging.debug("Engine Root : %s", str(verified_engine_root.resolve())) logging.debug("Build Path : %s", str(build_dir.resolve())) - logging.debug("Android NDK Path : %s", str(verified_android_ndk_path.resolve())) - logging.debug("Android SDK Path : %s", str(verified_android_sdk_path.resolve())) # Prepare the generator and execute generator = android_support.AndroidProjectGenerator(engine_root=verified_engine_root, - project_path=verified_project_path, build_dir=build_dir, - android_sdk_path=verified_android_sdk_path, - android_ndk_path=verified_android_ndk_path, - android_sdk_version=verified_android_sdk_platform, - android_ndk_platform=verified_android_ndk_platform, + android_sdk_path=android_sdk.android_sdk_path, + build_tool=build_tools_package, + android_sdk_platform=android_sdk_platform_version, + android_native_api_level=android_native_api_level, + android_ndk=android_ndk_package, + project_path=verified_project_path, third_party_path=third_party_path, cmake_version=cmake_version, override_cmake_path=override_cmake_path, override_gradle_path=override_gradle_path, + gradle_version=gradle_version, + gradle_plugin_version=android_gradle_plugin_version, override_ninja_path=override_ninja_path, - android_sdk_build_tool_version=android_sdk_build_tool_ver, include_assets_in_apk=parsed_args.get_argument(INCLUDE_APK_ASSETS_ARGUMENT_NAME), asset_mode=parsed_args.get_argument(ASSET_MODE_ARGUMENT_NAME), asset_type=parsed_args.get_argument(ASSET_TYPE_ARGUMENT_NAME), diff --git a/cmake/Tools/Platform/Android/unit_test_android_deployment.py b/cmake/Tools/Platform/Android/unit_test_android_deployment.py index 011033649a..5ee168ca30 100755 --- a/cmake/Tools/Platform/Android/unit_test_android_deployment.py +++ b/cmake/Tools/Platform/Android/unit_test_android_deployment.py @@ -21,7 +21,7 @@ from cmake.Tools.Platform.Android import android_deployment TEST_GAME_NAME = "Foo" TEST_DEV_ROOT = pathlib.Path("Foo") TEST_ASSET_MODE = 'LOOSE' -TEST_ASSET_TYPE = 'es3' +TEST_ASSET_TYPE = 'android' TEST_ANDROID_SDK_PATH = pathlib.Path('c:\\AndroidSDK') TEST_BUILD_DIR = 'android_gradle_test' TEST_DEVICE_ID = '9A201FFAZ000ER' @@ -661,10 +661,10 @@ def test_execute_success(tmpdir, test_config, test_package_name, test_device_sto @pytest.mark.parametrize( "test_game_name, test_config, test_package_name, test_device_storage_path, test_asset_type", [ - pytest.param('game1','profile', 'com.amazon.lumberyard.foo', '/data/fool_storage', 'es3'), - pytest.param('game1','debug', 'com.amazon.lumberyard.foo', '/data/fool_storage', 'es3'), - pytest.param('game2','profile', 'com.amazon.lumberyard.bar', '/data/fool_storage', 'es3'), - pytest.param('game2','debug', 'com.amazon.lumberyard.bar', '/data/fool_storage', 'es3'), + pytest.param('game1','profile', 'com.amazon.lumberyard.foo', '/data/fool_storage', 'android'), + pytest.param('game1','debug', 'com.amazon.lumberyard.foo', '/data/fool_storage', 'android'), + pytest.param('game2','profile', 'com.amazon.lumberyard.bar', '/data/fool_storage', 'android'), + pytest.param('game2','debug', 'com.amazon.lumberyard.bar', '/data/fool_storage', 'android'), pytest.param('game3','profile', 'com.amazon.lumberyard.foo', '/data/fool_storage2', 'pc'), pytest.param('game3','debug', 'com.amazon.lumberyard.foo', '/data/fool_storage2', 'pc'), pytest.param('game4','profile', 'com.amazon.lumberyard.bar', '/data/fool_storage2', 'pc'), diff --git a/cmake/Tools/Platform/Android/unit_test_generate_android_project.py b/cmake/Tools/Platform/Android/unit_test_generate_android_project.py index 5598942046..0cd0f16eaf 100755 --- a/cmake/Tools/Platform/Android/unit_test_generate_android_project.py +++ b/cmake/Tools/Platform/Android/unit_test_generate_android_project.py @@ -170,117 +170,3 @@ def test_verify_ninja(tmpdir, from_override, version_str, expected_result): finally: subprocess.check_output = orig_check_output - -TEST_VALIDATE_VERSION_MIN = 19 -TEST_VALIDATE_VERSION_MAX = 21 - - -@pytest.mark.parametrize( - "test_input, expected", [ - pytest.param('20', 20), - pytest.param('android-20', 20), - pytest.param('bad-21', "android-'XX'"), - pytest.param('10', "minimum"), - pytest.param('30', "maximum") - ] -) -def test_validate_android_platform_input(test_input, expected): - try: - result = android_support.validate_android_platform_input(input_android_platform=test_input, - platform_variable_type='test', - min_version=TEST_VALIDATE_VERSION_MIN, - max_version=TEST_VALIDATE_VERSION_MAX) - assert isinstance(expected, int) - assert result == expected - except Exception as e: - assert expected in str(e) - - -def test_verify_android_sdk_success(tmpdir): - - test_android_path = 'android_sdk' - sdk_version_number = 28 - sdk_version = f'android-{sdk_version_number}' - - tmpdir.ensure(f'{test_android_path}/platforms/{sdk_version}/package.xml') - - tmpdir.ensure(f'{test_android_path}/build-tools/28.0.3/package.xml') - tmpdir.ensure(f'{test_android_path}/build-tools/29.0.3/package.xml') - - input_sdk_path = tmpdir.join(test_android_path).realpath() - argument_name = '--android-sdk' - - requested_build_tool_version = '29.0.3' - - result_sdk_version, result_sdk_path, result_build_tool_version = android_support.verify_android_sdk(android_sdk_platform=sdk_version, - argument_name=argument_name, - override_android_sdk_path=input_sdk_path, - preferred_sdk_build_tools_ver=requested_build_tool_version) - assert result_sdk_version == sdk_version_number - assert result_sdk_path == input_sdk_path - assert result_build_tool_version == requested_build_tool_version - - sdk_version_number_only = str(sdk_version_number) - result_sdk_version, result_sdk_path, result_build_tool_version = android_support.verify_android_sdk(android_sdk_platform=sdk_version_number_only, - argument_name=argument_name, - override_android_sdk_path=input_sdk_path) - assert result_sdk_version == sdk_version_number - assert result_sdk_path == input_sdk_path - assert result_build_tool_version == '28.0.3' - - requested_build_tool_version = '30.0.3' - result_sdk_version, result_sdk_path, result_build_tool_version = android_support.verify_android_sdk(android_sdk_platform=sdk_version, - argument_name=argument_name, - override_android_sdk_path=input_sdk_path, - preferred_sdk_build_tools_ver=requested_build_tool_version) - assert result_sdk_version == sdk_version_number - assert result_sdk_path == input_sdk_path - assert result_build_tool_version == '28.0.3' - - -@pytest.mark.parametrize( - "desired_ndk_version_number, available_ndk_revisions, pkg_revision, mappings, expect_error", [ - pytest.param(21, [21, 22, 24], '15.2.4203891', None, False, id='preNdk19ExactMatch'), - pytest.param(23, [21, 22, 24], '15.2.4203891', None, False, id='preNdk19FallbackMatch'), - pytest.param(22, [21, 22, 24], '19.2.4203891', {'23': 21}, False, id='postNdk19ExactMatch'), - pytest.param(23, [21, 22, 24], '21.2.4203891', {'23': 21}, False, id='postNdk19MappingMatch'), - pytest.param(android_support.ANDROID_NDK_MIN_PLATFORM-1, [21, 22, 24], '15.2.4203891', None, True, id='preNdk19BelowMinVer'), - pytest.param(android_support.ANDROID_NDK_MAX_PLATFORM+1, [21, 22, 24], '15.2.4203891', None, True, id='preNdk19AboveMaxVer'), - pytest.param(25, [21, 22, 24], '19.2.4203891', {'23': 21}, True, id='postNdk19NoMatch') - ] -) -def test_verify_android_ndk_success(tmpdir, desired_ndk_version_number, available_ndk_revisions, pkg_revision, mappings, expect_error): - - test_android_path = 'android_ndk' - for ndk_number in available_ndk_revisions: - tmpdir.ensure(f'{test_android_path}/platforms/android-{ndk_number}/arch-arm64/usr/lib/libc.so') - - tmpdir.ensure(f'{test_android_path}/source.properties') - test_ndk_source_properties_file = tmpdir / test_android_path / 'source.properties' - test_ndk_source_properties_file.write_text(f'Pkg.Desc = Android NDK\nPkg.Revision = {pkg_revision}\n', encoding='ASCII') - - if mappings: - platform_mapping = { - # min and max are arbitrary for now since we dont use it during evaluation, but if we do, parameterize it here as well - "min": 16, # - "max": 29, - "aliases": {} - } - for key, value in mappings.items(): - platform_mapping['aliases'][key] = value - tmpdir.ensure(f'{test_android_path}/meta/platforms.json') - platform_mapping_file = tmpdir / test_android_path / 'meta/platforms.json' - platform_mapping_file.write_text(json.dumps(platform_mapping), encoding='ASCII') - - input_ndk_path = tmpdir.join(test_android_path).realpath() - - try: - android_ndk_platform_number, android_ndk_path = android_support.verify_android_ndk(android_ndk_platform=str(desired_ndk_version_number), - argument_name="--android-ndk", - override_android_ndk_path=input_ndk_path) - assert not expect_error - assert android_ndk_platform_number == desired_ndk_version_number - assert android_ndk_path == input_ndk_path - except Exception: - assert expect_error - diff --git a/cmake/Tools/common.py b/cmake/Tools/common.py index b271c59766..1990041c86 100755 --- a/cmake/Tools/common.py +++ b/cmake/Tools/common.py @@ -55,6 +55,7 @@ ENGINE_ROOT_CHECK_FILE = 'engine.json' HASH_CHUNK_SIZE = 200000 + class LmbrCmdError(Exception): """ Wrapper class to the general exception class where will absorb and prevent the printing of stack. @@ -137,19 +138,32 @@ def get_config_file_values(config_file_path, keys_to_extract): return result_map -def get_bootstrap_values(engine_root, keys_to_extract): +def get_bootstrap_values(bootstrap_dir, keys_to_extract): """ - Extract requested values from the bootstrap.cfg file in the def root folder - :param engine_root: The engine root folder where bootstrap.cfg exists + Extract requested values from the bootstrap.setreg file in the Registry folder + :param bootstrap_dir: The parent directory of the bootstrap.setreg file :param keys_to_extract: The keys to extract into a dictionary :return: Dictionary of keys and its values (for matched keys) """ - bootstrap_file = os.path.join(engine_root, 'bootstrap.cfg') + bootstrap_file = os.path.join(bootstrap_dir, 'bootstrap.setreg') if not os.path.isfile(bootstrap_file): - raise LmbrCmdError("Missing 'bootstrap.cfg' file from engine root ('{}')".format(engine_root), - ERROR_CODE_FILE_NOT_FOUND) + raise logging.error(f'Bootstrap.setreg file {bootstrap_file} does not exist.') + + result_map = {} + with open(bootstrap_file, 'r') as f: + try: + json_data = json.load(f) + except Exception as e: + logging.error(f'Bootstrap.setreg failed to load: {str(e)}') + else: + for search_key in keys_to_extract: + try: + search_result = json_data["Amazon"]["AzCore"]["Bootstrap"][search_key] + except KeyError as e: + logging.warning(f'Bootstrap.setreg cannot find /Amazon/AzCore/Bootstrap/{search_key}: {str(e)}') + else: + result_map[search_key] = search_result - result_map = get_config_file_values(bootstrap_file, keys_to_extract) return result_map @@ -231,6 +245,19 @@ def load_template_file(template_file_path, template_env): raise FileNotFoundError(f"Invalid file path. Cannot find template file located at {str(template_file_path)}") +# Determine the possible file extensions for executable files based on the host platform +PLATFORM_EXECUTABLE_EXTENSIONS = [''] # Files without extensions are always considered + +if platform.system() == 'Windows': + # Windows manages its executable extensions through the %PATHEXT% environment variable + path_extensions_str = os.environ.get('PATHEXT', default='.EXE;.COM;.BAT;.CMD') + PLATFORM_EXECUTABLE_EXTENSIONS.extend([pathext.lower() for pathext in path_extensions_str.split(';')]) +elif platform.system() == 'Linux': + PLATFORM_EXECUTABLE_EXTENSIONS = ['', '.out'] +else: + PLATFORM_EXECUTABLE_EXTENSIONS = [''] + + def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, tool_version_argument, tool_version_regex, min_version, max_version): """ Support method to validate a required system tool needed for the build either through an installed tool in the @@ -257,12 +284,21 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too elif not isinstance(override_tool_path, pathlib.Path): raise LmbrCmdError(f"Invalid {tool_name} path argument. '{override_tool_path}' must be a string or Path", ERROR_CODE_INVALID_PARAMETER) - check_tool_path = override_tool_path / tool_filename - if not check_tool_path.is_file(): - check_tool_path = pathlib.Path(override_tool_path) / 'bin' / tool_filename + file_found = False + for executable_path_ext in PLATFORM_EXECUTABLE_EXTENSIONS: + check_tool_filename = f'{tool_filename}{executable_path_ext}' + + check_tool_path = override_tool_path / check_tool_filename + if check_tool_path.is_file(): + file_found = True + break + check_tool_path = override_tool_path / 'bin' / check_tool_filename + if check_tool_path.is_file(): + file_found = True + break - if not check_tool_path.is_file(): + if not file_found: raise LmbrCmdError(f"Invalid {tool_name} path argument. '{override_tool_path}' is not a valid {tool_name} path", ERROR_CODE_INVALID_PARAMETER) resolved_override_tool_path = str(check_tool_path.resolve()) @@ -271,7 +307,7 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too else: resolved_override_tool_path = None tool_source = tool_name - tool_desc = "installed gradle in the system path" + tool_desc = f"installed {tool_name} in the system path" # Extract the version and verify version_output = subprocess.check_output([tool_source, tool_version_argument], @@ -280,13 +316,17 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too version_match = tool_version_regex.search(version_output) if not version_match: raise RuntimeError() - result_version = LooseVersion(str(version_match.group(1)).strip()) + + + # Since we are doing a compare, strip out any non-numeric and non . character from the version otherwise we will get a TypeError on the LooseVersion comparison + result_version_str = re.sub(r"[^\.0-9]", "", str(version_match.group(1)).strip()) + result_version = LooseVersion(result_version_str) if min_version and result_version < min_version: - raise LmbrCmdError(f"The {tool_desc} does not meet the minimum version of gradle required ({str(min_version)}).", + raise LmbrCmdError(f"The {tool_desc} does not meet the minimum version of {tool_name} required ({str(min_version)}).", ERROR_CODE_ENVIRONMENT_ERROR) elif max_version and result_version > max_version: - raise LmbrCmdError(f"The {tool_desc} exceeds maximum version of gradle supported ({str(max_version)}).", + raise LmbrCmdError(f"The {tool_desc} exceeds maximum version of {tool_name} supported ({str(max_version)}).", ERROR_CODE_ENVIRONMENT_ERROR) return result_version, resolved_override_tool_path diff --git a/cmake/Tools/generate_game_paks.py b/cmake/Tools/generate_game_paks.py deleted file mode 100755 index 6a1ff1e458..0000000000 --- a/cmake/Tools/generate_game_paks.py +++ /dev/null @@ -1,244 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -import argparse -import datetime -import logging -import pathlib -import platform -import sys -import os -import subprocess - -ROOT_DEV_PATH = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')) -if ROOT_DEV_PATH not in sys.path: - sys.path.append(ROOT_DEV_PATH) - -from cmake.Tools import common - -# The location of this python script is not portable relative to the engine root, we determine the engine root based -# on its relative location -DEV_ROOT = os.path.realpath(os.path.join(__file__, '../../..')) - -BOOTSTRAP_CFG = os.path.join(DEV_ROOT, 'bootstrap.cfg') - -EXECUTABLE_EXTN = '.exe' if platform.system() == 'Windows' else '' -RC_NAME = f'rc{EXECUTABLE_EXTN}' -APB_NAME = f'AssetProcessorBatch{EXECUTABLE_EXTN}' - -# Depending on the user request for verbosity, the argument list to subprocess may or may not redirect stdout to NULL -VERBOSE_CALL_ARGS = dict( - shell=True, - cwd=DEV_ROOT -) -NON_VERBOSE_CALL_ARGS = dict( - **VERBOSE_CALL_ARGS, - stdout=subprocess.DEVNULL -) - - -def command_arg(arg): - """ - Work-around for an issue when running subprocess on Linux: subprocess.check_call will take in the argument as an array - but only invokes the first item in the array, ignoring the arguments. As quick fix, we will combine the array into the - full command line and execute it that way on non-windows platforms - """ - if platform.system() == 'Windows': - return arg - else: - return ' '.join(arg) - - -def validate(binfolder, game_name, pak_script): - - # - # Validate the binfolder is relative and contains 'rc' and 'AssetProcessorBatch' - # - if os.path.isabs(binfolder): - raise common.LmbrCmdError("Invalid value for '-b/--binfolder'. It must be a path relative to the engine root folder", - common.ERROR_CODE_ERROR_DIRECTORY) - - binfolder_abs_path = pathlib.Path(DEV_ROOT) / binfolder - if not binfolder_abs_path.is_dir(): - raise common.LmbrCmdError("Invalid value for '-b/--binfolder'. Path does not exist or is not a directory", - common.ERROR_CODE_ERROR_DIRECTORY) - - rc_check = binfolder_abs_path / RC_NAME - if not rc_check.is_file(): - raise common.LmbrCmdError(f"Invalid value for '-b/--binfolder'. Path does not contain {RC_NAME}", - common.ERROR_CODE_ERROR_DIRECTORY) - - apb_check = binfolder_abs_path / APB_NAME - if not apb_check.is_file(): - raise common.LmbrCmdError(f"Invalid value for '-b/--binfolder'. Path does not contain {APB_NAME}", - common.ERROR_CODE_ERROR_DIRECTORY) - - # - # Validate the game name represents a game project within the game engine - # - gamefolder_abs_path = pathlib.Path(DEV_ROOT) / game_name - if not gamefolder_abs_path.is_dir(): - raise common.LmbrCmdError(f"Invalid value for '-g/--game-name'. No game '{game_name} exists.", - common.ERROR_CODE_ERROR_DIRECTORY) - - project_json_path = gamefolder_abs_path / 'project.json' - if not project_json_path.is_file(): - raise common.LmbrCmdError( - f"Invalid value for '-g/--game-name'. Folder '{game_name} is not a valid game project.", - common.ERROR_CODE_FILE_NOT_FOUND) - - if not os.path.isfile(pak_script): - raise common.LmbrCmdError(f'Pak script file {pak_script} does not exist.', - common.ERROR_CODE_FILE_NOT_FOUND) - - -def process(binfolder, game_name, asset_platform, autorun_assetprocessor, recompress, fastest_compression, target, - pak_script, warn_on_assetprocessor_error, verbose): - - logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG if verbose else logging.INFO) - - target_path_root_abs = pathlib.Path(DEV_ROOT) / target - if target_path_root_abs.is_file(): - raise common.LmbrCmdError(f"Target '{target}' already exists as a file.", - common.ERROR_CODE_GENERAL_ERROR) - os.makedirs(target_path_root_abs.absolute(), exist_ok=True) - - target_pak_folder_name = f'{game_name.lower()}_{asset_platform}_paks' - target_pak = target_path_root_abs / target_pak_folder_name - - # Prepare the asset processor batch arguments and execute if requested - if autorun_assetprocessor: - ap_executable = os.path.join(binfolder, APB_NAME) - ap_cmd_args = [ap_executable, - f'/gamefolder={game_name}', - f'/platforms={asset_platform}'] - logging.debug("Calling {}".format(' '.join(ap_cmd_args))) - try: - logging.info(f"Running {APB_NAME} on {game_name}") - start_time = datetime.datetime.now() - - call_args = VERBOSE_CALL_ARGS if verbose else NON_VERBOSE_CALL_ARGS - - subprocess.check_call(command_arg(ap_cmd_args), - **call_args) - - total_time = datetime.datetime.now() - start_time - logging.info(f"Asset Processing Complete. Elapse: {total_time}") - except subprocess.CalledProcessError: - if warn_on_assetprocessor_error: - logging.warning('AssetProcessorBatch reported errors') - else: - raise common.LmbrCmdError("AssetProcessorBatch has one or more failed assets.", - common.ERROR_CODE_GENERAL_ERROR) - - rc_executable = os.path.join(binfolder, RC_NAME) - rc_cmd_args = [rc_executable, - f'/job={pak_script}', - f'/p={asset_platform}', - f'/game={game_name}', - f'/trg={target_pak}'] - if recompress: - rc_cmd_args.append('/recompress=1') - if fastest_compression: - rc_cmd_args.append('/use_fastest=1') - logging.debug("Calling {}".format(' '.join(rc_cmd_args))) - - try: - logging.info(f"Running {APB_NAME} on {game_name}") - start_time = datetime.datetime.now() - - call_args = VERBOSE_CALL_ARGS if verbose else NON_VERBOSE_CALL_ARGS - - subprocess.check_call(command_arg(rc_cmd_args), - **call_args) - - total_time = datetime.datetime.now() - start_time - logging.info(f"Asset Processing Complete. Elapse: {total_time}") - logging.info(f"Pak files for {game_name} written to {target_pak}") - - except subprocess.CalledProcessError as err: - raise common.LmbrCmdError(f"{RC_NAME} returned an error: {str(err)}.", - err.returncode) - - -def main(args): - - parser = argparse.ArgumentParser() - - parser.add_argument('-b', '--binfolder', - help='The relative location of the binary folder that contains the resource compiler and asset processor') - - bootstrap = common.get_bootstrap_values(DEV_ROOT, ['project_path']) - parser.add_argument('-g', '--game-name', - help='The name of the Game whose asset pak will be generated for', - default=bootstrap.get('project_path')) - - parser.add_argument('-p', '--asset-platform', - help='The asset platform type to process') - - parser.add_argument('-a', '--autorun-assetprocessor', - help='Option to automatically invoke asset processor batch on the game before generating the pak', - action='store_true') - - parser.add_argument('-w', '--warn-on-assetprocessor-error', - help='When -a/--autorun-assetprocessor is specified, warn on asset processor failure rather than aborting the process', - action='store_true') - - parser.add_argument('-r', '--recompress', - action='store_true', - help='If present, the ResourceCompiler (RC.exe) will decompress and compress back each PAK file ' - 'found as they are transferred from the cache folder to the game_pc_pak folder.') - parser.add_argument('-fc', '--fastest-compression', - action='store_true', - help='As each file is being added to its PAK file, they will be compressed across all available ' - 'codecs (ZLIB, ZSTD and LZ4) and the one with the fastest decompression time will be ' - 'chosen. The default is to always use ZLIB') - parser.add_argument('--target', - default='Pak', - help='Specify a target folder for the pak files. (Default : Pak)') - parser.add_argument('--pak-script', - default=f'{DEV_ROOT}/{os.path.normpath("Code/Tools/RC/Config/rc/RCJob_Generic_MakePaks.xml")}', - help="The absolute path of the pak script configuration file to use to create the paks.") - - parser.add_argument('-v', '--verbose', - help='Enable debug messages', - action='store_true') - - parsed = parser.parse_args(args) - - validate(binfolder=parsed.binfolder, - game_name=parsed.game_name, - pak_script=parsed.pak_script) - - process(binfolder=parsed.binfolder, - game_name=parsed.game_name, - asset_platform=parsed.asset_platform, - autorun_assetprocessor=parsed.autorun_assetprocessor, - recompress=parsed.recompress, - fastest_compression=parsed.fastest_compression, - target=parsed.target, - pak_script=parsed.pak_script, - warn_on_assetprocessor_error=parsed.warn_on_assetprocessor_error, - verbose=parsed.verbose) - - -if __name__ == '__main__': - try: - if not os.path.isfile(BOOTSTRAP_CFG): - raise common.LmbrCmdError("Invalid dev root, missing bootstrap.cfg.", - common.ERROR_CODE_FILE_NOT_FOUND) - - main(sys.argv[1:]) - exit(0) - - except common.LmbrCmdError as err: - print(str(err), file=sys.stderr) - exit(err.code) diff --git a/cmake/Tools/global_project.py b/cmake/Tools/global_project.py deleted file mode 100644 index 1d84d9dcfb..0000000000 --- a/cmake/Tools/global_project.py +++ /dev/null @@ -1,168 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -import argparse -import logging -import os -import sys -import re -import pathlib -import json -import cmake.Tools.registration as registration - -logger = logging.getLogger() -logging.basicConfig() - - -def set_global_project(project_name: str or None, - project_path: str or pathlib.Path or None) -> int: - """ - set what the current project is - :param project_name: the name of the project you want to set, resolves project_path - :param project_path: the path of the project you want to set - :return: 0 for success or non 0 failure code - """ - if project_path and project_name: - logger.error(f'Project Name and Project Path provided, these are mutually exclusive.') - return 1 - - if not project_name and not project_path: - logger.error('Must specify either a Project name or Project Path.') - return 1 - - if project_name and not project_path: - project_path = registration.get_registered(project_name=project_name) - - if not project_path: - logger.error(f'Project Path {project_path} has not been registered.') - return 1 - - project_path = pathlib.Path(project_path).resolve() - - bootstrap_setreg_file = registration.get_o3de_registry_folder() / 'bootstrap.setreg' - if bootstrap_setreg_file.is_file(): - with bootstrap_setreg_file.open('r') as f: - try: - json_data = json.load(f) - except Exception as e: - logger.error(f'Bootstrap.setreg failed to load: {str(e)}') - else: - try: - json_data["Amazon"]["AzCore"]["Bootstrap"]["project_path"] = project_path - except Exception as e: - logger.error(f'Bootstrap.setreg failed to load: {str(e)}') - else: - try: - os.unlink(bootstrap_setreg_file) - except Exception as e: - logger.error(f'Failed to unlink bootstrap file {bootstrap_setreg_file}: {str(e)}') - return 1 - else: - json_data = {} - json_data.update({"Amazon":{"AzCore":{"Bootstrap":{"project_path":project_path.as_posix()}}}}) - - with bootstrap_setreg_file.open('w') as s: - s.write(json.dumps(json_data, indent=4)) - - return 0 - - -def get_global_project() -> pathlib.Path or None: - """ - get what the current project set is - :return: project_path or None on failure - """ - bootstrap_setreg_file = registration.get_o3de_registry_folder() / 'bootstrap.setreg' - if not bootstrap_setreg_file.is_file(): - logger.error(f'Bootstrap.setreg file {bootstrap_setreg_file} does not exist.') - return None - - with bootstrap_setreg_file.open('r') as f: - try: - json_data = json.load(f) - except Exception as e: - logger.error(f'Bootstrap.setreg failed to load: {str(e)}') - else: - try: - project_path = json_data["Amazon"]["AzCore"]["Bootstrap"]["project_path"] - except Exception as e: - logger.error(f'Bootstrap.setreg cannot find Amazon:AzCore:Bootstrap:project_path: {str(e)}') - else: - return pathlib.Path(project_path).resolve() - return None - -def _run_get_global_project(args: argparse) -> int: - if args.override_home_folder: - registration.override_home_folder = args.override_home_folder - - project_path = get_global_project() - if project_path: - print(project_path.as_posix()) - return 0 - return 1 - - -def _run_set_global_project(args: argparse) -> int: - if args.override_home_folder: - registration.override_home_folder = args.override_home_folder - - return set_global_project(args.project_name, - args.project_path) - - -def add_args(parser, subparsers) -> None: - """ - add_args is called to add expected parser arguments and subparsers arguments to each command such that it can be - invoked locally or aggregated by a central python file. - Ex. Directly run from this file alone with: python global_project.py set_global_project --project-name TestProject - OR - o3de.py can aggregate commands by importing global_project, call add_args and - execute: python o3de.py set_global_project --project-path C:/TestProject - :param parser: the caller instantiates a parser and passes it in here - :param subparsers: the caller instantiates subparsers and passes it in here - """ - get_global_project_subparser = subparsers.add_parser('get-global-project') - get_global_project_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') - - get_global_project_subparser.set_defaults(func=_run_get_global_project) - - set_global_project_subparser = subparsers.add_parser('set-global-project') - group = set_global_project_subparser.add_mutually_exclusive_group(required=True) - group.add_argument('-pn', '--project-name', required=False, - help='The name of the project. If supplied this will resolve the --project-path.') - group.add_argument('-pp', '--project-path', required=False, - help='The path to the project') - - set_global_project_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') - - set_global_project_subparser.set_defaults(func=_run_set_global_project) - - -if __name__ == "__main__": - # parse the command line args - the_parser = argparse.ArgumentParser() - - # add subparsers - the_subparsers = the_parser.add_subparsers(help='sub-command help') - - # add args to the parser - add_args(the_parser, the_subparsers) - - # parse args - the_args = the_parser.parse_args() - - # run - ret = the_args.func(the_args) - - # return - sys.exit(ret) diff --git a/cmake/Tools/layout_tool.py b/cmake/Tools/layout_tool.py index 8f573b61f5..3fcab241d7 100755 --- a/cmake/Tools/layout_tool.py +++ b/cmake/Tools/layout_tool.py @@ -78,19 +78,19 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ if remote_on_check is None: # Validate that if '_connect_to_remote is enabled, that the 'input_remote_ip' is not set to local host if input_remote_connect == '1' and input_remote_ip == LOCAL_HOST: - return _warn("'bootstrap.cfg' is configured to connect to Asset Processor remotely, but the 'remote_ip' " + return _warn("'bootstrap.setreg' is configured to connect to Asset Processor remotely, but the 'remote_ip' " " is configured for LOCAL HOST") else: if remote_on_check: # Verify we are set for remote AP connection if input_remote_ip == LOCAL_HOST: - return _warn(f"'bootstrap.cfg' is not configured for a remote Asset Processor connection (remote_ip={input_remote_ip})") + return _warn(f"'bootstrap.setreg' is not configured for a remote Asset Processor connection (remote_ip={input_remote_ip})") if input_remote_connect != '1': - return _warn(f"'bootstrap.cfg' is not configured for a remote Asset Processor connection ({platform_name}_connect_to_remote={input_remote_connect}") + return _warn(f"'bootstrap.setreg' is not configured for a remote Asset Processor connection ({platform_name}_connect_to_remote={input_remote_connect}") else: # Verify we are disabled for remote AP connection if input_remote_connect != '0': - return _warn(f"'bootstrap.cfg' is not configured for a remote Asset Processor connection ({platform_name}_connect_to_remote={input_remote_connect}") + return _warn(f"'bootstrap.setreg' is not configured for a remote Asset Processor connection ({platform_name}_connect_to_remote={input_remote_connect}") return 0 @@ -107,20 +107,15 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ project_name_lower = project_path.lower() layout_path = pathlib.Path(layout_dir) - # Validate bootstrap.cfg exists - bootstrap_file = layout_path / 'bootstrap.cfg' - if not bootstrap_file.is_file(): - warning_count += _warn(f"'bootstrap.cfg' is missing from {str(layout_path)}") - bootstrap_values = None - else: - bootstrap_values = common.get_config_file_values(str(bootstrap_file), [f'{platform_name_lower}_remote_filesystem', - f'{platform_name_lower}_connect_to_remote', - f'{platform_name_lower}_wait_for_connect', - f'{platform_name_lower}_assets', - f'assets', - f'{platform_name_lower}_remote_ip', - f'remote_ip' - ]) + bootstrap_path = pathlib.Path(ROOT_ENGINE_PATH) / 'Registry' + bootstrap_values = common.get_bootstrap_values(str(bootstrap_path), [f'{platform_name_lower}_remote_filesystem', + f'{platform_name_lower}_connect_to_remote', + f'{platform_name_lower}_wait_for_connect', + f'{platform_name_lower}_assets', + f'assets', + f'{platform_name_lower}_remote_ip', + f'remote_ip' + ]) # Validate the system_{platform}_{asset type}.cfg exists platform_system_cfg_file = layout_path / f'system_{platform_name_lower}_{asset_type}.cfg' @@ -141,9 +136,9 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ # Validate that the asset type for the platform matches the one set for the build bootstrap_asset_type = bootstrap_values.get(f'{platform_name_lower}_assets') or bootstrap_values.get('assets') if not bootstrap_asset_type: - warning_count += _warn("'bootstrap.cfg' is missing specifications for asset type.") + warning_count += _warn("'bootstrap.setreg' is missing specifications for asset type.") elif bootstrap_asset_type != asset_type: - warning_count += _warn(f"The asset type specified in bootstrap.cfg ({bootstrap_asset_type}) does not match the asset type specified for this deployment({asset_type}).") + warning_count += _warn(f"The asset type specified in bootstrap.setreg ({bootstrap_asset_type}) does not match the asset type specified for this deployment({asset_type}).") # Validate that if '_connect_to_remote is enabled, that the 'remote_ip' is not set to local host warning_count += _validate_remote_ap(remote_ip, remote_connect, None) @@ -211,7 +206,7 @@ def verify_layout(layout_dir, platform_name, project_path, asset_mode, asset_typ elif asset_mode == ASSET_MODE_VFS: remote_file_system = bootstrap_values.get(f'{platform_name_lower}_remote_filesystem') or '0' if not remote_file_system != '1': - warning_count += _warn("Remote file system is not configured in bootstrap.cfg for VFS mode.") + warning_count += _warn("Remote file system is not configured in bootstrap.setreg for VFS mode.") else: warning_count += _validate_remote_ap(remote_ip, remote_connect, True) diff --git a/cmake/Tools/registration.py b/cmake/Tools/registration.py deleted file mode 100755 index b13419414b..0000000000 --- a/cmake/Tools/registration.py +++ /dev/null @@ -1,4439 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# -""" -This file contains all the code that has to do with registering engines, projects, gems and templates -""" - -import argparse -import logging -import os -import sys -import json -import pathlib -import hashlib -import shutil -import zipfile -import urllib.parse -import urllib.request - -logger = logging.getLogger() -logging.basicConfig() - - -def backup_file(file_name: str or pathlib.Path) -> None: - index = 0 - renamed = False - while not renamed: - backup_file_name = pathlib.Path(str(file_name) + '.bak' + str(index)).resolve() - index += 1 - if not backup_file_name.is_file(): - file_name = pathlib.Path(file_name).resolve() - file_name.rename(backup_file_name) - if backup_file_name.is_file(): - renamed = True - - -def backup_folder(folder: str or pathlib.Path) -> None: - index = 0 - renamed = False - while not renamed: - backup_folder_name = pathlib.Path(str(folder) + '.bak' + str(index)).resolve() - index += 1 - if not backup_folder_name.is_dir(): - folder = pathlib.Path(folder).resolve() - folder.rename(backup_folder_name) - if backup_folder_name.is_dir(): - renamed = True - - -def get_this_engine_path() -> pathlib.Path: - return pathlib.Path(os.path.realpath(__file__)).parents[2].resolve() - - -override_home_folder = None - - -def get_home_folder() -> pathlib.Path: - if override_home_folder: - return pathlib.Path(override_home_folder).resolve() - else: - return pathlib.Path(os.path.expanduser("~")).resolve() - - -def get_o3de_folder() -> pathlib.Path: - o3de_folder = get_home_folder() / '.o3de' - o3de_folder.mkdir(parents=True, exist_ok=True) - return o3de_folder - - -def get_o3de_registry_folder() -> pathlib.Path: - registry_folder = get_o3de_folder() / 'Registry' - registry_folder.mkdir(parents=True, exist_ok=True) - return registry_folder - - -def get_o3de_cache_folder() -> pathlib.Path: - cache_folder = get_o3de_folder() / 'Cache' - cache_folder.mkdir(parents=True, exist_ok=True) - return cache_folder - - -def get_o3de_download_folder() -> pathlib.Path: - download_folder = get_o3de_folder() / 'Download' - download_folder.mkdir(parents=True, exist_ok=True) - return download_folder - - -def get_o3de_engines_folder() -> pathlib.Path: - engines_folder = get_o3de_folder() / 'Engines' - engines_folder.mkdir(parents=True, exist_ok=True) - return engines_folder - - -def get_o3de_projects_folder() -> pathlib.Path: - projects_folder = get_o3de_folder() / 'Projects' - projects_folder.mkdir(parents=True, exist_ok=True) - return projects_folder - - -def get_o3de_gems_folder() -> pathlib.Path: - gems_folder = get_o3de_folder() / 'Gems' - gems_folder.mkdir(parents=True, exist_ok=True) - return gems_folder - - -def get_o3de_templates_folder() -> pathlib.Path: - templates_folder = get_o3de_folder() / 'Templates' - templates_folder.mkdir(parents=True, exist_ok=True) - return templates_folder - - -def get_o3de_restricted_folder() -> pathlib.Path: - restricted_folder = get_o3de_folder() / 'Restricted' - restricted_folder.mkdir(parents=True, exist_ok=True) - return restricted_folder - - -def get_o3de_logs_folder() -> pathlib.Path: - restricted_folder = get_o3de_folder() / 'Logs' - restricted_folder.mkdir(parents=True, exist_ok=True) - return restricted_folder - - -def register_shipped_engine_o3de_objects() -> int: - engine_path = get_this_engine_path() - - ret_val = 0 - - # directories with engines - starting_engines_directories = [ - ] - for engines_directory in sorted(starting_engines_directories, reverse=True): - error_code = register_all_engines_in_folder(engines_path=engines_directory) - if error_code: - ret_val = error_code - - # specific engines - starting_engines = [ - ] - for engine_path in sorted(starting_engines): - error_code = register(engine_path=engine_path) - if error_code: - ret_val = error_code - - # directories with projects - starting_projects_directories = [ - ] - for projects_directory in sorted(starting_projects_directories, reverse=True): - error_code = register_all_projects_in_folder(engine_path=engine_path, projects_path=projects_directory) - if error_code: - ret_val = error_code - - # specific projects - starting_projects = [ - f'{engine_path}/AutomatedTesting' - ] - for project_path in sorted(starting_projects, reverse=True): - error_code = register(engine_path=engine_path, project_path=project_path) - if error_code: - ret_val = error_code - - # directories with gems - starting_gems_directories = [ - f'{engine_path}/Gems' - ] - for gems_directory in sorted(starting_gems_directories, reverse=True): - error_code = register_all_gems_in_folder(engine_path=engine_path, gems_path=gems_directory) - if error_code: - ret_val = error_code - - # specific gems - starting_gems = [ - ] - for gem_path in sorted(starting_gems, reverse=True): - error_code = register(engine_path=engine_path, gem_path=gem_path) - if error_code: - ret_val = error_code - - # directories with templates - starting_templates_directories = [ - f'{engine_path}/Templates' - ] - for templates_directory in sorted(starting_templates_directories, reverse=True): - error_code = register_all_templates_in_folder(engine_path=engine_path, templates_path=templates_directory) - if error_code: - ret_val = error_code - - # specific templates - starting_templates = [ - ] - for template_path in sorted(starting_templates, reverse=True): - error_code = register(engine_path=engine_path, template_path=template_path) - if error_code: - ret_val = error_code - - # directories with restricted - starting_restricted_directories = [ - ] - for restricted_directory in sorted(starting_restricted_directories, reverse=True): - error_code = register_all_restricted_in_folder(engine_path=engine_path, restricted_path=restricted_directory) - if error_code: - ret_val = error_code - - # specific restricted - starting_restricted = [ - ] - for restricted_path in sorted(starting_restricted, reverse=True): - error_code = register(engine_path=engine_path, restricted_path=restricted_path) - if error_code: - ret_val = error_code - - # directories with repos - starting_repo_directories = [ - ] - for repos_directory in sorted(starting_repo_directories, reverse=True): - error_code = register_all_repos_in_folder(engine_path=engine_path, repos_path=repos_directory) - if error_code: - ret_val = error_code - - # specific repos - starting_repos = [ - ] - for repo_uri in sorted(starting_repos, reverse=True): - error_code = register(repo_uri=repo_uri) - if error_code: - ret_val = error_code - - # register anything in the users default folders globally - error_code = register_all_engines_in_folder(get_registered(default_folder='engines')) - if error_code: - ret_val = error_code - error_code = register_all_projects_in_folder(get_registered(default_folder='projects')) - if error_code: - ret_val = error_code - error_code = register_all_gems_in_folder(get_registered(default_folder='gems')) - if error_code: - ret_val = error_code - error_code = register_all_templates_in_folder(get_registered(default_folder='templates')) - if error_code: - ret_val = error_code - error_code = register_all_restricted_in_folder(get_registered(default_folder='restricted')) - if error_code: - ret_val = error_code - error_code = register_all_restricted_in_folder(get_registered(default_folder='projects')) - if error_code: - ret_val = error_code - error_code = register_all_restricted_in_folder(get_registered(default_folder='gems')) - if error_code: - ret_val = error_code - error_code = register_all_restricted_in_folder(get_registered(default_folder='templates')) - if error_code: - ret_val = error_code - - json_data = load_o3de_manifest() - engine_object = find_engine_data(json_data) - gems = json_data['gems'].copy() - gems.extend(engine_object['gems']) - for gem_path in sorted(gems, key=len): - gem_path = pathlib.Path(gem_path).resolve() - gem_cmake_lists_txt = gem_path / 'CMakeLists.txt' - if gem_cmake_lists_txt.is_file(): - add_gem_to_cmake(engine_path=engine_path, gem_path=gem_path, supress_errors=True) # don't care about errors - - return ret_val - - -def register_all_in_folder(folder_path: str or pathlib.Path, - remove: bool = False, - engine_path: str or pathlib.Path = None, - exclude: list = None) -> int: - if not folder_path: - logger.error(f'Folder path cannot be empty.') - return 1 - - folder_path = pathlib.Path(folder_path).resolve() - if not folder_path.is_dir(): - logger.error(f'Folder path is not dir.') - return 1 - - engines_set = set() - projects_set = set() - gems_set = set() - templates_set = set() - restricted_set = set() - repo_set = set() - - ret_val = 0 - for root, dirs, files in os.walk(folder_path): - if root in exclude: - continue - - for name in files: - if name == 'engine.json': - engines_set.add(root) - elif name == 'project.json': - projects_set.add(root) - elif name == 'gem.json': - gems_set.add(root) - elif name == 'template.json': - templates_set.add(root) - elif name == 'restricted.json': - restricted_set.add(root) - elif name == 'repo.json': - repo_set.add(root) - - for engine in sorted(engines_set, reverse=True): - error_code = register(engine_path=engine, remove=remove) - if error_code: - ret_val = error_code - - for project in sorted(projects_set, reverse=True): - error_code = register(engine_path=engine_path, project_path=project, remove=remove) - if error_code: - ret_val = error_code - - for gem in sorted(gems_set, reverse=True): - error_code = register(engine_path=engine_path, gem_path=gem, remove=remove) - if error_code: - ret_val = error_code - - for template in sorted(templates_set, reverse=True): - error_code = register(engine_path=engine_path, template_path=template, remove=remove) - if error_code: - ret_val = error_code - - for restricted in sorted(restricted_set, reverse=True): - error_code = register(engine_path=engine_path, restricted_path=restricted, remove=remove) - if error_code: - ret_val = error_code - - for repo in sorted(repo_set, reverse=True): - error_code = register(engine_path=engine_path, repo_uri=repo, remove=remove) - if error_code: - ret_val = error_code - - return ret_val - - -def register_all_engines_in_folder(engines_path: str or pathlib.Path, - remove: bool = False) -> int: - if not engines_path: - logger.error(f'Engines path cannot be empty.') - return 1 - - engines_path = pathlib.Path(engines_path).resolve() - if not engines_path.is_dir(): - logger.error(f'Engines path is not dir.') - return 1 - - engines_set = set() - - ret_val = 0 - for root, dirs, files in os.walk(engines_path): - for name in files: - if name == 'engine.json': - engines_set.add(name) - - for engine in sorted(engines_set, reverse=True): - error_code = register(engine_path=engine, remove=remove) - if error_code: - ret_val = error_code - - return ret_val - - -def register_all_projects_in_folder(projects_path: str or pathlib.Path, - remove: bool = False, - engine_path: str or pathlib.Path = None) -> int: - if not projects_path: - logger.error(f'Projects path cannot be empty.') - return 1 - - projects_path = pathlib.Path(projects_path).resolve() - if not projects_path.is_dir(): - logger.error(f'Projects path is not dir.') - return 1 - - projects_set = set() - - ret_val = 0 - for root, dirs, files in os.walk(projects_path): - for name in files: - if name == 'project.json': - projects_set.add(root) - - for project in sorted(projects_set, reverse=True): - error_code = register(engine_path=engine_path, project_path=project, remove=remove) - if error_code: - ret_val = error_code - - return ret_val - - -def register_all_gems_in_folder(gems_path: str or pathlib.Path, - remove: bool = False, - engine_path: str or pathlib.Path = None) -> int: - if not gems_path: - logger.error(f'Gems path cannot be empty.') - return 1 - - gems_path = pathlib.Path(gems_path).resolve() - if not gems_path.is_dir(): - logger.error(f'Gems path is not dir.') - return 1 - - gems_set = set() - - ret_val = 0 - for root, dirs, files in os.walk(gems_path): - for name in files: - if name == 'gem.json': - gems_set.add(root) - - for gem in sorted(gems_set, reverse=True): - error_code = register(engine_path=engine_path, gem_path=gem, remove=remove) - if error_code: - ret_val = error_code - - return ret_val - - -def register_all_templates_in_folder(templates_path: str or pathlib.Path, - remove: bool = False, - engine_path: str or pathlib.Path = None) -> int: - if not templates_path: - logger.error(f'Templates path cannot be empty.') - return 1 - - templates_path = pathlib.Path(templates_path).resolve() - if not templates_path.is_dir(): - logger.error(f'Templates path is not dir.') - return 1 - - templates_set = set() - - ret_val = 0 - for root, dirs, files in os.walk(templates_path): - for name in files: - if name == 'template.json': - templates_set.add(root) - - for template in sorted(templates_set, reverse=True): - error_code = register(engine_path=engine_path, template_path=template, remove=remove) - if error_code: - ret_val = error_code - - return ret_val - - -def register_all_restricted_in_folder(restricted_path: str or pathlib.Path, - remove: bool = False, - engine_path: str or pathlib.Path = None) -> int: - if not restricted_path: - logger.error(f'Restricted path cannot be empty.') - return 1 - - restricted_path = pathlib.Path(restricted_path).resolve() - if not restricted_path.is_dir(): - logger.error(f'Restricted path is not dir.') - return 1 - - restricted_set = set() - - ret_val = 0 - for root, dirs, files in os.walk(restricted_path): - for name in files: - if name == 'restricted.json': - restricted_set.add(root) - - for restricted in sorted(restricted_set, reverse=True): - error_code = register(engine_path=engine_path, restricted_path=restricted, remove=remove) - if error_code: - ret_val = error_code - - return ret_val - - -def register_all_repos_in_folder(repos_path: str or pathlib.Path, - remove: bool = False, - engine_path: str or pathlib.Path = None) -> int: - if not repos_path: - logger.error(f'Repos path cannot be empty.') - return 1 - - repos_path = pathlib.Path(repos_path).resolve() - if not repos_path.is_dir(): - logger.error(f'Repos path is not dir.') - return 1 - - repo_set = set() - - ret_val = 0 - for root, dirs, files in os.walk(repos_path): - for name in files: - if name == 'repo.json': - repo_set.add(root) - - for repo in sorted(repo_set, reverse=True): - error_code = register(engine_path=engine_path, repo_uri=repo, remove=remove) - if error_code: - ret_val = error_code - - return ret_val - - -def get_o3de_manifest() -> pathlib.Path: - manifest_path = get_o3de_folder() / 'o3de_manifest.json' - if not manifest_path.is_file(): - username = os.path.split(get_home_folder())[-1] - - o3de_folder = get_o3de_folder() - default_registry_folder = get_o3de_registry_folder() - default_cache_folder = get_o3de_cache_folder() - default_downloads_folder = get_o3de_download_folder() - default_logs_folder = get_o3de_logs_folder() - default_engines_folder = get_o3de_engines_folder() - default_projects_folder = get_o3de_projects_folder() - default_gems_folder = get_o3de_gems_folder() - default_templates_folder = get_o3de_templates_folder() - default_restricted_folder = get_o3de_restricted_folder() - - default_projects_restricted_folder = default_projects_folder / 'Restricted' - default_projects_restricted_folder.mkdir(parents=True, exist_ok=True) - default_gems_restricted_folder = default_gems_folder / 'Restricted' - default_gems_restricted_folder.mkdir(parents=True, exist_ok=True) - default_templates_restricted_folder = default_templates_folder / 'Restricted' - default_templates_restricted_folder.mkdir(parents=True, exist_ok=True) - - json_data = {} - json_data.update({'o3de_manifest_name': f'{username}'}) - json_data.update({'origin': o3de_folder.as_posix()}) - json_data.update({'default_engines_folder': default_engines_folder.as_posix()}) - json_data.update({'default_projects_folder': default_projects_folder.as_posix()}) - json_data.update({'default_gems_folder': default_gems_folder.as_posix()}) - json_data.update({'default_templates_folder': default_templates_folder.as_posix()}) - json_data.update({'default_restricted_folder': default_restricted_folder.as_posix()}) - - json_data.update({'projects': []}) - json_data.update({'gems': []}) - json_data.update({'templates': []}) - json_data.update({'restricted': []}) - json_data.update({'repos': []}) - json_data.update({'engines': []}) - - default_restricted_folder_json = default_restricted_folder / 'restricted.json' - if not default_restricted_folder_json.is_file(): - with default_restricted_folder_json.open('w') as s: - restricted_json_data = {} - restricted_json_data.update({'restricted_name': 'o3de'}) - s.write(json.dumps(restricted_json_data, indent=4)) - json_data.update({'default_restricted_folder': default_restricted_folder.as_posix()}) - - default_projects_restricted_folder_json = default_projects_restricted_folder / 'restricted.json' - if not default_projects_restricted_folder_json.is_file(): - with default_projects_restricted_folder_json.open('w') as s: - restricted_json_data = {} - restricted_json_data.update({'restricted_name': 'projects'}) - s.write(json.dumps(restricted_json_data, indent=4)) - - default_gems_restricted_folder_json = default_gems_restricted_folder / 'restricted.json' - if not default_gems_restricted_folder_json.is_file(): - with default_gems_restricted_folder_json.open('w') as s: - restricted_json_data = {} - restricted_json_data.update({'restricted_name': 'gems'}) - s.write(json.dumps(restricted_json_data, indent=4)) - - default_templates_restricted_folder_json = default_templates_restricted_folder / 'restricted.json' - if not default_templates_restricted_folder_json.is_file(): - with default_templates_restricted_folder_json.open('w') as s: - restricted_json_data = {} - restricted_json_data.update({'restricted_name': 'templates'}) - s.write(json.dumps(restricted_json_data, indent=4)) - - with manifest_path.open('w') as s: - s.write(json.dumps(json_data, indent=4)) - - return manifest_path - - -def load_o3de_manifest() -> dict: - with get_o3de_manifest().open('r') as f: - try: - json_data = json.load(f) - except Exception as e: - logger.error(f'Manifest json failed to load: {str(e)}') - else: - return json_data - - -def save_o3de_manifest(json_data: dict) -> None: - with get_o3de_manifest().open('w') as s: - try: - s.write(json.dumps(json_data, indent=4)) - except Exception as e: - logger.error(f'Manifest json failed to save: {str(e)}') - - -def register_engine_path(json_data: dict, - engine_path: str or pathlib.Path, - remove: bool = False) -> int: - if not engine_path: - logger.error(f'Engine path cannot be empty.') - return 1 - engine_path = pathlib.Path(engine_path).resolve() - - for engine_object in json_data['engines']: - engine_object_path = pathlib.Path(engine_object['path']).resolve() - if engine_object_path == engine_path: - json_data['engines'].remove(engine_object) - - if remove: - return 0 - - if not engine_path.is_dir(): - logger.error(f'Engine path {engine_path} does not exist.') - return 1 - - engine_json = engine_path / 'engine.json' - if not valid_o3de_engine_json(engine_json): - logger.error(f'Engine json {engine_json} is not valid.') - return 1 - - engine_object = {} - engine_object.update({'path': engine_path.as_posix()}) - engine_object.update({'projects': []}) - engine_object.update({'gems': []}) - engine_object.update({'templates': []}) - engine_object.update({'restricted': []}) - engine_object.update({'external_subdirectories': []}) - - json_data['engines'].insert(0, engine_object) - - return 0 - - -def register_gem_path(json_data: dict, - gem_path: str or pathlib.Path, - remove: bool = False, - engine_path: str or pathlib.Path = None) -> int: - if not gem_path: - logger.error(f'Gem path cannot be empty.') - return 1 - gem_path = pathlib.Path(gem_path).resolve() - - if engine_path: - engine_data = find_engine_data(json_data, engine_path) - if not engine_data: - logger.error(f'Engine path {engine_path} is not registered.') - return 1 - - while gem_path in engine_data['gems']: - engine_data['gems'].remove(gem_path) - - while gem_path.as_posix() in engine_data['gems']: - engine_data['gems'].remove(gem_path.as_posix()) - - if remove: - logger.warn(f'Removing Gem path {gem_path}.') - return 0 - else: - while gem_path in json_data['gems']: - json_data['gems'].remove(gem_path) - - while gem_path.as_posix() in json_data['gems']: - json_data['gems'].remove(gem_path.as_posix()) - - if remove: - logger.warn(f'Removing Gem path {gem_path}.') - return 0 - - if not gem_path.is_dir(): - logger.error(f'Gem path {gem_path} does not exist.') - return 1 - - gem_json = gem_path / 'gem.json' - if not valid_o3de_gem_json(gem_json): - logger.error(f'Gem json {gem_json} is not valid.') - return 1 - - if engine_path: - engine_data['gems'].insert(0, gem_path.as_posix()) - else: - json_data['gems'].insert(0, gem_path.as_posix()) - - return 0 - - -def register_project_path(json_data: dict, - project_path: str or pathlib.Path, - remove: bool = False, - engine_path: str or pathlib.Path = None) -> int: - if not project_path: - logger.error(f'Project path cannot be empty.') - return 1 - project_path = pathlib.Path(project_path).resolve() - - if engine_path: - engine_data = find_engine_data(json_data, engine_path) - if not engine_data: - logger.error(f'Engine path {engine_path} is not registered.') - return 1 - - while project_path in engine_data['projects']: - engine_data['projects'].remove(project_path) - - while project_path.as_posix() in engine_data['projects']: - engine_data['projects'].remove(project_path.as_posix()) - - if remove: - logger.warn(f'Engine {engine_path} removing Project path {project_path}.') - return 0 - else: - while project_path in json_data['projects']: - json_data['projects'].remove(project_path) - - while project_path.as_posix() in json_data['projects']: - json_data['projects'].remove(project_path.as_posix()) - - if remove: - logger.warn(f'Removing Project path {project_path}.') - return 0 - - if not project_path.is_dir(): - logger.error(f'Project path {project_path} does not exist.') - return 1 - - project_json = project_path / 'project.json' - if not valid_o3de_project_json(project_json): - logger.error(f'Project json {project_json} is not valid.') - return 1 - - if engine_path: - engine_data['projects'].insert(0, project_path.as_posix()) - else: - json_data['projects'].insert(0, project_path.as_posix()) - - # registering a project has the additional step of setting the project.json 'engine' field - this_engine_json = get_this_engine_path() / 'engine.json' - with this_engine_json.open('r') as f: - try: - this_engine_json = json.load(f) - except Exception as e: - logger.error(f'Engine json failed to load: {str(e)}') - return 1 - with project_json.open('r') as f: - try: - project_json_data = json.load(f) - except Exception as e: - logger.error(f'Project json failed to load: {str(e)}') - return 1 - - update_project_json = False - try: - update_project_json = project_json_data['engine'] != this_engine_json['engine_name'] - except Exception as e: - update_project_json = True - - if update_project_json: - project_json_data['engine'] = this_engine_json['engine_name'] - backup_file(project_json) - with project_json.open('w') as s: - try: - s.write(json.dumps(project_json_data, indent=4)) - except Exception as e: - logger.error(f'Project json failed to save: {str(e)}') - return 1 - - return 0 - - -def register_template_path(json_data: dict, - template_path: str or pathlib.Path, - remove: bool = False, - engine_path: str or pathlib.Path = None) -> int: - if not template_path: - logger.error(f'Template path cannot be empty.') - return 1 - template_path = pathlib.Path(template_path).resolve() - - if engine_path: - engine_data = find_engine_data(json_data, engine_path) - if not engine_data: - logger.error(f'Engine path {engine_path} is not registered.') - return 1 - - while template_path in engine_data['templates']: - engine_data['templates'].remove(template_path) - - while template_path.as_posix() in engine_data['templates']: - engine_data['templates'].remove(template_path.as_posix()) - - if remove: - logger.warn(f'Engine {engine_path} removing Template path {template_path}.') - return 0 - else: - while template_path in json_data['templates']: - json_data['templates'].remove(template_path) - - while template_path.as_posix() in json_data['templates']: - json_data['templates'].remove(template_path.as_posix()) - - if remove: - logger.warn(f'Removing Template path {template_path}.') - return 0 - - if not template_path.is_dir(): - logger.error(f'Template path {template_path} does not exist.') - return 1 - - template_json = template_path / 'template.json' - if not valid_o3de_template_json(template_json): - logger.error(f'Template json {template_json} is not valid.') - return 1 - - if engine_path: - engine_data['templates'].insert(0, template_path.as_posix()) - else: - json_data['templates'].insert(0, template_path.as_posix()) - - return 0 - - -def register_restricted_path(json_data: dict, - restricted_path: str or pathlib.Path, - remove: bool = False, - engine_path: str or pathlib.Path = None) -> int: - if not restricted_path: - logger.error(f'Restricted path cannot be empty.') - return 1 - restricted_path = pathlib.Path(restricted_path).resolve() - - if engine_path: - engine_data = find_engine_data(json_data, engine_path) - if not engine_data: - logger.error(f'Engine path {engine_path} is not registered.') - return 1 - - while restricted_path in engine_data['restricted']: - engine_data['restricted'].remove(restricted_path) - - while restricted_path.as_posix() in engine_data['restricted']: - engine_data['restricted'].remove(restricted_path.as_posix()) - - if remove: - logger.warn(f'Engine {engine_path} removing Restricted path {restricted_path}.') - return 0 - else: - while restricted_path in json_data['restricted']: - json_data['restricted'].remove(restricted_path) - - while restricted_path.as_posix() in json_data['restricted']: - json_data['restricted'].remove(restricted_path.as_posix()) - - if remove: - logger.warn(f'Removing Restricted path {restricted_path}.') - return 0 - - if not restricted_path.is_dir(): - logger.error(f'Restricted path {restricted_path} does not exist.') - return 1 - - restricted_json = restricted_path / 'restricted.json' - if not valid_o3de_restricted_json(restricted_json): - logger.error(f'Restricted json {restricted_json} is not valid.') - return 1 - - if engine_path: - engine_data['restricted'].insert(0, restricted_path.as_posix()) - else: - json_data['restricted'].insert(0, restricted_path.as_posix()) - - return 0 - - -def valid_o3de_repo_json(file_name: str or pathlib.Path) -> bool: - file_name = pathlib.Path(file_name).resolve() - if not file_name.is_file(): - return False - - with file_name.open('r') as f: - try: - json_data = json.load(f) - test = json_data['repo_name'] - test = json_data['origin'] - except Exception as e: - return False - - return True - - -def valid_o3de_engine_json(file_name: str or pathlib.Path) -> bool: - file_name = pathlib.Path(file_name).resolve() - if not file_name.is_file(): - return False - - with file_name.open('r') as f: - try: - json_data = json.load(f) - test = json_data['engine_name'] - # test = json_data['origin'] # will be required soon - except Exception as e: - return False - return True - - -def valid_o3de_project_json(file_name: str or pathlib.Path) -> bool: - file_name = pathlib.Path(file_name).resolve() - if not file_name.is_file(): - return False - - with file_name.open('r') as f: - try: - json_data = json.load(f) - test = json_data['project_name'] - # test = json_data['origin'] # will be required soon - except Exception as e: - return False - return True - - -def valid_o3de_gem_json(file_name: str or pathlib.Path) -> bool: - file_name = pathlib.Path(file_name).resolve() - if not file_name.is_file(): - return False - - with file_name.open('r') as f: - try: - json_data = json.load(f) - test = json_data['gem_name'] - # test = json_data['origin'] # will be required soon - except Exception as e: - return False - return True - - -def valid_o3de_template_json(file_name: str or pathlib.Path) -> bool: - file_name = pathlib.Path(file_name).resolve() - if not file_name.is_file(): - return False - with file_name.open('r') as f: - try: - json_data = json.load(f) - test = json_data['template_name'] - # test = json_data['origin'] # will be required soon - except Exception as e: - return False - return True - - -def valid_o3de_restricted_json(file_name: str or pathlib.Path) -> bool: - file_name = pathlib.Path(file_name).resolve() - if not file_name.is_file(): - return False - with file_name.open('r') as f: - try: - json_data = json.load(f) - test = json_data['restricted_name'] - # test = json_data['origin'] # will be required soon - except Exception as e: - return False - return True - - -def process_add_o3de_repo(file_name: str or pathlib.Path, - repo_set: set) -> int: - file_name = pathlib.Path(file_name).resolve() - if not valid_o3de_repo_json(file_name): - return 1 - - cache_folder = get_o3de_cache_folder() - - with file_name.open('r') as f: - try: - repo_data = json.load(f) - except Exception as e: - logger.error(f'{file_name} failed to load: {str(e)}') - return 1 - - for engine_uri in repo_data['engines']: - engine_uri = f'{engine_uri}/engine.json' - engine_sha256 = hashlib.sha256(engine_uri.encode()) - cache_file = cache_folder / str(engine_sha256.hexdigest() + '.json') - if not cache_file.is_file(): - parsed_uri = urllib.parse.urlparse(engine_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - with urllib.request.urlopen(engine_uri) as s: - with cache_file.open('wb') as f: - shutil.copyfileobj(s, f) - else: - engine_json = pathlib.Path(engine_uri).resolve() - if not engine_json.is_file(): - return 1 - shutil.copy(engine_json, cache_file) - - for project_uri in repo_data['projects']: - project_uri = f'{project_uri}/project.json' - project_sha256 = hashlib.sha256(project_uri.encode()) - cache_file = cache_folder / str(project_sha256.hexdigest() + '.json') - if not cache_file.is_file(): - parsed_uri = urllib.parse.urlparse(project_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - with urllib.request.urlopen(project_uri) as s: - with cache_file.open('wb') as f: - shutil.copyfileobj(s, f) - else: - project_json = pathlib.Path(project_uri).resolve() - if not project_json.is_file(): - return 1 - shutil.copy(project_json, cache_file) - - for gem_uri in repo_data['gems']: - gem_uri = f'{gem_uri}/gem.json' - gem_sha256 = hashlib.sha256(gem_uri.encode()) - cache_file = cache_folder / str(gem_sha256.hexdigest() + '.json') - if not cache_file.is_file(): - parsed_uri = urllib.parse.urlparse(gem_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - with urllib.request.urlopen(gem_uri) as s: - with cache_file.open('wb') as f: - shutil.copyfileobj(s, f) - else: - gem_json = pathlib.Path(gem_uri).resolve() - if not gem_json.is_file(): - return 1 - shutil.copy(gem_json, cache_file) - - for template_uri in repo_data['templates']: - template_uri = f'{template_uri}/template.json' - template_sha256 = hashlib.sha256(template_uri.encode()) - cache_file = cache_folder / str(template_sha256.hexdigest() + '.json') - if not cache_file.is_file(): - parsed_uri = urllib.parse.urlparse(template_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - with urllib.request.urlopen(template_uri) as s: - with cache_file.open('wb') as f: - shutil.copyfileobj(s, f) - else: - template_json = pathlib.Path(template_uri).resolve() - if not template_json.is_file(): - return 1 - shutil.copy(template_json, cache_file) - - for repo_uri in repo_data['repos']: - if repo_uri not in repo_set: - repo_set.add(repo_uri) - repo_uri = f'{repo_uri}/repo.json' - repo_sha256 = hashlib.sha256(repo_uri.encode()) - cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json') - if not cache_file.is_file(): - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - with urllib.request.urlopen(repo_uri) as s: - with cache_file.open('wb') as f: - shutil.copyfileobj(s, f) - else: - repo_json = pathlib.Path(repo_uri).resolve() - if not repo_json.is_file(): - return 1 - shutil.copy(repo_json, cache_file) - return 0 - - -def register_repo(json_data: dict, - repo_uri: str or pathlib.Path, - remove: bool = False) -> int: - if not repo_uri: - logger.error(f'Repo URI cannot be empty.') - return 1 - - url = f'{repo_uri}/repo.json' - parsed_uri = urllib.parse.urlparse(url) - - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - while repo_uri in json_data['repos']: - json_data['repos'].remove(repo_uri) - else: - repo_uri = pathlib.Path(repo_uri).resolve() - while repo_uri.as_posix() in json_data['repos']: - json_data['repos'].remove(repo_uri.as_posix()) - - if remove: - logger.warn(f'Removing repo uri {repo_uri}.') - return 0 - - repo_sha256 = hashlib.sha256(url.encode()) - cache_file = get_o3de_cache_folder() / str(repo_sha256.hexdigest() + '.json') - - result = 0 - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - if not cache_file.is_file(): - with urllib.request.urlopen(url) as s: - with cache_file.open('wb') as f: - shutil.copyfileobj(s, f) - json_data['repos'].insert(0, repo_uri) - else: - if not cache_file.is_file(): - origin_file = pathlib.Path(url).resolve() - if not origin_file.is_file(): - return 1 - shutil.copy(origin_file, origin_file) - json_data['repos'].insert(0, repo_uri.as_posix()) - - repo_set = set() - result = process_add_o3de_repo(cache_file, repo_set) - - return result - - -def register_default_engines_folder(json_data: dict, - default_engines_folder: str or pathlib.Path, - remove: bool = False) -> int: - if remove: - default_engines_folder = get_o3de_engines_folder() - - # make sure the path exists - default_engines_folder = pathlib.Path(default_engines_folder).resolve() - if not default_engines_folder.is_dir(): - logger.error(f'Default engines folder {default_engines_folder} does not exist.') - return 1 - - default_engines_folder = default_engines_folder.as_posix() - json_data['default_engines_folder'] = default_engines_folder - - return 0 - - -def register_default_projects_folder(json_data: dict, - default_projects_folder: str or pathlib.Path, - remove: bool = False) -> int: - if remove: - default_projects_folder = get_o3de_projects_folder() - - # make sure the path exists - default_projects_folder = pathlib.Path(default_projects_folder).resolve() - if not default_projects_folder.is_dir(): - logger.error(f'Default projects folder {default_projects_folder} does not exist.') - return 1 - - default_projects_folder = default_projects_folder.as_posix() - json_data['default_projects_folder'] = default_projects_folder - - return 0 - - -def register_default_gems_folder(json_data: dict, - default_gems_folder: str or pathlib.Path, - remove: bool = False) -> int: - if remove: - default_gems_folder = get_o3de_gems_folder() - - # make sure the path exists - default_gems_folder = pathlib.Path(default_gems_folder).resolve() - if not default_gems_folder.is_dir(): - logger.error(f'Default gems folder {default_gems_folder} does not exist.') - return 1 - - default_gems_folder = default_gems_folder.as_posix() - json_data['default_gems_folder'] = default_gems_folder - - return 0 - - -def register_default_templates_folder(json_data: dict, - default_templates_folder: str or pathlib.Path, - remove: bool = False) -> int: - if remove: - default_templates_folder = get_o3de_templates_folder() - - # make sure the path exists - default_templates_folder = pathlib.Path(default_templates_folder).resolve() - if not default_templates_folder.is_dir(): - logger.error(f'Default templates folder {default_templates_folder} does not exist.') - return 1 - - default_templates_folder = default_templates_folder.as_posix() - json_data['default_templates_folder'] = default_templates_folder - - return 0 - - -def register_default_restricted_folder(json_data: dict, - default_restricted_folder: str or pathlib.Path, - remove: bool = False) -> int: - if remove: - default_restricted_folder = get_o3de_restricted_folder() - - # make sure the path exists - default_restricted_folder = pathlib.Path(default_restricted_folder).resolve() - if not default_restricted_folder.is_dir(): - logger.error(f'Default restricted folder {default_restricted_folder} does not exist.') - return 1 - - default_restricted_folder = default_restricted_folder.as_posix() - json_data['default_restricted_folder'] = default_restricted_folder - - return 0 - - -def register(engine_path: str or pathlib.Path = None, - project_path: str or pathlib.Path = None, - gem_path: str or pathlib.Path = None, - template_path: str or pathlib.Path = None, - restricted_path: str or pathlib.Path = None, - repo_uri: str or pathlib.Path = None, - default_engines_folder: str or pathlib.Path = None, - default_projects_folder: str or pathlib.Path = None, - default_gems_folder: str or pathlib.Path = None, - default_templates_folder: str or pathlib.Path = None, - default_restricted_folder: str or pathlib.Path = None, - remove: bool = False - ) -> int: - """ - Adds/Updates entries to the .o3de/o3de_manifest.json - - :param engine_path: if engine folder is supplied the path will be added to the engine if it can, if not global - :param project_path: project folder - :param gem_path: gem folder - :param template_path: template folder - :param restricted_path: restricted folder - :param repo_uri: repo uri - :param default_engines_folder: default engines folder - :param default_projects_folder: default projects folder - :param default_gems_folder: default gems folder - :param default_templates_folder: default templates folder - :param default_restricted_folder: default restricted code folder - :param remove: add/remove the entries - - :return: 0 for success or non 0 failure code - """ - - json_data = load_o3de_manifest() - - result = 0 - - # do anything that could require a engine context first - if isinstance(project_path, str) or isinstance(project_path, pathlib.PurePath): - if not project_path: - logger.error(f'Project path cannot be empty.') - return 1 - result = register_project_path(json_data, project_path, remove, engine_path) - - elif isinstance(gem_path, str) or isinstance(gem_path, pathlib.PurePath): - if not gem_path: - logger.error(f'Gem path cannot be empty.') - return 1 - result = register_gem_path(json_data, gem_path, remove, engine_path) - - elif isinstance(template_path, str) or isinstance(template_path, pathlib.PurePath): - if not template_path: - logger.error(f'Template path cannot be empty.') - return 1 - result = register_template_path(json_data, template_path, remove, engine_path) - - elif isinstance(restricted_path, str) or isinstance(restricted_path, pathlib.PurePath): - if not restricted_path: - logger.error(f'Restricted path cannot be empty.') - return 1 - result = register_restricted_path(json_data, restricted_path, remove, engine_path) - - elif isinstance(repo_uri, str) or isinstance(repo_uri, pathlib.PurePath): - if not repo_uri: - logger.error(f'Repo URI cannot be empty.') - return 1 - result = register_repo(json_data, repo_uri, remove) - - elif isinstance(default_engines_folder, str) or isinstance(default_engines_folder, pathlib.PurePath): - result = register_default_engines_folder(json_data, default_engines_folder, remove) - - elif isinstance(default_projects_folder, str) or isinstance(default_projects_folder, pathlib.PurePath): - result = register_default_projects_folder(json_data, default_projects_folder, remove) - - elif isinstance(default_gems_folder, str) or isinstance(default_gems_folder, pathlib.PurePath): - result = register_default_gems_folder(json_data, default_gems_folder, remove) - - elif isinstance(default_templates_folder, str) or isinstance(default_templates_folder, pathlib.PurePath): - result = register_default_templates_folder(json_data, default_templates_folder, remove) - - elif isinstance(default_restricted_folder, str) or isinstance(default_restricted_folder, pathlib.PurePath): - result = register_default_restricted_folder(json_data, default_restricted_folder, remove) - - # engine is done LAST - # Now that everything that could have an engine context is done, if the engine is supplied that means this is - # registering the engine itself - elif isinstance(engine_path, str) or isinstance(engine_path, pathlib.PurePath): - if not engine_path: - logger.error(f'Engine path cannot be empty.') - return 1 - result = register_engine_path(json_data, engine_path, remove) - - if not result: - save_o3de_manifest(json_data) - - return result - - -def remove_invalid_o3de_objects() -> None: - json_data = load_o3de_manifest() - - for engine_object in json_data['engines']: - engine_path = engine_object['path'] - if not valid_o3de_engine_json(pathlib.Path(engine_path).resolve() / 'engine.json'): - logger.warn(f"Engine path {engine_path} is invalid.") - register(engine_path=engine_path, remove=True) - else: - for project in engine_object['projects']: - if not valid_o3de_project_json(pathlib.Path(project).resolve() / 'project.json'): - logger.warn(f"Project path {project} is invalid.") - register(engine_path=engine_path, project_path=project, remove=True) - - for gem_path in engine_object['gems']: - if not valid_o3de_gem_json(pathlib.Path(gem_path).resolve() / 'gem.json'): - logger.warn(f"Gem path {gem_path} is invalid.") - register(engine_path=engine_path, gem_path=gem_path, remove=True) - - for template_path in engine_object['templates']: - if not valid_o3de_template_json(pathlib.Path(template_path).resolve() / 'template.json'): - logger.warn(f"Template path {template_path} is invalid.") - register(engine_path=engine_path, template_path=template_path, remove=True) - - for restricted in engine_object['restricted']: - if not valid_o3de_restricted_json(pathlib.Path(restricted).resolve() / 'restricted.json'): - logger.warn(f"Restricted path {restricted} is invalid.") - register(engine_path=engine_path, restricted_path=restricted, remove=True) - - for external in engine_object['external_subdirectories']: - external = pathlib.Path(external).resolve() - if not external.is_dir(): - logger.warn(f"External subdirectory {external} is invalid.") - remove_external_subdirectory(external) - - for project in json_data['projects']: - if not valid_o3de_project_json(pathlib.Path(project).resolve() / 'project.json'): - logger.warn(f"Project path {project} is invalid.") - register(project_path=project, remove=True) - - for gem in json_data['gems']: - if not valid_o3de_gem_json(pathlib.Path(gem).resolve() / 'gem.json'): - logger.warn(f"Gem path {gem} is invalid.") - register(gem_path=gem, remove=True) - - for template in json_data['templates']: - if not valid_o3de_template_json(pathlib.Path(template).resolve() / 'template.json'): - logger.warn(f"Template path {template} is invalid.") - register(template_path=template, remove=True) - - for restricted in json_data['restricted']: - if not valid_o3de_restricted_json(pathlib.Path(restricted).resolve() / 'restricted.json'): - logger.warn(f"Restricted path {restricted} is invalid.") - register(restricted_path=restricted, remove=True) - - default_engines_folder = pathlib.Path(json_data['default_engines_folder']).resolve() - if not default_engines_folder.is_dir(): - new_default_engines_folder = get_o3de_folder() / 'Engines' - new_default_engines_folder.mkdir(parents=True, exist_ok=True) - logger.warn( - f"Default engines folder {default_engines_folder} is invalid. Set default {new_default_engines_folder}") - register(default_engines_folder=new_default_engines_folder.as_posix()) - - default_projects_folder = pathlib.Path(json_data['default_projects_folder']).resolve() - if not default_projects_folder.is_dir(): - new_default_projects_folder = get_o3de_folder() / 'Projects' - new_default_projects_folder.mkdir(parents=True, exist_ok=True) - logger.warn( - f"Default projects folder {default_projects_folder} is invalid. Set default {new_default_projects_folder}") - register(default_projects_folder=new_default_projects_folder.as_posix()) - - default_gems_folder = pathlib.Path(json_data['default_gems_folder']).resolve() - if not default_gems_folder.is_dir(): - new_default_gems_folder = get_o3de_folder() / 'Gems' - new_default_gems_folder.mkdir(parents=True, exist_ok=True) - logger.warn(f"Default gems folder {default_gems_folder} is invalid." - f" Set default {new_default_gems_folder}") - register(default_gems_folder=new_default_gems_folder.as_posix()) - - default_templates_folder = pathlib.Path(json_data['default_templates_folder']).resolve() - if not default_templates_folder.is_dir(): - new_default_templates_folder = get_o3de_folder() / 'Templates' - new_default_templates_folder.mkdir(parents=True, exist_ok=True) - logger.warn( - f"Default templates folder {default_templates_folder} is invalid." - f" Set default {new_default_templates_folder}") - register(default_templates_folder=new_default_templates_folder.as_posix()) - - default_restricted_folder = pathlib.Path(json_data['default_restricted_folder']).resolve() - if not default_restricted_folder.is_dir(): - default_restricted_folder = get_o3de_folder() / 'Restricted' - default_restricted_folder.mkdir(parents=True, exist_ok=True) - logger.warn( - f"Default restricted folder {default_restricted_folder} is invalid." - f" Set default {default_restricted_folder}") - register(default_restricted_folder=default_restricted_folder.as_posix()) - - -def refresh_repos() -> int: - json_data = load_o3de_manifest() - - # clear the cache - cache_folder = get_o3de_cache_folder() - shutil.rmtree(cache_folder) - cache_folder = get_o3de_cache_folder() # will recreate it - - result = 0 - - # set will stop circular references - repo_set = set() - - for repo_uri in json_data['repos']: - if repo_uri not in repo_set: - repo_set.add(repo_uri) - - repo_uri = f'{repo_uri}/repo.json' - repo_sha256 = hashlib.sha256(repo_uri.encode()) - cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json') - if not cache_file.is_file(): - parsed_uri = urllib.parse.urlparse(repo_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - with urllib.request.urlopen(repo_uri) as s: - with cache_file.open('wb') as f: - shutil.copyfileobj(s, f) - else: - origin_file = pathlib.Path(repo_uri).resolve() - if not origin_file.is_file(): - return 1 - shutil.copy(origin_file, cache_file) - - if not valid_o3de_repo_json(cache_file): - logger.error(f'Repo json {repo_uri} is not valid.') - cache_file.unlink() - return 1 - - last_failure = process_add_o3de_repo(cache_file, repo_set) - if last_failure: - result = last_failure - - return result - - -def search_repo(repo_set: set, - repo_json_data: dict, - engine_name: str = None, - project_name: str = None, - gem_name: str = None, - template_name: str = None, - restricted_name: str = None) -> dict or None: - cache_folder = get_o3de_cache_folder() - - if isinstance(engine_name, str) or isinstance(engine_name, pathlib.PurePath): - for engine_uri in repo_json_data['engines']: - engine_uri = f'{engine_uri}/engine.json' - engine_sha256 = hashlib.sha256(engine_uri.encode()) - engine_cache_file = cache_folder / str(engine_sha256.hexdigest() + '.json') - if engine_cache_file.is_file(): - with engine_cache_file.open('r') as f: - try: - engine_json_data = json.load(f) - except Exception as e: - logger.warn(f'{engine_cache_file} failed to load: {str(e)}') - else: - if engine_json_data['engine_name'] == engine_name: - return engine_json_data - - elif isinstance(project_name, str) or isinstance(project_name, pathlib.PurePath): - for project_uri in repo_json_data['projects']: - project_uri = f'{project_uri}/project.json' - project_sha256 = hashlib.sha256(project_uri.encode()) - project_cache_file = cache_folder / str(project_sha256.hexdigest() + '.json') - if project_cache_file.is_file(): - with project_cache_file.open('r') as f: - try: - project_json_data = json.load(f) - except Exception as e: - logger.warn(f'{project_cache_file} failed to load: {str(e)}') - else: - if project_json_data['project_name'] == project_name: - return project_json_data - - elif isinstance(gem_name, str) or isinstance(gem_name, pathlib.PurePath): - for gem_uri in repo_json_data['gems']: - gem_uri = f'{gem_uri}/gem.json' - gem_sha256 = hashlib.sha256(gem_uri.encode()) - gem_cache_file = cache_folder / str(gem_sha256.hexdigest() + '.json') - if gem_cache_file.is_file(): - with gem_cache_file.open('r') as f: - try: - gem_json_data = json.load(f) - except Exception as e: - logger.warn(f'{gem_cache_file} failed to load: {str(e)}') - else: - if gem_json_data['gem_name'] == gem_name: - return gem_json_data - - elif isinstance(template_name, str) or isinstance(template_name, pathlib.PurePath): - for template_uri in repo_json_data['templates']: - template_uri = f'{template_uri}/template.json' - template_sha256 = hashlib.sha256(template_uri.encode()) - template_cache_file = cache_folder / str(template_sha256.hexdigest() + '.json') - if template_cache_file.is_file(): - with template_cache_file.open('r') as f: - try: - template_json_data = json.load(f) - except Exception as e: - logger.warn(f'{template_cache_file} failed to load: {str(e)}') - else: - if template_json_data['template_name'] == template_name: - return template_json_data - - elif isinstance(restricted_name, str) or isinstance(restricted_name, pathlib.PurePath): - for restricted_uri in repo_json_data['restricted']: - restricted_uri = f'{restricted_uri}/restricted.json' - restricted_sha256 = hashlib.sha256(restricted_uri.encode()) - restricted_cache_file = cache_folder / str(restricted_sha256.hexdigest() + '.json') - if restricted_cache_file.is_file(): - with restricted_cache_file.open('r') as f: - try: - restricted_json_data = json.load(f) - except Exception as e: - logger.warn(f'{restricted_cache_file} failed to load: {str(e)}') - else: - if restricted_json_data['restricted_name'] == restricted_name: - return restricted_json_data - # recurse - else: - for repo_repo_uri in repo_json_data['repos']: - if repo_repo_uri not in repo_set: - repo_set.add(repo_repo_uri) - repo_repo_uri = f'{repo_repo_uri}/repo.json' - repo_repo_sha256 = hashlib.sha256(repo_repo_uri.encode()) - repo_repo_cache_file = cache_folder / str(repo_repo_sha256.hexdigest() + '.json') - if repo_repo_cache_file.is_file(): - with repo_repo_cache_file.open('r') as f: - try: - repo_repo_json_data = json.load(f) - except Exception as e: - logger.warn(f'{repo_repo_cache_file} failed to load: {str(e)}') - else: - item = search_repo(repo_set, - repo_repo_json_data, - engine_name, - project_name, - gem_name, - template_name) - if item: - return item - return None - - -def get_downloadable(engine_name: str = None, - project_name: str = None, - gem_name: str = None, - template_name: str = None, - restricted_name: str = None) -> dict or None: - json_data = load_o3de_manifest() - cache_folder = get_o3de_cache_folder() - repo_set = set() - for repo_uri in json_data['repos']: - if repo_uri not in repo_set: - repo_set.add(repo_uri) - repo_uri = f'{repo_uri}/repo.json' - repo_sha256 = hashlib.sha256(repo_uri.encode()) - repo_cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json') - if repo_cache_file.is_file(): - with repo_cache_file.open('r') as f: - try: - repo_json_data = json.load(f) - except Exception as e: - logger.warn(f'{repo_cache_file} failed to load: {str(e)}') - else: - item = search_repo(repo_set, - repo_json_data, - engine_name, - project_name, - gem_name, - template_name, - restricted_name) - if item: - return item - return None - - -def get_registered(engine_name: str = None, - project_name: str = None, - gem_name: str = None, - template_name: str = None, - default_folder: str = None, - repo_name: str = None, - restricted_name: str = None) -> pathlib.Path or None: - json_data = load_o3de_manifest() - - # check global first then this engine - if isinstance(engine_name, str): - for engine in json_data['engines']: - engine_path = pathlib.Path(engine['path']).resolve() - engine_json = engine_path / 'engine.json' - with engine_json.open('r') as f: - try: - engine_json_data = json.load(f) - except Exception as e: - logger.warn(f'{engine_json} failed to load: {str(e)}') - else: - this_engines_name = engine_json_data['engine_name'] - if this_engines_name == engine_name: - return engine_path - - elif isinstance(project_name, str): - engine_object = find_engine_data(json_data) - projects = json_data['projects'].copy() - projects.extend(engine_object['projects']) - for project_path in projects: - project_path = pathlib.Path(project_path).resolve() - project_json = project_path / 'project.json' - with project_json.open('r') as f: - try: - project_json_data = json.load(f) - except Exception as e: - logger.warn(f'{project_json} failed to load: {str(e)}') - else: - this_projects_name = project_json_data['project_name'] - if this_projects_name == project_name: - return project_path - - elif isinstance(gem_name, str): - engine_object = find_engine_data(json_data) - gems = json_data['gems'].copy() - gems.extend(engine_object['gems']) - for gem_path in gems: - gem_path = pathlib.Path(gem_path).resolve() - gem_json = gem_path / 'gem.json' - with gem_json.open('r') as f: - try: - gem_json_data = json.load(f) - except Exception as e: - logger.warn(f'{gem_json} failed to load: {str(e)}') - else: - this_gems_name = gem_json_data['gem_name'] - if this_gems_name == gem_name: - return gem_path - - elif isinstance(template_name, str): - engine_object = find_engine_data(json_data) - templates = json_data['templates'].copy() - templates.extend(engine_object['templates']) - for template_path in templates: - template_path = pathlib.Path(template_path).resolve() - template_json = template_path / 'template.json' - with template_json.open('r') as f: - try: - template_json_data = json.load(f) - except Exception as e: - logger.warn(f'{template_path} failed to load: {str(e)}') - else: - this_templates_name = template_json_data['template_name'] - if this_templates_name == template_name: - return template_path - - elif isinstance(restricted_name, str): - engine_object = find_engine_data(json_data) - restricted = json_data['restricted'].copy() - restricted.extend(engine_object['restricted']) - for restricted_path in restricted: - restricted_path = pathlib.Path(restricted_path).resolve() - restricted_json = restricted_path / 'restricted.json' - with restricted_json.open('r') as f: - try: - restricted_json_data = json.load(f) - except Exception as e: - logger.warn(f'{restricted_json} failed to load: {str(e)}') - else: - this_restricted_name = restricted_json_data['restricted_name'] - if this_restricted_name == restricted_name: - return restricted_path - - elif isinstance(default_folder, str): - if default_folder == 'engines': - default_engines_folder = pathlib.Path(json_data['default_engines_folder']).resolve() - return default_engines_folder - elif default_folder == 'projects': - default_projects_folder = pathlib.Path(json_data['default_projects_folder']).resolve() - return default_projects_folder - elif default_folder == 'gems': - default_gems_folder = pathlib.Path(json_data['default_gems_folder']).resolve() - return default_gems_folder - elif default_folder == 'templates': - default_templates_folder = pathlib.Path(json_data['default_templates_folder']).resolve() - return default_templates_folder - elif default_folder == 'restricted': - default_restricted_folder = pathlib.Path(json_data['default_restricted_folder']).resolve() - return default_restricted_folder - - elif isinstance(repo_name, str): - cache_folder = get_o3de_cache_folder() - for repo_uri in json_data['repos']: - repo_uri = pathlib.Path(repo_uri).resolve() - repo_sha256 = hashlib.sha256(repo_uri.encode()) - cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json') - if cache_file.is_file(): - repo = pathlib.Path(cache_file).resolve() - with repo.open('r') as f: - try: - repo_json_data = json.load(f) - except Exception as e: - logger.warn(f'{cache_file} failed to load: {str(e)}') - else: - this_repos_name = repo_json_data['repo_name'] - if this_repos_name == repo_name: - return repo_uri - return None - - -def print_engines_data(engines_data: dict) -> None: - print('\n') - print("Engines================================================") - for engine_object in engines_data: - # if it's not local it should be in the cache - engine_uri = engine_object['path'] - parsed_uri = urllib.parse.urlparse(engine_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - repo_sha256 = hashlib.sha256(engine_uri.encode()) - cache_folder = get_o3de_cache_folder() - engine = cache_folder / str(repo_sha256.hexdigest() + '.json') - print(f'{engine_uri}/engine.json cached as:') - else: - engine_json = pathlib.Path(engine_uri).resolve() / 'engine.json' - - with engine_json.open('r') as f: - try: - engine_json_data = json.load(f) - except Exception as e: - logger.warn(f'{engine_json} failed to load: {str(e)}') - else: - print(engine_json) - print(json.dumps(engine_json_data, indent=4)) - print('\n') - - -def print_projects_data(projects_data: dict) -> None: - print('\n') - print("Projects================================================") - for project_uri in projects_data: - # if it's not local it should be in the cache - parsed_uri = urllib.parse.urlparse(project_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - repo_sha256 = hashlib.sha256(project_uri.encode()) - cache_folder = get_o3de_cache_folder() - project_json = cache_folder / str(repo_sha256.hexdigest() + '.json') - else: - project_json = pathlib.Path(project_uri).resolve() / 'project.json' - - with project_json.open('r') as f: - try: - project_json_data = json.load(f) - except Exception as e: - logger.warn(f'{project_json} failed to load: {str(e)}') - else: - print(project_json) - print(json.dumps(project_json_data, indent=4)) - print('\n') - - -def print_gems_data(gems_data: dict) -> None: - print('\n') - print("Gems================================================") - for gem_uri in gems_data: - # if it's not local it should be in the cache - parsed_uri = urllib.parse.urlparse(gem_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - repo_sha256 = hashlib.sha256(gem_uri.encode()) - cache_folder = get_o3de_cache_folder() - gem_json = cache_folder / str(repo_sha256.hexdigest() + '.json') - else: - gem_json = pathlib.Path(gem_uri).resolve() / 'gem.json' - - with gem_json.open('r') as f: - try: - gem_json_data = json.load(f) - except Exception as e: - logger.warn(f'{gem_json} failed to load: {str(e)}') - else: - print(gem_json) - print(json.dumps(gem_json_data, indent=4)) - print('\n') - - -def print_templates_data(templates_data: dict) -> None: - print('\n') - print("Templates================================================") - for template_uri in templates_data: - # if it's not local it should be in the cache - parsed_uri = urllib.parse.urlparse(template_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - repo_sha256 = hashlib.sha256(template_uri.encode()) - cache_folder = get_o3de_cache_folder() - template_json = cache_folder / str(repo_sha256.hexdigest() + '.json') - else: - template_json = pathlib.Path(template_uri).resolve() / 'template.json' - - with template_json.open('r') as f: - try: - template_json_data = json.load(f) - except Exception as e: - logger.warn(f'{template_json} failed to load: {str(e)}') - else: - print(template_json) - print(json.dumps(template_json_data, indent=4)) - print('\n') - - -def print_repos_data(repos_data: dict) -> None: - print('\n') - print("Repos================================================") - cache_folder = get_o3de_cache_folder() - for repo_uri in repos_data: - repo_sha256 = hashlib.sha256(repo_uri.encode()) - cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json') - if valid_o3de_repo_json(cache_file): - with cache_file.open('r') as s: - try: - repo_json_data = json.load(s) - except Exception as e: - logger.warn(f'{cache_file} failed to load: {str(e)}') - else: - print(f'{repo_uri}/repo.json cached as:') - print(cache_file) - print(json.dumps(repo_json_data, indent=4)) - print('\n') - - -def print_restricted_data(restricted_data: dict) -> None: - print('\n') - print("Restricted================================================") - for restricted_path in restricted_data: - restricted_json = pathlib.Path(restricted_path).resolve() / 'restricted.json' - with restricted_json.open('r') as f: - try: - restricted_json_data = json.load(f) - except Exception as e: - logger.warn(f'{restricted_json} failed to load: {str(e)}') - else: - print(restricted_json) - print(json.dumps(restricted_json_data, indent=4)) - print('\n') - - -def get_this_engine() -> dict: - json_data = load_o3de_manifest() - engine_data = find_engine_data(json_data) - return engine_data - - -def get_engines() -> dict: - json_data = load_o3de_manifest() - return json_data['engines'] - - -def get_projects() -> dict: - json_data = load_o3de_manifest() - return json_data['projects'] - - -def get_gems() -> dict: - json_data = load_o3de_manifest() - return json_data['gems'] - - -def get_templates() -> dict: - json_data = load_o3de_manifest() - return json_data['templates'] - - -def get_restricted() -> dict: - json_data = load_o3de_manifest() - return json_data['restricted'] - - -def get_repos() -> dict: - json_data = load_o3de_manifest() - return json_data['repos'] - - -def get_engine_projects() -> dict: - json_data = load_o3de_manifest() - engine_object = find_engine_data(json_data) - return engine_object['projects'] - - -def get_engine_gems() -> dict: - json_data = load_o3de_manifest() - engine_object = find_engine_data(json_data) - return engine_object['gems'] - - -def get_engine_templates() -> dict: - json_data = load_o3de_manifest() - engine_object = find_engine_data(json_data) - return engine_object['templates'] - - -def get_engine_restricted() -> dict: - json_data = load_o3de_manifest() - engine_object = find_engine_data(json_data) - return engine_object['restricted'] - - -def get_external_subdirectories() -> dict: - json_data = load_o3de_manifest() - engine_object = find_engine_data(json_data) - return engine_object['external_subdirectories'] - - -def get_all_projects() -> dict: - json_data = load_o3de_manifest() - engine_object = find_engine_data(json_data) - projects_data = json_data['projects'].copy() - projects_data.extend(engine_object['projects']) - return projects_data - - -def get_all_gems() -> dict: - json_data = load_o3de_manifest() - engine_object = find_engine_data(json_data) - gems_data = json_data['gems'].copy() - gems_data.extend(engine_object['gems']) - return gems_data - - -def get_all_templates() -> dict: - json_data = load_o3de_manifest() - engine_object = find_engine_data(json_data) - templates_data = json_data['templates'].copy() - templates_data.extend(engine_object['templates']) - return templates_data - - -def get_all_restricted() -> dict: - json_data = load_o3de_manifest() - engine_object = find_engine_data(json_data) - restricted_data = json_data['restricted'].copy() - restricted_data.extend(engine_object['restricted']) - return restricted_data - - -def print_this_engine(verbose: int) -> None: - engine_data = get_this_engine() - print(json.dumps(engine_data, indent=4)) - if verbose > 0: - print_engines_data(engine_data) - - -def print_engines(verbose: int) -> None: - engines_data = get_engines() - print(json.dumps(engines_data, indent=4)) - if verbose > 0: - print_engines_data(engines_data) - - -def print_projects(verbose: int) -> None: - projects_data = get_projects() - print(json.dumps(projects_data, indent=4)) - if verbose > 0: - print_projects_data(projects_data) - - -def print_gems(verbose: int) -> None: - gems_data = get_gems() - print(json.dumps(gems_data, indent=4)) - if verbose > 0: - print_gems_data(gems_data) - - -def print_templates(verbose: int) -> None: - templates_data = get_templates() - print(json.dumps(templates_data, indent=4)) - if verbose > 0: - print_templates_data(templates_data) - - -def print_restricted(verbose: int) -> None: - restricted_data = get_restricted() - print(json.dumps(restricted_data, indent=4)) - if verbose > 0: - print_restricted_data(restricted_data) - - -def register_show_repos(verbose: int) -> None: - repos_data = get_repos() - print(json.dumps(repos_data, indent=4)) - if verbose > 0: - print_repos_data(repos_data) - - -def print_engine_projects(verbose: int) -> None: - engine_projects_data = get_engine_projects() - print(json.dumps(engine_projects_data, indent=4)) - if verbose > 0: - print_projects_data(engine_projects_data) - - -def print_engine_gems(verbose: int) -> None: - engine_gems_data = get_engine_gems() - print(json.dumps(engine_gems_data, indent=4)) - if verbose > 0: - print_gems_data(engine_gems_data) - - -def print_engine_templates(verbose: int) -> None: - engine_templates_data = get_engine_templates() - print(json.dumps(engine_templates_data, indent=4)) - if verbose > 0: - print_templates_data(engine_templates_data) - - -def print_engine_restricted(verbose: int) -> None: - engine_restricted_data = get_engine_restricted() - print(json.dumps(engine_restricted_data, indent=4)) - if verbose > 0: - print_restricted_data(engine_restricted_data) - - -def print_external_subdirectories(verbose: int) -> None: - external_subdirs_data = get_external_subdirectories() - print(json.dumps(external_subdirs_data, indent=4)) - - -def print_all_projects(verbose: int) -> None: - all_projects_data = get_all_projects() - print(json.dumps(all_projects_data, indent=4)) - if verbose > 0: - print_projects_data(all_projects_data) - - -def print_all_gems(verbose: int) -> None: - all_gems_data = get_all_gems() - print(json.dumps(all_gems_data, indent=4)) - if verbose > 0: - print_gems_data(all_gems_data) - - -def print_all_templates(verbose: int) -> None: - all_templates_data = get_all_templates() - print(json.dumps(all_templates_data, indent=4)) - if verbose > 0: - print_templates_data(all_templates_data) - - -def print_all_restricted(verbose: int) -> None: - all_restricted_data = get_all_restricted() - print(json.dumps(all_restricted_data, indent=4)) - if verbose > 0: - print_restricted_data(all_restricted_data) - - -def register_show(verbose: int) -> None: - json_data = load_o3de_manifest() - print(f"{get_o3de_manifest()}:") - print(json.dumps(json_data, indent=4)) - - if verbose > 0: - print_engines_data(get_engines()) - print_projects_data(get_all_projects()) - print_gems_data(get_gems()) - print_templates_data(get_all_templates()) - print_restricted_data(get_all_restricted()) - print_repos_data(get_repos()) - - -def find_engine_data(json_data: dict, - engine_path: str or pathlib.Path = None) -> dict or None: - if not engine_path: - engine_path = get_this_engine_path() - engine_path = pathlib.Path(engine_path).resolve() - - for engine_object in json_data['engines']: - engine_object_path = pathlib.Path(engine_object['path']).resolve() - if engine_path == engine_object_path: - return engine_object - - return None - - -def _validate_engine_name_and_path(engine_name: str = None, - engine_path: str or pathlib.Path = None) -> pathlib.Path or None: - if not engine_name and not engine_path: - logger.error('Must specify either a Engine name or Engine Path.') - return None - - if engine_name and not engine_path: - engine_path = get_registered(engine_name=engine_name) - - if not engine_path: - logger.error(f'Engine Path {engine_path} has not been registered.') - return None - - engine_path = pathlib.Path(engine_path).resolve() - engine_json = engine_path / 'engine.json' - if not engine_json.is_file(): - logger.error(f'Engine json {engine_json} is not present.') - return None - if not valid_o3de_engine_json(engine_json): - logger.error(f'Engine json {engine_json} is not valid.') - return None - - return engine_json - -def get_engine_data(engine_name: str = None, - engine_path: str or pathlib.Path = None ) -> dict or None: - engine_json = _validate_engine_name_and_path(engine_name, engine_path) - if not engine_json: - return None - - with engine_json.open('r') as f: - try: - engine_json_data = json.load(f) - except Exception as e: - logger.warn(f'{engine_json} failed to load: {str(e)}') - else: - return engine_json_data - - return None - -def set_engine_data(engine_name: str = None, - engine_path: str or pathlib.Path = None, - engine_data: dict = None ) -> int: - if not engine_data: - logger.error('Must provide engine data.') - return 1 - - engine_json = _validate_engine_name_and_path(engine_name, engine_path) - if not engine_json: - return 1 - - with engine_json.open('w') as f: - try: - json.dump(engine_data, f, indent=4) - except Exception as e: - logger.warn(f'Failed to load or write {engine_json}: {str(e)}') - return 1 - - return 0 - - -def get_project_data(project_name: str = None, - project_path: str or pathlib.Path = None, ) -> dict or None: - if not project_name and not project_path: - logger.error('Must specify either a Project name or Project Path.') - return 1 - - if project_name and not project_path: - project_path = get_registered(project_name=project_name) - - if not project_path: - logger.error(f'Project Path {project_path} has not been registered.') - return 1 - - project_path = pathlib.Path(project_path).resolve() - project_json = project_path / 'project.json' - if not project_json.is_file(): - logger.error(f'Project json {project_json} is not present.') - return 1 - if not valid_o3de_project_json(project_json): - logger.error(f'Project json {project_json} is not valid.') - return 1 - - with project_json.open('r') as f: - try: - project_json_data = json.load(f) - except Exception as e: - logger.warn(f'{project_json} failed to load: {str(e)}') - else: - return project_json_data - - return None - - -def _validate_gem_name_and_path(gem_name: str = None, - gem_path: str or pathlib.Path = None) -> pathlib.Path or None: - if not gem_name and not gem_path: - logger.error('Must specify either a Gem name or Gem Path.') - return None - - if gem_name and not gem_path: - gem_path = get_registered(gem_name=gem_name) - - if not gem_path: - logger.error(f'Gem Path {gem_path} has not been registered.') - return None - - gem_path = pathlib.Path(gem_path).resolve() - gem_json = gem_path / 'gem.json' - if not gem_json.is_file(): - logger.error(f'Gem json {gem_json} is not present.') - return None - if not valid_o3de_gem_json(gem_json): - logger.error(f'Gem json {gem_json} is not valid.') - return None - - return gem_json - - -def get_gem_data(gem_name: str = None, - gem_path: str or pathlib.Path = None) -> dict or None: - gem_json = _validate_gem_name_and_path(gem_name, gem_path) - if not gem_json: - return None - - with gem_json.open('r') as f: - try: - gem_json_data = json.load(f) - except Exception as e: - logger.warn(f'{gem_json} failed to load: {str(e)}') - else: - return gem_json_data - - return None - - -def set_gem_data(gem_name: str = None, - gem_path: str or pathlib.Path = None, - gem_data: dict = None) -> int: - if not gem_data: - logger.error('Must provide Gem data.') - return 1 - - gem_json = _validate_gem_name_and_path(gem_name, gem_path) - if not gem_json: - return 1 - - with gem_json.open('w') as f: - try: - json.dump(gem_data, f, indent=4) - except Exception as e: - logger.warn(f'Failed to load and write {gem_json}: {str(e)}') - return 1 - - return 0 - - -def get_template_data(template_name: str = None, - template_path: str or pathlib.Path = None, ) -> dict or None: - if not template_name and not template_path: - logger.error('Must specify either a Template name or Template Path.') - return 1 - - if template_name and not template_path: - template_path = get_registered(template_name=template_name) - - if not template_path: - logger.error(f'Template Path {template_path} has not been registered.') - return 1 - - template_path = pathlib.Path(template_path).resolve() - template_json = template_path / 'template.json' - if not template_json.is_file(): - logger.error(f'Template json {template_json} is not present.') - return 1 - if not valid_o3de_template_json(template_json): - logger.error(f'Template json {template_json} is not valid.') - return 1 - - with template_json.open('r') as f: - try: - template_json_data = json.load(f) - except Exception as e: - logger.warn(f'{template_json} failed to load: {str(e)}') - else: - return template_json_data - - return None - - -def get_restricted_data(restricted_name: str = None, - restricted_path: str or pathlib.Path = None, ) -> dict or None: - if not restricted_name and not restricted_path: - logger.error('Must specify either a Restricted name or Restricted Path.') - return 1 - - if restricted_name and not restricted_path: - restricted_path = get_registered(restricted_name=restricted_name) - - if not restricted_path: - logger.error(f'Restricted Path {restricted_path} has not been registered.') - return 1 - - restricted_path = pathlib.Path(restricted_path).resolve() - restricted_json = restricted_path / 'restricted.json' - if not restricted_json.is_file(): - logger.error(f'Restricted json {restricted_json} is not present.') - return 1 - if not valid_o3de_restricted_json(restricted_json): - logger.error(f'Restricted json {restricted_json} is not valid.') - return 1 - - with restricted_json.open('r') as f: - try: - restricted_json_data = json.load(f) - except Exception as e: - logger.warn(f'{restricted_json} failed to load: {str(e)}') - else: - return restricted_json_data - - return None - - -def get_downloadables() -> dict: - json_data = load_o3de_manifest() - downloadable_data = {} - downloadable_data.update({'engines': []}) - downloadable_data.update({'projects': []}) - downloadable_data.update({'gems': []}) - downloadable_data.update({'templates': []}) - downloadable_data.update({'restricted': []}) - - def recurse_downloadables(repo_uri: str or pathlib.Path) -> None: - cache_folder = get_o3de_cache_folder() - repo_sha256 = hashlib.sha256(repo_uri.encode()) - cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json') - if valid_o3de_repo_json(cache_file): - with cache_file.open('r') as s: - try: - repo_json_data = json.load(s) - except Exception as e: - logger.warn(f'{cache_file} failed to load: {str(e)}') - else: - for engine in repo_json_data['engines']: - if engine not in downloadable_data['engines']: - downloadable_data['engines'].append(engine) - - for project in repo_json_data['projects']: - if project not in downloadable_data['projects']: - downloadable_data['projects'].append(project) - - for gem in repo_json_data['gems']: - if gem not in downloadable_data['gems']: - downloadable_data['gems'].append(gem) - - for template in repo_json_data['templates']: - if template not in downloadable_data['templates']: - downloadable_data['templates'].append(template) - - for restricted in repo_json_data['restricted']: - if restricted not in downloadable_data['restricted']: - downloadable_data['restricted'].append(restricted) - - for repo in repo_json_data['repos']: - if repo not in downloadable_data['repos']: - downloadable_data['repos'].append(repo) - - for repo in downloadable_data['repos']: - recurse_downloadables(repo) - - for repo_entry in json_data['repos']: - recurse_downloadables(repo_entry) - return downloadable_data - - -def get_downloadable_engines() -> dict: - downloadable_data = get_downloadables() - return downloadable_data['engines'] - - -def get_downloadable_projects() -> dict: - downloadable_data = get_downloadables() - return downloadable_data['projects'] - - -def get_downloadable_gems() -> dict: - downloadable_data = get_downloadables() - return downloadable_data['gems'] - - -def get_downloadable_templates() -> dict: - downloadable_data = get_downloadables() - return downloadable_data['templates'] - - -def get_downloadable_restricted() -> dict: - downloadable_data = get_downloadables() - return downloadable_data['restricted'] - - -def print_downloadable_engines(verbose: int) -> None: - downloadable_engines = get_downloadable_engines() - for engine_data in downloadable_engines: - print(json.dumps(engine_data, indent=4)) - if verbose > 0: - print_engines_data(downloadable_engines) - - -def print_downloadable_projects(verbose: int) -> None: - downloadable_projects = get_downloadable_projects() - for projects_data in downloadable_projects: - print(json.dumps(projects_data, indent=4)) - if verbose > 0: - print_projects_data(downloadable_projects) - - -def print_downloadable_gems(verbose: int) -> None: - downloadable_gems = get_downloadable_gems() - for gem_data in downloadable_gems: - print(json.dumps(gem_data, indent=4)) - if verbose > 0: - print_gems_data(downloadable_gems) - - -def print_downloadable_templates(verbose: int) -> None: - downloadable_templates = get_downloadable_templates() - for template_data in downloadable_templates: - print(json.dumps(template_data, indent=4)) - if verbose > 0: - print_engines_data(downloadable_templates) - - -def print_downloadable_restricted(verbose: int) -> None: - downloadable_restricted = get_downloadable_restricted() - for restricted_data in downloadable_restricted: - print(json.dumps(restricted_data, indent=4)) - if verbose > 0: - print_engines_data(downloadable_restricted) - - -def print_downloadables(verbose: int) -> None: - downloadable_data = get_downloadables() - print(json.dumps(downloadable_data, indent=4)) - if verbose > 0: - print_engines_data(downloadable_data['engines']) - print_projects_data(downloadable_data['projects']) - print_gems_data(downloadable_data['gems']) - print_templates_data(downloadable_data['templates']) - print_restricted_data(downloadable_data['templates']) - - -def download_engine(engine_name: str, - dest_path: str) -> int: - if not dest_path: - dest_path = get_registered(default_folder='engines') - if not dest_path: - logger.error(f'Destination path not cannot be empty.') - return 1 - - dest_path = pathlib.Path(dest_path).resolve() - dest_path.mkdir(exist_ok=True) - - download_path = get_o3de_download_folder() / 'engines' / engine_name - download_path.mkdir(exist_ok=True) - download_zip_path = download_path / 'engine.zip' - - downloadable_engine_data = get_downloadable(engine_name=engine_name) - if not downloadable_engine_data: - logger.error(f'Downloadable engine {engine_name} not found.') - return 1 - - origin = downloadable_engine_data['origin'] - url = f'{origin}/project.zip' - parsed_uri = urllib.parse.urlparse(url) - - if download_zip_path.is_file(): - logger.warn(f'Project already downloaded to {download_zip_path}.') - elif parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - with urllib.request.urlopen(url) as s: - with download_zip_path.open('wb') as f: - shutil.copyfileobj(s, f) - else: - origin_file = pathlib.Path(url).resolve() - if not origin_file.is_file(): - return 1 - shutil.copy(origin_file, download_zip_path) - - if not zipfile.is_zipfile(download_zip_path): - logger.error(f"Engine zip {download_zip_path} is invalid.") - download_zip_path.unlink() - return 1 - - # if the engine.json has a sha256 check it against a sha256 of the zip - try: - sha256A = downloadable_engine_data['sha256'] - except Exception as e: - logger.warn(f'SECURITY WARNING: The advertised engine you downloaded has no "sha256"!!! Be VERY careful!!!' - f' We cannot verify this is the actually the advertised engine!!!') - else: - sha256B = hashlib.sha256(download_zip_path.open('rb').read()).hexdigest() - if sha256A != sha256B: - logger.error(f'SECURITY VIOLATION: Downloaded engine.zip sha256 {sha256B} does not match' - f' the advertised "sha256":{sha256A} in the engine.json. Deleting unzipped files!!!') - shutil.rmtree(dest_path) - return 1 - - dest_engine_folder = dest_path / engine_name - if dest_engine_folder.is_dir(): - backup_folder(dest_engine_folder) - with zipfile.ZipFile(download_zip_path, 'r') as project_zip: - try: - project_zip.extractall(dest_path) - except Exception as e: - logger.error(f'UnZip exception:{str(e)}') - shutil.rmtree(dest_path) - return 1 - - unzipped_engine_json = dest_engine_folder / 'engine.json' - if not unzipped_engine_json.is_file(): - logger.error(f'Engine json {unzipped_engine_json} is missing.') - return 1 - - if not valid_o3de_engine_json(unzipped_engine_json): - logger.error(f'Engine json {unzipped_engine_json} is invalid.') - return 1 - - # remove the sha256 if present in the advertised downloadable engine.json - # then compare it to the engine.json in the zip, they should now be identical - try: - del downloadable_engine_data['sha256'] - except Exception as e: - pass - - sha256A = hashlib.sha256(json.dumps(downloadable_engine_data, indent=4).encode('utf8')).hexdigest() - with unzipped_engine_json.open('r') as s: - try: - unzipped_engine_json_data = json.load(s) - except Exception as e: - logger.error(f'Failed to read engine json {unzipped_engine_json}. Unable to confirm this' - f' is the same template that was advertised.') - return 1 - sha256B = hashlib.sha256(json.dumps(unzipped_engine_json_data, indent=4).encode('utf8')).hexdigest() - if sha256A != sha256B: - logger.error(f'SECURITY VIOLATION: Downloaded engine.json does not match' - f' the advertised engine.json. Deleting unzipped files!!!') - shutil.rmtree(dest_path) - return 1 - - return 0 - - -def download_project(project_name: str, - dest_path: str or pathlib.Path) -> int: - if not dest_path: - dest_path = get_registered(default_folder='projects') - if not dest_path: - logger.error(f'Destination path not specified and not default projects path.') - return 1 - - dest_path = pathlib.Path(dest_path).resolve() - dest_path.mkdir(exist_ok=True, parents=True) - - download_path = get_o3de_download_folder() / 'projects' / project_name - download_path.mkdir(exist_ok=True, parents=True) - download_zip_path = download_path / 'project.zip' - - downloadable_project_data = get_downloadable(project_name=project_name) - if not downloadable_project_data: - logger.error(f'Downloadable project {project_name} not found.') - return 1 - - origin = downloadable_project_data['origin'] - url = f'{origin}/project.zip' - parsed_uri = urllib.parse.urlparse(url) - - if download_zip_path.is_file(): - logger.warn(f'Project already downloaded to {download_zip_path}.') - elif parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - with urllib.request.urlopen(url) as s: - with download_zip_path.open('wb') as f: - shutil.copyfileobj(s, f) - else: - origin_file = pathlib.Path(url).resolve() - if not origin_file.is_file(): - return 1 - shutil.copy(origin_file, download_zip_path) - - if not zipfile.is_zipfile(download_zip_path): - logger.error(f"Project zip {download_zip_path} is invalid.") - download_zip_path.unlink() - return 1 - - # if the project.json has a sha256 check it against a sha256 of the zip - try: - sha256A = downloadable_project_data['sha256'] - except Exception as e: - logger.warn(f'SECURITY WARNING: The advertised project you downloaded has no "sha256"!!! Be VERY careful!!!' - f' We cannot verify this is the actually the advertised project!!!') - else: - sha256B = hashlib.sha256(download_zip_path.open('rb').read()).hexdigest() - if sha256A != sha256B: - logger.error(f'SECURITY VIOLATION: Downloaded project.zip sha256 {sha256B} does not match' - f' the advertised "sha256":{sha256A} in the project.json. Deleting unzipped files!!!') - shutil.rmtree(dest_path) - return 1 - - dest_project_folder = dest_path / project_name - if dest_project_folder.is_dir(): - backup_folder(dest_project_folder) - with zipfile.ZipFile(download_zip_path, 'r') as project_zip: - try: - project_zip.extractall(dest_project_folder) - except Exception as e: - logger.error(f'UnZip exception:{str(e)}') - shutil.rmtree(dest_path) - return 1 - - unzipped_project_json = dest_project_folder / 'project.json' - if not unzipped_project_json.is_file(): - logger.error(f'Project json {unzipped_project_json} is missing.') - return 1 - - if not valid_o3de_project_json(unzipped_project_json): - logger.error(f'Project json {unzipped_project_json} is invalid.') - return 1 - - # remove the sha256 if present in the advertised downloadable project.json - # then compare it to the project.json in the zip, they should now be identical - try: - del downloadable_project_data['sha256'] - except Exception as e: - pass - - sha256A = hashlib.sha256(json.dumps(downloadable_project_data, indent=4).encode('utf8')).hexdigest() - with unzipped_project_json.open('r') as s: - try: - unzipped_project_json_data = json.load(s) - except Exception as e: - logger.error(f'Failed to read Project json {unzipped_project_json}. Unable to confirm this' - f' is the same project that was advertised.') - return 1 - sha256B = hashlib.sha256(json.dumps(unzipped_project_json_data, indent=4).encode('utf8')).hexdigest() - if sha256A != sha256B: - logger.error(f'SECURITY VIOLATION: Downloaded project.json does not match' - f' the advertised project.json. Deleting unzipped files!!!') - shutil.rmtree(dest_path) - return 1 - - return 0 - - -def download_gem(gem_name: str, - dest_path: str or pathlib.Path) -> int: - if not dest_path: - dest_path = get_registered(default_folder='gems') - if not dest_path: - logger.error(f'Destination path not cannot be empty.') - return 1 - - dest_path = pathlib.Path(dest_path).resolve() - dest_path.mkdir(exist_ok=True, parents=True) - - download_path = get_o3de_download_folder() / 'gems' / gem_name - download_path.mkdir(exist_ok=True, parents=True) - download_zip_path = download_path / 'gem.zip' - - downloadable_gem_data = get_downloadable(gem_name=gem_name) - if not downloadable_gem_data: - logger.error(f'Downloadable gem {gem_name} not found.') - return 1 - - origin = downloadable_gem_data['origin'] - url = f'{origin}/gem.zip' - parsed_uri = urllib.parse.urlparse(url) - - if download_zip_path.is_file(): - logger.warn(f'Project already downloaded to {download_zip_path}.') - elif parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - with urllib.request.urlopen(url) as s: - with download_zip_path.open('wb') as f: - shutil.copyfileobj(s, f) - else: - origin_file = pathlib.Path(url).resolve() - if not origin_file.is_file(): - return 1 - shutil.copy(origin_file, download_zip_path) - - if not zipfile.is_zipfile(download_zip_path): - logger.error(f"Gem zip {download_zip_path} is invalid.") - download_zip_path.unlink() - return 1 - - # if the gem.json has a sha256 check it against a sha256 of the zip - try: - sha256A = downloadable_gem_data['sha256'] - except Exception as e: - logger.warn(f'SECURITY WARNING: The advertised gem you downloaded has no "sha256"!!! Be VERY careful!!!' - f' We cannot verify this is the actually the advertised gem!!!') - else: - sha256B = hashlib.sha256(download_zip_path.open('rb').read()).hexdigest() - if sha256A != sha256B: - logger.error(f'SECURITY VIOLATION: Downloaded gem.zip sha256 {sha256B} does not match' - f' the advertised "sha256":{sha256A} in the gem.json. Deleting unzipped files!!!') - shutil.rmtree(dest_path) - return 1 - - dest_gem_folder = dest_path / gem_name - if dest_gem_folder.is_dir(): - backup_folder(dest_gem_folder) - with zipfile.ZipFile(download_zip_path, 'r') as gem_zip: - try: - gem_zip.extractall(dest_path) - except Exception as e: - logger.error(f'UnZip exception:{str(e)}') - shutil.rmtree(dest_path) - return 1 - - unzipped_gem_json = dest_gem_folder / 'gem.json' - if not unzipped_gem_json.is_file(): - logger.error(f'Engine json {unzipped_gem_json} is missing.') - return 1 - - if not valid_o3de_engine_json(unzipped_gem_json): - logger.error(f'Engine json {unzipped_gem_json} is invalid.') - return 1 - - # remove the sha256 if present in the advertised downloadable gem.json - # then compare it to the gem.json in the zip, they should now be identical - try: - del downloadable_gem_data['sha256'] - except Exception as e: - pass - - sha256A = hashlib.sha256(json.dumps(downloadable_gem_data, indent=4).encode('utf8')).hexdigest() - with unzipped_gem_json.open('r') as s: - try: - unzipped_gem_json_data = json.load(s) - except Exception as e: - logger.error(f'Failed to read gem json {unzipped_gem_json}. Unable to confirm this' - f' is the same gem that was advertised.') - return 1 - sha256B = hashlib.sha256(json.dumps(unzipped_gem_json_data, indent=4).encode('utf8')).hexdigest() - if sha256A != sha256B: - logger.error(f'SECURITY VIOLATION: Downloaded gem.json does not match' - f' the advertised gem.json. Deleting unzipped files!!!') - shutil.rmtree(dest_path) - return 1 - - return 0 - - -def download_template(template_name: str, - dest_path: str or pathlib.Path) -> int: - if not dest_path: - dest_path = get_registered(default_folder='templates') - if not dest_path: - logger.error(f'Destination path not cannot be empty.') - return 1 - - dest_path = pathlib.Path(dest_path).resolve() - dest_path.mkdir(exist_ok=True, parents=True) - - download_path = get_o3de_download_folder() / 'templates' / template_name - download_path.mkdir(exist_ok=True, parents=True) - download_zip_path = download_path / 'template.zip' - - downloadable_template_data = get_downloadable(template_name=template_name) - if not downloadable_template_data: - logger.error(f'Downloadable template {template_name} not found.') - return 1 - - origin = downloadable_template_data['origin'] - url = f'{origin}/project.zip' - parsed_uri = urllib.parse.urlparse(url) - - result = 0 - - if download_zip_path.is_file(): - logger.warn(f'Project already downloaded to {download_zip_path}.') - elif parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - with urllib.request.urlopen(url) as s: - with download_zip_path.open('wb') as f: - shutil.copyfileobj(s, f) - else: - origin_file = pathlib.Path(url).resolve() - if not origin_file.is_file(): - return 1 - shutil.copy(origin_file, download_zip_path) - - if not zipfile.is_zipfile(download_zip_path): - logger.error(f"Template zip {download_zip_path} is invalid.") - download_zip_path.unlink() - return 1 - - # if the template.json has a sha256 check it against a sha256 of the zip - try: - sha256A = downloadable_template_data['sha256'] - except Exception as e: - logger.warn(f'SECURITY WARNING: The advertised template you downloaded has no "sha256"!!! Be VERY careful!!!' - f' We cannot verify this is the actually the advertised template!!!') - else: - sha256B = hashlib.sha256(download_zip_path.open('rb').read()).hexdigest() - if sha256A != sha256B: - logger.error(f'SECURITY VIOLATION: Downloaded template.zip sha256 {sha256B} does not match' - f' the advertised "sha256":{sha256A} in the template.json. Deleting unzipped files!!!') - shutil.rmtree(dest_path) - return 1 - - dest_template_folder = dest_path / template_name - if dest_template_folder.is_dir(): - backup_folder(dest_template_folder) - with zipfile.ZipFile(download_zip_path, 'r') as project_zip: - try: - project_zip.extractall(dest_path) - except Exception as e: - logger.error(f'UnZip exception:{str(e)}') - shutil.rmtree(dest_path) - return 1 - - unzipped_template_json = dest_template_folder / 'template.json' - if not unzipped_template_json.is_file(): - logger.error(f'Template json {unzipped_template_json} is missing.') - return 1 - - if not valid_o3de_engine_json(unzipped_template_json): - logger.error(f'Template json {unzipped_template_json} is invalid.') - return 1 - - # remove the sha256 if present in the advertised downloadable template.json - # then compare it to the template.json in the zip, they should now be identical - try: - del downloadable_template_data['sha256'] - except Exception as e: - pass - - sha256A = hashlib.sha256(json.dumps(downloadable_template_data, indent=4).encode('utf8')).hexdigest() - with unzipped_template_json.open('r') as s: - try: - unzipped_template_json_data = json.load(s) - except Exception as e: - logger.error(f'Failed to read Template json {unzipped_template_json}. Unable to confirm this' - f' is the same template that was advertised.') - return 1 - sha256B = hashlib.sha256(json.dumps(unzipped_template_json_data, indent=4).encode('utf8')).hexdigest() - if sha256A != sha256B: - logger.error(f'SECURITY VIOLATION: Downloaded template.json does not match' - f' the advertised template.json. Deleting unzipped files!!!') - shutil.rmtree(dest_path) - return 1 - - return 0 - - -def download_restricted(restricted_name: str, - dest_path: str or pathlib.Path) -> int: - if not dest_path: - dest_path = get_registered(default_folder='restricted') - if not dest_path: - logger.error(f'Destination path not cannot be empty.') - return 1 - - dest_path = pathlib.Path(dest_path).resolve() - dest_path.mkdir(exist_ok=True, parents=True) - - download_path = get_o3de_download_folder() / 'restricted' / restricted_name - download_path.mkdir(exist_ok=True, parents=True) - download_zip_path = download_path / 'restricted.zip' - - downloadable_restricted_data = get_downloadable(restricted_name=restricted_name) - if not downloadable_restricted_data: - logger.error(f'Downloadable Restricted {restricted_name} not found.') - return 1 - - origin = downloadable_restricted_data['origin'] - url = f'{origin}/restricted.zip' - parsed_uri = urllib.parse.urlparse(url) - - if download_zip_path.is_file(): - logger.warn(f'Restricted already downloaded to {download_zip_path}.') - elif parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - with urllib.request.urlopen(url) as s: - with download_zip_path.open('wb') as f: - shutil.copyfileobj(s, f) - else: - origin_file = pathlib.Path(url).resolve() - if not origin_file.is_file(): - return 1 - shutil.copy(origin_file, download_zip_path) - - if not zipfile.is_zipfile(download_zip_path): - logger.error(f"Restricted zip {download_zip_path} is invalid.") - download_zip_path.unlink() - return 1 - - # if the restricted.json has a sha256 check it against a sha256 of the zip - try: - sha256A = downloadable_restricted_data['sha256'] - except Exception as e: - logger.warn(f'SECURITY WARNING: The advertised restricted you downloaded has no "sha256"!!! Be VERY careful!!!' - f' We cannot verify this is the actually the advertised restricted!!!') - else: - sha256B = hashlib.sha256(download_zip_path.open('rb').read()).hexdigest() - if sha256A != sha256B: - logger.error(f'SECURITY VIOLATION: Downloaded restricted.zip sha256 {sha256B} does not match' - f' the advertised "sha256":{sha256A} in the restricted.json. Deleting unzipped files!!!') - shutil.rmtree(dest_path) - return 1 - - dest_restricted_folder = dest_path / restricted_name - if dest_restricted_folder.is_dir(): - backup_folder(dest_restricted_folder) - with zipfile.ZipFile(download_zip_path, 'r') as project_zip: - try: - project_zip.extractall(dest_path) - except Exception as e: - logger.error(f'UnZip exception:{str(e)}') - shutil.rmtree(dest_path) - return 1 - - unzipped_restricted_json = dest_restricted_folder / 'restricted.json' - if not unzipped_restricted_json.is_file(): - logger.error(f'Restricted json {unzipped_restricted_json} is missing.') - return 1 - - if not valid_o3de_engine_json(unzipped_restricted_json): - logger.error(f'Restricted json {unzipped_restricted_json} is invalid.') - return 1 - - # remove the sha256 if present in the advertised downloadable restricted.json - # then compare it to the restricted.json in the zip, they should now be identical - try: - del downloadable_restricted_data['sha256'] - except Exception as e: - pass - - sha256A = hashlib.sha256(json.dumps(downloadable_restricted_data, indent=4).encode('utf8')).hexdigest() - with unzipped_restricted_json.open('r') as s: - try: - unzipped_restricted_json_data = json.load(s) - except Exception as e: - logger.error( - f'Failed to read Restricted json {unzipped_restricted_json}. Unable to confirm this' - f' is the same restricted that was advertised.') - return 1 - sha256B = hashlib.sha256( - json.dumps(unzipped_restricted_json_data, indent=4).encode('utf8')).hexdigest() - if sha256A != sha256B: - logger.error(f'SECURITY VIOLATION: Downloaded restricted.json does not match' - f' the advertised restricted.json. Deleting unzipped files!!!') - shutil.rmtree(dest_path) - return 1 - - return 0 - - -def add_gem_dependency(cmake_file: str or pathlib.Path, - gem_target: str) -> int: - """ - adds a gem dependency to a cmake file - :param cmake_file: path to the cmake file - :param gem_target: name of the cmake target - :return: 0 for success or non 0 failure code - """ - if not os.path.isfile(cmake_file): - logger.error(f'Failed to locate cmake file {cmake_file}') - return 1 - - # on a line by basis, see if there already is Gem::{gem_name} - # find the first occurrence of a gem, copy its formatting and replace - # the gem name with the new one and append it - # if the gem is already present fail - t_data = [] - added = False - with open(cmake_file, 'r') as s: - for line in s: - if f'Gem::{gem_target}' in line: - logger.warning(f'{gem_target} is already a gem dependency.') - return 0 - if not added and r'Gem::' in line: - new_gem = ' ' * line.find(r'Gem::') + f'Gem::{gem_target}\n' - t_data.append(new_gem) - added = True - t_data.append(line) - - # if we didn't add it the set gem dependencies could be empty so - # add a new gem, if empty the correct format is 1 tab=4spaces - if not added: - index = 0 - for line in t_data: - index = index + 1 - if r'set(GEM_DEPENDENCIES' in line: - t_data.insert(index, f' Gem::{gem_target}\n') - added = True - break - - # if we didn't add it then it's not here, add a whole new one - if not added: - t_data.append('\n') - t_data.append('set(GEM_DEPENDENCIES\n') - t_data.append(f' Gem::{gem_target}\n') - t_data.append(')\n') - - # write the cmake - os.unlink(cmake_file) - with open(cmake_file, 'w') as s: - s.writelines(t_data) - - return 0 - - -def get_project_runtime_gem_targets(project_path: str or pathlib.Path, - platform: str = 'Common') -> set: - return get_gem_targets_from_cmake_file(get_dependencies_cmake_file(project_path=project_path, dependency_type='runtime', platform=platform)) - - -def get_project_tool_gem_targets(project_path: str or pathlib.Path, - platform: str = 'Common') -> set: - return get_gem_targets_from_cmake_file(get_dependencies_cmake_file(project_path=project_path, dependency_type='tool', platform=platform)) - - -def get_project_server_gem_targets(project_path: str or pathlib.Path, - platform: str = 'Common') -> set: - return get_gem_targets_from_cmake_file(get_dependencies_cmake_file(project_path=project_path, dependency_type='server', platform=platform)) - - -def get_project_gem_targets(project_path: str or pathlib.Path, - platform: str = 'Common') -> set: - runtime_gems = get_gem_targets_from_cmake_file(get_dependencies_cmake_file(project_path=project_path, dependency_type='runtime', platform=platform)) - tool_gems = get_gem_targets_from_cmake_file(get_dependencies_cmake_file(project_path=project_path, dependency_type='tool', platform=platform)) - server_gems = get_gem_targets_from_cmake_file(get_dependencies_cmake_file(project_path=project_path, dependency_type='server', platform=platform)) - return runtime_gems.union(tool_gems.union(server_gems)) - - -def get_gem_targets_from_cmake_file(cmake_file: str or pathlib.Path) -> set: - """ - Gets a list of declared gem targets dependencies of a cmake file - :param cmake_file: path to the cmake file - :return: set of gem targets found - """ - cmake_file = pathlib.Path(cmake_file).resolve() - - if not cmake_file.is_file(): - logger.error(f'Failed to locate cmake file {cmake_file}') - return set() - - gem_target_set = set() - with cmake_file.open('r') as s: - for line in s: - gem_name = line.split('Gem::') - if len(gem_name) > 1: - # Only take the name as everything leading up to the first '.' if found - # Gem naming conventions will have GemName.Editor, GemName.Server, and GemName - # as different targets of the GemName Gem - gem_target_set.add(gem_name[1].replace('\n', '')) - return gem_target_set - - -def get_project_runtime_gem_names(project_path: str or pathlib.Path, - platform: str = 'Common') -> set: - return get_gem_names_from_cmake_file(get_dependencies_cmake_file(project_path=project_path, dependency_type='runtime', platform=platform)) - - -def get_project_tool_gem_names(project_path: str or pathlib.Path, - platform: str = 'Common') -> set: - return get_gem_names_from_cmake_file(get_dependencies_cmake_file(project_path=project_path, dependency_type='tool', platform=platform)) - - -def get_project_server_gem_names(project_path: str or pathlib.Path, - platform: str = 'Common') -> set: - return get_gem_names_from_cmake_file(get_dependencies_cmake_file(project_path=project_path, dependency_type='server', platform=platform)) - - -def get_project_gem_names(project_path: str or pathlib.Path, - platform: str = 'Common') -> set: - runtime_gem_names = get_gem_names_from_cmake_file(get_dependencies_cmake_file(project_path=project_path, dependency_type='runtime', platform=platform)) - tool_gem_names = get_gem_names_from_cmake_file(get_dependencies_cmake_file(project_path=project_path, dependency_type='tool', platform=platform)) - server_gem_names = get_gem_names_from_cmake_file(get_dependencies_cmake_file(project_path=project_path, dependency_type='server', platform=platform)) - return runtime_gem_names.union(tool_gem_names.union(server_gem_names)) - - -def get_gem_names_from_cmake_file(cmake_file: str or pathlib.Path) -> set: - """ - Gets a list of declared gem dependencies of a cmake file - :param cmake_file: path to the cmake file - :return: set of gems found - """ - cmake_file = pathlib.Path(cmake_file).resolve() - - if not cmake_file.is_file(): - logger.error(f'Failed to locate cmake file {cmake_file}') - return set() - - gem_set = set() - with cmake_file.open('r') as s: - for line in s: - gem_name = line.split('Gem::') - if len(gem_name) > 1: - # Only take the name as everything leading up to the first '.' if found - # Gem naming conventions will have GemName.Editor, GemName.Server, and GemName - # as different targets of the GemName Gem - gem_set.add(gem_name[1].split('.')[0].replace('\n', '')) - return gem_set - - -def get_project_runtime_gem_paths(project_path: str or pathlib.Path, - platform: str = 'Common') -> set: - gem_names = get_project_runtime_gem_names(project_path, platform) - gem_paths = set() - for gem_name in gem_names: - gem_paths.add(get_registered(gem_name=gem_name)) - return gem_paths - - -def get_project_tool_gem_paths(project_path: str or pathlib.Path, - platform: str = 'Common') -> set: - gem_names = get_project_tool_gem_names(project_path, platform) - gem_paths = set() - for gem_name in gem_names: - gem_paths.add(get_registered(gem_name=gem_name)) - return gem_paths - - -def get_project_server_gem_paths(project_path: str or pathlib.Path, - platform: str = 'Common') -> set: - gem_names = get_project_server_gem_names(project_path, platform) - gem_paths = set() - for gem_name in gem_names: - gem_paths.add(get_registered(gem_name=gem_name)) - return gem_paths - - -def get_project_gem_paths(project_path: str or pathlib.Path, - platform: str = 'Common') -> set: - gem_names = get_project_gem_names(project_path, platform) - gem_paths = set() - for gem_name in gem_names: - gem_paths.add(get_registered(gem_name=gem_name)) - return gem_paths - - -def remove_gem_dependency(cmake_file: str or pathlib.Path, - gem_target: str) -> int: - """ - removes a gem dependency from a cmake file - :param cmake_file: path to the cmake file - :param gem_target: cmake target name - :return: 0 for success or non 0 failure code - """ - if not os.path.isfile(cmake_file): - logger.error(f'Failed to locate cmake file {cmake_file}') - return 1 - - # on a line by basis, remove any line with Gem::{gem_name} - t_data = [] - # Remove the gem from the cmake_dependencies file by skipping the gem name entry - removed = False - with open(cmake_file, 'r') as s: - for line in s: - if f'Gem::{gem_target}' in line: - removed = True - else: - t_data.append(line) - - if not removed: - logger.error(f'Failed to remove Gem::{gem_target} from cmake file {cmake_file}') - return 1 - - # write the cmake - os.unlink(cmake_file) - with open(cmake_file, 'w') as s: - s.writelines(t_data) - - return 0 - - -def get_project_templates(): # temporary until we have a better way to do this... maybe template_type element - project_templates = [] - for template in get_all_templates(): - if 'Project' in template: - project_templates.append(template) - return project_templates - - -def get_gem_templates(): # temporary until we have a better way to do this... maybe template_type element - gem_templates = [] - for template in get_all_templates(): - if 'Gem' in template: - gem_templates.append(template) - return gem_templates - - -def get_generic_templates(): # temporary until we have a better way to do this... maybe template_type element - generic_templates = [] - for template in get_all_templates(): - if 'Project' not in template and 'Gem' not in template: - generic_templates.append(template) - return generic_templates - - -def get_dependencies_cmake_file(project_name: str = None, - project_path: str or pathlib.Path = None, - dependency_type: str = 'runtime', - platform: str = 'Common') -> str or None: - """ - get the standard cmake file name for a particular type of dependency - :param gem_name: name of the gem, resolves gem_path - :param gem_path: path of the gem - :return: list of gem targets - """ - if not project_name and not project_path: - logger.error(f'Must supply either a Project Name or Project Path.') - return None - - if project_name and not project_path: - project_path = get_registered(project_name=project_name) - - project_path = pathlib.Path(project_path).resolve() - - if platform == 'Common': - dependencies_file = f'{dependency_type}_dependencies.cmake' - dependencies_file_path = project_path / 'Gem/Code' / dependencies_file - if dependencies_file_path.is_file(): - return dependencies_file_path - return project_path / 'Code' / dependencies_file - else: - dependencies_file = f'{platform.lower()}_{dependency_type}_dependencies.cmake' - dependencies_file_path = project_path / 'Gem/Code/Platform' / platform / dependencies_file - if dependencies_file_path.is_file(): - return dependencies_file_path - return project_path / 'Code/Platform' / platform / dependencies_file - - -def get_all_gem_targets() -> list: - modules = [] - for gem_path in get_all_gems(): - this_gems_targets = get_gem_targets(gem_path=gem_path) - modules.extend(this_gems_targets) - return modules - - -def get_gem_targets(gem_name: str = None, - gem_path: str or pathlib.Path = None) -> list: - """ - Finds gem targets in a gem - :param gem_name: name of the gem, resolves gem_path - :param gem_path: path of the gem - :return: list of gem targets - """ - if not gem_name and not gem_path: - return [] - - if gem_name and not gem_path: - gem_path = get_registered(gem_name=gem_name) - - if not gem_path: - return [] - - gem_path = pathlib.Path(gem_path).resolve() - gem_json = gem_path / 'gem.json' - if not valid_o3de_gem_json(gem_json): - return [] - - module_identifiers = [ - 'MODULE', - 'GEM_MODULE', - '${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}' - ] - modules = [] - for root, dirs, files in os.walk(gem_path): - for file in files: - if file == 'CMakeLists.txt': - with open(os.path.join(root, file), 'r') as s: - for line in s: - trimmed = line.lstrip() - if trimmed.startswith('NAME '): - trimmed = trimmed.rstrip(' \n') - split_trimmed = trimmed.split(' ') - if len(split_trimmed) == 3 and split_trimmed[2] in module_identifiers: - modules.append(split_trimmed[1]) - return modules - - -def add_external_subdirectory(external_subdir: str or pathlib.Path, - engine_path: str or pathlib.Path = None, - supress_errors: bool = False) -> int: - """ - add external subdirectory to a cmake - :param external_subdir: external subdirectory to add to cmake - :param engine_path: optional engine path, defaults to this engine - :param supress_errors: optional silence errors - :return: 0 for success or non 0 failure code - """ - external_subdir = pathlib.Path(external_subdir).resolve() - if not external_subdir.is_dir(): - if not supress_errors: - logger.error(f'Add External Subdirectory Failed: {external_subdir} does not exist.') - return 1 - - external_subdir_cmake = external_subdir / 'CMakeLists.txt' - if not external_subdir_cmake.is_file(): - if not supress_errors: - logger.error(f'Add External Subdirectory Failed: {external_subdir} does not contain a CMakeLists.txt.') - return 1 - - json_data = load_o3de_manifest() - engine_object = find_engine_data(json_data, engine_path) - if not engine_object: - if not supress_errors: - logger.error(f'Add External Subdirectory Failed: {engine_path} not registered.') - return 1 - - while external_subdir.as_posix() in engine_object['external_subdirectories']: - engine_object['external_subdirectories'].remove(external_subdir.as_posix()) - - def parse_cmake_file(cmake: str or pathlib.Path, - files: set()): - cmake_path = pathlib.Path(cmake).resolve() - cmake_file = cmake_path - if cmake_path.is_dir(): - files.add(cmake_path) - cmake_file = cmake_path / 'CMakeLists.txt' - elif cmake_path.is_file(): - cmake_path = cmake_path.parent - else: - return - - with cmake_file.open('r') as s: - lines = s.readlines() - for line in lines: - line = line.strip() - start = line.find('include(') - if start == 0: - end = line.find(')', start) - if end > start + len('include('): - try: - include_cmake_file = pathlib.Path(engine_path / line[start + len('include('): end]).resolve() - except Exception as e: - pass - else: - parse_cmake_file(include_cmake_file, files) - else: - start = line.find('add_subdirectory(') - if start == 0: - end = line.find(')', start) - if end > start + len('add_subdirectory('): - try: - include_cmake_file = pathlib.Path( - cmake_path / line[start + len('add_subdirectory('): end]).resolve() - except Exception as e: - pass - else: - parse_cmake_file(include_cmake_file, files) - - cmake_files = set() - parse_cmake_file(engine_path, cmake_files) - for external in engine_object["external_subdirectories"]: - parse_cmake_file(external, cmake_files) - - if external_subdir in cmake_files: - save_o3de_manifest(json_data) - if not supress_errors: - logger.error(f'External subdirectory {external_subdir.as_posix()} already included by add_subdirectory().') - return 1 - - engine_object['external_subdirectories'].insert(0, external_subdir.as_posix()) - engine_object['external_subdirectories'] = sorted(engine_object['external_subdirectories']) - - save_o3de_manifest(json_data) - - return 0 - - -def remove_external_subdirectory(external_subdir: str or pathlib.Path, - engine_path: str or pathlib.Path = None) -> int: - """ - remove external subdirectory from cmake - :param external_subdir: external subdirectory to add to cmake - :param engine_path: optional engine path, defaults to this engine - :return: 0 for success or non 0 failure code - """ - json_data = load_o3de_manifest() - engine_object = find_engine_data(json_data, engine_path) - if not engine_object: - logger.error(f'Remove External Subdirectory Failed: {engine_path} not registered.') - return 1 - - external_subdir = pathlib.Path(external_subdir).resolve() - while external_subdir.as_posix() in engine_object['external_subdirectories']: - engine_object['external_subdirectories'].remove(external_subdir.as_posix()) - - save_o3de_manifest(json_data) - - return 0 - - -def add_gem_to_cmake(gem_name: str = None, - gem_path: str or pathlib.Path = None, - engine_name: str = None, - engine_path: str or pathlib.Path = None, - supress_errors: bool = False) -> int: - """ - add a gem to a cmake as an external subdirectory for an engine - :param gem_name: name of the gem to add to cmake - :param gem_path: the path of the gem to add to cmake - :param engine_name: name of the engine to add to cmake - :param engine_path: the path of the engine to add external subdirectory to, default to this engine - :param supress_errors: optional silence errors - :return: 0 for success or non 0 failure code - """ - if not gem_name and not gem_path: - if not supress_errors: - logger.error('Must specify either a Gem name or Gem Path.') - return 1 - - if gem_name and not gem_path: - gem_path = get_registered(gem_name=gem_name) - - if not gem_path: - if not supress_errors: - logger.error(f'Gem Path {gem_path} has not been registered.') - return 1 - - gem_path = pathlib.Path(gem_path).resolve() - gem_json = gem_path / 'gem.json' - if not gem_json.is_file(): - if not supress_errors: - logger.error(f'Gem json {gem_json} is not present.') - return 1 - if not valid_o3de_gem_json(gem_json): - if not supress_errors: - logger.error(f'Gem json {gem_json} is not valid.') - return 1 - - if not engine_name and not engine_path: - engine_path = get_this_engine_path() - - if engine_name and not engine_path: - engine_path = get_registered(engine_name=engine_name) - - if not engine_path: - if not supress_errors: - logger.error(f'Engine Path {engine_path} has not been registered.') - return 1 - - engine_json = engine_path / 'engine.json' - if not engine_json.is_file(): - if not supress_errors: - logger.error(f'Engine json {engine_json} is not present.') - return 1 - if not valid_o3de_engine_json(engine_json): - if not supress_errors: - logger.error(f'Engine json {engine_json} is not valid.') - return 1 - - return add_external_subdirectory(external_subdir=gem_path, engine_path=engine_path, supress_errors=supress_errors) - - -def remove_gem_from_cmake(gem_name: str = None, - gem_path: str or pathlib.Path = None, - engine_name: str = None, - engine_path: str or pathlib.Path = None) -> int: - """ - remove a gem to cmake as an external subdirectory - :param gem_name: name of the gem to remove from cmake - :param gem_path: the path of the gem to add to cmake - :param engine_name: optional name of the engine to remove from cmake - :param engine_path: the path of the engine to remove external subdirectory from, defaults to this engine - :return: 0 for success or non 0 failure code - """ - if not gem_name and not gem_path: - logger.error('Must specify either a Gem name or Gem Path.') - return 1 - - if gem_name and not gem_path: - gem_path = get_registered(gem_name=gem_name) - - if not gem_path: - logger.error(f'Gem Path {gem_path} has not been registered.') - return 1 - - if not engine_name and not engine_path: - engine_path = get_this_engine_path() - - if engine_name and not engine_path: - engine_path = get_registered(engine_name=engine_name) - - if not engine_path: - logger.error(f'Engine Path {engine_path} is not registered.') - return 1 - - return remove_external_subdirectory(external_subdir=gem_path, engine_path=engine_path) - - -def add_gem_to_project(gem_name: str = None, - gem_path: str or pathlib.Path = None, - gem_target: str = None, - project_name: str = None, - project_path: str or pathlib.Path = None, - dependencies_file: str or pathlib.Path = None, - runtime_dependency: bool = False, - tool_dependency: bool = False, - server_dependency: bool = False, - platforms: str = 'Common', - add_to_cmake: bool = True) -> int: - """ - add a gem to a project - :param gem_name: name of the gem to add - :param gem_path: path to the gem to add - :param gem_target: the name of the cmake gem module - :param project_name: name of to the project to add the gem to - :param project_path: path to the project to add the gem to - :param dependencies_file: if this dependency goes/is in a specific file - :param runtime_dependency: bool to specify this is a runtime gem for the game - :param tool_dependency: bool to specify this is a tool gem for the editor - :param server_dependency: bool to specify this is a server gem for the server - :param platforms: str to specify common or which specific platforms - :param add_to_cmake: bool to specify that this gem should be added to cmake - :return: 0 for success or non 0 failure code - """ - # we need either a project name or path - if not project_name and not project_path: - logger.error(f'Must either specify a Project path or Project Name.') - return 1 - - # if project name resolve it into a path - if project_name and not project_path: - project_path = get_registered(project_name=project_name) - project_path = pathlib.Path(project_path).resolve() - if not project_path.is_dir(): - logger.error(f'Project path {project_path} is not a folder.') - return 1 - - # get the engine name this project is associated with - # and resolve that engines path - project_json = project_path / 'project.json' - if not valid_o3de_project_json(project_json): - logger.error(f'Project json {project_json} is not valid.') - return 1 - with project_json.open('r') as s: - try: - project_json_data = json.load(s) - except Exception as e: - logger.error(f'Error loading Project json {project_json}: {str(e)}') - return 1 - else: - try: - engine_name = project_json_data['engine'] - except Exception as e: - logger.error(f'Project json {project_json} "engine" not found: {str(e)}') - return 1 - else: - engine_path = get_registered(engine_name=engine_name) - if not engine_path: - logger.error(f'Engine {engine_name} is not registered.') - return 1 - - # we need either a gem name or path - if not gem_name and not gem_path: - logger.error(f'Must either specify a Gem path or Gem Name.') - return 1 - - # if gem name resolve it into a path - if gem_name and not gem_path: - gem_path = get_registered(gem_name=gem_name) - gem_path = pathlib.Path(gem_path).resolve() - # make sure this gem already exists if we're adding. We can always remove a gem. - if not gem_path.is_dir(): - logger.error(f'Gem Path {gem_path} does not exist.') - return 1 - - # if add to cmake, make sure the gem.json exists and valid before we proceed - if add_to_cmake: - gem_json = gem_path / 'gem.json' - if not gem_json.is_file(): - logger.error(f'Gem json {gem_json} is not present.') - return 1 - if not valid_o3de_gem_json(gem_json): - logger.error(f'Gem json {gem_json} is not valid.') - return 1 - - # find all available modules in this gem_path - modules = get_gem_targets(gem_path=gem_path) - if len(modules) == 0: - logger.error(f'No gem modules found under {gem_path}.') - return 1 - - # if the gem has no modules and the user has specified a target fail - if gem_target and not modules: - logger.error(f'Gem has no targets, but gem target {gem_target} was specified.') - return 1 - - # if the gem target is not in the modules - if gem_target not in modules: - logger.error(f'Gem target not in gem modules: {modules}') - return 1 - - if gem_target: - # if the user has not specified either we will assume they meant the most common which is runtime - if not runtime_dependency and not tool_dependency and not server_dependency and not dependencies_file: - logger.warning("Dependency type not specified: Assuming '--runtime-dependency'") - runtime_dependency = True - - ret_val = 0 - - # if the user has specified the dependencies file then ignore the runtime_dependency and tool_dependency flags - if dependencies_file: - dependencies_file = pathlib.Path(dependencies_file).resolve() - # make sure this is a project has a dependencies_file - if not dependencies_file.is_file(): - logger.error(f'Dependencies file {dependencies_file} is not present.') - return 1 - # add the dependency - ret_val = add_gem_dependency(dependencies_file, gem_target) - - else: - if ',' in platforms: - platforms = platforms.split(',') - else: - platforms = [platforms] - for platform in platforms: - if runtime_dependency: - # make sure this is a project has a runtime_dependencies.cmake file - project_runtime_dependencies_file = pathlib.Path( - get_dependencies_cmake_file(project_path=project_path, dependency_type='runtime', platform=platform)).resolve() - if not project_runtime_dependencies_file.is_file(): - logger.error(f'Runtime dependencies file {project_runtime_dependencies_file} is not present.') - return 1 - # add the dependency - ret_val = add_gem_dependency(project_runtime_dependencies_file, gem_target) - - if (ret_val == 0) and tool_dependency: - # make sure this is a project has a tool_dependencies.cmake file - project_tool_dependencies_file = pathlib.Path( - get_dependencies_cmake_file(project_path=project_path, dependency_type='tool', platform=platform)).resolve() - if not project_tool_dependencies_file.is_file(): - logger.error(f'Tool dependencies file {project_tool_dependencies_file} is not present.') - return 1 - # add the dependency - ret_val = add_gem_dependency(project_tool_dependencies_file, gem_target) - - if (ret_val == 0) and server_dependency: - # make sure this is a project has a tool_dependencies.cmake file - project_server_dependencies_file = pathlib.Path( - get_dependencies_cmake_file(project_path=project_path, dependency_type='server', platform=platform)).resolve() - if not project_server_dependencies_file.is_file(): - logger.error(f'Server dependencies file {project_server_dependencies_file} is not present.') - return 1 - # add the dependency - ret_val = add_gem_dependency(project_server_dependencies_file, gem_target) - - if not ret_val and add_to_cmake: - ret_val = add_gem_to_cmake(gem_path=gem_path, engine_path=engine_path) - - return ret_val - - -def remove_gem_from_project(gem_name: str = None, - gem_path: str or pathlib.Path = None, - gem_target: str = None, - project_name: str = None, - project_path: str or pathlib.Path = None, - dependencies_file: str or pathlib.Path = None, - runtime_dependency: bool = False, - tool_dependency: bool = False, - server_dependency: bool = False, - platforms: str = 'Common', - remove_from_cmake: bool = False) -> int: - """ - remove a gem from a project - :param gem_name: name of the gem to add - :param gem_path: path to the gem to add - :param gem_target: the name of teh cmake gem module - :param project_name: name of the project to add the gem to - :param project_path: path to the project to add the gem to - :param dependencies_file: if this dependency goes/is in a specific file - :param runtime_dependency: bool to specify this is a runtime gem for the game - :param tool_dependency: bool to specify this is a tool gem for the editor - :param server_dependency: bool to specify this is a server gem for the server - :param platforms: str to specify common or which specific platforms - :param remove_from_cmake: bool to specify that this gem should be removed from cmake - :return: 0 for success or non 0 failure code - """ - - # we need either a project name or path - if not project_name and not project_path: - logger.error(f'Must either specify a Project path or Project Name.') - return 1 - - # if project name resolve it into a path - if project_name and not project_path: - project_path = get_registered(project_name=project_name) - project_path = pathlib.Path(project_path).resolve() - if not project_path.is_dir(): - logger.error(f'Project path {project_path} is not a folder.') - return 1 - - # We need either a gem name or path - if not gem_name and not gem_path: - logger.error(f'Must either specify a Gem path or Gem Name.') - return 1 - - # if gem name resolve it into a path - if gem_name and not gem_path: - gem_path = get_registered(gem_name=gem_name) - gem_path = pathlib.Path(gem_path).resolve() - # make sure this gem already exists if we're adding. We can always remove a gem. - if not gem_path.is_dir(): - logger.error(f'Gem Path {gem_path} does not exist.') - return 1 - - # find all available modules in this gem_path - modules = get_gem_targets(gem_path=gem_path) - if len(modules) == 0: - logger.error(f'No gem modules found.') - return 1 - - # if the user has not set a specific gem target remove all of them - - # if gem target not specified, see if there is only 1 module - if not gem_target: - if len(modules) == 1: - gem_target = modules[0] - else: - logger.error(f'Gem target not specified: {modules}') - return 1 - elif gem_target not in modules: - logger.error(f'Gem target not in gem modules: {modules}') - return 1 - - # if the user has not specified either we will assume they meant the most common which is runtime - if not runtime_dependency and not tool_dependency and not server_dependency and not dependencies_file: - logger.warning("Dependency type not specified: Assuming '--runtime-dependency'") - runtime_dependency = True - - # when removing we will try to do as much as possible even with failures so ret_val will be the last error code - ret_val = 0 - - # if the user has specified the dependencies file then ignore the runtime_dependency and tool_dependency flags - if dependencies_file: - dependencies_file = pathlib.Path(dependencies_file).resolve() - # make sure this is a project has a dependencies_file - if not dependencies_file.is_file(): - logger.error(f'Dependencies file {dependencies_file} is not present.') - return 1 - # remove the dependency - error_code = remove_gem_dependency(dependencies_file, gem_target) - if error_code: - ret_val = error_code - else: - if ',' in platforms: - platforms = platforms.split(',') - else: - platforms = [platforms] - for platform in platforms: - if runtime_dependency: - # make sure this is a project has a runtime_dependencies.cmake file - project_runtime_dependencies_file = pathlib.Path( - get_dependencies_cmake_file(project_path=project_path, dependency_type='runtime', platform=platform)).resolve() - if not project_runtime_dependencies_file.is_file(): - logger.error(f'Runtime dependencies file {project_runtime_dependencies_file} is not present.') - else: - # remove the dependency - error_code = remove_gem_dependency(project_runtime_dependencies_file, gem_target) - if error_code: - ret_val = error_code - - if tool_dependency: - # make sure this is a project has a tool_dependencies.cmake file - project_tool_dependencies_file = pathlib.Path( - get_dependencies_cmake_file(project_path=project_path, dependency_type='tool', platform=platform)).resolve() - if not project_tool_dependencies_file.is_file(): - logger.error(f'Tool dependencies file {project_tool_dependencies_file} is not present.') - else: - # remove the dependency - error_code = remove_gem_dependency(project_tool_dependencies_file, gem_target) - if error_code: - ret_val = error_code - - if server_dependency: - # make sure this is a project has a tool_dependencies.cmake file - project_server_dependencies_file = pathlib.Path( - get_dependencies_cmake_file(project_path=project_path, dependency_type='server', platform=platform)).resolve() - if not project_server_dependencies_file.is_file(): - logger.error(f'Server dependencies file {project_server_dependencies_file} is not present.') - else: - # remove the dependency - error_code = remove_gem_dependency(project_server_dependencies_file, gem_target) - if error_code: - ret_val = error_code - - if remove_from_cmake: - error_code = remove_gem_from_cmake(gem_path=gem_path) - if error_code: - ret_val = error_code - - return ret_val - - -def sha256(file_path: str or pathlib.Path, - json_path: str or pathlib.Path = None) -> int: - if not file_path: - logger.error(f'File path cannot be empty.') - return 1 - file_path = pathlib.Path(file_path).resolve() - if not file_path.is_file(): - logger.error(f'File path {file_path} does not exist.') - return 1 - - if json_path: - json_path = pathlib.Path(json_path).resolve() - if not json_path.is_file(): - logger.error(f'Json path {json_path} does not exist.') - return 1 - - sha256 = hashlib.sha256(file_path.open('rb').read()).hexdigest() - - if json_path: - with json_path.open('r') as s: - try: - json_data = json.load(s) - except Exception as e: - logger.error(f'Failed to read Json path {json_path}: {str(e)}') - return 1 - json_data.update({"sha256": sha256}) - backup_file(json_path) - with json_path.open('w') as s: - try: - s.write(json.dumps(json_data, indent=4)) - except Exception as e: - logger.error(f'Failed to write Json path {json_path}: {str(e)}') - return 1 - else: - print(sha256) - return 0 - - -def _run_get_registered(args: argparse) -> str or pathlib.Path: - if args.override_home_folder: - global override_home_folder - override_home_folder = args.override_home_folder - - return get_registered(args.engine_name, - args.project_name, - args.gem_name, - args.template_name, - args.default_folder, - args.repo_name, - args.restricted_name) - - -def _run_register_show(args: argparse) -> int: - if args.override_home_folder: - global override_home_folder - override_home_folder = args.override_home_folder - - if args.this_engine: - print_this_engine(args.verbose) - return 0 - - elif args.engines: - print_engines(args.verbose) - return 0 - elif args.projects: - print_projects(args.verbose) - return 0 - elif args.gems: - print_gems(args.verbose) - return 0 - elif args.templates: - print_templates(args.verbose) - return 0 - elif args.repos: - register_show_repos(args.verbose) - return 0 - elif args.restricted: - print_restricted(args.verbose) - return 0 - - elif args.engine_projects: - print_engine_projects(args.verbose) - return 0 - elif args.engine_gems: - print_engine_gems(args.verbose) - return 0 - elif args.engine_templates: - print_engine_templates(args.verbose) - return 0 - elif args.engine_restricted: - print_engine_restricted(args.verbose) - return 0 - elif args.external_subdirectories: - print_external_subdirectories(args.verbose) - return 0 - - elif args.all_projects: - print_all_projects(args.verbose) - return 0 - elif args.all_gems: - print_all_gems(args.verbose) - return 0 - elif args.all_templates: - print_all_templates(args.verbose) - return 0 - elif args.all_restricted: - print_all_restricted(args.verbose) - return 0 - - elif args.downloadables: - print_downloadables(args.verbose) - return 0 - if args.downloadable_engines: - print_downloadable_engines(args.verbose) - return 0 - elif args.downloadable_projects: - print_downloadable_projects(args.verbose) - return 0 - elif args.downloadable_gems: - print_downloadable_gems(args.verbose) - return 0 - elif args.downloadable_templates: - print_downloadable_templates(args.verbose) - return 0 - else: - register_show(args.verbose) - return 0 - - -def _run_download(args: argparse) -> int: - if args.override_home_folder: - global override_home_folder - override_home_folder = args.override_home_folder - - if args.engine_name: - return download_engine(args.engine_name, - args.dest_path) - elif args.project_name: - return download_project(args.project_name, - args.dest_path) - elif args.gem_nanme: - return download_gem(args.gem_name, - args.dest_path) - elif args.template_name: - return download_template(args.template_name, - args.dest_path) - - -def _run_register(args: argparse) -> int: - if args.override_home_folder: - global override_home_folder - override_home_folder = args.override_home_folder - - if args.update: - remove_invalid_o3de_objects() - return refresh_repos() - elif args.this_engine: - ret_val = register(engine_path=get_this_engine_path()) - error_code = register_shipped_engine_o3de_objects() - if error_code: - ret_val = error_code - return ret_val - elif args.all_engines_path: - return register_all_engines_in_folder(args.all_engines_path, args.remove) - elif args.all_projects_path: - return register_all_projects_in_folder(args.all_projects_path, args.remove) - elif args.all_gems_path: - return register_all_gems_in_folder(args.all_gems_path, args.remove) - elif args.all_templates_path: - return register_all_templates_in_folder(args.all_templates_path, args.remove) - elif args.all_restricted_path: - return register_all_restricted_in_folder(args.all_restricted_path, args.remove) - elif args.all_repo_uri: - return register_all_repos_in_folder(args.all_restricted_path, args.remove) - else: - return register(engine_path=args.engine_path, - project_path=args.project_path, - gem_path=args.gem_path, - template_path=args.template_path, - restricted_path=args.restricted_path, - repo_uri=args.repo_uri, - default_engines_folder=args.default_engines_folder, - default_projects_folder=args.default_projects_folder, - default_gems_folder=args.default_gems_folder, - default_templates_folder=args.default_templates_folder, - default_restricted_folder=args.default_restricted_folder, - remove=args.remove) - - -def _run_add_external_subdirectory(args: argparse) -> int: - if args.override_home_folder: - global override_home_folder - override_home_folder = args.override_home_folder - - return add_external_subdirectory(args.external_subdirectory) - - -def _run_remove_external_subdirectory(args: argparse) -> int: - if args.override_home_folder: - global override_home_folder - override_home_folder = args.override_home_folder - - return remove_external_subdirectory(args.external_subdirectory) - - -def _run_add_gem_to_cmake(args: argparse) -> int: - if args.override_home_folder: - global override_home_folder - override_home_folder = args.override_home_folder - - return add_gem_to_cmake(gem_name=args.gem_name, gem_path=args.gem_path) - - -def _run_remove_gem_from_cmake(args: argparse) -> int: - if args.override_home_folder: - global override_home_folder - override_home_folder = args.override_home_folder - - return remove_gem_from_cmake(args.gem_name, args.gem_path) - - -def _run_add_gem_to_project(args: argparse) -> int: - if args.override_home_folder: - global override_home_folder - override_home_folder = args.override_home_folder - - return add_gem_to_project(args.gem_name, - args.gem_path, - args.gem_target, - args.project_name, - args.project_path, - args.dependencies_file, - args.runtime_dependency, - args.tool_dependency, - args.server_dependency, - args.platforms, - args.add_to_cmake) - - -def _run_remove_gem_from_project(args: argparse) -> int: - if args.override_home_folder: - global override_home_folder - override_home_folder = args.override_home_folder - - return remove_gem_from_project(args.gem_name, - args.gem_path, - args.gem_target, - args.project_path, - args.project_name, - args.dependencies_file, - args.runtime_dependency, - args.tool_dependency, - args.server_dependency, - args.platforms, - args.remove_from_cmake) - - -def _run_sha256(args: argparse) -> int: - return sha256(args.file_path, - args.json_path) - - -def add_args(parser, subparsers) -> None: - """ - add_args is called to add expected parser arguments and subparsers arguments to each command such that it can be - invoked locally or added by a central python file. - Ex. Directly run from this file alone with: python register.py register --gem-path "C:/TestGem" - OR - o3de.py can downloadable commands by importing engine_template, - call add_args and execute: python o3de.py register --gem-path "C:/TestGem" - :param parser: the caller instantiates a parser and passes it in here - :param subparsers: the caller instantiates subparsers and passes it in here - """ - # register - register_subparser = subparsers.add_parser('register') - group = register_subparser.add_mutually_exclusive_group(required=True) - group.add_argument('--this-engine', action='store_true', required=False, - default=False, - help='Registers the engine this script is running from.') - group.add_argument('-ep', '--engine-path', type=str, required=False, - help='Engine path to register/remove.') - group.add_argument('-pp', '--project-path', type=str, required=False, - help='Project path to register/remove.') - group.add_argument('-gp', '--gem-path', type=str, required=False, - help='Gem path to register/remove.') - group.add_argument('-tp', '--template-path', type=str, required=False, - help='Template path to register/remove.') - group.add_argument('-rp', '--restricted-path', type=str, required=False, - help='A restricted folder to register/remove.') - group.add_argument('-ru', '--repo-uri', type=str, required=False, - help='A repo uri to register/remove.') - group.add_argument('-aep', '--all-engines-path', type=str, required=False, - help='All engines under this folder to register/remove.') - group.add_argument('-app', '--all-projects-path', type=str, required=False, - help='All projects under this folder to register/remove.') - group.add_argument('-agp', '--all-gems-path', type=str, required=False, - help='All gems under this folder to register/remove.') - group.add_argument('-atp', '--all-templates-path', type=str, required=False, - help='All templates under this folder to register/remove.') - group.add_argument('-arp', '--all-restricted-path', type=str, required=False, - help='All templates under this folder to register/remove.') - group.add_argument('-aru', '--all-repo-uri', type=str, required=False, - help='All repos under this folder to register/remove.') - group.add_argument('-def', '--default-engines-folder', type=str, required=False, - help='The default engines folder to register/remove.') - group.add_argument('-dpf', '--default-projects-folder', type=str, required=False, - help='The default projects folder to register/remove.') - group.add_argument('-dgf', '--default-gems-folder', type=str, required=False, - help='The default gems folder to register/remove.') - group.add_argument('-dtf', '--default-templates-folder', type=str, required=False, - help='The default templates folder to register/remove.') - group.add_argument('-drf', '--default-restricted-folder', type=str, required=False, - help='The default restricted folder to register/remove.') - group.add_argument('-u', '--update', action='store_true', required=False, - default=False, - help='Refresh the repo cache.') - - register_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') - - register_subparser.add_argument('-r', '--remove', action='store_true', required=False, - default=False, - help='Remove entry.') - register_subparser.set_defaults(func=_run_register) - - # show - register_show_subparser = subparsers.add_parser('register-show') - group = register_show_subparser.add_mutually_exclusive_group(required=False) - group.add_argument('-te', '--this-engine', action='store_true', required=False, - default=False, - help='Just the local engines.') - - group.add_argument('-e', '--engines', action='store_true', required=False, - default=False, - help='Just the local engines.') - group.add_argument('-p', '--projects', action='store_true', required=False, - default=False, - help='Just the local projects.') - group.add_argument('-g', '--gems', action='store_true', required=False, - default=False, - help='Just the local gems.') - group.add_argument('-t', '--templates', action='store_true', required=False, - default=False, - help='Just the local templates.') - group.add_argument('-r', '--repos', action='store_true', required=False, - default=False, - help='Just the local repos. Ignores repos.') - group.add_argument('-rs', '--restricted', action='store_true', required=False, - default=False, - help='The local restricted folders.') - - group.add_argument('-ep', '--engine-projects', action='store_true', required=False, - default=False, - help='Just the local projects. Ignores repos.') - group.add_argument('-eg', '--engine-gems', action='store_true', required=False, - default=False, - help='Just the local gems. Ignores repos') - group.add_argument('-et', '--engine-templates', action='store_true', required=False, - default=False, - help='Just the local templates. Ignores repos.') - group.add_argument('-ers', '--engine-restricted', action='store_true', required=False, - default=False, - help='The restricted folders.') - group.add_argument('-x', '--external-subdirectories', action='store_true', required=False, - default=False, - help='The external subdirectories.') - - group.add_argument('-ap', '--all-projects', action='store_true', required=False, - default=False, - help='Just the local projects. Ignores repos.') - group.add_argument('-ag', '--all-gems', action='store_true', required=False, - default=False, - help='Just the local gems. Ignores repos') - group.add_argument('-at', '--all-templates', action='store_true', required=False, - default=False, - help='Just the local templates. Ignores repos.') - group.add_argument('-ars', '--all-restricted', action='store_true', required=False, - default=False, - help='The restricted folders.') - - group.add_argument('-d', '--downloadables', action='store_true', required=False, - default=False, - help='Combine all repos into a single list of resources.') - group.add_argument('-de', '--downloadable-engines', action='store_true', required=False, - default=False, - help='Combine all repos engines into a single list of resources.') - group.add_argument('-dp', '--downloadable-projects', action='store_true', required=False, - default=False, - help='Combine all repos projects into a single list of resources.') - group.add_argument('-dg', '--downloadable-gems', action='store_true', required=False, - default=False, - help='Combine all repos gems into a single list of resources.') - group.add_argument('-dt', '--downloadable-templates', action='store_true', required=False, - default=False, - help='Combine all repos templates into a single list of resources.') - - register_show_subparser.add_argument('-v', '--verbose', action='count', required=False, - default=0, - help='How verbose do you want the output to be.') - - register_show_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') - - register_show_subparser.set_defaults(func=_run_register_show) - - # get-registered - get_registered_subparser = subparsers.add_parser('get-registered') - group = get_registered_subparser.add_mutually_exclusive_group(required=True) - group.add_argument('-en', '--engine-name', type=str, required=False, - help='Engine name.') - group.add_argument('-pn', '--project-name', type=str, required=False, - help='Project name.') - group.add_argument('-gn', '--gem-name', type=str, required=False, - help='Gem name.') - group.add_argument('-tn', '--template-name', type=str, required=False, - help='Template name.') - group.add_argument('-df', '--default-folder', type=str, required=False, - choices=['engines', 'projects', 'gems', 'templates', 'restricted'], - help='The default folders for o3de.') - group.add_argument('-rn', '--repo-name', type=str, required=False, - help='Repo name.') - group.add_argument('-rsn', '--restricted-name', type=str, required=False, - help='Restricted name.') - - get_registered_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') - - get_registered_subparser.set_defaults(func=_run_get_registered) - - # download - download_subparser = subparsers.add_parser('download') - group = download_subparser.add_mutually_exclusive_group(required=True) - group.add_argument('-e', '--engine-name', type=str, required=False, - help='Downloadable engine name.') - group.add_argument('-p', '--project-name', type=str, required=False, - help='Downloadable project name.') - group.add_argument('-g', '--gem-name', type=str, required=False, - help='Downloadable gem name.') - group.add_argument('-t', '--template-name', type=str, required=False, - help='Downloadable template name.') - download_subparser.add_argument('-dp', '--dest-path', type=str, required=False, - default=None, - help='Optional destination folder to download into.' - ' i.e. download --project-name "StarterGame" --dest-path "C:/projects"' - ' will result in C:/projects/StarterGame' - ' If blank will download to default object type folder') - - download_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') - - download_subparser.set_defaults(func=_run_download) - - # add external subdirectories - add_external_subdirectory_subparser = subparsers.add_parser('add-external-subdirectory') - add_external_subdirectory_subparser.add_argument('external_subdirectory', metavar='external_subdirectory', type=str, - help='add an external subdirectory to cmake') - - add_external_subdirectory_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') - - add_external_subdirectory_subparser.set_defaults(func=_run_add_external_subdirectory) - - # remove external subdirectories - remove_external_subdirectory_subparser = subparsers.add_parser('remove-external-subdirectory') - remove_external_subdirectory_subparser.add_argument('external_subdirectory', metavar='external_subdirectory', - type=str, - help='remove external subdirectory from cmake') - - remove_external_subdirectory_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') - - remove_external_subdirectory_subparser.set_defaults(func=_run_remove_external_subdirectory) - - # add gems to cmake - # convenience functions to disambiguate the gem name -> gem_path and call add-external-subdirectory on gem_path - add_gem_to_cmake_subparser = subparsers.add_parser('add-gem-to-cmake') - group = add_gem_to_cmake_subparser.add_mutually_exclusive_group(required=True) - group.add_argument('-gp', '--gem-path', type=str, required=False, - help='The path to the gem.') - group.add_argument('-gn', '--gem-name', type=str, required=False, - help='The name of the gem.') - - add_gem_to_cmake_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') - - add_gem_to_cmake_subparser.set_defaults(func=_run_add_gem_to_cmake) - - # remove gems from cmake - # convenience functions to disambiguate the gem name -> gem_path and call remove-external-subdirectory on gem_path - remove_gem_from_cmake_subparser = subparsers.add_parser('remove-gem-from-cmake') - group = remove_gem_from_cmake_subparser.add_mutually_exclusive_group(required=True) - group.add_argument('-gp', '--gem-path', type=str, required=False, - help='The path to the gem.') - group.add_argument('-gn', '--gem-name', type=str, required=False, - help='The name of the gem.') - - remove_gem_from_cmake_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') - - remove_gem_from_cmake_subparser.set_defaults(func=_run_remove_gem_from_cmake) - - # add a gem to a project - add_gem_subparser = subparsers.add_parser('add-gem-to-project') - group = add_gem_subparser.add_mutually_exclusive_group(required=True) - group.add_argument('-pp', '--project-path', type=str, required=False, - help='The path to the project.') - group.add_argument('-pn', '--project-name', type=str, required=False, - help='The name of the project.') - group = add_gem_subparser.add_mutually_exclusive_group(required=True) - group.add_argument('-gp', '--gem-path', type=str, required=False, - help='The path to the gem.') - group.add_argument('-gn', '--gem-name', type=str, required=False, - help='The name of the gem.') - add_gem_subparser.add_argument('-gt', '--gem-target', type=str, required=False, - help='The cmake target name to add. If not specified it will assume gem_name') - add_gem_subparser.add_argument('-df', '--dependencies-file', type=str, required=False, - help='The cmake dependencies file in which the gem dependencies are specified.' - 'If not specified it will assume ') - add_gem_subparser.add_argument('-rd', '--runtime-dependency', action='store_true', required=False, - default=False, - help='Optional toggle if this gem should be added as a runtime dependency') - add_gem_subparser.add_argument('-td', '--tool-dependency', action='store_true', required=False, - default=False, - help='Optional toggle if this gem should be added as a tool dependency') - add_gem_subparser.add_argument('-sd', '--server-dependency', action='store_true', required=False, - default=False, - help='Optional toggle if this gem should be added as a server dependency') - add_gem_subparser.add_argument('-pl', '--platforms', type=str, required=False, - default='Common', - help='Optional list of platforms this gem should be added to.' - ' Ex. --platforms Mac,Windows,Linux') - add_gem_subparser.add_argument('-a', '--add-to-cmake', type=bool, required=False, - default=True, - help='Automatically call add-gem-to-cmake.') - - add_gem_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') - - add_gem_subparser.set_defaults(func=_run_add_gem_to_project) - - # remove a gem from a project - remove_gem_subparser = subparsers.add_parser('remove-gem-from-project') - group = remove_gem_subparser.add_mutually_exclusive_group(required=True) - group.add_argument('-pp', '--project-path', type=str, required=False, - help='The path to the project.') - group.add_argument('-pn', '--project-name', type=str, required=False, - help='The name of the project.') - group = remove_gem_subparser.add_mutually_exclusive_group(required=True) - group.add_argument('-gp', '--gem-path', type=str, required=False, - help='The path to the gem.') - group.add_argument('-gn', '--gem-name', type=str, required=False, - help='The name of the gem.') - remove_gem_subparser.add_argument('-gt', '--gem-target', type=str, required=False, - help='The cmake target name to add. If not specified it will assume gem_name') - remove_gem_subparser.add_argument('-df', '--dependencies-file', type=str, required=False, - help='The cmake dependencies file in which the gem dependencies are specified.' - 'If not specified it will assume ') - remove_gem_subparser.add_argument('-rd', '--runtime-dependency', action='store_true', required=False, - default=False, - help='Optional toggle if this gem should be removed as a runtime dependency') - remove_gem_subparser.add_argument('-td', '--tool-dependency', action='store_true', required=False, - default=False, - help='Optional toggle if this gem should be removed as a server dependency') - remove_gem_subparser.add_argument('-sd', '--server-dependency', action='store_true', required=False, - default=False, - help='Optional toggle if this gem should be removed as a server dependency') - remove_gem_subparser.add_argument('-pl', '--platforms', type=str, required=False, - default='Common', - help='Optional list of platforms this gem should be removed from' - ' Ex. --platforms Mac,Windows,Linux') - remove_gem_subparser.add_argument('-r', '--remove-from-cmake', type=bool, required=False, - default=False, - help='Automatically call remove-from-cmake.') - - remove_gem_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') - - remove_gem_subparser.set_defaults(func=_run_remove_gem_from_project) - - # sha256 - sha256_subparser = subparsers.add_parser('sha256') - sha256_subparser.add_argument('-f', '--file-path', type=str, required=True, - help='The path to the file you want to sha256.') - sha256_subparser.add_argument('-j', '--json-path', type=str, required=False, - help='optional path to an o3de json file to add the "sha256" element to.') - sha256_subparser.set_defaults(func=_run_sha256) - - -if __name__ == "__main__": - # parse the command line args - the_parser = argparse.ArgumentParser() - - # add subparsers - the_subparsers = the_parser.add_subparsers(help='sub-command help') - - # add args to the parser - add_args(the_parser, the_subparsers) - - # parse args - the_args = the_parser.parse_args() - - # run - ret = the_args.func(the_args) - - # return - sys.exit(ret) diff --git a/cmake/Tools/unit_test_add_remove_gem.py b/cmake/Tools/unit_test_add_remove_gem.py deleted file mode 100755 index 81f0fa615e..0000000000 --- a/cmake/Tools/unit_test_add_remove_gem.py +++ /dev/null @@ -1,259 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -import os -import pytest - -from . import add_remove_gem - -TEST_WITHOUT_NO_GEM_CONTENT = """ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES -) -""" - -TEST_WITHOUT_ONLY_GEM_CONTENT = """ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Gem::TestGem -) -""" - -TEST_WITHOUT_ADDED_GEM_CONTENT = """ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Gem::ExistingGem -) -""" - -TEST_WITH_ADDED_GEM_CONTENT = """ -# {BEGIN_LICENSE} -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# {END_LICENSE} - -set(GEM_DEPENDENCIES - Gem::TestGem - Gem::ExistingGem -) -""" - - -@pytest.mark.parametrize( - "contents, gem, expected_result, runtime_present, expect_failure", [ - pytest.param(TEST_WITHOUT_ADDED_GEM_CONTENT, "TestGem", TEST_WITH_ADDED_GEM_CONTENT, True, False), - pytest.param(TEST_WITHOUT_ADDED_GEM_CONTENT, "TestGem", TEST_WITH_ADDED_GEM_CONTENT, False, True), - pytest.param(TEST_WITHOUT_ADDED_GEM_CONTENT, "/TestGem", TEST_WITH_ADDED_GEM_CONTENT, True, True), - pytest.param(TEST_WITHOUT_NO_GEM_CONTENT, "TestGem", TEST_WITHOUT_ONLY_GEM_CONTENT, True, False), - ] -) -def test_add_gem_dependency(tmpdir, contents, gem, expected_result, runtime_present, expect_failure): - dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/') - os.makedirs(dev_root, exist_ok=True) - - dev_project_gem_code = f'{dev_root}/TestProject/Gem/Code' - os.makedirs(dev_project_gem_code, exist_ok=True) - - runtime_dependencies_cmake_file = f'{dev_project_gem_code}/runtime_dependencies.cmake' - if runtime_present: - if os.path.isfile(runtime_dependencies_cmake_file): - os.unlink(runtime_dependencies_cmake_file) - with open(runtime_dependencies_cmake_file, 'a') as s: - s.write(contents) - - result = add_remove_gem.add_gem_dependency(runtime_dependencies_cmake_file, gem) - - if expect_failure: - assert result != 0 - else: - assert result == 0 - with open(runtime_dependencies_cmake_file, 'r') as s: - s_data = s.read() - assert s_data == expected_result - - -@pytest.mark.parametrize( - "contents, gem, expected_result, runtime_present, expect_failure", [ - pytest.param(TEST_WITH_ADDED_GEM_CONTENT, "TestGem", TEST_WITHOUT_ADDED_GEM_CONTENT, True, False), - pytest.param(TEST_WITH_ADDED_GEM_CONTENT, "TestGem", TEST_WITHOUT_ADDED_GEM_CONTENT, False, True), - pytest.param(TEST_WITHOUT_ADDED_GEM_CONTENT, "TestGem", TEST_WITHOUT_ADDED_GEM_CONTENT, True, True) - ] -) -def test_remove_gem_dependency(tmpdir, contents, gem, expected_result, runtime_present, expect_failure): - dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/') - os.makedirs(dev_root, exist_ok=True) - - dev_project_gem_code = f'{dev_root}/TestProject/Gem/Code' - os.makedirs(dev_project_gem_code, exist_ok=True) - - runtime_dependencies_cmake_file = f'{dev_project_gem_code}/runtime_dependencies.cmake' - if runtime_present: - if os.path.isfile(runtime_dependencies_cmake_file): - os.unlink(runtime_dependencies_cmake_file) - with open(runtime_dependencies_cmake_file, 'a') as s: - s.write(contents) - - result = add_remove_gem.remove_gem_dependency(runtime_dependencies_cmake_file, gem) - - if expect_failure: - assert result != 0 - else: - assert result == 0 - with open(runtime_dependencies_cmake_file, 'r') as s: - s_data = s.read() - assert s_data == expected_result - - -@pytest.mark.parametrize("add," - " contents, gem, project, expected_result," - " runtime_present, tool_present," - " ask_for_runtime, ask_for_tool," - " expect_failure", [ - pytest.param(True, - TEST_WITHOUT_ADDED_GEM_CONTENT, "TestGem", "TestProject", - TEST_WITH_ADDED_GEM_CONTENT, - True, True, - True, True, - False), - pytest.param(True, - TEST_WITHOUT_ADDED_GEM_CONTENT, "TestGem", "TestProject", - TEST_WITH_ADDED_GEM_CONTENT, - True, False, - True, True, - True), - pytest.param(True, - TEST_WITHOUT_ADDED_GEM_CONTENT, "TestGem", "TestProject", - TEST_WITH_ADDED_GEM_CONTENT, - False, True, - True, True, - True), - pytest.param(True, - TEST_WITHOUT_ADDED_GEM_CONTENT, "TestGem", "TestProject", - TEST_WITH_ADDED_GEM_CONTENT, - False, False, - True, True, - True), - - pytest.param(False, - TEST_WITH_ADDED_GEM_CONTENT, "TestGem", "TestProject", - TEST_WITHOUT_ADDED_GEM_CONTENT, - True, True, - True, True, - False), - pytest.param(False, - TEST_WITH_ADDED_GEM_CONTENT, "TestGem", "TestProject", - TEST_WITHOUT_ADDED_GEM_CONTENT, - True, False, - True, True, - True), - pytest.param(False, - TEST_WITH_ADDED_GEM_CONTENT, "TestGem", "TestProject", - TEST_WITHOUT_ADDED_GEM_CONTENT, - False, True, - True, True, - True), - pytest.param(False, - TEST_WITH_ADDED_GEM_CONTENT, "TestGem", "TestProject", - TEST_WITHOUT_ADDED_GEM_CONTENT, - False, False, - True, True, - True) - ] - ) -def test_add_remove_gem(tmpdir, - add, - contents, gem, project, - expected_result, - runtime_present, tool_present, - ask_for_runtime, ask_for_tool, - expect_failure): - dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/') - os.makedirs(dev_root, exist_ok=True) - - dev_project_gem_code = f'{dev_root}/TestProject/Gem/Code' - os.makedirs(dev_project_gem_code, exist_ok=True) - - runtime_dependencies_cmake_file = f'{dev_project_gem_code}/runtime_dependencies.cmake' - if runtime_present: - if os.path.isfile(runtime_dependencies_cmake_file): - os.unlink(runtime_dependencies_cmake_file) - with open(runtime_dependencies_cmake_file, 'a') as s: - s.write(contents) - - tool_dependencies_cmake_file = f'{dev_project_gem_code}/tool_dependencies.cmake' - os.makedirs(dev_project_gem_code, exist_ok=True) - - if tool_present: - if os.path.isfile(tool_dependencies_cmake_file): - os.unlink(tool_dependencies_cmake_file) - with open(tool_dependencies_cmake_file, 'w') as s: - s.write(contents) - - project_folder = f'{dev_root}/TestProject' - os.makedirs(project_folder, exist_ok=True) - - gems_folder = f'{dev_root}/Gems' - os.makedirs(gems_folder, exist_ok=True) - - gem_folder = f'{gems_folder}/{gem}' - os.makedirs(gem_folder, exist_ok=True) - - result = add_remove_gem.add_remove_gem(add, dev_root, gem, project, ask_for_runtime, ask_for_tool) - - if expect_failure: - assert result != 0 - else: - assert result == 0 - if runtime_present: - with open(runtime_dependencies_cmake_file, 'r') as s: - s_data = s.read() - assert s_data == expected_result - if tool_present: - with open(tool_dependencies_cmake_file, 'r') as s: - s_data = s.read() - assert s_data == expected_result - diff --git a/cmake/Tools/unit_test_common.py b/cmake/Tools/unit_test_common.py index 58f89876c3..655c2a32c1 100755 --- a/cmake/Tools/unit_test_common.py +++ b/cmake/Tools/unit_test_common.py @@ -48,60 +48,6 @@ def test_determine_engine_root(tmpdir, engine_json_content, expected_success): assert result is None -TEST_BOOTSTRAP_CONTENT_1 = """ -project_path = Game1 -foo = bar -key1 = value1 -key2 = value2 -assets = pc ---No Assets -""" - -TEST_BOOTSTRAP_CONTENT_2 = """ -project_path = Game2 - foo = bar -#------------------------- - key1 = value1 -key2 = value2 -assets = pc ---No Assets -""" - - -@pytest.mark.parametrize( - "contents, input_keys, expected_result_map", [ - pytest.param(TEST_BOOTSTRAP_CONTENT_1, ['project_path', 'foo', 'assets'], {'project_path': 'Game1', - 'foo': 'bar', - 'assets': 'pc'}, id="TestFullMatch"), - pytest.param(TEST_BOOTSTRAP_CONTENT_2, ['project_path', 'foo', 'barnone'], {'project_path': 'Game2', - 'foo': 'bar'}, id="TestPartialMatch"), - pytest.param(TEST_BOOTSTRAP_CONTENT_2, ['project_pathnone', 'foonone', 'barnone'], {}, id="TestNoMatch") - ] -) -def test_get_bootstrap_values_success(tmpdir, contents, input_keys, expected_result_map): - - test_dev_root = 'dev' - tmpdir.ensure('{}/bootstrap.cfg'.format(test_dev_root)) - bootstrap_file = tmpdir.join('{}/bootstrap.cfg'.format(test_dev_root)) - bootstrap_file.write(contents) - - bootstrap_file_path = str(tmpdir.join(test_dev_root).realpath()) - - result = common.get_bootstrap_values(bootstrap_file_path, input_keys) - - assert expected_result_map == result - - -def test_get_bootstrap_values_fail(): - try: - bad_file = 'x:\\foo\\bar\\file\\' - common.get_bootstrap_values(bad_file, ['input_keys']) - except common.LmbrCmdError as err: - assert 'Missing' in str(err) - else: - assert False, "Excepted LayoutToolError (missing file)" - - TEST_AP_CONFIG_1 = """ [Platforms] ;pc=enabled @@ -245,7 +191,6 @@ def test_verify_game_project_and_dev_root_success(tmpdir): game_name = 'MyFoo' game_folder = 'myfoo' game_project_json = TEST_GAME_PROJECT_JSON_FORMAT.format(project_name=game_name) - tmpdir.ensure(f'{dev_root}/bootstrap.cfg') tmpdir.ensure(f'{dev_root}/{game_folder}/project.json') project_json_path = tmpdir / dev_root / game_folder / 'project.json' project_json_path.write_text(game_project_json, encoding='ascii') @@ -285,72 +230,6 @@ asset_deploy_type={test_asset_deploy_type} assert result.asset_deploy_type == test_asset_deploy_type -def test_transform_bootstrap_project_path(tmpdir): - - tmpdir.ensure('bootstrap.cfg') - - test_bootstrap_content = """ --- Blah Blah --- Blah Blah - -project_path=OldProject - --- remote_filesystem - enable Virtual File System (VFS) --- This feature allows a remote instance of the game to run off assets --- on the asset processor computers cache instead of deploying them the remote device --- By default it is off and can be overridden for any platform -remote_filesystem=0 -""" - test_src_bootstrap = tmpdir / 'bootstrap.cfg' - test_src_bootstrap.write_text(test_bootstrap_content, encoding='ascii') - - test_dst_bootstrap = tmpdir / 'bootstrap.transformed.cfg' - test_game_name = 'FooBar' - - common.transform_bootstrap_for_project(game_name=test_game_name, - src_bootstrap=str(test_src_bootstrap), - dst_bootstrap=str(test_dst_bootstrap)) - - transformed_text = test_dst_bootstrap.read_text('ascii') - - search_gamename = re.search(r"project_path\s*=\s*(.*)", transformed_text) - assert search_gamename - assert search_gamename.group(1) - assert search_gamename.group(1) == test_game_name - - -def test_transform_bootstrap_project_path_missing(tmpdir): - - tmpdir.ensure('bootstrap.cfg') - - test_bootstrap_content = """ --- Blah Blah --- Blah Blah - --- remote_filesystem - enable Virtual File System (VFS) --- This feature allows a remote instance of the game to run off assets --- on the asset processor computers cache instead of deploying them the remote device --- By default it is off and can be overridden for any platform -remote_filesystem=0 -""" - test_src_bootstrap = tmpdir / 'bootstrap.cfg' - test_src_bootstrap.write_text(test_bootstrap_content, encoding='ascii') - - test_dst_bootstrap = tmpdir / 'bootstrap.transformed.cfg' - test_game_name = 'FooBar' - - common.transform_bootstrap_for_project(game_name=test_game_name, - src_bootstrap=str(test_src_bootstrap), - dst_bootstrap=str(test_dst_bootstrap)) - - transformed_text = test_dst_bootstrap.read_text('ascii') - - search_gamename = re.search(r"project_path\s*=\s*(.*)", transformed_text) - assert search_gamename - assert search_gamename.group(1) - assert search_gamename.group(1) == test_game_name - - def test_cmake_dependency_success(tmpdir): test_module = 'FooBar' diff --git a/cmake/Tools/unit_test_current_project.py b/cmake/Tools/unit_test_current_project.py deleted file mode 100755 index 7db48b62aa..0000000000 --- a/cmake/Tools/unit_test_current_project.py +++ /dev/null @@ -1,102 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -import os -import pytest - -from . import current_project - -TEST_BOOTSTRAP_CONTENT_1 = """ -project_path = Game1 -foo = bar -key1 = value1 -key2 = value2 -assets = pc -""" -TEST_BOOTSTRAP_CONTENT_2 = """ -project_path=Game1 -foo = bar -key1 = value1 -key2 = value2 -assets = pc -""" -TEST_BOOTSTRAP_CONTENT_3 = """ -project_path= Game1 -foo = bar -key1 = value1 -key2 = value2 -assets = pc -""" -TEST_BOOTSTRAP_CONTENT_4 = """ -project_path =Game1 -foo = bar -key1 = value1 -key2 = value2 -assets = pc -""" -TEST_BOOTSTRAP_CONTENT_5 = """ -project_path = Game1 -foo = bar -key1 = value1 -key2 = value2 -assets = pc -""" - -@pytest.mark.parametrize( - "contents, expected_result", [ - pytest.param(TEST_BOOTSTRAP_CONTENT_1, 'Game1'), - pytest.param(TEST_BOOTSTRAP_CONTENT_2, 'Game1'), - pytest.param(TEST_BOOTSTRAP_CONTENT_3, 'Game1'), - pytest.param(TEST_BOOTSTRAP_CONTENT_4, 'Game1'), - pytest.param(TEST_BOOTSTRAP_CONTENT_5, 'Game1'), - ] -) -def test_get_current_project(tmpdir, contents, expected_result): - dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/') - os.makedirs(dev_root, exist_ok=True) - - bootstrap_file = f'{dev_root}/bootstrap.cfg' - if os.path.isfile(bootstrap_file): - os.unlink(bootstrap_file) - with open(bootstrap_file, 'a') as s: - s.write(contents) - - result = current_project.get_current_project(dev_root) - assert expected_result == result - - -@pytest.mark.parametrize( - "contents, project_to_set, expected_result", [ - pytest.param(TEST_BOOTSTRAP_CONTENT_1, 'Test1', 0), - pytest.param(TEST_BOOTSTRAP_CONTENT_1, ' Test2', 0), - pytest.param(TEST_BOOTSTRAP_CONTENT_1, 'Test3 ', 0), - pytest.param(TEST_BOOTSTRAP_CONTENT_1, '/Test4', 1), - pytest.param(TEST_BOOTSTRAP_CONTENT_1, '=Test5', 1), - ] -) -def test_set_current_project(tmpdir, contents, project_to_set, expected_result): - dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/') - os.makedirs(dev_root, exist_ok=True) - - bootstrap_file = f'{dev_root}/bootstrap.cfg' - if os.path.isfile(bootstrap_file): - os.unlink(bootstrap_file) - with open(bootstrap_file, 'a') as s: - s.write(contents) - - result = current_project.set_current_project(dev_root, project_to_set) - assert expected_result == result - - if result == 0: - project_that_is_set = current_project.get_current_project(dev_root) - print(project_that_is_set) - print(project_to_set) - assert project_to_set.strip() == project_that_is_set \ No newline at end of file diff --git a/cmake/Tools/unit_test_layout_tool.py b/cmake/Tools/unit_test_layout_tool.py index 5be2c23f11..37684654b1 100755 --- a/cmake/Tools/unit_test_layout_tool.py +++ b/cmake/Tools/unit_test_layout_tool.py @@ -212,16 +212,15 @@ def test_create_link_error(): @pytest.mark.parametrize( - "project_path, asset_type, ensure_path, warn_on_missing, expected_result", [ - pytest.param('Foo', 'pc', 'Foo/Cache/pc/bootstrap.cfg', False, 'Foo/Cache/pc'), - pytest.param('Foo', 'pc', 'dev/bootstrap.cfg', True, None), - pytest.param('Foo', 'pc', 'Foo/Cache/es3/bootstrap.cfg', True, None), - pytest.param('Foo', 'pc', 'dev/bootstrap.cfg', False, common.LmbrCmdError), - pytest.param('Foo', 'pc', 'Foo/Cache/es3/bootstrap.cfg', False, common.LmbrCmdError), + "project_path, asset_type, warn_on_missing, expected_result", [ + pytest.param('Foo', 'pc', False, 'Foo/Cache/pc'), + pytest.param('Foo', 'pc', True, None), + pytest.param('Foo', 'pc', True, None), + pytest.param('Foo', 'pc', False, common.LmbrCmdError), + pytest.param('Foo', 'pc', False, common.LmbrCmdError), ] ) -def test_construct_and_validate_cache_game_asset_folder_success(tmpdir, project_path, asset_type, ensure_path, warn_on_missing, expected_result): - tmpdir.ensure(ensure_path) +def test_construct_and_validate_cache_game_asset_folder_success(tmpdir, project_path, asset_type, warn_on_missing, expected_result): if isinstance(expected_result, str): expected_path_realpath = str(tmpdir.join(expected_result).realpath()) elif expected_result == common.LmbrCmdError: @@ -385,7 +384,6 @@ def test_sync_layout_non_vfs_success(tmpdir, mode, existing_game_link, existing_ old_remove_link = layout_tool.remove_link try: # Simple Test Parameters - tmpdir.ensure('engine-root/bootstrap.cfg') engine_root_realpath = str(tmpdir.join('engine-root').realpath()) test_project_path = str(tmpdir.join('Foo').realpath()) test_project_name_lower = 'foo' diff --git a/cmake/Tools/utils.py b/cmake/Tools/utils.py deleted file mode 100755 index 37c84ea331..0000000000 --- a/cmake/Tools/utils.py +++ /dev/null @@ -1,47 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# -""" -This file contains utility functions -""" - -import uuid - - -def validate_identifier(identifier: str) -> bool: - """ - Determine if the identifier supplied is valid. - :param identifier: the name which needs to to checked - :return: bool: if the identifier is valid or not - """ - if not identifier: - return False - elif len(identifier) > 64: - return False - elif not identifier[0].isalpha(): - return False - else: - for character in identifier: - if not (character.isalnum() or character == '_' or character == '-'): - return False - return True - - -def validate_uuid4(uuid_string: str) -> bool: - """ - Determine if the uuid supplied is valid. - :param uuid_string: the uuid which needs to to checked - :return: bool: if the uuid is valid or not - """ - try: - val = uuid.UUID(uuid_string, version=4) - except ValueError: - return False - return str(val) == uuid_string diff --git a/cmake/Version.cmake b/cmake/Version.cmake index 08d79d4ce6..1d484fb059 100644 --- a/cmake/Version.cmake +++ b/cmake/Version.cmake @@ -12,4 +12,5 @@ string(TIMESTAMP current_year "%Y") set(LY_VERSION_COPYRIGHT_YEAR ${current_year} CACHE STRING "Open 3D Engine's copyright year") set(LY_VERSION_STRING "0.0.0.0" CACHE STRING "Open 3D Engine's version") -set(LY_VERSION_BUILD_NUMBER 0 CACHE STRING "Open 3D Engine's build number") \ No newline at end of file +set(LY_VERSION_BUILD_NUMBER 0 CACHE STRING "Open 3D Engine's build number") +set(LY_VERSION_ENGINE_NAME "o3de" CACHE STRING "Open 3D Engine's engine name") diff --git a/cmake/cmake_files.cmake b/cmake/cmake_files.cmake index e423eb0dbc..a1fd66a06d 100644 --- a/cmake/cmake_files.cmake +++ b/cmake/cmake_files.cmake @@ -12,20 +12,25 @@ set(FILES 3rdParty.cmake 3rdPartyPackages.cmake + CMakeFiles.cmake CommandExecution.cmake Configurations.cmake Dependencies.cmake Deployment.cmake - EngineFinder.cmake + EngineJson.cmake FileUtil.cmake Findo3de.cmake + Gems.cmake + GeneralSettings.cmake Install.cmake LyAutoGen.cmake + LYPackage_S3Downloader.cmake LySet.cmake LYTestWrappers.cmake LYPython.cmake LYWrappers.cmake Monolithic.cmake + OutputDirectory.cmake Packaging.cmake PAL.cmake PALTools.cmake diff --git a/cmake/install/Copyright.in b/cmake/install/Copyright.in new file mode 100644 index 0000000000..4d5680a30d --- /dev/null +++ b/cmake/install/Copyright.in @@ -0,0 +1,10 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# diff --git a/cmake/Findo3de.cmake.in b/cmake/install/Findo3de.cmake.in similarity index 100% rename from cmake/Findo3de.cmake.in rename to cmake/install/Findo3de.cmake.in diff --git a/cmake/install/InstalledTarget.in b/cmake/install/InstalledTarget.in new file mode 100644 index 0000000000..0503fd5f2b --- /dev/null +++ b/cmake/install/InstalledTarget.in @@ -0,0 +1,23 @@ + +# Generated by O3DE + +ly_add_target( + NAME @NAME_PLACEHOLDER@ IMPORTED @TARGET_TYPE_PLACEHOLDER@ + @NAMESPACE_PLACEHOLDER@ + COMPILE_DEFINITIONS + INTERFACE +@COMPILE_DEFINITIONS_PLACEHOLDER@ + INCLUDE_DIRECTORIES + INTERFACE +@INCLUDE_DIRECTORIES_PLACEHOLDER@ + BUILD_DEPENDENCIES + INTERFACE +@INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER@ + RUNTIME_DEPENDENCIES +@RUNTIME_DEPENDENCIES_PLACEHOLDER@ +) + +set(configs @CMAKE_CONFIGURATION_TYPES@) +foreach(config ${configs}) + include("@NAME_PLACEHOLDER@_${config}.cmake" OPTIONAL) +endforeach() diff --git a/cmake/install/engine.json.in b/cmake/install/engine.json.in new file mode 100644 index 0000000000..ce2e1be25c --- /dev/null +++ b/cmake/install/engine.json.in @@ -0,0 +1,11 @@ +{ + "engine_name": "@LY_VERSION_ENGINE_NAME@", + "restricted_name": "o3de", + "FileVersion": 1, + "O3DEVersion": "@LY_VERSION_STRING@", + "O3DECopyrightYear": @LY_VERSION_COPYRIGHT_YEAR@, + "O3DEBuildNumber": @LY_VERSION_BUILD_NUMBER@, + "external_subdirectories": [@LY_INSTALL_EXTERNAL_SUBDIRS@], + "projects": [@LY_INSTALL_PROJECTS@], + "templates": [@LY_INSTALL_TEMPLATES@] +} diff --git a/cmake/o3de_manifest.cmake b/cmake/o3de_manifest.cmake deleted file mode 100644 index 1585ec2d2f..0000000000 --- a/cmake/o3de_manifest.cmake +++ /dev/null @@ -1,986 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -# Set the user home directory -set(O3DE_HOME_PATH "" CACHE PATH "Override the user home to this path") -if(O3DE_HOME_PATH) - set(home_directory ${O3DE_HOME_PATH}) -elseif(CMAKE_HOST_WIN32) - file(TO_CMAKE_PATH "$ENV{USERPROFILE}" home_directory) -else() - file(TO_CMAKE_PATH "$ENV{HOME}" home_directory) -endif() -if (NOT home_directory) - message(FATAL_ERROR "Cannot find user home directory, without the user home directory the o3de manifest cannot be found") -endif() - -# Optionally delete the home directory -if(O3DE_DELETE_HOME_PATH) - message(STATUS "O3DE_DELETE_HOME_PATH=${O3DE_DELETE_HOME_PATH}") - if(EXISTS ${home_directory}/.o3de) - message(STATUS "Deleting ${home_directory}/.o3de") - file(REMOVE_RECURSE ${home_directory}/.o3de) - else() - message(STATUS "Home path ${home_directory}/.o3de doesnt exist.") - endif() -endif() - -######################################################################################################################## -# If O3DE_REGISTER_ENGINE_PATH variable is set on the commandline this will allow registration of anything using -# O3DE_REGISTER_ENGINE_PATH o3de script. This is handy for situations like build servers which download the code and -# are expected to build without the need for someone to register o3de objects like this engine by manually typing it in. -# If O3DE_REGISTER_THIS_ENGINE=TRUE is set on the commandline when O3DE_REGISTER_ENGINE_PATH is also set this will call: -# O3DE_REGISTER_ENGINE_PATH/scripts>o3de register --this-engine --override-home-folder -# Note: register --this-engine will automatically register anything it finds in known folders, so if you put your -# o3de objects like projects/gems/templates/restricted/etc... in known folders for those types they will get registered -# automatically. Known folders for types are your .o3de/Projects and .o3de/Gems etc. So if I wanted my project to be -# registered and built by this build server I could simply put them in those known folders on the build server and they -# would get registered automatically by this call. -# OR -# I could put them on the commandline as well. This would be the way if the o3de objects we need to regiater are NOT -# in known folders or you do not intend to call with O3DE_REGISTER_THIS_ENGINE=TRUE Ex. -# -DO3DE_REGISTER_ENGINE_PATH=C:\this\engine -# -DO3DE_REGISTER_PROJECT_PATHS=C:\ThisGame;C:\ThatGame -# -DO3DE_REGISTER_GEM_PATHS=C:\ThisGem;C:\ThatGem;C:\And\Some\Other\Gem -# -DO3DE_REGISTER_RESTRICTED_PATHS=C:\this\engine\Restricted;C:\ThisGame\Restricted;C:\ThisGem\Restricted -######################################################################################################################## -if(O3DE_REGISTER_ENGINE_PATH) - message(STATUS "O3DE_REGISTER_ENGINE_PATH=${O3DE_REGISTER_ENGINE_PATH}") - - if(O3DE_REGISTER_THIS_ENGINE) - message(STATUS "O3DE_REGISTER_THIS_ENGINE=${O3DE_REGISTER_THIS_ENGINE}") - message(STATUS "register --this-engine") - if(CMAKE_HOST_WIN32) - execute_process( - COMMAND cmd /c ${O3DE_REGISTER_ENGINE_PATH}/scripts/o3de.bat register --this-engine --override-home-folder ${home_directory} - RESULT_VARIABLE o3de_register_this_engine_cmd_result - ) - else() - execute_process( - COMMAND sh ${O3DE_REGISTER_ENGINE_PATH}/scripts/o3de.sh register --this-engine --override-home-folder ${home_directory} - RESULT_VARIABLE o3de_register_this_engine_cmd_result - ) - endif() - if(o3de_register_this_engine_cmd_result) - message(FATAL_ERROR "An error occured trying to register --this-engine: ${o3de_register_this_engine_cmd_result}") - else() - message(STATUS "Engine ${O3DE_REGISTER_ENGINE_PATH} registration successfull.") - endif() - endif() - - if(O3DE_REGISTER_RESTRICTED_PATHS) - message(STATUS "O3DE_REGISTER_RESTRICTED_PATHS=${O3DE_REGISTER_RESTRICTED_PATHS}") - foreach(restricted_path ${O3DE_REGISTER_RESTRICTED_PATHS}) - if(CMAKE_HOST_WIN32) - execute_process( - COMMAND cmd /c ${O3DE_REGISTER_ENGINE_PATH}/scripts/o3de.bat register --restricted-path ${restricted_path} --override-home-folder ${home_directory} - RESULT_VARIABLE o3de_register_restricted_cmd_result - ) - else() - execute_process( - COMMAND sh ${O3DE_REGISTER_ENGINE_PATH}/scripts/o3de.sh register --restricted-path ${restricted_path} --override-home-folder ${home_directory} - RESULT_VARIABLE o3de_register_restricted_cmd_result - ) - endif() - if(o3de_register_restricted_cmd_result) - message(FATAL_ERROR "An error occured trying to ${O3DE_REGISTER_ENGINE_PATH}/scripts>o3de register --restricted-path ${restricted_path} --override-home-folder ${home_directory}: ${o3de_register_restricted_cmd_result}") - else() - message(STATUS "Restricted ${restricted_path} registration successfull.") - endif() - endforeach() - endif() - - if(O3DE_REGISTER_PROJECT_PATHS) - message(STATUS "O3DE_REGISTER_PROJECT_PATHS=${O3DE_REGISTER_PROJECT_PATHS}") - foreach(project_path ${O3DE_REGISTER_PROJECT_PATHS}) - if(CMAKE_HOST_WIN32) - execute_process( - COMMAND cmd /c ${O3DE_REGISTER_ENGINE_PATH}/scripts/o3de.bat register --project-path ${project_path} --override-home-folder ${home_directory} - RESULT_VARIABLE o3de_register_project_cmd_result - ) - else() - execute_process( - COMMAND sh ${O3DE_REGISTER_ENGINE_PATH}/scripts/o3de.sh register --project-path ${project_path} --override-home-folder ${home_directory} - RESULT_VARIABLE o3de_register_project_cmd_result - ) - endif() - if(o3de_register_project_cmd_result) - message(FATAL_ERROR "An error occured trying to ${O3DE_REGISTER_ENGINE_PATH}/scripts>o3de register --project-path ${project_path} --override-home-folder ${home_directory}") - else() - message(STATUS "Project ${project_path} registration successfull.") - endif() - endforeach() - endif() - - if(O3DE_REGISTER_GEM_PATHS) - message(STATUS "O3DE_REGISTER_GEM_PATHS=${O3DE_REGISTER_GEM_PATHS}") - foreach(gem_path ${O3DE_REGISTER_GEM_PATHS}) - if(CMAKE_HOST_WIN32) - execute_process( - COMMAND cmd /c ${O3DE_REGISTER_ENGINE_PATH}/scripts/o3de.bat register --gem-path ${gem_path} --override-home-folder ${home_directory} - RESULT_VARIABLE o3de_register_gem_cmd_result - ) - else() - execute_process( - COMMAND sh ${O3DE_REGISTER_ENGINE_PATH}/scripts/o3de.sh register --gem-path ${gem_path} --override-home-folder ${home_directory} - RESULT_VARIABLE o3de_register_gem_cmd_result - ) - endif() - if(o3de_register_gem_cmd_result) - message(FATAL_ERROR "An error occured trying to ${O3DE_REGISTER_ENGINE_PATH}/scripts>o3de register --gem-path ${gem_path} --override-home-folder ${home_directory}") - else() - message(STATUS "Gem ${gem_path} registration successfull.") - endif() - endforeach() - endif() - - if(O3DE_REGISTER_TEMPLATE_PATHS) - message(STATUS "O3DE_REGISTER_TEMPLATE_PATHS=${O3DE_REGISTER_TEMPLATE_PATHS}") - foreach(template_path ${O3DE_REGISTER_TEMPLATE_PATHS}) - if(CMAKE_HOST_WIN32) - execute_process( - COMMAND cmd /c ${O3DE_REGISTER_ENGINE_PATH}/scripts/o3de.bat register --template-path ${template_path} --override-home-folder ${home_directory} - RESULT_VARIABLE o3de_register_template_cmd_result - ) - else() - execute_process( - COMMAND sh ${O3DE_REGISTER_ENGINE_PATH}/scripts/o3de.sh register --template-path ${template_path} --override-home-folder ${home_directory} - RESULT_VARIABLE o3de_register_template_cmd_result - ) - endif() - if(o3de_register_template_cmd_result) - message(FATAL_ERROR "An error occured trying to ${O3DE_REGISTER_ENGINE_PATH}/scripts>o3de register --template-path ${template_path} --override-home-folder ${home_directory}") - else() - message(STATUS "Template ${template_path} registration successfull.") - endif() - endforeach() - endif() - - if(O3DE_REGISTER_REPO_URIS) - message(STATUS "O3DE_REGISTER_REPO_URIS=${O3DE_REGISTER_REPO_URIS}") - foreach(repo_uri ${O3DE_REGISTER_REPO_URIS}) - if(CMAKE_HOST_WIN32) - execute_process( - COMMAND cmd /c ${O3DE_REGISTER_ENGINE_PATH}/scripts/o3de.bat register --repo-uri ${repo_uri} --override-home-folder ${home_directory} - RESULT_VARIABLE o3de_register_repo_cmd_result - ) - else() - execute_process( - COMMAND sh ${O3DE_REGISTER_ENGINE_PATH}/scripts/o3de.sh register --repo-uri ${repo_uri} --override-home-folder ${home_directory} - RESULT_VARIABLE o3de_register_repo_cmd_result - ) - endif() - if(o3de_register_repo_cmd_result) - message(FATAL_ERROR "An error occured trying to ${O3DE_REGISTER_ENGINE_PATH}/scripts>o3de register --repo-uri ${repo_uri} --override-home-folder ${home_directory}") - else() - message(STATUS "Repo ${repo_uri} registration successfull.") - endif() - endforeach() - endif() -endif() - -################################################################################ -# o3de manifest -################################################################################ -# Set manifest json path to the /.o3de/o3de_manifest.json -set(o3de_manifest_json_path ${home_directory}/.o3de/o3de_manifest.json) -if(NOT EXISTS ${o3de_manifest_json_path}) - message(FATAL_ERROR "${o3de_manifest_json_path} not found. You must o3de register --this-engine.") -endif() -file(READ ${o3de_manifest_json_path} manifest_json_data) - -################################################################################ -# o3de manifest name -################################################################################ -string(JSON o3de_manifest_name ERROR_VARIABLE json_error GET ${manifest_json_data} o3de_manifest_name) -message(STATUS "o3de_manifest_name: ${o3de_manifest_name}") -if(json_error) - message(FATAL_ERROR "Unable to read repo_name from '${o3de_manifest_json_path}', error: ${json_error}") -endif() - -################################################################################ -# o3de origin -################################################################################ -string(JSON o3de_origin ERROR_VARIABLE json_error GET ${manifest_json_data} origin) -if(json_error) - message(FATAL_ERROR "Unable to read origin from '${o3de_manifest_json_path}', error: ${json_error}") -endif() - -################################################################################ -# o3de default engines folder -################################################################################ -string(JSON o3de_default_engines_folder ERROR_VARIABLE json_error GET ${manifest_json_data} default_engines_folder) -message(STATUS "default_engines_folder: ${o3de_default_engines_folder}") -if(json_error) - message(FATAL_ERROR "Unable to read default_engines_folder from '${o3de_manifest_json_path}', error: ${json_error}") -endif() - -################################################################################ -# o3de default projects folder -################################################################################ -string(JSON o3de_default_projects_folder ERROR_VARIABLE json_error GET ${manifest_json_data} default_projects_folder) -message(STATUS "default_projects_folder: ${o3de_default_projects_folder}") -if(json_error) - message(FATAL_ERROR "Unable to read default_projects_folder from '${o3de_manifest_json_path}', error: ${json_error}") -endif() - -################################################################################ -# o3de default gems folder -################################################################################ -string(JSON o3de_default_gems_folder ERROR_VARIABLE json_error GET ${manifest_json_data} default_gems_folder) -message(STATUS "default_gems_folder: ${o3de_default_gems_folder}") -if(json_error) - message(FATAL_ERROR "Unable to read default_gems_folder from '${o3de_manifest_json_path}', error: ${json_error}") -endif() - -################################################################################ -# o3de default templates folder -################################################################################ -string(JSON o3de_default_templates_folder ERROR_VARIABLE json_error GET ${manifest_json_data} default_templates_folder) -message(STATUS "default_templates_folder: ${o3de_default_templates_folder}") -if(json_error) - message(FATAL_ERROR "Unable to read default_templates_folder from '${o3de_manifest_json_path}', error: ${json_error}") -endif() - -################################################################################ -# o3de default restricted folder -################################################################################ -string(JSON o3de_default_restricted_folder ERROR_VARIABLE json_error GET ${manifest_json_data} default_restricted_folder) -message(STATUS "default_restricted_folder: ${o3de_default_restricted_folder}") -if(json_error) - message(FATAL_ERROR "Unable to read default_restricted_folder from '${o3de_manifest_json_path}', error: ${json_error}") -endif() - -################################################################################ -# o3de projects -################################################################################ -string(JSON o3de_projects_count ERROR_VARIABLE json_error LENGTH ${manifest_json_data} projects) -if(json_error) - message(FATAL_ERROR "Unable to read key 'projects' from '${o3de_manifest_json_path}', error: ${json_error}") -endif() -if(${o3de_projects_count} GREATER 0) - math(EXPR o3de_projects_count "${o3de_projects_count}-1") - foreach(projects_index RANGE ${o3de_projects_count}) - string(JSON projects_path ERROR_VARIABLE json_error GET ${manifest_json_data} projects ${projects_index}) - if(json_error) - message(FATAL_ERROR "Unable to read projects[${projects_index}] '${o3de_manifest_json_path}', error: ${json_error}") - endif() - list(APPEND o3de_projects ${projects_path}) - list(APPEND o3de_global_projects ${projects_path}) - endforeach() -endif() - -################################################################################ -# o3de gems -################################################################################ -string(JSON o3de_gems_count ERROR_VARIABLE json_error LENGTH ${manifest_json_data} gems) -if(json_error) - message(FATAL_ERROR "Unable to read key 'gems' from '${o3de_manifest_json_path}', error: ${json_error}") -endif() -if(${o3de_gems_count} GREATER 0) - math(EXPR o3de_gems_count "${o3de_gems_count}-1") - foreach(gems_index RANGE ${o3de_gems_count}) - string(JSON gems_path ERROR_VARIABLE json_error GET ${manifest_json_data} gems ${gems_index}) - if(json_error) - message(FATAL_ERROR "Unable to read gems[${gems_index}] '${o3de_manifest_json_path}', error: ${json_error}") - endif() - list(APPEND o3de_gems ${gems_path}) - list(APPEND o3de_global_gems ${gems_path}) - endforeach() -endif() - -################################################################################ -# o3de templates -################################################################################ -string(JSON o3de_templates_count ERROR_VARIABLE json_error LENGTH ${manifest_json_data} templates) -if(json_error) - message(FATAL_ERROR "Unable to read key 'templates' from '${o3de_manifest_json_path}', error: ${json_error}") -endif() -if(${o3de_templates_count} GREATER 0) - math(EXPR o3de_templates_count "${o3de_templates_count}-1") - foreach(templates_index RANGE ${o3de_templates_count}) - string(JSON templates_path ERROR_VARIABLE json_error GET ${manifest_json_data} templates ${templates_index}) - if(json_error) - message(FATAL_ERROR "Unable to read templates[${templates_index}] '${o3de_manifest_json_path}', error: ${json_error}") - endif() - list(APPEND o3de_templates ${templates_path}) - list(APPEND o3de_global_templates ${templates_path}) - endforeach() -endif() - -################################################################################ -# o3de repos -################################################################################ -string(JSON o3de_repos_count ERROR_VARIABLE json_error LENGTH ${manifest_json_data} repos) -if(json_error) - message(FATAL_ERROR "Unable to read key 'repos' from '${o3de_manifest_json_path}', error: ${json_error}") -endif() -if(${o3de_repos_count} GREATER 0) - math(EXPR o3de_repos_count "${o3de_repos_count}-1") - foreach(repos_index RANGE ${o3de_repos_count}) - string(JSON repo_uri ERROR_VARIABLE json_error GET ${manifest_json_data} repos ${repos_index}) - if(json_error) - message(FATAL_ERROR "Unable to read repos[${repos_index}] '${o3de_manifest_json_path}', error: ${json_error}") - endif() - list(APPEND o3de_repos ${repo_uri}) - list(APPEND o3de_global_repos ${repo_uri}) - endforeach() -endif() - -################################################################################ -# o3de restricted -################################################################################ -string(JSON o3de_restricted_count ERROR_VARIABLE json_error LENGTH ${manifest_json_data} restricted) -if(json_error) - message(FATAL_ERROR "Unable to read key 'restricted' from '${o3de_manifest_json_path}', error: ${json_error}") -endif() -if(${o3de_restricted_count} GREATER 0) - math(EXPR o3de_restricted_count "${o3de_restricted_count}-1") - foreach(restricted_index RANGE ${o3de_restricted_count}) - string(JSON restricted_path ERROR_VARIABLE json_error GET ${manifest_json_data} restricted ${restricted_index}) - if(json_error) - message(FATAL_ERROR "Unable to read restricted[${restricted_index}] '${o3de_manifest_json_path}', error: ${json_error}") - endif() - list(APPEND o3de_restricted ${restricted_path}) - list(APPEND o3de_global_restricted ${restricted_path}) - endforeach() -endif() - -################################################################################ -# o3de engines -################################################################################ -string(JSON o3de_engines_count ERROR_VARIABLE json_error LENGTH ${manifest_json_data} engines) -if(json_error) - message(FATAL_ERROR "Unable to read key 'engines' from '${o3de_manifest_json_path}', error: ${json_error}") -endif() - -if(${o3de_engines_count} GREATER 0) - math(EXPR o3de_engines_count "${o3de_engines_count}-1") - # Either the engine_path and engine_json are set in which case the user is configuring from the engine - # or project_path and project_json are set in which case the user is configuring from the project. - # We need to know which engine_path the user is using so if the project_json is set then we need - # to read the project_json and disambiguate the engine_path. - if(NOT o3de_engine_path) - if(NOT o3de_project_json) - message(FATAL_ERROR "Neither o3de_engine_path nor o3de_project_json defined. Cannot determine engine!") - endif() - - # get the name of the engine this project uses - file(READ ${o3de_project_json} project_json_data) - string(JSON project_engine_name ERROR_VARIABLE json_error GET ${project_json_data} engine) - if(json_error) - message(FATAL_ERROR "Unable to read 'engine' from '${o3de_project_json}', error: ${json_error}") - endif() - - # search each engine in order from the manifest to find the matching engine name - foreach(engines_index RANGE ${o3de_engines_count}) - string(JSON engine_data ERROR_VARIABLE json_error GET ${manifest_json_data} engines ${engines_index}) - if(json_error) - message(FATAL_ERROR "Unable to read engines[${engines_index}] '${o3de_manifest_json_path}', error: ${json_error}") - endif() - - # get this engines path - string(JSON this_engine_path ERROR_VARIABLE json_error GET ${engine_data} path) - if(json_error) - message(FATAL_ERROR "Unable to read engine path from '${o3de_manifest_json_path}', error: ${json_error}") - endif() - - # add this engine to the engines list - list(APPEND o3de_engines ${this_engine_path}) - - # use path to get the engine.json - set(this_engine_json ${this_engine_path}/engine.json) - - # read the name of this engine - file(READ ${this_engine_json} this_engine_json_data) - string(JSON this_engine_name ERROR_VARIABLE json_error GET ${this_engine_json_data} engine_name) - if(json_error) - message(FATAL_ERROR "Unable to read engine_name from '${this_engine_json}', error: ${json_error}") - endif() - - # see if this engines name is the same as the one this projects should use - if(${this_engine_name} STREQUAL ${project_engine_name}) - message(STATUS "Found engine: '${project_engine_name}' at ${this_engine_path}") - set(o3de_engine_path ${this_engine_path}) - break() - endif() - endforeach() - endif() -endif() - -#we should have an engine_path at this point -if(NOT o3de_engine_path) - message(FATAL_ERROR "o3de_engine_path not defined. Cannot determine engine!") -endif() - -# now that we have an engine_path read in that engines o3de resources -if(${o3de_engines_count} GREATER -1) - foreach(engines_index RANGE ${o3de_engines_count}) - string(JSON engine_data ERROR_VARIABLE json_error GET ${manifest_json_data} engines ${engines_index}) - if(json_error) - message(FATAL_ERROR "Unable to read engines[${engines_index}] '${o3de_manifest_json_path}', error: ${json_error}") - endif() - - # get this engines path - string(JSON this_engine_path ERROR_VARIABLE json_error GET ${engine_data} path) - if(json_error) - message(FATAL_ERROR "Unable to read engine path from '${o3de_manifest_json_path}', error: ${json_error}") - endif() - - if(${o3de_engine_path} STREQUAL ${this_engine_path}) - ################################################################################ - # o3de engine projects - ################################################################################ - string(JSON o3de_engine_projects_count ERROR_VARIABLE json_error LENGTH ${engine_data} projects) - if(json_error) - message(FATAL_ERROR "Unable to read key 'projects' from '${engine_data}', error: ${json_error}") - endif() - if(${o3de_engine_projects_count} GREATER 0) - math(EXPR o3de_engine_projects_count "${o3de_engine_projects_count}-1") - foreach(engine_projects_index RANGE ${o3de_engine_projects_count}) - string(JSON engine_projects_path ERROR_VARIABLE json_error GET ${engine_data} projects ${engine_projects_index}) - if(json_error) - message(FATAL_ERROR "Unable to read engine projects[${projects_index}] '${o3de_manifest_json_path}', error: ${json_error}") - endif() - list(APPEND o3de_projects ${engine_projects_path}) - list(APPEND o3de_engine_projects ${engine_projects_path}) - endforeach() - endif() - - ################################################################################ - # o3de engine gems - ################################################################################ - string(JSON o3de_engine_gems_count ERROR_VARIABLE json_error LENGTH ${engine_data} gems) - if(json_error) - message(FATAL_ERROR "Unable to read key 'gems' from '${engine_data}', error: ${json_error}") - endif() - if(${o3de_engine_gems_count} GREATER 0) - math(EXPR o3de_engine_gems_count "${o3de_engine_gems_count}-1") - foreach(engine_gems_index RANGE ${o3de_engine_gems_count}) - string(JSON engine_gems_path ERROR_VARIABLE json_error GET ${engine_data} gems ${engine_gems_index}) - if(json_error) - message(FATAL_ERROR "Unable to read engine gems[${gems_index}] '${o3de_manifest_json_path}', error: ${json_error}") - endif() - list(APPEND o3de_gems ${engine_gems_path}) - list(APPEND o3de_engine_gems ${engine_gems_path}) - endforeach() - endif() - - ################################################################################ - # o3de engine templates - ################################################################################ - string(JSON o3de_engine_templates_count ERROR_VARIABLE json_error LENGTH ${engine_data} templates) - if(json_error) - message(FATAL_ERROR "Unable to read key 'templates' from '${o3de_manifest_json_path}', error: ${json_error}") - endif() - if(${o3de_engine_gems_count} GREATER 0) - math(EXPR o3de_engine_templates_count "${o3de_engine_templates_count}-1") - foreach(engine_templates_index RANGE ${o3de_engine_templates_count}) - string(JSON engine_templates_path ERROR_VARIABLE json_error GET ${engine_data} templates ${engine_templates_index}) - if(json_error) - message(FATAL_ERROR "Unable to read engine templates[${templates_index}] '${o3de_manifest_json_path}', error: ${json_error}") - endif() - list(APPEND o3de_templates ${engine_templates_path}) - list(APPEND o3de_engine_templates ${engine_templates_path}) - endforeach() - endif() - - ################################################################################ - # o3de engine restricted - ################################################################################ - string(JSON o3de_engine_restricted_count ERROR_VARIABLE json_error LENGTH ${engine_data} restricted) - if(json_error) - message(FATAL_ERROR "Unable to read key 'restricted' from '${o3de_manifest_json_path}', error: ${json_error}") - endif() - if(${o3de_engine_restricted_count} GREATER 0) - math(EXPR o3de_engine_restricted_count "${o3de_engine_restricted_count}-1") - foreach(engine_restricted_index RANGE ${o3de_engine_restricted_count}) - string(JSON engine_restricted_path ERROR_VARIABLE json_error GET ${engine_data} restricted ${engine_restricted_index}) - if(json_error) - message(FATAL_ERROR "Unable to read engine restricted[${engine_restricted_index}] '${o3de_manifest_json_path}', error: ${json_error}") - endif() - list(APPEND o3de_restricted ${engine_restricted_path}) - list(APPEND o3de_engine_restricted ${engine_restricted_path}) - endforeach() - endif() - - ################################################################################ - # o3de engine external_subdirectories - ################################################################################ - string(JSON o3de_external_subdirectories_count ERROR_VARIABLE json_error LENGTH ${engine_data} external_subdirectories) - if(json_error) - message(FATAL_ERROR "Unable to read key 'external_subdirectories' from '${o3de_manifest_json_path}', error: ${json_error}") - endif() - if(${o3de_external_subdirectories_count} GREATER 0) - math(EXPR o3de_external_subdirectories_count "${o3de_external_subdirectories_count}-1") - foreach(external_subdirectories_index RANGE ${o3de_external_subdirectories_count}) - string(JSON external_subdirectories_path ERROR_VARIABLE json_error GET ${engine_data} external_subdirectories ${external_subdirectories_index}) - if(json_error) - message(FATAL_ERROR "Unable to read engine external_subdirectories[${gems_index}] '${o3de_manifest_json_path}', error: ${json_error}") - endif() - list(APPEND o3de_engine_external_subdirectories ${external_subdirectories_path}) - endforeach() - endif() - - break() - - endif() - endforeach() -endif() - - -################################################################################ -#! o3de_engine_id: -# -# \arg:engine returns the engine association element from an o3de json -# \arg:o3de_json_file name of the o3de json file -################################################################################ -function(o3de_engine_id o3de_json_file engine) - file(READ ${o3de_json_file} json_data) - string(JSON engine_entry ERROR_VARIABLE json_error GET ${json_data} engine) - if(json_error) - message(WARNING "Unable to read engine from '${o3de_json_file}', error: ${json_error}") - message(WARNING "Setting engine to engine default 'o3de'") - set(engine_entry "o3de") - endif() - if(engine_entry) - set(${engine} ${engine_entry} PARENT_SCOPE) - endif() -endfunction() - - -################################################################################ -#! o3de_project_id: -# -# \arg:project returns the project association element from an o3de json -# \arg:o3de_json_file name of the o3de json file -################################################################################ -function(o3de_project_id o3de_json_file project) - file(READ ${o3de_json_file} json_data) - string(JSON project_entry ERROR_VARIABLE json_error GET ${json_data} project) - if(json_error) - message(FATAL_ERROR "Unable to read project from '${o3de_json_file}', error: ${json_error}") - endif() - if(project_entry) - set(${project} ${project_entry} PARENT_SCOPE) - endif() -endfunction() - - -################################################################################ -#! o3de_gem_id: -# -# \arg:gem returns the gem association element from an o3de json -# \arg:o3de_json_file name of the o3de json file -################################################################################ -function(o3de_gem_id o3de_json_file gem) - file(READ ${o3de_json_file} json_data) - string(JSON gem_entry ERROR_VARIABLE json_error GET ${json_data} gem) - if(json_error) - message(FATAL_ERROR "Unable to read gem from '${o3de_json_file}', error: ${json_error}") - endif() - if(gem_entry) - set(${gem} ${gem_entry} PARENT_SCOPE) - endif() -endfunction() - - -################################################################################ -#! o3de_template_id: -# -# \arg:template returns the template association element from an o3de json -# \arg:o3de_json_file name of the o3de json file -################################################################################ -function(o3de_template_id o3de_json_file template) - file(READ ${o3de_json_file} json_data) - string(JSON template_entry ERROR_VARIABLE json_error GET ${json_data} template) - if(json_error) - message(FATAL_ERROR "Unable to read template from '${o3de_json_file}', error: ${json_error}") - endif() - if(template_entry) - set(${template} ${template_entry} PARENT_SCOPE) - endif() -endfunction() - - -################################################################################ -#! o3de_repo_id: -# -# \arg:repo returns the repo association element from an o3de json -# \arg:o3de_json_file name of the o3de json file -################################################################################ -function(o3de_repo_id o3de_json_file repo) - file(READ ${o3de_json_file} json_data) - string(JSON repo_entry ERROR_VARIABLE json_error GET ${json_data} repo) - if(json_error) - message(FATAL_ERROR "Unable to read repo from '${o3de_json_file}', error: ${json_error}") - endif() - if(repo_entry) - set(${repo} ${repo_entry} PARENT_SCOPE) - endif() -endfunction() - - -################################################################################ -#! o3de_restricted_id: -# -# \arg:restricted returns the restricted association element from an o3de json, otherwise engine 'o3de' is assumed -# \arg:o3de_json_file name of the o3de json file -################################################################################ -function(o3de_restricted_id o3de_json_file restricted) - file(READ ${o3de_json_file} json_data) - string(JSON restricted_entry ERROR_VARIABLE json_error GET ${json_data} restricted) - if(json_error) - message(WARNING "Unable to read restricted from '${o3de_json_file}', error: ${json_error}") - message(WARNING "Setting restricted to engine default 'o3de'") - set(restricted_entry "o3de") - endif() - if(restricted_entry) - set(${restricted} ${restricted_entry} PARENT_SCOPE) - endif() -endfunction() - - -################################################################################ -#! o3de_find_engine_folder: -# -# \arg:engine_path returns the path of the o3de engine folder with name engine_name -# \arg:engine_name name of the engine -################################################################################ -function(o3de_find_engine_folder engine_name engine_path) - foreach(engine_entry ${o3de_engines}) - set(engine_json_file ${engine_entry}/engine.json) - file(READ ${engine_json_file} engine_json) - string(JSON this_engine_name ERROR_VARIABLE json_error GET ${engine_json} engine_name) - if(json_error) - message(WARNING "Unable to read engine_name from '${engine_json_file}', error: ${json_error}") - else() - if(this_engine_name STREQUAL engine_name) - set(${engine_path} ${engine_entry} PARENT_SCOPE) - return() - endif() - endif() - endforeach() - message(FATAL_ERROR "Unable to find repo_name: '${engine_name}'") -endfunction() - - -################################################################################ -#! o3de_find_project_folder: -# -# \arg:project_path returns the path of the o3de project folder with name project_name -# \arg:project_name name of the project -################################################################################ -function(o3de_find_project_folder project_name project_path) - foreach(project_entry ${o3de_projects}) - set(project_json_file ${project_entry}/project.json) - file(READ ${project_json_file} project_json) - string(JSON this_project_name ERROR_VARIABLE json_error GET ${project_json} project_name) - if(json_error) - message(WARNING "Unable to read project_name from '${project_json_file}', error: ${json_error}") - else() - if(this_project_name STREQUAL project_name) - set(${project_path} ${project_entry} PARENT_SCOPE) - return() - endif() - endif() - endforeach() - message(FATAL_ERROR "Unable to find project_name: '${project_name}'") -endfunction() - - -################################################################################ -#! o3de_find_gem_folder: -# -# \arg:gem_path returns the path of the o3de gem folder with name gem_name -# \arg:gem_name name of the gem -################################################################################ -function(o3de_find_gem_folder gem_name gem_path) - foreach(gem_entry ${o3de_gems}) - set(gem_json_file ${gem_entry}/gem.json) - file(READ ${gem_json_file} gem_json) - string(JSON this_gem_name ERROR_VARIABLE json_error GET ${gem_json} gem_name) - if(json_error) - message(WARNING "Unable to read gem_name from '${gem_json_file}', error: ${json_error}") - else() - if(this_gem_name STREQUAL gem_name) - set(${gem_path} ${gem_entry} PARENT_SCOPE) - return() - endif() - endif() - endforeach() - message(FATAL_ERROR "Unable to find gem_name: '${gem_name}'") -endfunction() - - -################################################################################ -#! o3de_find_template_folder: -# -# \arg:template_path returns the path of the o3de template folder with name template_name -# \arg:template_name name of the template -################################################################################ -function(o3de_find_template_folder template_name template_path) - foreach(template_entry ${o3de_templates}) - set(template_json_file ${template_entry}/template.json) - file(READ ${template_json_file} template_json) - string(JSON this_template_name ERROR_VARIABLE json_error GET ${template_json} template_name) - if(json_error) - message(WARNING "Unable to read template_name from '${template_json_file}', error: ${json_error}") - else() - if(this_template_name STREQUAL template_name) - set(${template_path} ${template_entry} PARENT_SCOPE) - return() - endif() - endif() - endforeach() - message(FATAL_ERROR "Unable to find template_name: '${template_name}'") -endfunction() - - -################################################################################ -#! o3de_find_repo_folder: -# -# \arg:repo_path returns the path of the o3de repo folder with name repo_name -# \arg:repo_name name of the repo -################################################################################ -function(o3de_find_repo_folder repo_name repo_path) - foreach(repo_entry ${o3de_repos}) - set(repo_json_file ${repo_entry}/repo.json) - file(READ ${repo_json_file} repo_json) - string(JSON this_repo_name ERROR_VARIABLE json_error GET ${repo_json} repo_name) - if(json_error) - message(WARNING "Unable to read repo_name from '${repo_json_file}', error: ${json_error}") - else() - if(this_repo_name STREQUAL repo_name) - set(${repo_path} ${repo_entry} PARENT_SCOPE) - return() - endif() - endif() - endforeach() - message(FATAL_ERROR "Unable to find repo_name: '${repo_name}'") -endfunction() - - -################################################################################ -#! o3de_find_restricted_folder: -# -# \arg:restricted_path returns the path of the o3de restricted folder with name restricted_name -# \arg:restricted_name name of the restricted -################################################################################ -function(o3de_find_restricted_folder restricted_name restricted_path) - foreach(restricted_entry ${o3de_restricted}) - set(restricted_json_file ${restricted_entry}/restricted.json) - file(READ ${restricted_json_file} restricted_json) - string(JSON this_restricted_name ERROR_VARIABLE json_error GET ${restricted_json} restricted_name) - if(json_error) - message(WARNING "Unable to read restricted_name from '${restricted_json_file}', error: ${json_error}") - else() - if(this_restricted_name STREQUAL restricted_name) - set(${restricted_path} ${restricted_entry} PARENT_SCOPE) - return() - endif() - endif() - endforeach() - message(FATAL_ERROR "Unable to find restricted_name: '${restricted_name}'") -endfunction() - - -################################################################################ -#! o3de_engine_name: -# -# \arg:engine returns the engine_name element from an engine.json -# \arg:o3de_engine_json_file name of the o3de json file -################################################################################ -function(o3de_engine_name o3de_engine_json_file engine) - file(READ ${o3de_engine_json_file} json_data) - string(JSON engine_entry ERROR_VARIABLE json_error GET ${json_data} engine_name) - if(json_error) - message(FATAL_ERROR "Unable to read engine_name from '${o3de_json_file}', error: ${json_error}") - endif() - if(engine_entry) - set(${engine} ${engine_entry} PARENT_SCOPE) - endif() -endfunction() - - -################################################################################ -#! o3de_project_name: -# -# \arg:project returns the project_name element from an project.json -# \arg:o3de_project_json_file name of the o3de json file -################################################################################ -function(o3de_project_name o3de_project_json_file project) - file(READ ${o3de_project_json_file} json_data) - string(JSON project_entry ERROR_VARIABLE json_error GET ${json_data} project_name) - if(json_error) - message(FATAL_ERROR "Unable to read project_name from '${o3de_json_file}', error: ${json_error}") - endif() - if(project_entry) - set(${project} ${project_entry} PARENT_SCOPE) - endif() -endfunction() - - -################################################################################ -#! o3de_gem_name: -# -# \arg:gem returns the gem_name element from an gem.json -# \arg:o3de_gem_json_file name of the o3de json file -################################################################################ -function(o3de_gem_name o3de_gem_json_file gem) - file(READ ${o3de_gem_json_file} json_data) - string(JSON gem_entry ERROR_VARIABLE json_error GET ${json_data} gem_name) - if(json_error) - message(FATAL_ERROR "Unable to read gem_name from '${o3de_json_file}', error: ${json_error}") - endif() - if(gem_entry) - set(${gem} ${gem_entry} PARENT_SCOPE) - endif() -endfunction() - - -################################################################################ -#! o3de_template_name: -# -# \arg:template returns the template_name element from an template json -# \arg:o3de_template_json_file name of the o3de json file -################################################################################ -function(o3de_template_name o3de_template_json_file template) - file(READ ${o3de_template_json_file} json_data) - string(JSON template_entry ERROR_VARIABLE json_error GET ${json_data} template_name) - if(json_error) - message(FATAL_ERROR "Unable to read template_name from '${o3de_json_file}', error: ${json_error}") - endif() - if(template_entry) - set(${template} ${template_entry} PARENT_SCOPE) - endif() -endfunction() - - -################################################################################ -#! o3de_repo_name: -# -# \arg:repo returns the repo_name element from an repo.json or o3de_manifest.json -# \arg:o3de_repo_json_file name of the o3de json file -################################################################################ -function(o3de_repo_name o3de_repo_json_file repo) - file(READ ${o3de_repo_json_file} json_data) - string(JSON repo_entry ERROR_VARIABLE json_error GET ${json_data} repo_name) - if(json_error) - message(FATAL_ERROR "Unable to read repo_name from '${o3de_json_file}', error: ${json_error}") - endif() - if(repo_entry) - set(${repo} ${repo_entry} PARENT_SCOPE) - endif() -endfunction() - - -################################################################################ -#! o3de_restricted_name: -# -# \arg:restricted returns the restricted association element from an o3de json -# \arg:o3de_json_file name of the o3de json file -################################################################################ -function(o3de_restricted_name o3de_json_file restricted) - file(READ ${o3de_json_file} json_data) - string(JSON restricted_entry ERROR_VARIABLE json_error GET ${json_data} restricted_name) - if(json_error) - message(WARNING "FATAL_ERROR to read restricted_name from '${o3de_json_file}', error: ${json_error}") - endif() - if(restricted_entry) - set(${restricted} ${restricted_entry} PARENT_SCOPE) - endif() -endfunction() - - -################################################################################ -#! o3de_engine_path: -# -# \arg:engine_path returns the path of the o3de engine folder with name engine_name -# \arg:engine_name name of the engine -################################################################################ -function(o3de_engine_path o3de_json_file engine_path) - o3de_engine_id(${o3de_json_file} engine_name) - if(engine_name) - o3de_find_engine_folder(${engine_name} engine_folder) - if(engine_folder) - set(${engine_path} ${engine_folder} PARENT_SCOPE) - endif() - endif() -endfunction() - - -################################################################################ -#! o3de_project_path: -# -# \arg:project_path returns the path of the o3de project folder with name project_name -# \arg:project_name name of the project -################################################################################ -function(o3de_project_path o3de_json_file project_path) - o3de_project_id(${o3de_json_file} project_name) - if(project_name) - o3de_find_project_folder(${project_name} project_folder) - if(project_folder) - set(${project_path} ${project_folder} PARENT_SCOPE) - endif() - endif() -endfunction() - - -################################################################################ -#! o3de_template_path: -# -# \arg:template_path returns the path of the o3de template folder with name template_name -# \arg:template_name name of the template -################################################################################ -function(o3de_template_path o3de_json_file template_path) - o3de_template_id(${o3de_json_file} template_name) - if(template_name) - o3de_find_template_folder(${template_name} template_folder) - if(template_folder) - set(${template_path} ${template_folder} PARENT_SCOPE) - endif() - endif() -endfunction() - - -################################################################################ -#! o3de_repo_path: -# -# \arg:repo_path returns the path of the o3de repo folder with name repo_name -# \arg:repo_name name of the repo -################################################################################ -function(o3de_repo_path o3de_json_file repo_path) - o3de_repo_id(${o3de_json_file} repo_name) - if(repo_name) - o3de_find_repo_folder(${repo_name} repo_folder) - if(repo_folder) - set(${repo_path} ${repo_folder} PARENT_SCOPE) - endif() - endif() -endfunction() - - -################################################################################ -#! o3de_restricted_path: -# -# \arg:restricted_path returns the path of the o3de restricted folder with name restricted_name -# \arg:restricted_name name of the restricted -################################################################################ -function(o3de_restricted_path o3de_json_file restricted_path) - o3de_restricted_id(${o3de_json_file} restricted_name) - if(restricted_name) - o3de_find_restricted_folder(${restricted_name} restricted_folder) - if(restricted_folder) - set(${restricted_path} ${restricted_folder} PARENT_SCOPE) - endif() - endif() -endfunction() diff --git a/engine.json b/engine.json index 22ac2512f5..09bb3f5ff6 100644 --- a/engine.json +++ b/engine.json @@ -1,7 +1,98 @@ { "engine_name": "o3de", - "restricted": "o3de", + "restricted_name": "o3de", "FileVersion": 1, "O3DEVersion": "0.0.0.0", - "O3DECopyrightYear": 2021 + "O3DECopyrightYear": 2021, + "O3DEBuildNumber": 0, + "external_subdirectories": [ + "Gems/Achievements", + "Gems/AssetMemoryAnalyzer", + "Gems/AssetValidation", + "Gems/Atom", + "Gems/AtomContent", + "Gems/AtomLyIntegration", + "Gems/AtomTressFX", + "Gems/AudioEngineWwise", + "Gems/AudioSystem", + "Gems/AutomatedLauncherTesting", + "Gems/AWSClientAuth", + "Gems/AWSCore", + "Gems/AWSMetrics", + "Gems/Blast", + "Gems/Camera", + "Gems/CameraFramework", + "Gems/CertificateManager", + "Gems/CrashReporting", + "Gems/CustomAssetExample", + "Gems/DebugDraw", + "Gems/DevTextures", + "Gems/EditorPythonBindings", + "Gems/EMotionFX", + "Gems/ExpressionEvaluation", + "Gems/FastNoise", + "Gems/GameState", + "Gems/GameStateSamples", + "Gems/Gestures", + "Gems/GradientSignal", + "Gems/GraphCanvas", + "Gems/GraphModel", + "Gems/HttpRequestor", + "Gems/ImGui", + "Gems/InAppPurchases", + "Gems/LandscapeCanvas", + "Gems/LmbrCentral", + "Gems/LocalUser", + "Gems/LyShine", + "Gems/LyShineExamples", + "Gems/Maestro", + "Gems/MessagePopup", + "Gems/Metastream", + "Gems/Microphone", + "Gems/Multiplayer", + "Gems/MultiplayerCompression", + "Gems/NvCloth", + "Gems/PBSreferenceMaterials", + "Gems/PhysicsEntities", + "Gems/PhysX", + "Gems/PhysXDebug", + "Gems/PhysXSamples", + "Gems/Prefab", + "Gems/Presence", + "Gems/PrimitiveAssets", + "Gems/PythonAssetBuilder", + "Gems/QtForPython", + "Gems/RADTelemetry", + "Gems/SaveData", + "Gems/SceneLoggingExample", + "Gems/SceneProcessing", + "Gems/ScriptCanvas", + "Gems/ScriptCanvasDeveloper", + "Gems/ScriptCanvasPhysics", + "Gems/ScriptCanvasTesting", + "Gems/ScriptedEntityTweener", + "Gems/ScriptEvents", + "Gems/SliceFavorites", + "Gems/StartingPointCamera", + "Gems/StartingPointInput", + "Gems/StartingPointMovement", + "Gems/SurfaceData", + "Gems/TestAssetBuilder", + "Gems/TextureAtlas", + "Gems/TickBusOrderViewer", + "Gems/Twitch", + "Gems/UiBasics", + "Gems/Vegetation", + "Gems/Vegetation_Gem_Assets", + "Gems/VideoPlaybackFramework", + "Gems/VirtualGamepad", + "Gems/WhiteBox" + ], + "projects": [ + "AutomatedTesting" + ], + "templates": [ + "Templates/DefaultGem", + "Templates/DefaultProject" + ] } diff --git a/python/get_python.bat b/python/get_python.bat index e9f18441b5..e11c4ab92f 100644 --- a/python/get_python.bat +++ b/python/get_python.bat @@ -25,7 +25,8 @@ call python.cmd --version > NUL IF !ERRORLEVEL!==0 ( echo get_python.bat: Python is already installed: call python.cmd --version - call "%CMD_DIR%\pip.cmd" install -r "%CMD_DIR%/requirements.txt" --quiet --disable-pip-version-check + call "%CMD_DIR%\pip.cmd" install -r "%CMD_DIR%/requirements.txt" --quiet --disable-pip-version-check --no-warn-script-location + call "%CMD_DIR%\pip.cmd" install -e "%CMD_DIR%/../scripts/o3de" --quiet --disable-pip-version-check --no-warn-script-location --no-deps exit /B 0 ) @@ -65,6 +66,7 @@ if ERRORLEVEL 1 ( ) echo calling PIP to install requirements... -call "%CMD_DIR%\pip.cmd" install -r "%CMD_DIR%/requirements.txt" --disable-pip-version-check +call "%CMD_DIR%\pip.cmd" install -r "%CMD_DIR%/requirements.txt" --disable-pip-version-check --no-warn-script-location +call "%CMD_DIR%\pip.cmd" install -e "%CMD_DIR%/../scripts/o3de" --disable-pip-version-check --no-warn-script-location --no-deps exit /B %ERRORLEVEL% diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index d2843a9013..d3c9640665 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -11,5 +11,6 @@ add_subdirectory(detect_file_changes) add_subdirectory(commit_validation) +add_subdirectory(o3de) add_subdirectory(project_manager) add_subdirectory(ctest) diff --git a/scripts/build/Platform/Android/build_config.json b/scripts/build/Platform/Android/build_config.json index d0fce80964..33248ffe26 100644 --- a/scripts/build/Platform/Android/build_config.json +++ b/scripts/build/Platform/Android/build_config.json @@ -35,7 +35,7 @@ "PARAMETERS": { "CONFIGURATION":"debug", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -60,7 +60,7 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -76,7 +76,7 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=FALSE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=FALSE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -93,13 +93,13 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\windows_vs2019", - "CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"AssetProcessorBatch", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", "ASSET_PROCESSOR_BINARY": "bin\\profile\\AssetProcessorBatch.exe", "ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode --regset=\"/Amazon/AssetProcessor/Settings/Exclude Android/pattern=.*/DiffuseGlobalIllumination/.*precompiledshader\"", - "ASSET_PROCESSOR_PLATFORMS":"es3" + "ASSET_PROCESSOR_PLATFORMS":"android" } }, "release": { @@ -112,7 +112,7 @@ "PARAMETERS": { "CONFIGURATION":"release", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -128,7 +128,7 @@ "PARAMETERS": { "CONFIGURATION":"release", "OUTPUT_DIRECTORY":"build\\mono_android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -136,15 +136,14 @@ }, "gradle": { "TAGS":[ + "default", "weekly-build-metrics" ], "COMMAND":"gradle_windows.cmd", "PARAMETERS": { "CONFIGURATION":"profile", - "OUTPUT_DIRECTORY":"build\\android_gradle", + "OUTPUT_DIRECTORY":"build\\ad_grd", "GAME_PROJECT": "AutomatedTesting", - "ANDROID_NDK_PLATFORM": "21", - "ANDROID_SDK_PLATFORM": "29", "SIGN_APK": "false", "GRADLE_BUILD_CMD": "build", "ADDITIONAL_GENERATE_ARGS": "" @@ -158,8 +157,6 @@ "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\android_unittest", "GAME_PROJECT": "AutomatedTesting", - "ANDROID_NDK_PLATFORM": "21", - "ANDROID_SDK_PLATFORM": "29", "SIGN_APK": "true", "GRADLE_BUILD_CMD": "assemble", "ADDITIONAL_GENERATE_ARGS": "--unit-test" diff --git a/scripts/build/Platform/Android/gradle_windows.cmd b/scripts/build/Platform/Android/gradle_windows.cmd index 56423af95a..dd5285bdbf 100644 --- a/scripts/build/Platform/Android/gradle_windows.cmd +++ b/scripts/build/Platform/Android/gradle_windows.cmd @@ -17,20 +17,12 @@ IF NOT EXIST "%LY_3RDPARTY_PATH%" ( GOTO :error ) -IF NOT EXIST "%GRADLE_HOME%" ( +IF NOT EXIST "%GRADLE_BUILD_HOME%" ( REM This is the default for developers - SET GRADLE_HOME=C:\Gradle\gradle-5.6.4 + SET GRADLE_BUILD_HOME=C:\Gradle\gradle-7.0 ) -IF NOT EXIST "%GRADLE_HOME%" ( - ECHO [ci_build] FAIL: GRADLE_HOME=%GRADLE_HOME% - GOTO :error -) - -IF NOT EXIST "%CMAKE_HOME%" ( - SET CMAKE_HOME=%LY_3RDPARTY_PATH%/CMake/3.19.1/Windows/ -) -IF NOT EXIST "%CMAKE_HOME%" ( - ECHO [ci_build] FAIL: CMAKE_HOME=%CMAKE_HOME% +IF NOT EXIST "%GRADLE_BUILD_HOME%" ( + ECHO [ci_build] FAIL: GRADLE_BUILD_HOME=%GRADLE_BUILD_HOME% GOTO :error ) @@ -50,20 +42,9 @@ ECHO Ninja wasnt in the call path, add the value set by LY_NINJA_PATH SET PATH=%PATH%;%LY_NINJA_PATH% :ninja_on_path -IF NOT EXIST "%LY_ANDROID_SDK%" ( - SET LY_ANDROID_SDK=!LY_3RDPARTY_PATH!/android-sdk/platform-29 -) -IF NOT EXIST "%LY_ANDROID_SDK%" ( - ECHO [ci_build] FAIL: LY_ANDROID_SDK=!LY_ANDROID_SDK! - GOTO :error -) -IF NOT EXIST "%LY_ANDROID_NDK%" ( - set LY_ANDROID_NDK=!LY_3RDPARTY_PATH!/android-ndk/r21d -) -IF NOT EXIST "%LY_ANDROID_NDK%" ( - ECHO [ci_build] LY_ANDROID_NDK=!LY_ANDROID_NDK! - GOTO :error +IF NOT "%ANDROID_GRADLE_PLUGIN%" == "" ( + set ANDROID_GRADLE_PLUGIN_OPTION=--gradle-plugin-version=%ANDROID_GRADLE_PLUGIN% ) IF NOT EXIST %OUTPUT_DIRECTORY% ( @@ -154,11 +135,11 @@ IF "%GENERATE_SIGNED_APK%"=="true" ( ECHO Using keystore file at %CI_ANDROID_KEYSTORE_FILE_ABS% ) - ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %OPTIONAL_TEST_FLAG% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing - CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing + ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_BUILD_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-sdk-path=%ANDROID_HOME% %ANDROID_GRADLE_PLUGIN_OPTION% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing + CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_BUILD_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-sdk-path=%ANDROID_HOME% %ANDROID_GRADLE_PLUGIN_OPTION% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing ) ELSE ( - ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing - CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing + ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% %GRADLE_OVERRIDE_OPTION% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% %ANDROID_GRADLE_PLUGIN_OPTION% --android-sdk-path=%ANDROID_HOME% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing + CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_BUILD_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% %ANDROID_GRADLE_PLUGIN_OPTION% --android-sdk-path=%ANDROID_HOME% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing ) REM Validate the android project generation diff --git a/scripts/build/Platform/Android/pipeline.json b/scripts/build/Platform/Android/pipeline.json index ed10e7022d..551374a027 100644 --- a/scripts/build/Platform/Android/pipeline.json +++ b/scripts/build/Platform/Android/pipeline.json @@ -1,7 +1,7 @@ { "ENV": { - "GRADLE_HOME": "C:/Gradle/gradle-5.6.4", - "NODE_LABEL": "windows-047e5cdf", + "GRADLE_HOME": "C:/Gradle/gradle-7.0", + "NODE_LABEL": "windows-b3c8994f1", "LY_3RDPARTY_PATH": "C:/ly/3rdParty", "TIMEOUT": 30, "WORKSPACE": "D:/workspace", diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index c1646fc863..ee6da77b29 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -37,7 +37,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -53,7 +53,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -66,7 +66,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -80,10 +80,10 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -L FRAMEWORK_googletest" + "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest" } }, "test_profile_nounity": { @@ -92,10 +92,10 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -L FRAMEWORK_googletest" + "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest" } }, "asset_profile": { @@ -108,7 +108,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "AssetProcessorBatch", "ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch", @@ -122,7 +122,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "AssetProcessorBatch", "ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch", @@ -140,7 +140,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", "CTEST_OPTIONS": "-L (SUITE_periodic)" @@ -175,7 +175,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", "CTEST_OPTIONS": "-L (SUITE_benchmark)" @@ -191,7 +191,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -206,7 +206,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/mono_linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } diff --git a/scripts/build/Platform/Mac/build_config.json b/scripts/build/Platform/Mac/build_config.json index f312279fe6..971e5e47a1 100644 --- a/scripts/build/Platform/Mac/build_config.json +++ b/scripts/build/Platform/Mac/build_config.json @@ -37,7 +37,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD" } @@ -51,7 +51,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD" } @@ -66,7 +66,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=FALSE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=FALSE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD" } @@ -81,12 +81,12 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "AssetProcessorBatch", "ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch", "ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode", - "ASSET_PROCESSOR_PLATFORMS": "osx_gl" + "ASSET_PROCESSOR_PLATFORMS": "mac" } }, "periodic_test_profile": { @@ -99,7 +99,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", "CTEST_OPTIONS": "-L \"(SUITE_periodic)\"" @@ -115,7 +115,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", "CTEST_OPTIONS": "-L \"(SUITE_benchmark)\"" @@ -131,7 +131,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD" } @@ -146,7 +146,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/mono_mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD" } diff --git a/scripts/build/Platform/Mac/pipeline.json b/scripts/build/Platform/Mac/pipeline.json index 58f62b421d..81c57002b6 100644 --- a/scripts/build/Platform/Mac/pipeline.json +++ b/scripts/build/Platform/Mac/pipeline.json @@ -1,6 +1,6 @@ { "ENV": { - "NODE_LABEL": "mac", + "NODE_LABEL": "mac-catalina-7ad2e45b", "LY_3RDPARTY_PATH": "/Users/lybuilder/3rdParty", "TIMEOUT": 30, "WORKSPACE": "/Users/lybuilder/workspace", diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index ee34bae3e3..38cd7d6ad8 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -87,7 +87,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -101,7 +101,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -119,7 +119,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -135,7 +135,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=FALSE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=FALSE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -150,7 +150,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -171,7 +171,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -190,7 +190,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "AssetProcessorBatch", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -209,7 +209,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -231,7 +231,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_sandbox", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -250,7 +250,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -269,7 +269,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -285,7 +285,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build\\mono_windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -300,7 +300,7 @@ "PARAMETERS": { "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 -DCMAKE_INSTALL_PREFIX=install -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE", "CMAKE_LY_PROJECTS": "", "CMAKE_TARGET": "INSTALL", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -332,7 +332,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DCMAKE_MODULE_PATH=!WORKSPACE!/o3de/build/windows_vs2019/install/cmake", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DCMAKE_MODULE_PATH=!WORKSPACE!/o3de/install/cmake", "CMAKE_LY_PROJECTS": "", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" diff --git a/scripts/build/Platform/Windows/package_build_config.json b/scripts/build/Platform/Windows/package_build_config.json index 40f479a098..a5ca861377 100644 --- a/scripts/build/Platform/Windows/package_build_config.json +++ b/scripts/build/Platform/Windows/package_build_config.json @@ -4,7 +4,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "windows_vs2017", - "CMAKE_OPTIONS": "-G \"Visual Studio 15 2017\" -A x64 -T host=x64 -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 15 2017\" -A x64 -T host=x64 -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AtomTest;AtomSampleViewer", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m:4 /p:CL_MPCount=!HALF_PROCESSORS! /nologo" @@ -15,7 +15,7 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"windows_vs2019", - "CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"!WORKSPACE!/home\" -DO3DE_REGISTER_ENGINE_PATH=\"!WORKSPACE!/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS":"AtomTest;AtomSampleViewer", "CMAKE_TARGET":"ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" diff --git a/scripts/build/Platform/iOS/build_config.json b/scripts/build/Platform/iOS/build_config.json index d689c600a1..2d9c57f6ee 100644 --- a/scripts/build/Platform/iOS/build_config.json +++ b/scripts/build/Platform/iOS/build_config.json @@ -27,7 +27,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build/ios", - "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "-destination generic/platform=iOS" @@ -44,7 +44,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/ios", - "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "-destination generic/platform=iOS" @@ -60,7 +60,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/ios", - "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=FALSE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=FALSE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "-destination generic/platform=iOS" @@ -94,7 +94,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/ios", - "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "-destination generic/platform=iOS" diff --git a/scripts/build/Platform/iOS/pipeline.json b/scripts/build/Platform/iOS/pipeline.json index 58f62b421d..81c57002b6 100644 --- a/scripts/build/Platform/iOS/pipeline.json +++ b/scripts/build/Platform/iOS/pipeline.json @@ -1,6 +1,6 @@ { "ENV": { - "NODE_LABEL": "mac", + "NODE_LABEL": "mac-catalina-7ad2e45b", "LY_3RDPARTY_PATH": "/Users/lybuilder/3rdParty", "TIMEOUT": 30, "WORKSPACE": "/Users/lybuilder/workspace", diff --git a/scripts/build/lambda/trigger_first_build.py b/scripts/build/lambda/trigger_first_build.py index 6ebe09f7ee..3b4587e737 100755 --- a/scripts/build/lambda/trigger_first_build.py +++ b/scripts/build/lambda/trigger_first_build.py @@ -53,7 +53,7 @@ def lambda_handler(event, context): backoff = 30 status_list = [404] # Retry if the branch doesn't exist yet and provide time for Jenkins to discover it. method_list = ['POST'] - retry_config = Retry(total=retries, backoff_factor=backoff, status_forcelist=status_list, method_whitelist=method_list) + retry_config = Retry(total=retries, backoff_factor=backoff, status_forcelist=status_list, allowed_methods=method_list) session = requests.Session() session.mount('https://', HTTPAdapter(max_retries=retry_config)) diff --git a/scripts/build/package/package.py b/scripts/build/package/package.py index 96b39f0753..4cb09f3918 100755 --- a/scripts/build/package/package.py +++ b/scripts/build/package/package.py @@ -25,9 +25,6 @@ from glob3 import glob def package(options): package_env = PackageEnv(options.platform, options.type, options.package_env) - # Override values in bootstrap.cfg for PC package - override_bootstrap_cfg(package_env) - if not package_env.get('SKIP_BUILD'): print(package_env.get('SKIP_BUILD')) print('SKIP_BUILD is False, running CMake build...') @@ -51,34 +48,6 @@ def get_python_path(package_env): return os.path.join(package_env.get('ENGINE_ROOT'), 'python', 'python.sh') -def override_bootstrap_cfg(package_env): - print('Override values in bootstrap.cfg') - engine_root = package_env.get('ENGINE_ROOT') - bootstrap_path = os.path.join(engine_root, 'bootstrap.cfg') - replace_values = {'project_path':'{}'.format(package_env.get('BOOTSTRAP_CFG_GAME_FOLDER'))} - try: - with open(bootstrap_path, 'r') as bootstrap_cfg: - content = bootstrap_cfg.read() - except: - error('Cannot read file {}'.format(bootstrap_path)) - content = content.split('\n') - new_content = [] - for line in content: - if not line.startswith('--'): - strs = line.split('=') - if len(strs): - key = strs[0].strip(' ') - if key in replace_values: - line = '{}={}'.format(key, replace_values[key]) - new_content.append(line) - try: - with open(bootstrap_path, 'w') as out: - out.write('\n'.join(new_content)) - except: - error('Cannot write to file {}'.format(bootstrap_path)) - print('{} updated with value {}'.format(bootstrap_path, replace_values)) - - def cmake_build(package_env): build_targets = package_env.get('BUILD_TARGETS') for build_target in build_targets: diff --git a/scripts/bundler/gen_shaders.py b/scripts/bundler/gen_shaders.py index 46857179b1..bfb60287af 100644 --- a/scripts/bundler/gen_shaders.py +++ b/scripts/bundler/gen_shaders.py @@ -163,11 +163,11 @@ def add_shaders_types(): shaders.append(gl4) gles3 = _ShaderType('GLES3', 'GLSL_HLSLcc') - gles3.add_configuration('Android', 'es3') + gles3.add_configuration('Android', 'android') shaders.append(gles3) metal = _ShaderType('METAL', 'METAL_LLVM_DXC') - metal.add_configuration('Mac', 'osx_gl') + metal.add_configuration('Mac', 'mac') metal.add_configuration('iOS', 'ios') shaders.append(metal) diff --git a/scripts/o3de.bat b/scripts/o3de.bat index 0a65b722d7..9031933e61 100644 --- a/scripts/o3de.bat +++ b/scripts/o3de.bat @@ -12,15 +12,15 @@ REM pushd %~dp0% CD %~dp0.. -SET BASE_PATH=%CD% +SET "BASE_PATH=%CD%" CD %~dp0 -SET PYTHON_DIRECTORY=%BASE_PATH%\python +SET "PYTHON_DIRECTORY=%BASE_PATH%\python" IF EXIST "%PYTHON_DIRECTORY%" GOTO pythonPathAvailable GOTO pythonDirNotFound :pythonPathAvailable SET PYTHON_EXECUTABLE=%PYTHON_DIRECTORY%\python.cmd IF NOT EXIST "%PYTHON_EXECUTABLE%" GOTO pythonExeNotFound -CALL "%PYTHON_EXECUTABLE%" %BASE_PATH%\scripts\o3de.py %* +CALL "%PYTHON_EXECUTABLE%" "%BASE_PATH%\scripts\o3de.py" %* GOTO end :pythonDirNotFound ECHO Python directory not found: %PYTHON_DIRECTORY% diff --git a/scripts/o3de.py b/scripts/o3de.py index dbb9c53e4b..8d7532878c 100755 --- a/scripts/o3de.py +++ b/scripts/o3de.py @@ -10,23 +10,54 @@ # import argparse +import pathlib import sys -import os -# Resolve the common python module -ROOT_DEV_PATH = os.path.realpath(os.path.join(os.path.dirname(__file__), '..')) -if ROOT_DEV_PATH not in sys.path: - sys.path.append(ROOT_DEV_PATH) -from cmake.Tools import engine_template -from cmake.Tools import global_project -from cmake.Tools import registration +def add_args(parser, subparsers) -> None: + """ + add_args is called to add expected parser arguments and subparsers arguments to each command such that it can be + invoked by o3de.py + Ex o3de.py can invoke the register downloadable commands by importing register, + call add_args and execute: python o3de.py register --gem-path "C:/TestGem" + :param parser: the caller instantiates a parser and passes it in here + :param subparsers: the caller instantiates subparsers and passes it in here + """ + # As o3de.py shares the same name as the o3de package attempting to use a regular + # from o3de import line tries to import from the current o3de.py script and not the package + # So the {current script directory} / 'o3de' is added to the front of the sys.path + script_dir = pathlib.Path(__file__).parent + o3de_package_dir = (script_dir / 'o3de').resolve() + # add the scripts/o3de directory to the front of the sys.path + sys.path.insert(0, str(o3de_package_dir)) + from o3de import engine_template, global_project, register, print_registration, get_registration, \ + enable_gem, disable_gem, sha256 + # Remove the temporarily added path + sys.path = sys.path[1:] -def add_args(parser, subparsers) -> None: - global_project.add_args(parser, subparsers) - engine_template.add_args(parser, subparsers) - registration.add_args(parser, subparsers) + # global_project + global_project.add_args(subparsers) + # engine templaate + engine_template.add_args(subparsers) + + # register + register.add_args(subparsers) + + # show + print_registration.add_args(subparsers) + + # get-registered + get_registration.add_args(subparsers) + + # add a gem to a project + enable_gem.add_args(subparsers) + + # remove a gem from a project + disable_gem.add_args(subparsers) + + # sha256 + sha256.add_args(subparsers) if __name__ == "__main__": diff --git a/scripts/o3de/CMakeLists.txt b/scripts/o3de/CMakeLists.txt new file mode 100644 index 0000000000..9819c1cd6e --- /dev/null +++ b/scripts/o3de/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +add_subdirectory(tests) diff --git a/scripts/o3de/README.txt b/scripts/o3de/README.txt new file mode 100644 index 0000000000..51bbf78cbd --- /dev/null +++ b/scripts/o3de/README.txt @@ -0,0 +1,41 @@ +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + + +INTRODUCTION +------------ + +o3de is a package of scripts containing functionality to register engine, projects, gems, +templates and download repositories with the o3de manifests +It also contains functionality for creating new projects, gems and templates as well +as querying existing gems and templates + + +REQUIREMENTS +------------ + + * Python 3.7.10 (64-bit) + +INSTALL +----------- +It is recommended to set up these these tools with O3DE's CMake build commands. +Assuming CMake is already setup on your operating system, below are some sample build commands: + cd /path/to/od3e/ + cmake -B windows_vs2019 -S . -G"Visual Studio 16" -DLY_3RDPARTY_PATH="%LY_3RDPARTY_PATH%" + +To manually install the project in development mode using your own installed Python interpreter: + cd /path/to/od3e/o3de + /path/to/your/python -m pip install -e . + + +UNINSTALLATION +-------------- + +The preferred way to uninstall the project is: + /path/to/your/python -m pip uninstall o3de diff --git a/scripts/o3de/o3de/__init__.py b/scripts/o3de/o3de/__init__.py new file mode 100644 index 0000000000..4d5680a30d --- /dev/null +++ b/scripts/o3de/o3de/__init__.py @@ -0,0 +1,10 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# diff --git a/scripts/o3de/o3de/cmake.py b/scripts/o3de/o3de/cmake.py new file mode 100644 index 0000000000..f8e8d6ce0c --- /dev/null +++ b/scripts/o3de/o3de/cmake.py @@ -0,0 +1,193 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +""" +Contains methods for query CMake gem target information +""" + +import logging +import os +import pathlib + +from o3de import manifest + +logger = logging.getLogger() +logging.basicConfig() + +enable_gem_start_marker = 'set(ENABLED_GEMS' +enable_gem_end_marker = ')' + + +def add_gem_dependency(cmake_file: pathlib.Path, + gem_name: str) -> int: + """ + adds a gem dependency to a cmake file + :param cmake_file: path to the cmake file + :param gem_name: name of the gem + :return: 0 for success or non 0 failure code + """ + if not cmake_file.is_file(): + logger.error(f'Failed to locate cmake file {str(cmake_file)}') + return 1 + + # on a line by basis, see if there already is {gem_name} + # find the first occurrence of a gem, copy its formatting and replace + # the gem name with the new one and append it + # if the gem is already present fail + t_data = [] + added = False + line_index_to_append = None + with open(cmake_file, 'r') as s: + line_index = 0 + for line in s: + if line.strip().startswith(enable_gem_start_marker): + line_index_to_append = line_index + if f'{gem_name}' == line.strip(): + logger.warning(f'{gem_name} is already enabled in file {str(cmake_file)}.') + return 0 + t_data.append(line) + line_index += 1 + + + indent = 4 + if line_index_to_append: + # Insert the gem after the 'set(ENABLED_GEMS)...` line + t_data.insert(line_index_to_append + 1, f'{" " * indent}{gem_name}\n') + added = True + + # if we didn't add, then create a new set(ENABLED_GEMS) variable + # add a new gem, if empty the correct format is 1 tab=4spaces + if not added: + t_data.append('\n') + t_data.append(f'{enable_gem_start_marker}\n') + t_data.append(f'{" " * indent}{gem_name}\n') + t_data.append(f'{enable_gem_end_marker}\n') + + # write the cmake + with open(cmake_file, 'w') as s: + s.writelines(t_data) + + return 0 + +def remove_gem_dependency(cmake_file: pathlib.Path, + gem_name: str) -> int: + """ + removes a gem dependency from a cmake file + :param cmake_file: path to the cmake file + :param gem_name: name of the gem + :return: 0 for success or non 0 failure code + """ + if not cmake_file.is_file(): + logger.error(f'Failed to locate cmake file {cmake_file}') + return 1 + + # on a line by basis, remove any line with {gem_name} + t_data = [] + # Remove the gem from the enabled_gem file by skipping the gem name entry + removed = False + with open(cmake_file, 'r') as s: + for line in s: + if gem_name == line.strip(): + removed = True + else: + t_data.append(line) + + if not removed: + logger.error(f'Failed to remove {gem_name} from cmake file {cmake_file}') + return 1 + + # write the cmake + with open(cmake_file, 'w') as s: + s.writelines(t_data) + + return 0 + + +def get_project_gems(project_path: pathlib.Path, + platform: str = 'Common') -> set: + return get_gems_from_cmake_file(get_enabled_gem_cmake_file(project_path=project_path, platform=platform)) + + +def get_enabled_gems(cmake_file: pathlib.Path) -> set: + """ + Gets a list of enabled gems from the cmake file + :param cmake_file: path to the cmake file + :return: set of gem targets found + """ + cmake_file = pathlib.Path(cmake_file).resolve() + + if not cmake_file.is_file(): + logger.error(f'Failed to locate cmake file {cmake_file}') + return set() + + gem_target_set = set() + with cmake_file.open('r') as s: + in_gem_list = False + for line in s: + line = line.strip() + if line.startswith(enable_gem_start_marker): + # Set the flag to indicate that we are in the ENABLED_GEMS variable + in_gem_list = True + # Skip pass the 'set(ENABLED_GEMS' marker just in case their are gems declared on the same line + line = line[len(enable_gem_start_marker):] + if in_gem_list: + # Since we are inside the ENABLED_GEMS variable determine if the line has the end_marker of ')' + if line.endswith(enable_gem_end_marker): + # Strip away the line end marker + line = line[:-len(enable_gem_end_marker)] + # Set the flag to indicate that we are no longer in the ENABLED_GEMS variable after this line + in_gem_list = False + # Split the rest of the line on whitespace just in case there are multiple gems in a line + gem_name_list = line.split() + gem_target_set.update(gem_name_list) + + return gem_target_set + + +def get_project_gem_paths(project_path: pathlib.Path, + platform: str = 'Common') -> set: + gem_names = get_project_gems(project_path, platform) + gem_paths = set() + for gem_name in gem_names: + gem_paths.add(manifest.get_registered(gem_name=gem_name)) + return gem_paths + + +def get_enabled_gem_cmake_file(project_name: str = None, + project_path: str or pathlib.Path = None, + platform: str = 'Common') -> pathlib.Path or None: + """ + get the standard cmake file name for a particular type of dependency + :param gem_name: name of the gem, resolves gem_path + :param gem_path: path of the gem + :return: list of gem targets + """ + if not project_name and not project_path: + logger.error(f'Must supply either a Project Name or Project Path.') + return None + + if project_name and not project_path: + project_path = manifest.get_registered(project_name=project_name) + + project_path = pathlib.Path(project_path).resolve() + enable_gem_filename = "enabled_gems.cmake" + + if platform == 'Common': + project_code_dir = project_path / 'Gem/Code' + if project_code_dir.is_dir(): + dependencies_file_path = project_code_dir / enable_gem_filename + return dependencies_file_path.resolve() + return (project_path / 'Code' / enable_gem_filename).resolve() + else: + project_code_dir = project_path / 'Gem/Code/Platform' / platform + if project_code_dir.is_dir(): + dependencies_file_path = project_code_dir / enable_gem_filename + return dependencies_file_path.resolve() + return (project_path / 'Code/Platform' / platform / enable_gem_filename).resolve() diff --git a/scripts/o3de/o3de/disable_gem.py b/scripts/o3de/o3de/disable_gem.py new file mode 100644 index 0000000000..82fe9c8ac6 --- /dev/null +++ b/scripts/o3de/o3de/disable_gem.py @@ -0,0 +1,177 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +""" +Contains methods for removing a gem from a project +""" + +import argparse +import logging +import os +import pathlib +import sys + +from o3de import cmake, manifest + +logger = logging.getLogger() +logging.basicConfig() + + +def disable_gem_in_project(gem_name: str = None, + gem_path: pathlib.Path = None, + project_name: str = None, + project_path: pathlib.Path = None, + enabled_gem_file: pathlib.Path = None) -> int: + """ + disable a gem in a projects enabled_gems.cmake file + :param gem_name: name of the gem to add + :param gem_path: path to the gem to add + :param project_name: name of the project to add the gem to + :param project_path: path to the project to add the gem to + :param enabled_gem_file: File to remove enabled gem from + :return: 0 for success or non 0 failure code + """ + + # we need either a project name or path + if not project_name and not project_path: + logger.error(f'Must either specify a Project path or Project Name.') + return 1 + + # if project name resolve it into a path + if project_name and not project_path: + project_path = manifest.get_registered(project_name=project_name) + if not project_path: + logger.error(f'Unable to locate project path from the registered manifest.json files:' + f' {str(pathlib.Path("~/.o3de/o3de_manifest.json").expanduser())}, engine.json') + return 1 + + project_path = pathlib.Path(project_path).resolve() + if not project_path.is_dir(): + logger.error(f'Project path {project_path} is not a folder.') + return 1 + + # We need either a gem name or path + if not gem_name and not gem_path: + logger.error(f'Must either specify a Gem path or Gem Name.') + return 1 + + # if gem name resolve it into a path + if gem_name and not gem_path: + gem_path = manifest.get_registered(gem_name=gem_name) + if not gem_path: + logger.error(f'Unable to locate gem path from the registered manifest.json files:' + f' {str(pathlib.Path.home() / ".o3de/manifest.json")},' + f' {project_path / "project.json"}, engine.json') + return 1 + gem_path = pathlib.Path(gem_path).resolve() + # make sure this gem already exists if we're adding. We can always remove a gem. + if not gem_path.is_dir(): + logger.error(f'Gem Path {gem_path} does not exist.') + return 1 + + + # Read gem.json from the gem path + gem_json_data = manifest.get_gem_json_data(gem_path=gem_path) + if not gem_json_data: + logger.error(f'Could not read gem.json content under {gem_path}.') + return 1 + + # when removing we will try to do as much as possible even with failures so ret_val will be the last error code + ret_val = 0 + + if not enabled_gem_file: + enabled_gem_file = cmake.get_enabled_gem_cmake_file(project_path=project_path) + + # make sure this is a project has an enabled gems file + if not enabled_gem_file.is_file(): + logger.error(f'Enabled gem file {enabled_gem_file} is not present.') + return 1 + # remove the gem + error_code = cmake.remove_gem_dependency(enabled_gem_file, gem_json_data['gem_name']) + if error_code: + ret_val = error_code + + return ret_val + + +def _run_disable_gem_in_project(args: argparse) -> int: + if args.override_home_folder: + manifest.override_home_folder = args.override_home_folder + + return disable_gem_in_project(args.gem_name, + args.gem_path, + args.project_name, + args.project_path, + args.enabled_gem_file) + + +def add_parser_args(parser): + """ + add_parser_args is called to add arguments to each command such that it can be + invoked locally or added by a central python file. + Ex. Directly run from this file alone with: python disable_gem.py --project-path D:/Test --gem-name Atom + :param parser: the caller passes an argparse parser like instance to this method + """ + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-pp', '--project-path', type=pathlib.Path, required=False, + help='The path to the project.') + group.add_argument('-pn', '--project-name', type=str, required=False, + help='The name of the project.') + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-gp', '--gem-path', type=pathlib.Path, required=False, + help='The path to the gem.') + group.add_argument('-gn', '--gem-name', type=str, required=False, + help='The name of the gem.') + parser.add_argument('-egf', '--enabled-gem-file', type=pathlib.Path, required=False, + help='The cmake enabled gem file in which gem names are to be removed from.' + 'If not specified it will assume ') + + parser.add_argument('-ohf', '--override-home-folder', type=pathlib.Path, required=False, + help='By default the home folder is the user folder, override it to this folder.') + + parser.set_defaults(func=_run_disable_gem_in_project) + + +def add_args(subparsers) -> None: + """ + add_args is called to add subparsers arguments to each command such that it can be + a central python file such as o3de.py. + It can be run from the o3de.py script as follows + call add_args and execute: python o3de.py disable-gem-from-cmake --project-path D:/Test --gem-name Atom + :param subparsers: the caller instantiates subparsers and passes it in here + """ + disable_gem_project_subparser = subparsers.add_parser('disable-gem') + add_parser_args(disable_gem_project_subparser) + + +def main(): + """ + Runs disable_gem_project.py script as standalone script + """ + # parse the command line args + the_parser = argparse.ArgumentParser() + + # add subparsers + + # add args to the parser + add_parser_args(the_parser) + + # parse args + the_args = the_parser.parse_args() + + # run + ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1 + + # return + sys.exit(ret) + + +if __name__ == "__main__": + main() diff --git a/scripts/o3de/o3de/download.py b/scripts/o3de/o3de/download.py new file mode 100644 index 0000000000..6f1b82e754 --- /dev/null +++ b/scripts/o3de/o3de/download.py @@ -0,0 +1,240 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +""" +Implements functionality for downloading o3de objecs either locally or from a URI +""" + +import argparse +import hashlib +import json +import logging +import pathlib +import shutil +import sys +import urllib.parse +import urllib.request + +from o3de import manifest, repo, utils, validation + +logger = logging.getLogger() +logging.basicConfig() + +def unzip_manifest_json_data(download_zip_path: pathlib.Path, zip_file_name: str) -> dict: + json_data = {} + with zipfile.ZipFile(download_zip_path, 'r') as zip_data: + with zip_data.open(zip_file_name) as manifest_json_file: + try: + json_data = json.load(manifest_json_file) + except json.JSONDecodeError as e: + logger.error(f'UnZip exception:{str(e)}') + + return json_data + +def validate_downloaded_zip_sha256(download_uri_json_data: dict, download_zip_path: pathlib.Path, + manifest_json_name) -> int: + # if the engine.json has a sha256 check it against a sha256 of the zip + try: + sha256A = download_uri_json_data['sha256'] + except KeyError as e: + logger.warn(f'SECURITY WARNING: The advertised o3de object you downloaded has no "sha256"!!! Be VERY careful!!!' + f' We cannot verify this is the actually the advertised object!!!') + else: + sha256B = hashlib.sha256(download_zip_path.open('rb').read()).hexdigest() + if sha256A != sha256B: + logger.error(f'SECURITY VIOLATION: Downloaded zip sha256 {sha256B} does not match' + f' the advertised "sha256":{sha256A} in the f{manifest_json_name}. Deleting unzipped files!!!') + shutil.rmtree(dest_path) + return 1 + + manifest_json_data = unzip_manifest_json_data(download_zip_path, manifest_json_name) + + # remove the sha256 if present in the advertised downloadable manifest json + # then compare it to the json in the zip, they should now be identical + try: + del download_uri_json_data['sha256'] + except KeyError as e: + pass + + sha256A = hashlib.sha256(json.dumps(download_uri_json_data, indent=4).encode('utf8')).hexdigest() + with unzipped_manifest_json.open('r') as s: + try: + unzipped_manifest_json_data = json.load(s) + except json.JSONDecodeError as e: + logger.error(f'Failed to read manifest json {unzipped_manifest_json}. Unable to confirm this' + f' is the same template that was advertised.') + return 1 + sha256B = hashlib.sha256(json.dumps(unzipped_manifest_json_data, indent=4).encode('utf8')).hexdigest() + if sha256A != sha256B: + logger.error(f'SECURITY VIOLATION: Downloaded manifest json does not match' + f' the advertised manifest json. Deleting unzipped files!!!') + shutil.rmtree(dest_path) + return 1 + + return 0 + + +def get_downloadable(engine_name: str = None, + project_name: str = None, + gem_name: str = None, + template_name: str = None, + restricted_name: str = None) -> dict or None: + json_data = manifest.load_o3de_manifest() + try: + o3de_object_uris = json_data['repos'] + except KeyError as key_err: + logger.error(f'Unable to load repos from o3de manifest: {str(key_err)}') + return None + + manifest_json = 'repo.json' + search_func = lambda: repo.search_repo(manifest_json, engine_name, project_name, gem_name, template_name) + return repo.search_o3de_object(manifest_json, o3de_object_uris, search_func) + + +def download_o3de_object(object_name: str, default_folder_name: str, dest_path: str or pathlib.Path, + object_type: str, downloadable_kwarg_key) -> int: + if not dest_path: + dest_path = manifest.get_registered(default_folder=default_folder_name) + if not dest_path: + logger.error(f'Destination path not cannot be empty.') + return 1 + + dest_path = pathlib.Path(dest_path).resolve() + dest_path.mkdir(exist_ok=True) + + download_path = manifest.get_o3de_download_folder() / default_folder_name / object_name + download_path.mkdir(exist_ok=True) + download_zip_path = download_path / f'{object_type}.zip' + + downloadable_object_data = get_downloadable(**{downloadable_kwarg_key : object_name}) + if not downloadable_object_data: + logger.error(f'Downloadable o3de object {object_name} not found.') + return 1 + + origin = downloadable_json_data['origin'] + url = f'{origin}/object_type.zip' + parsed_uri = urllib.parse.urlparse(url) + + download_zip_result = utils.download_zip_file(parsed_uri, download_zip_path) + if download_zip_result != 0: + return download_zip_result + + return validate_downloaded_zip_sha256(downloadable_object_data, download_zip_path) + + +def download_engine(engine_name: str, + dest_path: str or pathlib.Path) -> int: + return download_o3de_object(engine_name, 'engines', dest_path, 'engine', 'engine_name') + + +def download_project(project_name: str, + dest_path: str or pathlib.Path) -> int: + return download_o3de_object(project_name, 'projects', dest_path, 'project', 'project_name') + + +def download_gem(gem_name: str, + dest_path: str or pathlib.Path) -> int: + return download_o3de_object(gem_name, 'gems', dest_path, 'gem', 'gem_name') + + +def download_template(template_name: str, + dest_path: str or pathlib.Path) -> int: + return download_o3de_object(template_name, 'templates', dest_path, 'template', 'template_name') + + + +def download_restricted(restricted_name: str, + dest_path: str or pathlib.Path) -> int: + return download_o3de_object(restricted_name, 'restricted', dest_path, 'restricted', 'restricted_name') + + +def _run_download(args: argparse) -> int: + if args.override_home_folder: + manifest.override_home_folder = args.override_home_folder + + if args.engine_name: + return download_engine(args.engine_name, + args.dest_path) + elif args.project_name: + return download_project(args.project_name, + args.dest_path) + elif args.gem_nanme: + return download_gem(args.gem_name, + args.dest_path) + elif args.template_name: + return download_template(args.template_name, + args.dest_path) + + return 1 + +def add_parser_args(parser): + """ + add_parser_args is called to add arguments to each command such that it can be + invoked locally or added by a central python file. + Ex. Directly run from this file alone with: python download.py --engine-name "o3de" + :param parser: the caller passes an argparse parser like instance to this method + """ + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-e', '--engine-name', type=str, required=False, + help='Downloadable engine name.') + group.add_argument('-p', '--project-name', type=str, required=False, + help='Downloadable project name.') + group.add_argument('-g', '--gem-name', type=str, required=False, + help='Downloadable gem name.') + group.add_argument('-t', '--template-name', type=str, required=False, + help='Downloadable template name.') + parser.add_argument('-dp', '--dest-path', type=str, required=False, + default=None, + help='Optional destination folder to download into.' + ' i.e. download --project-name "AstomSamplerViewer" --dest-path "C:/projects"' + ' will result in C:/projects/AtomSampleViewer' + ' If blank will download to default object type folder') + + parser.add_argument('-ohf', '--override-home-folder', type=str, required=False, + help='By default the home folder is the user folder, override it to this folder.') + + parser.set_defaults(func=_run_download) + + +def add_args(subparsers) -> None: + """ + add_args is called to add subparsers arguments to each command such that it can be + a central python file such as o3de.py. + It can be run from the o3de.py script as follows + call add_args and execute: python o3de.py download --engine-name "o3de" + :param subparsers: the caller instantiates subparsers and passes it in here + """ + download_subparser = subparsers.add_parser('download') + add_parser_args(download_subparser) + + +def main(): + """ + Runs download.py script as standalone script + """ + # parse the command line args + the_parser = argparse.ArgumentParser() + + # add subparsers + + # add args to the parser + add_parser_args(the_parser) + + # parse args + the_args = the_parser.parse_args() + + # run + ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1 + + # return + sys.exit(ret) + + +# Do not allow running the download.py script as a standalone script until it is reviewed by app-sec diff --git a/scripts/o3de/o3de/enable_gem.py b/scripts/o3de/o3de/enable_gem.py new file mode 100644 index 0000000000..0e007f6370 --- /dev/null +++ b/scripts/o3de/o3de/enable_gem.py @@ -0,0 +1,181 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +""" +Contains command to add a gem to a project's enabled_gem.cmake file +""" + +import argparse +import json +import logging +import os +import pathlib +import sys + +from o3de import cmake, manifest, validation + +logger = logging.getLogger() +logging.basicConfig() + + +def enable_gem_in_project(gem_name: str = None, + gem_path: pathlib.Path = None, + project_name: str = None, + project_path: pathlib.Path = None, + enabled_gem_file: pathlib.Path = None) -> int: + """ + enable a gem in a projects enabled_gems.cmake file + :param gem_name: name of the gem to add + :param gem_path: path to the gem to add + :param project_name: name of to the project to add the gem to + :param project_path: path to the project to add the gem to + :param enabled_gem_file_file: if this dependency goes/is in a specific file + :return: 0 for success or non 0 failure code + """ + # we need either a project name or path + if not project_name and not project_path: + logger.error(f'Must either specify a Project path or Project Name.') + return 1 + + # if project name resolve it into a path + if project_name and not project_path: + project_path = manifest.get_registered(project_name=project_name) + if not project_path: + logger.error(f'Unable to locate project path from the registered manifest.json files:' + f' {str(pathlib.Path.home() / ".o3de/manifest.json")}, engine.json') + return 1 + + project_path = pathlib.Path(project_path).resolve() + if not project_path.is_dir(): + logger.error(f'Project path {project_path} is not a folder.') + return 1 + + # we need either a gem name or path + if not gem_name and not gem_path: + logger.error(f'Must either specify a Gem path or Gem Name.') + return 1 + + # if gem name resolve it into a path + if gem_name and not gem_path: + gem_path = manifest.get_registered(gem_name=gem_name) + if not gem_path: + logger.error(f'Unable to locate gem path from the registered manifest.json files:' + f' {str(pathlib.Path( "~/.o3de/o3de_manifest.json").expanduser())},' + f' {project_path / "project.json"}, engine.json') + return 1 + + gem_path = pathlib.Path(gem_path).resolve() + # make sure this gem already exists if we're adding. We can always remove a gem. + if not gem_path.is_dir(): + logger.error(f'Gem Path {gem_path} does not exist.') + return 1 + + # Read gem.json from the gem path + gem_json_data = manifest.get_gem_json_data(gem_path=gem_path) + if not gem_json_data: + logger.error(f'Could not read gem.json content under {gem_path}.') + return 1 + + + ret_val = 0 + if enabled_gem_file: + # make sure this is a project has an enabled gems file + if not enabled_gem_file.is_file(): + logger.error(f'Enabled gem file {enabled_gem_file} is not present.') + return 1 + # add the gem + ret_val = cmake.add_gem_dependency(enabled_gem_file, gem_json_data['gem_name']) + + else: + # Find the path to enabled gem file. + # It will be created if it doesn't exist + project_enabled_gem_file = cmake.get_enabled_gem_cmake_file(project_path=project_path) + if not project_enabled_gem_file.is_file(): + project_enabled_gem_file.touch() + # add the gem + ret_val = cmake.add_gem_dependency(project_enabled_gem_file, gem_json_data['gem_name']) + + return ret_val + + +def _run_enable_gem_in_project(args: argparse) -> int: + if args.override_home_folder: + manifest.override_home_folder = args.override_home_folder + + return enable_gem_in_project(args.gem_name, + args.gem_path, + args.project_name, + args.project_path, + args.enabled_gem_file) + + +def add_parser_args(parser): + """ + add_parser_args is called to add arguments to each command such that it can be + invoked locally or added by a central python file. + Ex. Directly run from this file alone with: python enable_gem.py --project-path "D:/TestProject" --gem-path "D:/TestGem" + :param parser: the caller passes an argparse parser like instance to this method + """ + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-pp', '--project-path', type=pathlib.Path, required=False, + help='The path to the project.') + group.add_argument('-pn', '--project-name', type=str, required=False, + help='The name of the project.') + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-gp', '--gem-path', type=pathlib.Path, required=False, + help='The path to the gem.') + group.add_argument('-gn', '--gem-name', type=str, required=False, + help='The name of the gem.') + parser.add_argument('-egf', '--enabled-gem-file', type=pathlib.Path, required=False, + help='The cmake enabled_gem file in which the gem names are specified.' + 'If not specified it will assume enabled_gems.cmake') + + parser.add_argument('-ohf', '--override-home-folder', type=pathlib.Path, required=False, + help='By default the home folder is the user folder, override it to this folder.') + + parser.set_defaults(func=_run_enable_gem_in_project) + + +def add_args(subparsers) -> None: + """ + add_args is called to add subparsers arguments to each command such that it can be + a central python file such as o3de.py. + It can be run from the o3de.py script as follows + call add_args and execute: python o3de.py add-gem-to-project --project-path "D:/TestProject" --gem-path "D:/TestGem" + :param subparsers: the caller instantiates subparsers and passes it in here + """ + enable_gem_project_subparser = subparsers.add_parser('enable-gem') + add_parser_args(enable_gem_project_subparser) + + +def main(): + """ + Runs enable_gem.py script as standalone script + """ + # parse the command line args + the_parser = argparse.ArgumentParser() + + # add subparsers + + # add args to the parser + add_parser_args(the_parser) + + # parse args + the_args = the_parser.parse_args() + + # run + ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1 + + # return + sys.exit(ret) + + +if __name__ == "__main__": + main() diff --git a/cmake/Tools/engine_template.py b/scripts/o3de/o3de/engine_template.py similarity index 92% rename from cmake/Tools/engine_template.py rename to scripts/o3de/o3de/engine_template.py index 78de209ab4..9bb62eff35 100755 --- a/cmake/Tools/engine_template.py +++ b/scripts/o3de/o3de/engine_template.py @@ -20,8 +20,8 @@ import json import uuid import re -from cmake.Tools import utils -import cmake.Tools.registration as registration + +from o3de import manifest, validation, utils logger = logging.getLogger() logging.basicConfig() @@ -79,7 +79,7 @@ restricted_platforms = { } template_file_name = 'template.json' - +this_script_parent = os.path.dirname(os.path.realpath(__file__)) def _transform(s_data: str, replacements: list, @@ -321,7 +321,7 @@ def _instantiate_template(template_json_data: dict, platform_json = f'{template_restricted_platform_path_rel}/{template_file_name}'.replace('//', '/') if os.path.isfile(platform_json): - if not registration.valid_o3de_template_json(platform_json): + if not validation.valid_o3de_template_json(platform_json): logger.error(f'Template json {platform_json} is invalid.') return 1 @@ -329,7 +329,7 @@ def _instantiate_template(template_json_data: dict, with open(platform_json, 'r') as s: try: json_data = json.load(s) - except Exception as e: + except json.JSONDecodeError as e: logger.error(f'Failed to load {platform_json}: ' + str(e)) return 1 else: @@ -403,11 +403,11 @@ def create_template(source_path: str, template_path = source_name template_path = template_path.replace('\\', '/') if not os.path.isabs(template_path): - default_templates_folder = registration.get_registered(default_folder='templates') + default_templates_folder = manifest.get_registered(default_folder='templates') template_path = f'{default_templates_folder}/{template_path}' logger.info(f'Template path not a full path. Using default templates folder {template_path}') if os.path.isdir(template_path): - logger.error(f'Template path {template_path} is already exists.') + logger.error(f'Template path {template_path} already exists.') return 1 # template name is now the last component of the template_path @@ -419,28 +419,28 @@ def create_template(source_path: str, return 1 if source_restricted_name and not source_restricted_path: - source_restricted_path = registration.get_registered(restricted_name=source_restricted_name) + source_restricted_path = manifest.get_registered(restricted_name=source_restricted_name) # source_restricted_path if source_restricted_path: source_restricted_path = source_restricted_path.replace('\\', '/') if not os.path.isabs(source_restricted_path): - engine_json = f'{registration.get_this_engine_path()}/engine.json' - if not registration.valid_o3de_engine_json(engine_json): + engine_json = f'{manifest.get_this_engine_path()}/engine.json' + if not validation.valid_o3de_engine_json(engine_json): logger.error(f"Engine json {engine_json} is not valid.") return 1 with open(engine_json) as s: try: engine_json_data = json.load(s) - except Exception as e: + except json.JSONDecodeError as e: logger.error(f"Failed to read engine json {engine_json}: {str(e)}") return 1 try: - engine_restricted = engine_json_data['restricted'] - except Exception as e: + engine_restricted = engine_json_data['restricted_name'] + except KeyError as e: logger.error(f"Engine json {engine_json} restricted not found.") return 1 - engine_restricted_folder = registration.get_registered(restricted_name=engine_restricted) + engine_restricted_folder = manifest.get_registered(restricted_name=engine_restricted) new_source_restricted_path = f'{engine_restricted_folder}/{source_restricted_path}' logger.info(f'Source restricted path {source_restricted_path} not a full path. We must assume this engines' f' restricted folder {new_source_restricted_path}') @@ -449,7 +449,7 @@ def create_template(source_path: str, return 1 if template_restricted_name and not template_restricted_path: - template_restricted_path = registration.get_registered(restricted_name=template_restricted_name) + template_restricted_path = manifest.get_registered(restricted_name=template_restricted_name) if not template_restricted_name: template_restricted_name = template_name @@ -458,7 +458,7 @@ def create_template(source_path: str, if template_restricted_path: template_restricted_path = template_restricted_path.replace('\\', '/') if not os.path.isabs(template_restricted_path): - default_templates_restricted_folder = registration.get_registered(restricted_name='templates') + default_templates_restricted_folder = manifest.get_registered(restricted_name='templates') new_template_restricted_path = f'{default_templates_restricted_folder}/{template_restricted_path}' logger.info(f'Template restricted path {template_restricted_path} not a full path. We must assume the' f' default templates restricted folder {new_template_restricted_path}') @@ -466,21 +466,21 @@ def create_template(source_path: str, if os.path.isdir(template_restricted_path): # see if this is already a restricted path, if it is get the "restricted_name" from the restricted json - # so we can set "restricted" to it for this template + # so we can set "restricted_name" to it for this template restricted_json = f'{template_restricted_path}/restricted.json' if os.path.isfile(restricted_json): - if not registration.valid_o3de_restricted_json(restricted_json): + if not validation.valid_o3de_restricted_json(restricted_json): logger.error(f'{restricted_json} is not valid.') return 1 with open(restricted_json, 'r') as s: try: restricted_json_data = json.load(s) - except Exception as e: + except json.JSONDecodeError as e: logger.error(f'Failed to load {restricted_json}: ' + str(e)) return 1 try: template_restricted_name = restricted_json_data['restricted_name'] - except Exception as e: + except KeyError as e: logger.error(f'Failed to read restricted_name from {restricted_json}') return 1 else: @@ -928,7 +928,7 @@ def create_template(source_path: str, json_data.update({'user_tags': [f"{template_name}"]}) json_data.update({'icon_path': "preview.png"}) if template_restricted_path: - json_data.update({'restricted': template_restricted_name}) + json_data.update({'restricted_name': template_restricted_name}) if template_restricted_platform_relative_path != '': json_data.update({'template_restricted_platform_relative_path': template_restricted_platform_relative_path}) json_data.update({'copyFiles': copy_files}) @@ -943,8 +943,7 @@ def create_template(source_path: str, s.write(json.dumps(json_data, indent=4)) # copy the default preview.png - this_script_parent = os.path.dirname(os.path.realpath(__file__)) - preview_png_src = f'{this_script_parent}/preview.png' + preview_png_src = f'{this_script_parent}/resources/preview.png' preview_png_dst = f'{template_path}/Template/preview.png' if not os.path.isfile(preview_png_dst): shutil.copy(preview_png_src, preview_png_dst) @@ -1048,7 +1047,7 @@ def create_from_template(destination_path: str, return 1 if template_name: - template_path = registration.get_registered(template_name=template_name) + template_path = manifest.get_registered(template_name=template_name) if not os.path.isdir(template_path): logger.error(f'Could not find the template {template_name}=>{template_path}') @@ -1059,7 +1058,7 @@ def create_from_template(destination_path: str, # the template.json should be in the template_path, make sure it's there a nd valid template_json = f'{template_path}/template.json' - if not registration.valid_o3de_template_json(template_json): + if not validation.valid_o3de_template_json(template_json): logger.error(f'Template json {template_path} is invalid.') return 1 @@ -1067,14 +1066,14 @@ def create_from_template(destination_path: str, with open(template_json) as s: try: template_json_data = json.load(s) - except Exception as e: + except KeyError as e: logger.error(f'Could read template json {template_json}: {str(e)}.') return 1 # read template name from the json try: template_name = template_json_data['template_name'] - except Exception as e: + except KeyError as e: logger.error(f'Could not read "template_name" from template json {template_json}: {str(e)}.') return 1 @@ -1082,57 +1081,57 @@ def create_from_template(destination_path: str, # see if the template itself specifies a restricted name if not template_restricted_name and not template_restricted_path: try: - template_json_restricted_name = template_json_data['restricted'] - except Exception as e: - # the template json doesn't have a 'restricted' element warn and use it - logger.info(f'The template does not specify a "restricted".') + template_json_restricted_name = template_json_data['restricted_name'] + except KeyError as e: + # the template json doesn't have a 'restricted_name' element warn and use it + logger.info(f'The template does not specify a "restricted_name".') else: template_restricted_name = template_json_restricted_name # if no restricted name or path we continue on as if there is no template restricted files. if template_restricted_name or template_restricted_path: # If the user specified a --template-restricted-name we need to check that against the templates - # 'restricted' if it has one and see if they match. If they match then we don't have a problem. + # 'restricted_name' if it has one and see if they match. If they match then we don't have a problem. # If they don't then we error out. If supplied but not present in the template we warn and use it. # If not supplied we set what's in the template. If not supplied and not in the template we continue # on as if there is no template restricted files. if template_restricted_name: # The user specified a --template-restricted-name try: - template_json_restricted_name = template_json_data['restricted'] - except Exception as e: - # the template json doesn't have a 'restricted' element warn and use it - logger.info(f'The template does not specify a "restricted".' + template_json_restricted_name = template_json_data['restricted_name'] + except KeyError as e: + # the template json doesn't have a 'restricted_name' element warn and use it + logger.info(f'The template does not specify a "restricted_name".' f' Using supplied {template_restricted_name}') else: if template_json_restricted_name != template_restricted_name: logger.error( f'The supplied --template-restricted-name {template_restricted_name} does not match the' - f' templates "restricted". Either the the --template-restricted-name is incorrect or the' - f' templates "restricted" is wrong. Note that since this template specifies "restricted" as' + f' templates "restricted_name". Either the the --template-restricted-name is incorrect or the' + f' templates "restricted_name" is wrong. Note that since this template specifies "restricted_name" as' f' {template_json_restricted_name}, --template-restricted-name need not be supplied.') - template_restricted_path = registration.get_registered(restricted_name=template_restricted_name) + template_restricted_path = manifest.get_registered(restricted_name=template_restricted_name) else: # The user has supplied the --template-restricted-path, see if that matches the template specifies. # If it does then we do not have a problem. If it doesn't match then error out. If not specified # in the template then warn and use the --template-restricted-path template_restricted_path = template_restricted_path.replace('\\', '/') try: - template_json_restricted_name = template_json_data['restricted'] - except Exception as e: - # the template json doesn't have a 'restricted' element warn and use it - logger.info(f'The template does not specify a "restricted".' + template_json_restricted_name = template_json_data['restricted_name'] + except KeyError as e: + # the template json doesn't have a 'restricted_name' element warn and use it + logger.info(f'The template does not specify a "restricted_name".' f' Using supplied {template_restricted_path}') else: - template_json_restricted_path = registration.get_registered( + template_json_restricted_path = manifest.get_registered( restricted_name=template_json_restricted_name) if template_json_restricted_path != template_restricted_path: logger.error( f'The supplied --template-restricted-path {template_restricted_path} does not match the' - f' templates "restricted" {template_restricted_name} => {template_json_restricted_path}.' + f' templates "restricted_name" {template_restricted_name} => {template_json_restricted_path}.' f' Either the the supplied --template-restricted-path is incorrect or the templates' - f' "restricted" is wrong. Note that since this template specifies "restricted" as' + f' "restricted_name" is wrong. Note that since this template specifies "restricted_name" as' f' {template_json_restricted_name} --template-restricted-path need not be supplied' f' and {template_json_restricted_path} will be used.') return 1 @@ -1154,7 +1153,7 @@ def create_from_template(destination_path: str, try: template_json_restricted_platform_relative_path = template_json_data[ 'restricted_platform_relative_path'] - except Exception as e: + except KeyError as e: # the template json doesn't have a 'restricted_platform_relative_path' element warn and use it logger.info(f'The template does not specify a "restricted_platform_relative_path".' f' Using {template_restricted_platform_relative_path}') @@ -1175,7 +1174,7 @@ def create_from_template(destination_path: str, try: template_restricted_platform_relative_path = template_json_data[ 'restricted_platform_relative_path'] - except Exception as e: + except KeyError as e: # The template json doesn't have a 'restricted_platform_relative_path' element, set empty string. template_restricted_platform_relative_path = '' @@ -1203,19 +1202,19 @@ def create_from_template(destination_path: str, # destination restricted name if destination_restricted_name: - destination_restricted_path = registration.get_registered(restricted_name=destination_restricted_name) + destination_restricted_path = manifest.get_registered(restricted_name=destination_restricted_name) # destination restricted path elif destination_restricted_path: destination_restricted_path = destination_restricted_path.replace('\\', '/') if os.path.isabs(destination_restricted_path): - restricted_default_path = registration.get_registered(default='restricted') + restricted_default_path = manifest.get_registered(default='restricted') new_destination_restricted_path = f'{restricted_default_path}/{destination_restricted_path}' logger.info(f'{destination_restricted_path} is not a full path, making it relative' f' to default restricted path = {new_destination_restricted_path}') destination_restricted_path = new_destination_restricted_path elif template_restricted_path: - restricted_default_path = registration.get_registered(default='restricted') + restricted_default_path = manifest.get_registered(default='restricted') logger.info(f'--destination-restricted-path is not specified, using default restricted path / destination name' f' = {restricted_default_path}') destination_restricted_path = restricted_default_path @@ -1280,6 +1279,7 @@ def create_from_template(destination_path: str, def create_project(project_path: str, + project_name: str = None, template_path: str = None, template_name: str = None, project_restricted_path: str = None, @@ -1298,6 +1298,7 @@ def create_project(project_path: str, Template instantiation specialization that makes all default assumptions for a Project template instantiation, reducing the effort needed in instancing a project :param project_path: the project path, can be absolute or relative to default projects path + :param project_name: the project name, defaults to project_path basename if not provided :param template_path: the path to the template you want to instance, can be absolute or relative to default templates path :param template_name: the name the registered template you want to instance, defaults to DefaultProject, resolves template_path :param project_restricted_path: path to the projects restricted folder, can be absolute or relative to the restricted='projects' @@ -1337,8 +1338,12 @@ def create_project(project_path: str, template_name = 'DefaultProject' if template_name and not template_path: - template_path = registration.get_registered(template_name=template_name) + template_path = manifest.get_registered(template_name=template_name) + if not template_path: + logger.error(f'Could not find the template path using name {template_name}.\n' + f'Has the engine been registered yet. It can be registered via the "o3de.py register --this-engine" command') + return 1 if not os.path.isdir(template_path): logger.error(f'Could not find the template {template_name}=>{template_path}') return 1 @@ -1348,7 +1353,7 @@ def create_project(project_path: str, # the template.json should be in the template_path, make sure it's there and valid template_json = f'{template_path}/template.json' - if not registration.valid_o3de_template_json(template_json): + if not validation.valid_o3de_template_json(template_json): logger.error(f'Template json {template_path} is not valid.') return 1 @@ -1356,14 +1361,14 @@ def create_project(project_path: str, with open(template_json) as s: try: template_json_data = json.load(s) - except Exception as e: + except json.JSONDecodeError as e: logger.error(f'Could read template json {template_json}: {str(e)}.') return 1 # read template name from the json try: template_name = template_json_data['template_name'] - except Exception as e: + except KeyError as e: logger.error(f'Could not read "template_name" from template json {template_json}: {str(e)}.') return 1 @@ -1371,57 +1376,57 @@ def create_project(project_path: str, # see if the template itself specifies a restricted name if not template_restricted_name and not template_restricted_path: try: - template_json_restricted_name = template_json_data['restricted'] - except Exception as e: - # the template json doesn't have a 'restricted' element warn and use it - logger.info(f'The template does not specify a "restricted".') + template_json_restricted_name = template_json_data['restricted_name'] + except KeyError as e: + # the template json doesn't have a 'restricted_name' element warn and use it + logger.info(f'The template does not specify a "restricted_name".') else: template_restricted_name = template_json_restricted_name # if no restricted name or path we continue on as if there is no template restricted files. if template_restricted_name or template_restricted_path: # If the user specified a --template-restricted-name we need to check that against the templates - # 'restricted' if it has one and see if they match. If they match then we don't have a problem. + # 'restricted_name' if it has one and see if they match. If they match then we don't have a problem. # If they don't then we error out. If supplied but not present in the template we warn and use it. # If not supplied we set what's in the template. If not supplied and not in the template we continue # on as if there is no template restricted files. if template_restricted_name and not template_restricted_path: # The user specified a --template-restricted-name try: - template_json_restricted_name = template_json_data['restricted'] - except Exception as e: - # the template json doesn't have a 'restricted' element warn and use it - logger.info(f'The template does not specify a "restricted".' + template_json_restricted_name = template_json_data['restricted_name'] + except KeyError as e: + # the template json doesn't have a 'restricted_name' element warn and use it + logger.info(f'The template does not specify a "restricted_name".' f' Using supplied {template_restricted_name}') else: if template_json_restricted_name != template_restricted_name: logger.error( f'The supplied --template-restricted-name {template_restricted_name} does not match the' - f' templates "restricted". Either the the --template-restricted-name is incorrect or the' - f' templates "restricted" is wrong. Note that since this template specifies "restricted" as' + f' templates "restricted_name". Either the the --template-restricted-name is incorrect or the' + f' templates "restricted_name" is wrong. Note that since this template specifies "restricted_name" as' f' {template_json_restricted_name}, --template-restricted-name need not be supplied.') - template_restricted_path = registration.get_registered(restricted_name=template_restricted_name) + template_restricted_path = manifest.get_registered(restricted_name=template_restricted_name) else: # The user has supplied the --template-restricted-path, see if that matches the template specifies. # If it does then we do not have a problem. If it doesn't match then error out. If not specified # in the template then warn and use the --template-restricted-path template_restricted_path = template_restricted_path.replace('\\', '/') try: - template_json_restricted_name = template_json_data['restricted'] - except Exception as e: - # the template json doesn't have a 'restricted' element warn and use it - logger.info(f'The template does not specify a "restricted".' + template_json_restricted_name = template_json_data['restricted_name'] + except KeyError as e: + # the template json doesn't have a 'restricted_name' element warn and use it + logger.info(f'The template does not specify a "restricted_name".' f' Using supplied {template_restricted_path}') else: - template_json_restricted_path = registration.get_registered( + template_json_restricted_path = manifest.get_registered( restricted_name=template_json_restricted_name) if template_json_restricted_path != template_restricted_path: logger.error( f'The supplied --template-restricted-path {template_restricted_path} does not match the' - f' templates "restricted" {template_restricted_name} => {template_json_restricted_path}.' + f' templates "restricted_name" {template_restricted_name} => {template_json_restricted_path}.' f' Either the the supplied --template-restricted-path is incorrect or the templates' - f' "restricted" is wrong. Note that since this template specifies "restricted" as' + f' "restricted_name" is wrong. Note that since this template specifies "restricted_name" as' f' {template_json_restricted_name} --template-restricted-path need not be supplied' f' and {template_json_restricted_path} will be used.') return 1 @@ -1442,7 +1447,7 @@ def create_project(project_path: str, try: template_json_restricted_platform_relative_path = template_json_data[ 'restricted_platform_relative_path'] - except Exception as e: + except KeyError as e: # the template json doesn't have a 'restricted_platform_relative_path' element warn and use it logger.info(f'The template does not specify a "restricted_platform_relative_path".' f' Using {template_restricted_platform_relative_path}') @@ -1463,7 +1468,7 @@ def create_project(project_path: str, try: template_restricted_platform_relative_path = template_json_data[ 'restricted_platform_relative_path'] - except Exception as e: + except KeyError as e: # The template json doesn't have a 'restricted_platform_relative_path' element, set empty string. template_restricted_platform_relative_path = '' if not template_restricted_platform_relative_path: @@ -1475,7 +1480,7 @@ def create_project(project_path: str, return 1 project_path = project_path.replace('\\', '/') if not os.path.isabs(project_path): - default_projects_folder = registration.get_registered(default_folder='projects') + default_projects_folder = manifest.get_registered(default_folder='projects') new_project_path = f'{default_projects_folder}/{project_path}' logger.info(f'Project Path {project_path} is not a full path, we must assume its relative' f' to default projects path = {new_project_path}') @@ -1486,29 +1491,34 @@ def create_project(project_path: str, elif not os.path.isdir(project_path): os.makedirs(project_path) - # project name is now the last component of the project_path - project_name = os.path.basename(project_path) + if not project_name: + # project name is now the last component of the project_path + project_name = os.path.basename(project_path) + + if not utils.validate_identifier(project_name): + logger.error(f'Project name must be fewer than 64 characters, contain only alphanumeric, "_" or "-" characters, and start with a letter. {project_name}') + return 1 # project name cannot be the same as a restricted platform name if project_name in restricted_platforms: - logger.error(f'Project path cannot be a restricted name. {project_name}') + logger.error(f'Project name cannot be a restricted name. {project_name}') return 1 # project restricted name if project_restricted_name and not project_restricted_path: - project_restricted_path = registration.get_registered(restricted_name=project_restricted_name) + project_restricted_path = manifest.get_registered(restricted_name=project_restricted_name) # project restricted path elif project_restricted_path: project_restricted_path = project_restricted_path.replace('\\', '/') if not os.path.isabs(project_restricted_path): - default_projects_restricted_folder = registration.get_registered(restricted_name='projects') + default_projects_restricted_folder = manifest.get_registered(restricted_name='projects') new_project_restricted_path = f'{default_projects_restricted_folder}/{project_restricted_path}' logger.info(f'Project restricted path {project_restricted_path} is not a full path, we must assume its' f' relative to default projects restricted path = {new_project_restricted_path}') project_restricted_path = new_project_restricted_path elif template_restricted_path: - project_restricted_default_path = registration.get_registered(restricted_name='projects') + project_restricted_default_path = manifest.get_registered(restricted_name='projects') logger.info(f'--project-restricted-path is not specified, using default project restricted path / project name' f' = {project_restricted_default_path}') project_restricted_path = project_restricted_default_path @@ -1585,7 +1595,7 @@ def create_project(project_path: str, # read the restricted_name from the projects restricted.json restricted_json = f"{project_restricted_path}/restricted.json".replace('//', '/') if os.path.isfile(restricted_json): - if not registration.valid_o3de_restricted_json(restricted_json): + if not validation.valid_o3de_restricted_json(restricted_json): logger.error(f'Restricted json {restricted_json} is not valid.') return 1 else: @@ -1597,35 +1607,35 @@ def create_project(project_path: str, with open(restricted_json, 'r') as s: try: restricted_json_data = json.load(s) - except Exception as e: + except json.JSONDecodeError as e: logger.error(f'Failed to load restricted json {restricted_json}.') return 1 try: restricted_name = restricted_json_data["restricted_name"] - except Exception as e: + except KeyError as e: logger.error(f'Failed to read "restricted_name" from restricted json {restricted_json}.') return 1 - # set the "restricted": "restricted_name" element of the project.json + # set the "restricted_name": "restricted_name" element of the project.json project_json = f"{project_path}/project.json".replace('//', '/') - if not registration.valid_o3de_project_json(project_json): + if not validation.valid_o3de_project_json(project_json): logger.error(f'Project json {project_json} is not valid.') return 1 with open(project_json, 'r') as s: try: project_json_data = json.load(s) - except Exception as e: + except json.JSONDecodeError as e: logger.error(f'Failed to load project json {project_json}.') return 1 - project_json_data.update({"restricted": restricted_name}) + project_json_data.update({"restricted_name": restricted_name}) os.unlink(project_json) with open(project_json, 'w') as s: try: s.write(json.dumps(project_json_data, indent=4)) - except Exception as e: + except OSError as e: logger.error(f'Failed to write project json {project_json}.') return 1 @@ -1652,10 +1662,22 @@ def create_project(project_path: str, d.write('# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n') d.write('# {END_LICENSE}\n') - # copy the o3de_manifest.cmake into the project root - engine_path = registration.get_this_engine_path() - o3de_manifest_cmake = f'{engine_path}/cmake/o3de_manifest.cmake' - shutil.copy(o3de_manifest_cmake, project_path) + # set the "engine" element of the project.json + engine_json_data = manifest.get_engine_json_data(engine_path=manifest.get_this_engine_path()) + try: + engine_name = engine_json_data['engine_name'] + except KeyError as e: + logger.error(f"engine_name for this engine not found in engine.json.") + return 1 + + project_json_data = manifest.get_project_json_data(project_path=project_path) + project_json_data.update({"engine": engine_name}) + with open(project_json, 'w') as s: + try: + s.write(json.dumps(project_json_data, indent=4)) + except OSError as e: + logger.error(f'Failed to write project json at {project_path}.') + return 1 return 0 @@ -1718,7 +1740,7 @@ def create_gem(gem_path: str, template_name = 'DefaultGem' if template_name and not template_path: - template_path = registration.get_registered(template_name=template_name) + template_path = manifest.get_registered(template_name=template_name) if not os.path.isdir(template_path): logger.error(f'Could not find the template {template_name}=>{template_path}') @@ -1729,7 +1751,7 @@ def create_gem(gem_path: str, # the template.json should be in the template_path, make sure it's there and valid template_json = f'{template_path}/template.json' - if not registration.valid_o3de_template_json(template_json): + if not validation.valid_o3de_template_json(template_json): logger.error(f'Template json {template_path} is not valid.') return 1 @@ -1737,14 +1759,14 @@ def create_gem(gem_path: str, with open(template_json) as s: try: template_json_data = json.load(s) - except Exception as e: + except json.JSONDecodeError as e: logger.error(f'Could read template json {template_json}: {str(e)}.') return 1 # read template name from the json try: template_name = template_json_data['template_name'] - except Exception as e: + except KeyError as e: logger.error(f'Could not read "template_name" from template json {template_json}: {str(e)}.') return 1 @@ -1752,56 +1774,56 @@ def create_gem(gem_path: str, # see if the template itself specifies a restricted name if not template_restricted_name and not template_restricted_path: try: - template_json_restricted_name = template_json_data['restricted'] - except Exception as e: - # the template json doesn't have a 'restricted' element warn and use it - logger.info(f'The template does not specify a "restricted".') + template_json_restricted_name = template_json_data['restricted_name'] + except KeyError as e: + # the template json doesn't have a 'restricted_name' element warn and use it + logger.info(f'The template does not specify a "restricted_name".') else: template_restricted_name = template_json_restricted_name # if no restricted name or path we continue on as if there is no template restricted files. if template_restricted_name or template_restricted_path: - # if the user specified a --template-restricted-name we need to check that against the templates 'restricted' + # if the user specified a --template-restricted-name we need to check that against the templates 'restricted_name' # if it has one and see if they match. If they match then we don't have a problem. If they don't then we error # out. If supplied but not present in the template we warn and use it. If not supplied we set what's in the # template. If not supplied and not in the template we continue on as if there is no template restricted files. if template_restricted_name and not template_restricted_path: # The user specified a --template-restricted-name try: - template_json_restricted_name = template_json_data['restricted'] - except Exception as e: - # the template json doesn't have a 'restricted' element warn and use it - logger.info(f'The template does not specify a "restricted".' + template_json_restricted_name = template_json_data['restricted_name'] + except KeyError as e: + # the template json doesn't have a 'restricted_name' element warn and use it + logger.info(f'The template does not specify a "restricted_name".' f' Using supplied {template_restricted_name}') else: if template_json_restricted_name != template_restricted_name: logger.error( f'The supplied --template-restricted-name {template_restricted_name} does not match the' - f' templates "restricted". Either the the --template-restricted-name is incorrect or the' - f' templates "restricted" is wrong. Note that since this template specifies "restricted" as' + f' templates "restricted_name". Either the the --template-restricted-name is incorrect or the' + f' templates "restricted_name" is wrong. Note that since this template specifies "restricted_name" as' f' {template_json_restricted_name}, --template-restricted-name need not be supplied.') - template_restricted_path = registration.get_registered(restricted_name=template_restricted_name) + template_restricted_path = manifest.get_registered(restricted_name=template_restricted_name) else: # The user has supplied the --template-restricted-path, see if that matches the template specifies. # If it does then we do not have a problem. If it doesn't match then error out. If not specified # in the template then warn and use the --template-restricted-path template_restricted_path = template_restricted_path.replace('\\', '/') try: - template_json_restricted_name = template_json_data['restricted'] - except Exception as e: - # the template json doesn't have a 'restricted' element warn and use it - logger.info(f'The template does not specify a "restricted".' + template_json_restricted_name = template_json_data['restricted_name'] + except KeyError as e: + # the template json doesn't have a 'restricted_name' element warn and use it + logger.info(f'The template does not specify a "restricted_name".' f' Using supplied {template_restricted_path}') else: - template_json_restricted_path = registration.get_registered( + template_json_restricted_path = manifest.get_registered( restricted_name=template_json_restricted_name) if template_json_restricted_path != template_restricted_path: logger.error( f'The supplied --template-restricted-path {template_restricted_path} does not match the' - f' templates "restricted" {template_restricted_name} => {template_json_restricted_path}.' + f' templates "restricted_name" {template_restricted_name} => {template_json_restricted_path}.' f' Either the the supplied --template-restricted-path is incorrect or the templates' - f' "restricted" is wrong. Note that since this template specifies "restricted" as' + f' "restricted_name" is wrong. Note that since this template specifies "restricted_name" as' f' {template_json_restricted_name} --template-restricted-path need not be supplied' f' and {template_json_restricted_path} will be used.') return 1 @@ -1821,7 +1843,7 @@ def create_gem(gem_path: str, try: template_json_restricted_platform_relative_path = template_json_data[ 'restricted_platform_relative_path'] - except Exception as e: + except KeyError as e: # the template json doesn't have a 'restricted_platform_relative_path' element warn and use it logger.info(f'The template does not specify a "restricted_platform_relative_path".' f' Using {template_restricted_platform_relative_path}') @@ -1842,7 +1864,7 @@ def create_gem(gem_path: str, try: template_restricted_platform_relative_path = template_json_data[ 'restricted_platform_relative_path'] - except Exception as e: + except KeyError as e: # The template json doesn't have a 'restricted_platform_relative_path' element, set empty string. template_restricted_platform_relative_path = '' if not template_restricted_platform_relative_path: @@ -1854,7 +1876,7 @@ def create_gem(gem_path: str, return 1 gem_path = gem_path.replace('\\', '/') if not os.path.isabs(gem_path): - default_gems_folder = registration.get_registered(default_folder='gems') + default_gems_folder = manifest.get_registered(default_folder='gems') new_gem_path = f'{default_gems_folder}/{gem_path}' logger.info(f'Gem Path {gem_path} is not a full path, we must assume its relative' f' to default gems path = {new_gem_path}') @@ -1875,19 +1897,19 @@ def create_gem(gem_path: str, # gem restricted name if gem_restricted_name and not gem_restricted_path: - gem_restricted_path = registration.get_registered(restricted_name=gem_restricted_name) + gem_restricted_path = manifest.get_registered(restricted_name=gem_restricted_name) # gem restricted path elif gem_restricted_path: gem_restricted_path = gem_restricted_path.replace('\\', '/') if not os.path.isabs(gem_restricted_path): - default_gems_restricted_folder = registration.get_registered(restricted_name='gems') + default_gems_restricted_folder = manifest.get_registered(restricted_name='gems') new_gem_restricted_path = f'{default_gems_restricted_folder}/{gem_restricted_path}' logger.info(f'Gem restricted path {gem_restricted_path} is not a full path, we must assume its' f' relative to default gems restricted path = {new_gem_restricted_path}') gem_restricted_path = new_gem_restricted_path elif template_restricted_path: - gem_restricted_default_path = registration.get_registered(restricted_name='gems') + gem_restricted_default_path = manifest.get_registered(restricted_name='gems') logger.info(f'--gem-restricted-path is not specified, using default gem restricted path / gem name' f' = {gem_restricted_default_path}') gem_restricted_path = gem_restricted_default_path @@ -1964,7 +1986,7 @@ def create_gem(gem_path: str, # read the restricted_name from the gems restricted.json restricted_json = f"{gem_restricted_path}/restricted.json".replace('//', '/') if os.path.isfile(restricted_json): - if not registration.valid_o3de_restricted_json(restricted_json): + if not validation.valid_o3de_restricted_json(restricted_json): logger.error(f'Restricted json {restricted_json} is not valid.') return 1 else: @@ -1976,35 +1998,35 @@ def create_gem(gem_path: str, with open(restricted_json, 'r') as s: try: restricted_json_data = json.load(s) - except Exception as e: + except json.JSONDecodeError as e: logger.error(f'Failed to load restricted json {restricted_json}.') return 1 try: restricted_name = restricted_json_data["restricted_name"] - except Exception as e: + except KeyError as e: logger.error(f'Failed to read "restricted_name" from restricted json {restricted_json}.') return 1 - # set the "restricted": "restricted_name" element of the gem.json + # set the "restricted_name": "restricted_name" element of the gem.json gem_json = f"{gem_path}/gem.json".replace('//', '/') - if not registration.valid_o3de_gem_json(gem_json): + if not validation.valid_o3de_gem_json(gem_json): logger.error(f'Gem json {gem_json} is not valid.') return 1 with open(gem_json, 'r') as s: try: gem_json_data = json.load(s) - except Exception as e: + except json.JSONDecodeError as e: logger.error(f'Failed to load gem json {gem_json}.') return 1 - gem_json_data.update({"restricted": restricted_name}) + gem_json_data.update({"restricted_name": restricted_name}) os.unlink(gem_json) with open(gem_json, 'w') as s: try: s.write(json.dumps(gem_json_data, indent=4)) - except Exception as e: + except OSError as e: logger.error(f'Failed to write project json {gem_json}.') return 1 @@ -2064,6 +2086,7 @@ def _run_create_from_template(args: argparse) -> int: def _run_create_project(args: argparse) -> int: return create_project(args.project_path, + args.project_name, args.template_path, args.template_name, args.project_restricted_path, @@ -2098,15 +2121,14 @@ def _run_create_gem(args: argparse) -> int: args.module_id) -def add_args(parser, subparsers) -> None: +def add_args(subparsers) -> None: """ add_args is called to add expected parser arguments and subparsers arguments to each command such that it can be invoked locally or aggregated by a central python file. - Ex. Directly run from this file alone with: python engine_template.py create_gem --gem-path TestGem + Ex. Directly run from this file alone with: python engine_template.py create-gem --gem-path TestGem OR o3de.py can aggregate commands by importing engine_template, - call add_args and execute: python o3de.py create_gem --gem-path TestGem - :param parser: the caller instantiates a parser and passes it in here + call add_args and execute: python o3de.py create-gem --gem-path TestGem :param subparsers: the caller instantiates subparsers and passes it in here """ # turn a directory into a template @@ -2116,7 +2138,7 @@ def add_args(parser, subparsers) -> None: create_template_subparser.add_argument('-tp', '--template-path', type=str, required=False, help='The path to the template to create, can be absolute or relative' ' to default templates path') - group = create_template_subparser.add_mutually_exclusive_group(required=True) + group = create_template_subparser.add_mutually_exclusive_group(required=False) group.add_argument('-srp', '--source-restricted-path', type=str, required=False, default=None, help='The path to the source restricted folder.') @@ -2125,7 +2147,7 @@ def add_args(parser, subparsers) -> None: help='The name of the source restricted folder. If supplied this will resolve' ' the --source-restricted-path.') - group = create_template_subparser.add_mutually_exclusive_group(required=True) + group = create_template_subparser.add_mutually_exclusive_group(required=False) group.add_argument('-trp', '--template-restricted-path', type=str, required=False, default=None, help='The path to the templates restricted folder.') @@ -2248,10 +2270,15 @@ def add_args(parser, subparsers) -> None: # creation of a project from a template (like create from template but makes project assumptions) create_project_subparser = subparsers.add_parser('create-project') create_project_subparser.add_argument('-pp', '--project-path', type=str, required=True, - help='The name of the project you wish to create from the template,' + help='The location of the project you wish to create from the template,' ' can be an absolute path or dev root relative.' ' Ex. C:/o3de/TestProject' - ' TestProject = ') + ' TestProject = if --project-name not provided') + create_project_subparser.add_argument('-pn', '--project-name', type=str, required=False, + help='The name of the project you wish to use, must be alphanumeric, ' + ' and can contain _ and - characters.' + ' If no name is provided, will use last component of project path.' + ' Ex. New_Project-123') group = create_project_subparser.add_mutually_exclusive_group(required=False) group.add_argument('-tp', '--template-path', type=str, required=False, @@ -2423,16 +2450,16 @@ if __name__ == "__main__": the_parser = argparse.ArgumentParser() # add subparsers - the_subparsers = the_parser.add_subparsers(help='sub-command help') + the_subparsers = the_parser.add_subparsers(help='sub-command help', dest='command', required=True) # add args to the parser - add_args(the_parser, the_subparsers) + add_args(the_subparsers) # parse args the_args = the_parser.parse_args() # run - ret = the_args.func(the_args) + ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1 # return sys.exit(ret) diff --git a/scripts/o3de/o3de/get_registration.py b/scripts/o3de/o3de/get_registration.py new file mode 100644 index 0000000000..d51600826c --- /dev/null +++ b/scripts/o3de/o3de/get_registration.py @@ -0,0 +1,96 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import argparse +import pathlib +import sys + +from o3de import manifest + +def _run_get_registered(args: argparse) -> str or pathlib.Path: + if args.override_home_folder: + manifest.override_home_folder = args.override_home_folder + + return manifest.get_registered(args.engine_name, + args.project_name, + args.gem_name, + args.template_name, + args.default_folder, + args.repo_name, + args.restricted_name) + + +def add_parser_args(parser): + """ + add_parser_args is called to add arguments to each command such that it can be + invoked locally or added by a central python file. + Ex. Directly run from this file alone with: python get_registration.py --engine-name "o3de" + :param parser: the caller passes an argparse parser like instance to this method + """ + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-en', '--engine-name', type=str, required=False, + help='Engine name.') + group.add_argument('-pn', '--project-name', type=str, required=False, + help='Project name.') + group.add_argument('-gn', '--gem-name', type=str, required=False, + help='Gem name.') + group.add_argument('-tn', '--template-name', type=str, required=False, + help='Template name.') + group.add_argument('-df', '--default-folder', type=str, required=False, + choices=['engines', 'projects', 'gems', 'templates', 'restricted'], + help='The default folders for o3de.') + group.add_argument('-rn', '--repo-name', type=str, required=False, + help='Repo name.') + group.add_argument('-rsn', '--restricted-name', type=str, required=False, + help='Restricted name.') + + parser.add_argument('-ohf', '--override-home-folder', type=str, required=False, + help='By default the home folder is the user folder, override it to this folder.') + + parser.set_defaults(func=_run_get_registered) + + +def add_args(subparsers) -> None: + """ + add_args is called to add subparsers arguments to each command such that it can be + a central python file such as o3de.py. + It can be run from the o3de.py script as follows + call add_args and execute: python o3de.py get-registered --engine-name "o3de" + :param subparsers: the caller instantiates subparsers and passes it in here + """ + get_registered_subparser = subparsers.add_parser('get-registered') + add_parser_args(get_registered_subparser) + + +def main(): + """ + Runs get_registration.py script as standalone script + """ + # parse the command line args + the_parser = argparse.ArgumentParser() + + # add subparsers + + # add args to the parser + add_parser_args(the_parser) + + # parse args + the_args = the_parser.parse_args() + + # run + ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1 + + # return + sys.exit(ret) + + +if __name__ == "__main__": + main() diff --git a/scripts/o3de/o3de/global_project.py b/scripts/o3de/o3de/global_project.py new file mode 100644 index 0000000000..d165510250 --- /dev/null +++ b/scripts/o3de/o3de/global_project.py @@ -0,0 +1,214 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import argparse +import logging +import os +import sys +import re +import pathlib +import json + +from o3de import manifest, validation + +logger = logging.getLogger() +logging.basicConfig() + +DEFAULT_BOOTSTRAP_SETREG = pathlib.Path('~/.o3de/Registry/bootstrap.setreg').expanduser() +PROJECT_PATH_KEY = ('Amazon', 'AzCore', 'Bootstrap', 'project_path') + +def get_json_data(input_path: pathlib.Path): + setreg_json_data = {} + # If the output_path exist validate that it is a valid json file + if input_path.is_file(): + with input_path.open('r') as f: + try: + setreg_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.error(f'The file: {input_path} is not a valid json file: {str(e)}') + + return setreg_json_data + +def set_global_project(output_path: pathlib.Path, + project_name: str = None, + project_path: pathlib.Path = None, + force: bool = False) -> int: + """ + Adds a project path the a settings registry file in the users ~/.o3de/Registry directory + :param output_path: path to .setreg file to store project_path value into + :param project_name: name of the project to lookup path for + :param project_path: path to the project to add to .setreg file + :param force: if set, the project path will be set within the .setreg file regardless of if the path doesn't exist + :return: 0 for success or non 0 failure code + """ + # we need either a project name or path + if not project_name and not project_path: + logger.error(f'Must either specify a Project path or Project Name.') + return 1 + + # if project name resolve it into a path + if project_name and not project_path: + project_path = manifest.get_registered(project_name=project_name) + + if not project_path: + logger.error( + f'The project name has been supplied. Unable to locate project path from the registered manifest.json files:' + f' {str(pathlib.Path("~/.o3de/o3de_manifest.json").expanduser())}, engine.json\n' + 'A The --project-path parameter can be used directly to skip checking the manifest') + return 1 + + # Only perform project path validations when force=False + if not force: + if not project_path.is_dir(): + logger.error(f'Project path {project_path} is not a folder.') + return 1 + + # Validate that the supplied path points contains a valid project.json + if not validation.valid_o3de_project_json(project_path / 'project.json'): + logger.error(f'The supplied project path does not contain a valid project.json.\n' + f'The Path will not be set') + return 1 + + # If the output_path exist validate that it is a valid json file and read it's json data + setreg_json_data = get_json_data(output_path) + if output_path.is_file(): + with output_path.open('r') as f: + try: + setreg_json_data = json.load(f) + except (json.JSONDecodeError) as e: + logger.error(f'The output file: {output_path} is not a valid json file: {str(e)}') + return 1 + + # Add a json dictionary that will be merged with any existing json data from the .setreg file + merge_json_data = {} + json_object_iter = merge_json_data + for json_key in PROJECT_PATH_KEY[:-1]: + # Add the parent json object for the key to update + json_object_iter = json_object_iter.setdefault(json_key, {}) + + # Set the project path value here + json_object_iter[PROJECT_PATH_KEY[-1]] = project_path.as_posix() + setreg_json_data.update(merge_json_data) + + # Create the parent directories + if output_path.parent: + output_path.parent.mkdir(parents=True, exist_ok=True) + try: + with output_path.open('w') as s: + s.write(json.dumps(setreg_json_data, indent=4) + '\n') + except OSError as e: + logger.error(f'Failed to write project path {project_path} to file {output_path}: {str(e)}') + return 1 + + return 0 + + +def get_global_project(input_path: pathlib.Path) -> pathlib.Path or None: + """ + Retrieves the /Amazon/AzCore/Bootstrap/project_path key from the supplied file path + :return: project_path or None on failure + """ + setreg_json_data = get_json_data(input_path) + + try: + # Iterate over each element of the tuple and read the json key from each successive json object + json_object_iter = setreg_json_data + for json_key in PROJECT_PATH_KEY: + json_object_iter = json_object_iter[json_key] + except KeyError as e: + logger.error(f'Cannot read key /{"/".join(PROJECT_PATH_KEY)} from file {input_path.as_posix()}: {str(e)}') + else: + project_path = json_object_iter + return pathlib.Path(project_path).resolve() + return None + +def _run_get_global_project(args: argparse) -> int: + project_path = get_global_project(args.input_path) + if project_path: + print(project_path.as_posix()) + return 0 + return 1 + + +def _run_set_global_project(args: argparse) -> int: + return set_global_project(args.output_path, + args.project_name, + args.project_path, + args.force) + + +def add_parser_args(get_project_parser, set_project_parser): + """ + add_parser_args is called to add arguments to each command such that it can be + invoked locally or added by a central python file. + Ex. Directly run from this file alone with: python global_project.py --project-path "D:/TestProject" + :param parser: the caller passes an argparse parser like instance to this method + """ + + # get-current-project + get_project_parser.add_argument('-i', '--input-path', type=pathlib.Path, required=False, default=DEFAULT_BOOTSTRAP_SETREG, + help=f'Optional path to file to read /{"/".join(PROJECT_PATH_KEY)} key from.' + f' If not supplied, then {DEFAULT_BOOTSTRAP_SETREG} is used instead') + get_project_parser.set_defaults(func=_run_get_global_project) + + # set-current-project + group = set_project_parser.add_mutually_exclusive_group(required=True) + group.add_argument('-pp', '--project-path', type=pathlib.Path, required=False, + help='The path to the project.') + group.add_argument('-pn', '--project-name', type=str, required=False, + help='The name of the project.') + set_project_parser.add_argument('-o', '--output-path', type=pathlib.Path, required=False, + default=DEFAULT_BOOTSTRAP_SETREG, + help=f'Optional path to output file to write project_path key to. ' + f'If not supplied, then {DEFAULT_BOOTSTRAP_SETREG} is used instead') + set_project_parser.add_argument('-f', '--force', action='store_true', default=False, + help=f'Force the setting of the project path in the supplied setreg file') + set_project_parser.set_defaults(func=_run_set_global_project) + +def add_args(subparsers) -> None: + """ + add_args is called to add subparsers arguments to each command such that it can be + a central python file such as o3de.py. + It can be run from the o3de.py script as follows + call add_args and execute: python o3de.py set-global-project --project-path "D:/TestProject" + :param subparsers: the caller instantiates subparsers and passes it in here + """ + get_project_subparser = subparsers.add_parser('get-global-project') + set_project_subparser = subparsers.add_parser('set-global-project') + add_parser_args(get_project_subparser, set_project_subparser) + + +def main(): + """ + Runs this script as standalone script + """ + # parse the command line args + the_parser = argparse.ArgumentParser() + + # add subparsers + project_subparsers = the_parser.add_subparsers(help="Commands for modifying the project path in the user's home" + " setreg files") + + # add args to the parser + add_args(project_subparsers) + + # parse args + the_args = the_parser.parse_args() + + # run + ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1 + + # return + sys.exit(ret) + + +if __name__ == "__main__": + main() diff --git a/scripts/o3de/o3de/manifest.py b/scripts/o3de/o3de/manifest.py new file mode 100644 index 0000000000..2a7e5bba11 --- /dev/null +++ b/scripts/o3de/o3de/manifest.py @@ -0,0 +1,675 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +""" +Contains functions for data from json files such as the o3de_manifests.json, engine.json, project.json, etc... +""" + +import json +import logging +import os +import pathlib + +from o3de import validation + +logger = logging.getLogger() +logging.basicConfig() + +# Directory methods +override_home_folder = None + + +def get_this_engine_path() -> pathlib.Path: + return pathlib.Path(os.path.realpath(__file__)).parents[3].resolve() + + +def get_home_folder() -> pathlib.Path: + if override_home_folder: + return pathlib.Path(override_home_folder).resolve() + else: + return pathlib.Path(os.path.expanduser("~")).resolve() + + +def get_o3de_folder() -> pathlib.Path: + o3de_folder = get_home_folder() / '.o3de' + o3de_folder.mkdir(parents=True, exist_ok=True) + return o3de_folder + + +def get_o3de_registry_folder() -> pathlib.Path: + registry_folder = get_o3de_folder() / 'Registry' + registry_folder.mkdir(parents=True, exist_ok=True) + return registry_folder + + +def get_o3de_cache_folder() -> pathlib.Path: + cache_folder = get_o3de_folder() / 'Cache' + cache_folder.mkdir(parents=True, exist_ok=True) + return cache_folder + + +def get_o3de_download_folder() -> pathlib.Path: + download_folder = get_o3de_folder() / 'Download' + download_folder.mkdir(parents=True, exist_ok=True) + return download_folder + + +def get_o3de_engines_folder() -> pathlib.Path: + engines_folder = get_o3de_folder() / 'Engines' + engines_folder.mkdir(parents=True, exist_ok=True) + return engines_folder + + +def get_o3de_projects_folder() -> pathlib.Path: + projects_folder = get_o3de_folder() / 'Projects' + projects_folder.mkdir(parents=True, exist_ok=True) + return projects_folder + + +def get_o3de_gems_folder() -> pathlib.Path: + gems_folder = get_o3de_folder() / 'Gems' + gems_folder.mkdir(parents=True, exist_ok=True) + return gems_folder + + +def get_o3de_templates_folder() -> pathlib.Path: + templates_folder = get_o3de_folder() / 'Templates' + templates_folder.mkdir(parents=True, exist_ok=True) + return templates_folder + + +def get_o3de_restricted_folder() -> pathlib.Path: + restricted_folder = get_o3de_folder() / 'Restricted' + restricted_folder.mkdir(parents=True, exist_ok=True) + return restricted_folder + + +def get_o3de_logs_folder() -> pathlib.Path: + logs_folder = get_o3de_folder() / 'Logs' + logs_folder.mkdir(parents=True, exist_ok=True) + return logs_folder + + +# o3de manifest file methods +def get_o3de_manifest() -> pathlib.Path: + manifest_path = get_o3de_folder() / 'o3de_manifest.json' + if not manifest_path.is_file(): + username = os.path.split(get_home_folder())[-1] + + o3de_folder = get_o3de_folder() + default_registry_folder = get_o3de_registry_folder() + default_cache_folder = get_o3de_cache_folder() + default_downloads_folder = get_o3de_download_folder() + default_logs_folder = get_o3de_logs_folder() + default_engines_folder = get_o3de_engines_folder() + default_projects_folder = get_o3de_projects_folder() + default_gems_folder = get_o3de_gems_folder() + default_templates_folder = get_o3de_templates_folder() + default_restricted_folder = get_o3de_restricted_folder() + + default_projects_restricted_folder = default_projects_folder / 'Restricted' + default_projects_restricted_folder.mkdir(parents=True, exist_ok=True) + default_gems_restricted_folder = default_gems_folder / 'Restricted' + default_gems_restricted_folder.mkdir(parents=True, exist_ok=True) + default_templates_restricted_folder = default_templates_folder / 'Restricted' + default_templates_restricted_folder.mkdir(parents=True, exist_ok=True) + + json_data = {} + json_data.update({'o3de_manifest_name': f'{username}'}) + json_data.update({'origin': o3de_folder.as_posix()}) + json_data.update({'default_engines_folder': default_engines_folder.as_posix()}) + json_data.update({'default_projects_folder': default_projects_folder.as_posix()}) + json_data.update({'default_gems_folder': default_gems_folder.as_posix()}) + json_data.update({'default_templates_folder': default_templates_folder.as_posix()}) + json_data.update({'default_restricted_folder': default_restricted_folder.as_posix()}) + + json_data.update({'projects': []}) + json_data.update({'external_subdirectories': []}) + json_data.update({'templates': []}) + json_data.update({'restricted': []}) + json_data.update({'repos': []}) + json_data.update({'engines': []}) + + default_restricted_folder_json = default_restricted_folder / 'restricted.json' + if not default_restricted_folder_json.is_file(): + with default_restricted_folder_json.open('w') as s: + restricted_json_data = {} + restricted_json_data.update({'restricted_name': 'o3de'}) + s.write(json.dumps(restricted_json_data, indent=4) + '\n') + json_data.update({'default_restricted_folder': default_restricted_folder.as_posix()}) + + default_projects_restricted_folder_json = default_projects_restricted_folder / 'restricted.json' + if not default_projects_restricted_folder_json.is_file(): + with default_projects_restricted_folder_json.open('w') as s: + restricted_json_data = {} + restricted_json_data.update({'restricted_name': 'projects'}) + s.write(json.dumps(restricted_json_data, indent=4) + '\n') + + default_gems_restricted_folder_json = default_gems_restricted_folder / 'restricted.json' + if not default_gems_restricted_folder_json.is_file(): + with default_gems_restricted_folder_json.open('w') as s: + restricted_json_data = {} + restricted_json_data.update({'restricted_name': 'gems'}) + s.write(json.dumps(restricted_json_data, indent=4) + '\n') + + default_templates_restricted_folder_json = default_templates_restricted_folder / 'restricted.json' + if not default_templates_restricted_folder_json.is_file(): + with default_templates_restricted_folder_json.open('w') as s: + restricted_json_data = {} + restricted_json_data.update({'restricted_name': 'templates'}) + s.write(json.dumps(restricted_json_data, indent=4) + '\n') + + with manifest_path.open('w') as s: + s.write(json.dumps(json_data, indent=4) + '\n') + + return manifest_path + + +def load_o3de_manifest(manifest_path: pathlib.Path = None) -> dict: + """ + Loads supplied manifest file or ~/.o3de/o3de_manifest.json if None + + :param manifest_path: optional path to manifest file to load + """ + if not manifest_path: + manifest_path = get_o3de_manifest() + with manifest_path.open('r') as f: + try: + json_data = json.load(f) + except json.JSONDecodeError as e: + logger.error(f'Manifest json failed to load: {str(e)}') + return {} + else: + return json_data + + +def save_o3de_manifest(json_data: dict, manifest_path: pathlib.Path = None) -> None: + """ + Save the json dictionary to the supplied manifest file or ~/.o3de/o3de_manifest.json if None + + :param json_data: dictionary to save in json format at the file path + :param manifest_path: optional path to manifest file to save + """ + if not manifest_path: + manifest_path = get_o3de_manifest() + with manifest_path.open('w') as s: + try: + s.write(json.dumps(json_data, indent=4) + '\n') + except OSError as e: + logger.error(f'Manifest json failed to save: {str(e)}') + + +# Data query methods +def get_this_engine() -> dict: + json_data = load_o3de_manifest() + engine_data = find_engine_data(json_data) + return engine_data + + +def get_engines() -> list: + json_data = load_o3de_manifest() + return json_data['engines'] if 'engines' in json_data else [] + + +def get_projects() -> list: + json_data = load_o3de_manifest() + return json_data['projects'] if 'projects' in json_data else [] + + +def get_gems() -> list: + def is_gem_subdirectory(subdir): + return (pathlib.Path(subdir) / 'gem.json').exists() + + external_subdirs = get_external_subdirectories() + return list(filter(is_gem_subdirectory, external_subdirs)) if external_subdirs else [] + + +def get_external_subdirectories() -> list: + json_data = load_o3de_manifest() + return json_data['external_subdirectories'] if 'external_subdirectories' in json_data else [] + + +def get_templates() -> list: + json_data = load_o3de_manifest() + return json_data['templates'] if 'templates' in json_data else [] + + +def get_restricted() -> list: + json_data = load_o3de_manifest() + return json_data['restricted'] if 'restricted' in json_data else [] + + +def get_repos() -> list: + json_data = load_o3de_manifest() + return json_data['repos'] if 'repos' in json_data else [] + +# engine.json queries +def get_engine_projects() -> list: + engine_path = get_this_engine_path() + engine_object = get_engine_json_data(engine_path=engine_path) + return list(map(lambda rel_path: (pathlib.Path(engine_path) / rel_path).as_posix(), + engine_object['projects'])) if 'projects' in engine_object else [] + + +def get_engine_gems() -> list: + def is_gem_subdirectory(subdir): + return (pathlib.Path(subdir) / 'gem.json').exists() + + external_subdirs = get_engine_external_subdirectories() + return list(filter(is_gem_subdirectory, external_subdirs)) if external_subdirs else [] + + +def get_engine_external_subdirectories() -> list: + engine_path = get_this_engine_path() + engine_object = get_engine_json_data(engine_path=engine_path) + return list(map(lambda rel_path: (pathlib.Path(engine_path) / rel_path).as_posix(), + engine_object['external_subdirectories'])) if 'external_subdirectories' in engine_object else [] + + +def get_engine_templates() -> list: + engine_path = get_this_engine_path() + engine_object = get_engine_json_data(engine_path=engine_path) + return list(map(lambda rel_path: (pathlib.Path(engine_path) / rel_path).as_posix(), + engine_object['templates'])) + + +def get_engine_restricted() -> list: + engine_path = get_this_engine_path() + engine_object = get_engine_json_data(engine_path=engine_path) + return list(map(lambda rel_path: (pathlib.Path(engine_path) / rel_path).as_posix(), + engine_object['restricted'])) if 'restricted' in engine_object else [] + + +# project.json queries +def get_project_gems(project_path: pathlib.Path) -> list: + def is_gem_subdirectory(subdir): + return (pathlib.Path(subdir) / 'gem.json').exists() + + external_subdirs = get_project_external_subdirectories(project_path) + return list(filter(is_gem_subdirectory, external_subdirs)) if external_subdirs else [] + + +def get_project_external_subdirectories(project_path: pathlib.Path) -> list: + project_object = get_project_json_data(project_path=project_path) + return list(map(lambda rel_path: (pathlib.Path(project_path) / rel_path).as_posix(), + project_object['external_subdirectories'])) if 'external_subdirectories' in project_object else [] + + +# Combined manifest queries +def get_all_projects() -> list: + projects_data = set(get_projects()) + projects_data.update(get_engine_projects()) + return list(projects_data) + + +def get_all_gems(project_path: pathlib.Path = None) -> list: + gems_data = set(get_gems()) + gems_data.update(get_engine_gems()) + if project_path: + gems_data.update(get_project_gems(project_path)) + return list(gems_data) + + +def get_all_external_subdirectories(project_path: pathlib.Path = None) -> list: + external_subdirectories_data = set(get_external_subdirectories()) + external_subdirectories_data.update(get_engine_external_subdirectories()) + if project_path: + external_subdirectories_data.update(get_project_external_subdirectories(project_path)) + return list(templates_data) + + +def get_all_templates() -> list: + templates_data = set(get_templates()) + templates_data.update(get_engine_templates()) + return list(templates_data) + + +def get_all_restricted() -> list: + restricted_data = set(get_restricted()) + restricted_data.update(get_engine_restricted()) + return list(gems_data) + + +# Template functions +def get_project_templates(): # temporary until we have a better way to do this... maybe template_type element + project_templates = [] + for template in get_all_templates(): + if 'Project' in template: + project_templates.append(template) + return project_templates + + +def get_gem_templates(): # temporary until we have a better way to do this... maybe template_type element + gem_templates = [] + for template in get_all_templates(): + if 'Gem' in template: + gem_templates.append(template) + return gem_templates + + +def get_generic_templates(): # temporary until we have a better way to do this... maybe template_type element + generic_templates = [] + for template in get_all_templates(): + if 'Project' not in template and 'Gem' not in template: + generic_templates.append(template) + return generic_templates + + +def get_all_restricted() -> list: + engine_restricted = get_engine_restricted() + restricted_data = get_restricted() + restricted_data.extend(engine_restricted) + return restricted_data + + +def find_engine_data(json_data: dict, + engine_path: str or pathlib.Path = None) -> dict or None: + if not engine_path: + engine_path = get_this_engine_path() + engine_path = pathlib.Path(engine_path).resolve() + + for engine_object in json_data['engines']: + engine_object_path = pathlib.Path(engine_object['path']).resolve() + if engine_path == engine_object_path: + return engine_object + + return None + + +def get_engine_json_data(engine_name: str = None, + engine_path: str or pathlib.Path = None) -> dict or None: + if not engine_name and not engine_path: + logger.error('Must specify either a Engine name or Engine Path.') + return None + + if engine_name and not engine_path: + engine_path = get_registered(engine_name=engine_name) + + if not engine_path: + logger.error(f'Engine Path {engine_path} has not been registered.') + return None + + engine_path = pathlib.Path(engine_path).resolve() + engine_json = engine_path / 'engine.json' + if not engine_json.is_file(): + logger.error(f'Engine json {engine_json} is not present.') + return None + if not validation.valid_o3de_engine_json(engine_json): + logger.error(f'Engine json {engine_json} is not valid.') + return None + + with engine_json.open('r') as f: + try: + engine_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{engine_json} failed to load: {str(e)}') + else: + return engine_json_data + + return None + + +def get_project_json_data(project_name: str = None, + project_path: str or pathlib.Path = None) -> dict or None: + if not project_name and not project_path: + logger.error('Must specify either a Project name or Project Path.') + return None + + if project_name and not project_path: + project_path = get_registered(project_name=project_name) + + if not project_path: + logger.error(f'Project Path {project_path} has not been registered.') + return None + + project_path = pathlib.Path(project_path).resolve() + project_json = project_path / 'project.json' + if not project_json.is_file(): + logger.error(f'Project json {project_json} is not present.') + return None + if not validation.valid_o3de_project_json(project_json): + logger.error(f'Project json {project_json} is not valid.') + return None + + with project_json.open('r') as f: + try: + project_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{project_json} failed to load: {str(e)}') + else: + return project_json_data + + return None + + +def get_gem_json_data(gem_name: str = None, + gem_path: str or pathlib.Path = None) -> dict or None: + if not gem_name and not gem_path: + logger.error('Must specify either a Gem name or Gem Path.') + return None + + if gem_name and not gem_path: + gem_path = get_registered(gem_name=gem_name) + + if not gem_path: + logger.error(f'Gem Path {gem_path} has not been registered.') + return None + + gem_path = pathlib.Path(gem_path).resolve() + gem_json = gem_path / 'gem.json' + if not gem_json.is_file(): + logger.error(f'Gem json {gem_json} is not present.') + return None + if not validation.valid_o3de_gem_json(gem_json): + logger.error(f'Gem json {gem_json} is not valid.') + return None + + with gem_json.open('r') as f: + try: + gem_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{gem_json} failed to load: {str(e)}') + else: + return gem_json_data + + return None + + +def get_template_json_data(template_name: str = None, + template_path: str or pathlib.Path = None) -> dict or None: + if not template_name and not template_path: + logger.error('Must specify either a Template name or Template Path.') + return None + + if template_name and not template_path: + template_path = get_registered(template_name=template_name) + + if not template_path: + logger.error(f'Template Path {template_path} has not been registered.') + return None + + template_path = pathlib.Path(template_path).resolve() + template_json = template_path / 'template.json' + if not template_json.is_file(): + logger.error(f'Template json {template_json} is not present.') + return None + if not validation.valid_o3de_template_json(template_json): + logger.error(f'Template json {template_json} is not valid.') + return None + + with template_json.open('r') as f: + try: + template_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{template_json} failed to load: {str(e)}') + else: + return template_json_data + + return None + + +def get_restricted_data(restricted_name: str = None, + restricted_path: str or pathlib.Path = None) -> dict or None: + if not restricted_name and not restricted_path: + logger.error('Must specify either a Restricted name or Restricted Path.') + return None + + if restricted_name and not restricted_path: + restricted_path = get_registered(restricted_name=restricted_name) + + if not restricted_path: + logger.error(f'Restricted Path {restricted_path} has not been registered.') + return None + + restricted_path = pathlib.Path(restricted_path).resolve() + restricted_json = restricted_path / 'restricted.json' + if not restricted_json.is_file(): + logger.error(f'Restricted json {restricted_json} is not present.') + return None + if not validation.valid_o3de_restricted_json(restricted_json): + logger.error(f'Restricted json {restricted_json} is not valid.') + return None + + with restricted_json.open('r') as f: + try: + restricted_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{restricted_json} failed to load: {str(e)}') + else: + return restricted_json_data + + return None + + +def get_registered(engine_name: str = None, + project_name: str = None, + gem_name: str = None, + template_name: str = None, + default_folder: str = None, + repo_name: str = None, + restricted_name: str = None) -> pathlib.Path or None: + json_data = load_o3de_manifest() + + # check global first then this engine + if isinstance(engine_name, str): + for engine in json_data['engines']: + engine_path = pathlib.Path(engine['path']).resolve() + engine_json = engine_path / 'engine.json' + with engine_json.open('r') as f: + try: + engine_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{engine_json} failed to load: {str(e)}') + else: + this_engines_name = engine_json_data['engine_name'] + if this_engines_name == engine_name: + return engine_path + + elif isinstance(project_name, str): + enging_projects = get_engine_projects() + projects = json_data['projects'].copy() + projects.extend(engine_object['projects']) + for project_path in projects: + project_path = pathlib.Path(project_path).resolve() + project_json = project_path / 'project.json' + with project_json.open('r') as f: + try: + project_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{project_json} failed to load: {str(e)}') + else: + this_projects_name = project_json_data['project_name'] + if this_projects_name == project_name: + return project_path + + elif isinstance(gem_name, str): + gems = get_all_gems() + for gem_path in gems: + gem_path = pathlib.Path(gem_path).resolve() + gem_json = gem_path / 'gem.json' + with gem_json.open('r') as f: + try: + gem_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{gem_json} failed to load: {str(e)}') + else: + this_gems_name = gem_json_data['gem_name'] + if this_gems_name == gem_name: + return gem_path + + elif isinstance(template_name, str): + engine_templates = get_engine_templates() + templates = json_data['templates'].copy() + templates.extend(engine_templates) + for template_path in templates: + template_path = pathlib.Path(template_path).resolve() + template_json = template_path / 'template.json' + with template_json.open('r') as f: + try: + template_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{template_path} failed to load: {str(e)}') + else: + this_templates_name = template_json_data['template_name'] + if this_templates_name == template_name: + return template_path + + elif isinstance(restricted_name, str): + engine_restricted = get_engine_restricted() + restricted = json_data['restricted'].copy() + restricted.extend(engine_restricted) + for restricted_path in restricted: + restricted_path = pathlib.Path(restricted_path).resolve() + restricted_json = restricted_path / 'restricted.json' + with restricted_json.open('r') as f: + try: + restricted_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{restricted_json} failed to load: {str(e)}') + else: + this_restricted_name = restricted_json_data['restricted_name'] + if this_restricted_name == restricted_name: + return restricted_path + + elif isinstance(default_folder, str): + if default_folder == 'engines': + default_engines_folder = pathlib.Path(json_data['default_engines_folder']).resolve() + return default_engines_folder + elif default_folder == 'projects': + default_projects_folder = pathlib.Path(json_data['default_projects_folder']).resolve() + return default_projects_folder + elif default_folder == 'gems': + default_gems_folder = pathlib.Path(json_data['default_gems_folder']).resolve() + return default_gems_folder + elif default_folder == 'templates': + default_templates_folder = pathlib.Path(json_data['default_templates_folder']).resolve() + return default_templates_folder + elif default_folder == 'restricted': + default_restricted_folder = pathlib.Path(json_data['default_restricted_folder']).resolve() + return default_restricted_folder + + elif isinstance(repo_name, str): + cache_folder = get_o3de_cache_folder() + for repo_uri in json_data['repos']: + repo_uri = pathlib.Path(repo_uri).resolve() + repo_sha256 = hashlib.sha256(repo_uri.encode()) + cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json') + if cache_file.is_file(): + repo = pathlib.Path(cache_file).resolve() + with repo.open('r') as f: + try: + repo_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{cache_file} failed to load: {str(e)}') + else: + this_repos_name = repo_json_data['repo_name'] + if this_repos_name == repo_name: + return repo_uri + return None diff --git a/scripts/o3de/o3de/print_registration.py b/scripts/o3de/o3de/print_registration.py new file mode 100644 index 0000000000..292f2224bc --- /dev/null +++ b/scripts/o3de/o3de/print_registration.py @@ -0,0 +1,490 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import argparse +import json +import hashlib +import logging +import sys +import urllib.parse + +from o3de import manifest, validation + +logger = logging.getLogger() +logging.basicConfig() + +def print_this_engine(verbose: int) -> None: + engine_data = manifest.get_this_engine() + print(json.dumps(engine_data, indent=4)) + if verbose > 0: + print_engines_data(engine_data) + + +def print_engines(verbose: int) -> None: + engines_data = manifest.get_engines() + print(json.dumps(engines_data, indent=4)) + if verbose > 0: + print_engines_data(engines_data) + + +def print_projects(verbose: int) -> None: + projects_data = manifest.get_projects() + print(json.dumps(projects_data, indent=4)) + if verbose > 0: + print_projects_data(projects_data) + + +def print_gems(verbose: int) -> None: + gems_data = manifest.get_gems() + print(json.dumps(gems_data, indent=4)) + if verbose > 0: + print_gems_data(gems_data) + + +def print_templates(verbose: int) -> None: + templates_data = manifest.get_templates() + print(json.dumps(templates_data, indent=4)) + if verbose > 0: + print_templates_data(templates_data) + + +def print_restricted(verbose: int) -> None: + restricted_data = manifest.get_restricted() + print(json.dumps(restricted_data, indent=4)) + if verbose > 0: + print_restricted_data(restricted_data) + +def print_engine_projects(verbose: int) -> None: + engine_projects_data = manifest.get_engine_projects() + print(json.dumps(engine_projects_data, indent=4)) + if verbose > 0: + print_projects_data(engine_projects_data) + + +def print_engine_gems(verbose: int) -> None: + engine_gems_data = manifest.get_engine_gems() + print(json.dumps(engine_gems_data, indent=4)) + if verbose > 0: + print_gems_data(engine_gems_data) + + +def print_engine_templates(verbose: int) -> None: + engine_templates_data = manifest.get_engine_templates() + print(json.dumps(engine_templates_data, indent=4)) + if verbose > 0: + print_templates_data(engine_templates_data) + + +def print_engine_restricted(verbose: int) -> None: + engine_restricted_data = manifest.get_engine_restricted() + print(json.dumps(engine_restricted_data, indent=4)) + if verbose > 0: + print_restricted_data(engine_restricted_data) + + +def print_engine_external_subdirectories(verbose: int) -> None: + external_subdirs_data = manifest.get_engine_external_subdirectories() + print(json.dumps(external_subdirs_data, indent=4)) + + +def print_all_projects(verbose: int) -> None: + all_projects_data = manifest.get_all_projects() + print(json.dumps(all_projects_data, indent=4)) + if verbose > 0: + print_projects_data(all_projects_data) + + +def print_all_gems(verbose: int) -> None: + all_gems_data = manifest.get_all_gems() + print(json.dumps(all_gems_data, indent=4)) + if verbose > 0: + print_gems_data(all_gems_data) + + +def print_all_templates(verbose: int) -> None: + all_templates_data = manifest.get_all_templates() + print(json.dumps(all_templates_data, indent=4)) + if verbose > 0: + print_templates_data(all_templates_data) + + +def print_all_restricted(verbose: int) -> None: + all_restricted_data = manifest.get_all_restricted() + print(json.dumps(all_restricted_data, indent=4)) + if verbose > 0: + print_restricted_data(all_restricted_data) + + +def print_engines_data(engines_data: dict) -> None: + print('\n') + print("Engines================================================") + for engine_object in engines_data: + # if it's not local it should be in the cache + engine_uri = engine_object['path'] + parsed_uri = urllib.parse.urlparse(engine_uri) + if parsed_uri.scheme == 'http' or \ + parsed_uri.scheme == 'https' or \ + parsed_uri.scheme == 'ftp' or \ + parsed_uri.scheme == 'ftps': + repo_sha256 = hashlib.sha256(engine_uri.encode()) + cache_folder = manifest.get_o3de_cache_folder() + engine = cache_folder / str(repo_sha256.hexdigest() + '.json') + print(f'{engine_uri}/engine.json cached as:') + else: + engine_json = pathlib.Path(engine_uri).resolve() / 'engine.json' + + with engine_json.open('r') as f: + try: + engine_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{engine_json} failed to load: {str(e)}') + else: + print(engine_json) + print(json.dumps(engine_json_data, indent=4)) + print('\n') + + +def print_projects_data(projects_data: dict) -> None: + print('\n') + print("Projects================================================") + for project_uri in projects_data: + # if it's not local it should be in the cache + parsed_uri = urllib.parse.urlparse(project_uri) + if parsed_uri.scheme == 'http' or \ + parsed_uri.scheme == 'https' or \ + parsed_uri.scheme == 'ftp' or \ + parsed_uri.scheme == 'ftps': + repo_sha256 = hashlib.sha256(project_uri.encode()) + cache_folder = manifest.get_o3de_cache_folder() + project_json = cache_folder / str(repo_sha256.hexdigest() + '.json') + else: + project_json = pathlib.Path(project_uri).resolve() / 'project.json' + + with project_json.open('r') as f: + try: + project_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{project_json} failed to load: {str(e)}') + else: + print(project_json) + print(json.dumps(project_json_data, indent=4)) + print('\n') + + +def print_gems_data(gems_data: dict) -> None: + print('\n') + print("Gems================================================") + for gem_uri in gems_data: + # if it's not local it should be in the cache + parsed_uri = urllib.parse.urlparse(gem_uri) + if parsed_uri.scheme == 'http' or \ + parsed_uri.scheme == 'https' or \ + parsed_uri.scheme == 'ftp' or \ + parsed_uri.scheme == 'ftps': + repo_sha256 = hashlib.sha256(gem_uri.encode()) + cache_folder = manifest.get_o3de_cache_folder() + gem_json = cache_folder / str(repo_sha256.hexdigest() + '.json') + else: + gem_json = pathlib.Path(gem_uri).resolve() / 'gem.json' + + with gem_json.open('r') as f: + try: + gem_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{gem_json} failed to load: {str(e)}') + else: + print(gem_json) + print(json.dumps(gem_json_data, indent=4)) + print('\n') + + +def print_templates_data(templates_data: dict) -> None: + print('\n') + print("Templates================================================") + for template_uri in templates_data: + # if it's not local it should be in the cache + parsed_uri = urllib.parse.urlparse(template_uri) + if parsed_uri.scheme == 'http' or \ + parsed_uri.scheme == 'https' or \ + parsed_uri.scheme == 'ftp' or \ + parsed_uri.scheme == 'ftps': + repo_sha256 = hashlib.sha256(template_uri.encode()) + cache_folder = manifest.get_o3de_cache_folder() + template_json = cache_folder / str(repo_sha256.hexdigest() + '.json') + else: + template_json = pathlib.Path(template_uri).resolve() / 'template.json' + + with template_json.open('r') as f: + try: + template_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{template_json} failed to load: {str(e)}') + else: + print(template_json) + print(json.dumps(template_json_data, indent=4)) + print('\n') + + +def print_repos_data(repos_data: dict) -> None: + print('\n') + print("Repos================================================") + cache_folder = manifest.get_o3de_cache_folder() + for repo_uri in repos_data: + repo_sha256 = hashlib.sha256(repo_uri.encode()) + cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json') + if validation.valid_o3de_repo_json(cache_file): + with cache_file.open('r') as s: + try: + repo_json_data = json.load(s) + except json.JSONDecodeError as e: + logger.warn(f'{cache_file} failed to load: {str(e)}') + else: + print(f'{repo_uri}/repo.json cached as:') + print(cache_file) + print(json.dumps(repo_json_data, indent=4)) + print('\n') + + +def print_restricted_data(restricted_data: dict) -> None: + print('\n') + print("Restricted================================================") + for restricted_path in restricted_data: + restricted_json = pathlib.Path(restricted_path).resolve() / 'restricted.json' + with restricted_json.open('r') as f: + try: + restricted_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{restricted_json} failed to load: {str(e)}') + else: + print(restricted_json) + print(json.dumps(restricted_json_data, indent=4)) + print('\n') + + +def register_show_repos(verbose: int) -> None: + repos_data = get_repos() + print(json.dumps(repos_data, indent=4)) + if verbose > 0: + print_repos_data(repos_data) + + +def register_show(verbose: int) -> None: + json_data = manifest.load_o3de_manifest() + print(f"{manifest.get_o3de_manifest()}:") + print(json.dumps(json_data, indent=4)) + + if verbose > 0: + print_engines_data(manifest.get_engines()) + print_projects_data(manifest.get_all_projects()) + print_gems_data(manifest.get_gems()) + print_templates_data(manifest.get_all_templates()) + print_restricted_data(manifest.get_all_restricted()) + print_repos_data(manifest.get_repos()) + + +def _run_register_show(args: argparse) -> int: + if args.override_home_folder: + manifest.override_home_folder = args.override_home_folder + + if args.this_engine: + print_this_engine(args.verbose) + return 0 + + elif args.engines: + print_engines(args.verbose) + return 0 + elif args.projects: + print_projects(args.verbose) + return 0 + elif args.gems: + print_gems(args.verbose) + return 0 + elif args.templates: + print_templates(args.verbose) + return 0 + elif args.repos: + register_show_repos(args.verbose) + return 0 + elif args.restricted: + print_restricted(args.verbose) + return 0 + + elif args.engine_projects: + print_engine_projects(args.verbose) + return 0 + elif args.engine_gems: + print_engine_gems(args.verbose) + return 0 + elif args.engine_templates: + print_engine_templates(args.verbose) + return 0 + elif args.engine_restricted: + print_engine_restricted(args.verbose) + return 0 + elif args.engine_external_subdirectories: + print_engine_external_subdirectories(args.verbose) + return 0 + + elif args.all_projects: + print_all_projects(args.verbose) + return 0 + elif args.all_gems: + print_all_gems(args.verbose) + return 0 + elif args.all_templates: + print_all_templates(args.verbose) + return 0 + elif args.all_restricted: + print_all_restricted(args.verbose) + return 0 + + elif args.downloadables: + print_downloadables(args.verbose) + return 0 + if args.downloadable_engines: + print_downloadable_engines(args.verbose) + return 0 + elif args.downloadable_projects: + print_downloadable_projects(args.verbose) + return 0 + elif args.downloadable_gems: + print_downloadable_gems(args.verbose) + return 0 + elif args.downloadable_templates: + print_downloadable_templates(args.verbose) + return 0 + else: + register_show(args.verbose) + return 0 + + +def add_parser_args(parser): + """ + add_parser_args is called to add arguments to each command such that it can be + invoked locally or added by a central python file. + Ex. Directly run from this file alone with: python print_registration.py --engine-projects + :param parser: the caller passes an argparse parser like instance to this method + """ + group = parser.add_mutually_exclusive_group(required=False) + group.add_argument('-te', '--this-engine', action='store_true', required=False, + default=False, + help='Just the local engines.') + + group.add_argument('-e', '--engines', action='store_true', required=False, + default=False, + help='Just the local engines.') + group.add_argument('-p', '--projects', action='store_true', required=False, + default=False, + help='Just the local projects.') + group.add_argument('-g', '--gems', action='store_true', required=False, + default=False, + help='Just the local gems.') + group.add_argument('-t', '--templates', action='store_true', required=False, + default=False, + help='Just the local templates.') + group.add_argument('-r', '--repos', action='store_true', required=False, + default=False, + help='Just the local repos. Ignores repos.') + group.add_argument('-rs', '--restricted', action='store_true', required=False, + default=False, + help='The local restricted folders.') + + group.add_argument('-ep', '--engine-projects', action='store_true', required=False, + default=False, + help='Just the local projects. Ignores repos.') + group.add_argument('-eg', '--engine-gems', action='store_true', required=False, + default=False, + help='Just the local gems. Ignores repos') + group.add_argument('-et', '--engine-templates', action='store_true', required=False, + default=False, + help='Just the local templates. Ignores repos.') + group.add_argument('-ers', '--engine-restricted', action='store_true', required=False, + default=False, + help='The restricted folders.') + group.add_argument('-x', '--engine-external-subdirectories', action='store_true', required=False, + default=False, + help='The external subdirectories.') + + group.add_argument('-ap', '--all-projects', action='store_true', required=False, + default=False, + help='Just the local projects. Ignores repos.') + group.add_argument('-ag', '--all-gems', action='store_true', required=False, + default=False, + help='Just the local gems. Ignores repos') + group.add_argument('-at', '--all-templates', action='store_true', required=False, + default=False, + help='Just the local templates. Ignores repos.') + group.add_argument('-ars', '--all-restricted', action='store_true', required=False, + default=False, + help='The restricted folders.') + + group.add_argument('-d', '--downloadables', action='store_true', required=False, + default=False, + help='Combine all repos into a single list of resources.') + group.add_argument('-de', '--downloadable-engines', action='store_true', required=False, + default=False, + help='Combine all repos engines into a single list of resources.') + group.add_argument('-dp', '--downloadable-projects', action='store_true', required=False, + default=False, + help='Combine all repos projects into a single list of resources.') + group.add_argument('-dg', '--downloadable-gems', action='store_true', required=False, + default=False, + help='Combine all repos gems into a single list of resources.') + group.add_argument('-dt', '--downloadable-templates', action='store_true', required=False, + default=False, + help='Combine all repos templates into a single list of resources.') + + parser.add_argument('-v', '--verbose', action='count', required=False, + default=0, + help='How verbose do you want the output to be.') + + parser.add_argument('-ohf', '--override-home-folder', type=str, required=False, + help='By default the home folder is the user folder, override it to this folder.') + + parser.set_defaults(func=_run_register_show) + + +def add_args(subparsers) -> None: + """ + add_args is called to add subparsers arguments to each command such that it can be + a central python file such as o3de.py. + It can be run from the o3de.py script as follows + call add_args and execute: python o3de.py register-show --engine-projects + :param subparsers: the caller instantiates subparsers and passes it in here + """ + register_show_subparser = subparsers.add_parser('register-show') + add_parser_args(register_show_subparser) + + +def main(): + """ + Runs print_registration.py script as standalone script + """ + # parse the command line args + the_parser = argparse.ArgumentParser() + + # add subparsers + + # add args to the parser + add_parser_args(the_parser) + + # parse args + the_args = the_parser.parse_args() + + # run + ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1 + + # return + sys.exit(ret) + + +if __name__ == "__main__": + main() diff --git a/scripts/o3de/o3de/register.py b/scripts/o3de/o3de/register.py new file mode 100644 index 0000000000..68575488dc --- /dev/null +++ b/scripts/o3de/o3de/register.py @@ -0,0 +1,864 @@ + +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +""" +This file contains all the code that has to do with registering engines, projects, gems and templates +""" + +import argparse +import hashlib +import logging +import json +import os +import pathlib +import shutil +import sys +import urllib.parse +import urllib.request + +from o3de import get_registration, manifest, repo, utils, validation + +logger = logging.getLogger() +logging.basicConfig() + + +def register_shipped_engine_o3de_objects(force: bool = False) -> int: + engine_path = manifest.get_this_engine_path() + + ret_val = 0 + + # register anything in the users default folders globally + error_code = register_all_engines_in_folder(manifest.get_registered(default_folder='engines'), force=force) + if error_code: + ret_val = error_code + error_code = register_all_projects_in_folder(manifest.get_registered(default_folder='projects')) + if error_code: + ret_val = error_code + error_code = register_all_gems_in_folder(manifest.get_registered(default_folder='gems')) + if error_code: + ret_val = error_code + error_code = register_all_templates_in_folder(manifest.get_registered(default_folder='templates')) + if error_code: + ret_val = error_code + error_code = register_all_restricted_in_folder(manifest.get_registered(default_folder='restricted')) + if error_code: + ret_val = error_code + error_code = register_all_restricted_in_folder(manifest.get_registered(default_folder='projects')) + if error_code: + ret_val = error_code + error_code = register_all_restricted_in_folder(manifest.get_registered(default_folder='gems')) + if error_code: + ret_val = error_code + error_code = register_all_restricted_in_folder(manifest.get_registered(default_folder='templates')) + if error_code: + ret_val = error_code + + return ret_val + + +def register_all_in_folder(folder_path: str or pathlib.Path, + remove: bool = False, + engine_path: str or pathlib.Path = None, + exclude: list = None) -> int: + if not folder_path: + logger.error(f'Folder path cannot be empty.') + return 1 + + folder_path = pathlib.Path(folder_path).resolve() + if not folder_path.is_dir(): + logger.error(f'Folder path is not dir.') + return 1 + + engines_set = set() + projects_set = set() + gems_set = set() + templates_set = set() + restricted_set = set() + repo_set = set() + + ret_val = 0 + for root, dirs, files in os.walk(folder_path): + if root in exclude: + continue + + for name in files: + if name == 'engine.json': + engines_set.add(root) + elif name == 'project.json': + projects_set.add(root) + elif name == 'gem.json': + gems_set.add(root) + elif name == 'template.json': + templates_set.add(root) + elif name == 'restricted.json': + restricted_set.add(root) + elif name == 'repo.json': + repo_set.add(root) + + for engine in sorted(engines_set, reverse=True): + error_code = register(engine_path=engine, remove=remove) + if error_code: + ret_val = error_code + + for project in sorted(projects_set, reverse=True): + error_code = register(engine_path=engine_path, project_path=project, remove=remove) + if error_code: + ret_val = error_code + + for gem in sorted(gems_set, reverse=True): + error_code = register(engine_path=engine_path, gem_path=gem, remove=remove) + if error_code: + ret_val = error_code + + for template in sorted(templates_set, reverse=True): + error_code = register(engine_path=engine_path, template_path=template, remove=remove) + if error_code: + ret_val = error_code + + for restricted in sorted(restricted_set, reverse=True): + error_code = register(engine_path=engine_path, restricted_path=restricted, remove=remove) + if error_code: + ret_val = error_code + + for repo in sorted(repo_set, reverse=True): + error_code = register(engine_path=engine_path, repo_uri=repo, remove=remove) + if error_code: + ret_val = error_code + + return ret_val + + +def register_all_o3de_objects_of_type_in_folder(o3de_object_path: str or pathlib.Path, + o3de_object_type: str, + remove: bool, + force: bool, + **register_kwargs) -> int: + if not o3de_object_path: + logger.error(f'Engines path cannot be empty.') + return 1 + + o3de_object_path = pathlib.Path(o3de_object_path).resolve() + if not o3de_object_path.is_dir(): + logger.error(f'Engines path is not dir.') + return 1 + + o3de_object_type_set = set() + register_path_kwarg = f'{o3de_object_type}_path' if o3de_object_type != 'repo' else f'{o3de_object_type}_uri' + + ret_val = 0 + for root, dirs, files in os.walk(o3de_object_path): + if f'{o3de_object_type}.json' in files: + o3de_object_type_set.add(root) + # Stop iteration of any subdirectories + # Nested o3de objects of the same type aren't supported(i.e an engine cannot be inside of a engine). + dirs[:] = [] + + for o3de_object_type_root in sorted(o3de_object_type_set, reverse=True): + error_code = register(**{register_path_kwarg: o3de_object_type_root}, + remove=remove, force=force, **register_kwargs) + if error_code: + ret_val = error_code + + return ret_val + + +def register_all_engines_in_folder(engines_path: str or pathlib.Path, + remove: bool = False, + force: bool = False) -> int: + return register_all_o3de_objects_of_type_in_folder(engines_path, 'engine', remove, force) + + +def register_all_projects_in_folder(projects_path: str or pathlib.Path, + remove: bool = False, + engine_path: str or pathlib.Path = None) -> int: + return register_all_o3de_objects_of_type_in_folder(projects_path, 'project', remove, False, engine_path=engine_path) + + +def register_all_gems_in_folder(gems_path: str or pathlib.Path, + remove: bool = False, + engine_path: pathlib.Path = None, + project_path: pathlib.Path = None) -> int: + return register_all_o3de_objects_of_type_in_folder(gems_path, 'gem', remove, False, engine_path=engine_path) + + +def register_all_templates_in_folder(templates_path: str or pathlib.Path, + remove: bool = False, + engine_path: str or pathlib.Path = None) -> int: + return register_all_o3de_objects_of_type_in_folder(templates_path, 'template', remove, False, engine_path=engine_path) + + +def register_all_restricted_in_folder(restricted_path: str or pathlib.Path, + remove: bool = False, + engine_path: str or pathlib.Path = None) -> int: + return register_all_o3de_objects_of_type_in_folder(restricted_path, 'restricted', remove, False, engine_path=engine_path) + + +def register_all_repos_in_folder(repos_path: str or pathlib.Path, + remove: bool = False, + engine_path: str or pathlib.Path = None) -> int: + return register_all_o3de_objects_of_type_in_folder(repos_path, 'repo', remove, force, engine_path=engine_path) + + +def remove_engine_name_to_path(json_data: dict, + engine_path: pathlib.Path) -> int: + """ + Remove the engine at the specified path if it exist in the o3de manifest + :param json_data in-memory json view of the o3de_manifest.json data + :param engine_path path to engine to remove from the manifest data + + returns 0 to indicate no issues has occurred with removal + """ + if engine_path.is_dir() and validation.valid_o3de_engine_json(engine_path): + engine_json_data = manifest.get_engine_json_data(engine_path=engine_path) + if 'engine_name' in engine_json_data and 'engines_path' in json_data: + engine_name = engine_json_data['engine_name'] + try: + del json_data['engines_path'][engine_name] + except KeyError: + # Attempting to remove a non-existent engine_name is fine + pass + return 0 + + +def add_engine_name_to_path(json_data: dict, engine_path: pathlib.Path, force: bool): + # Add an engine path JSON object which maps the "engine_name" -> "engine_path" + engine_json_data = manifest.get_engine_json_data(engine_path=engine_path) + if not engine_json_data: + logger.error(f'Unable to retrieve json data from engine.json at path {engine_path.as_posix()}') + return 1 + engines_path_json = json_data.setdefault('engines_path', {}) + if 'engine_name' not in engine_json_data: + logger.error(f'engine.json at path {engine_path.as_posix()} is missing "engine_name" key') + return 1 + + engine_name = engine_json_data['engine_name'] + if not force and engine_name in engines_path_json and \ + pathlib.PurePath(engines_path_json[engine_name]) != engine_path: + logger.error( + f'Attempting to register existing engine "{engine_name}" with a new path of {engine_path.as_posix()}.' + f' The current path is {pathlib.Path(engines_path_json[engine_name]).as_posix()}.' + f' To force registration of a new engine path, specify the -f/--force option.') + return 1 + engines_path_json[engine_name] = engine_path.as_posix() + return 0 + + +def register_engine_path(json_data: dict, + engine_path: str or pathlib.Path, + remove: bool = False, + force: bool = False) -> int: + if not engine_path: + logger.error(f'Engine path cannot be empty.') + return 1 + engine_path = pathlib.Path(engine_path).resolve() + + for engine_object in json_data.get('engines', {}): + engine_object_path = pathlib.Path(engine_object['path']).resolve() + if engine_object_path == engine_path: + json_data['engines'].remove(engine_object) + + if remove: + return remove_engine_name_to_path(json_data, engine_path) + + if not engine_path.is_dir(): + logger.error(f'Engine path {engine_path} does not exist.') + return 1 + + engine_json = engine_path / 'engine.json' + if not validation.valid_o3de_engine_json(engine_json): + logger.error(f'Engine json {engine_json} is not valid.') + return 1 + + engine_object = {} + engine_object.update({'path': engine_path.as_posix()}) + + json_data.setdefault('engines', []).insert(0, engine_object) + + return add_engine_name_to_path(json_data, engine_path, force) + + +def register_o3de_object_path(json_data: dict, + o3de_object_path: str or pathlib.Path, + o3de_object_key: str, + o3de_json_filename: str, + validation_func: callable, + remove: bool = False, + engine_path: pathlib.Path = None, + project_path: pathlib.Path = None) -> int: + # save_path variable is used to save the changes to the store the path to the file to save + # if the registration is for the project or engine + save_path = None + + if not o3de_object_path: + logger.error(f'o3de object path cannot be empty.') + return 1 + + o3de_object_path = pathlib.Path(o3de_object_path).resolve() + + if engine_path and project_path: + logger.error(f'Both a project path: {project_path} and engine path: {engine_path} has been supplied.' + 'A subdirectory can only be registered to either the engine path or project in one command') + + manifest_data = None + if engine_path: + manifest_data = manifest.get_engine_json_data(json_data, engine_path) + if not manifest_data: + logger.error(f'Cannot load engine.json data at path {engine_path}') + return 1 + + save_path = engine_path / 'engine.json' + elif project_path: + manifest_data = manifest.get_project_json_data(json_data, project_path) + if not manifest_data: + logger.error(f'Cannot load project.json data at path {project_path}') + return 1 + + save_path = project_path / 'project.json' + else: + manifest_data = json_data + + paths_to_remove = [o3de_object_path] + if save_path: + try: + paths_to_remove.append(o3de_object_path.relative_to(save_path.parent)) + except ValueError: + pass # It is OK relative path cannot be formed + manifest_data[o3de_object_key] = list(filter(lambda p: pathlib.Path(p) not in paths_to_remove, + manifest_data.setdefault(o3de_object_key, []))) + + if remove: + if save_path: + manifest.save_o3de_manifest(manifest_data, save_path) + return 0 + + if not o3de_object_path.is_dir(): + logger.error(f'o3de object path {o3de_object_path} does not exist.') + return 1 + + manifest_json_path = o3de_object_path / o3de_json_filename + if validation_func and not validation_func(manifest_json_path): + logger.error(f'o3de json {manifest_json_path} is not valid.') + return 1 + + # if there is a save path make it relative the directory containing o3de object json file + if save_path: + try: + o3de_object_path = o3de_object_path.relative_to(save_path.parent) + except ValueError: + pass # It is OK relative path cannot be formed + manifest_data[o3de_object_key].insert(0, o3de_object_path.as_posix()) + if save_path: + manifest.save_o3de_manifest(manifest_data, save_path) + + return 0 + + +def register_external_subdirectory(json_data: dict, + external_subdir_path: str or pathlib.Path, + remove: bool = False, + engine_path: pathlib.Path = None, + project_path: pathlib.Path = None) -> int: + """ + :return An integer return code indicating whether registration or removal of the external subdirectory + completed successfully + """ + return register_o3de_object_path(json_data, external_subdir_path, 'external_subdirectories', '', None, remove, + engine_path, project_path) + + +def register_gem_path(json_data: dict, + gem_path: str or pathlib.Path, + remove: bool = False, + engine_path: pathlib.Path = None, + project_path: pathlib.Path = None) -> int: + return register_o3de_object_path(json_data, gem_path, 'external_subdirectories', 'gem.json', + validation.valid_o3de_gem_json, remove, engine_path, project_path) + + +def register_project_path(json_data: dict, + project_path: str or pathlib.Path, + remove: bool = False, + engine_path: str or pathlib.Path = None) -> int: + result = register_o3de_object_path(json_data, project_path, 'projects', 'project.json', + validation.valid_o3de_project_json, remove, engine_path, None) + + if result != 0: + return result + + # registering a project has the additional step of setting the project.json 'engine' field + this_engine_json = manifest.get_engine_json_data(engine_path=manifest.get_this_engine_path()) + if not this_engine_json: + return 1 + project_json_data = manifest.get_project_json_data(project_path=project_path) + if not project_json_data: + return 1 + + update_project_json = False + try: + update_project_json = project_json_data['engine'] != this_engine_json['engine_name'] + except KeyError as e: + update_project_json = True + + if update_project_json: + project_json_data['engine'] = this_engine_json['engine_name'] + utils.backup_file(project_json) + if not manifest.save_o3de_manifest(project_json_data, project_path): + return 1 + + + return 0 + + +def register_template_path(json_data: dict, + template_path: str or pathlib.Path, + remove: bool = False, + engine_path: str or pathlib.Path = None) -> int: + return register_o3de_object_path(json_data, template_path, 'templates', 'template.json', + validation.valid_o3de_template_json, remove, engine_path, None) + + +def register_restricted_path(json_data: dict, + restricted_path: str or pathlib.Path, + remove: bool = False, + engine_path: str or pathlib.Path = None) -> int: + return register_o3de_object_path(json_data, restricted_path, 'restricted', 'restricted.json', + validation.valid_o3de_restricted_json, remove, engine_path, None) + + +def register_repo(json_data: dict, + repo_uri: str or pathlib.Path, + remove: bool = False) -> int: + if not repo_uri: + logger.error(f'Repo URI cannot be empty.') + return 1 + + url = f'{repo_uri}/repo.json' + parsed_uri = urllib.parse.urlparse(url) + + if parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']: + while repo_uri in json_data['repos']: + json_data['repos'].remove(repo_uri) + else: + repo_uri = pathlib.Path(repo_uri).resolve() + while repo_uri.as_posix() in json_data['repos']: + json_data['repos'].remove(repo_uri.as_posix()) + + if remove: + logger.warn(f'Removing repo uri {repo_uri}.') + return 0 + + repo_sha256 = hashlib.sha256(url.encode()) + cache_file = manifest.get_o3de_cache_folder() / str(repo_sha256.hexdigest() + '.json') + + result = utils.download_file(url, cache_file) + if result == 0: + json_data['repos'].insert(0, repo_uri.as_posix()) + + result = repo.process_add_o3de_repo(cache_file, repo_set) + + return result + + +def register_default_o3de_object_folder(json_data: dict, + default_o3de_object_folder: str or pathlib.Path, + o3de_object_key: str) -> int: + # make sure the path exists + default_o3de_object_folder = pathlib.Path(default_o3de_object_folder).resolve() + if not default_o3de_object_folder.is_dir(): + logger.error(f'Default o3de object folder {default_o3de_object_folder} does not exist.') + return 1 + + json_data[o3de_object_key] = default_o3de_object_folder.as_posix() + + return 0 + + +def register_default_engines_folder(json_data: dict, + default_engines_folder: str or pathlib.Path, + remove: bool = False) -> int: + return register_default_o3de_object_folder(json_data, + manifest.get_o3de_engines_folder() if remove else default_engines_folder, + 'default_engines_folder', remove) + + +def register_default_projects_folder(json_data: dict, + default_projects_folder: str or pathlib.Path, + remove: bool = False) -> int: + return register_default_o3de_object_folder(json_data, + manifest.get_o3de_projects_folder() if remove else default_projects_folder, + 'default_projects_folder', remove) + + +def register_default_gems_folder(json_data: dict, + default_gems_folder: str or pathlib.Path, + remove: bool = False) -> int: + return register_default_o3de_object_folder(json_data, + manifest.get_o3de_gems_folder() if remove else default_gems_folder, + 'default_gems_folder', remove) + + +def register_default_templates_folder(json_data: dict, + default_templates_folder: str or pathlib.Path, + remove: bool = False) -> int: + return register_default_o3de_object_folder(json_data, + manifest.get_o3de_templates_folder() if remove else default_templates_folder, + 'default_templates_folder', remove) + + +def register_default_restricted_folder(json_data: dict, + default_restricted_folder: str or pathlib.Path, + reset_to_default: bool = False) -> int: + return register_default_o3de_object_folder(json_data, + manifest.get_o3de_restricted_folder() if remove else default_restricted_folder, + 'default_restricted_folder', remove) + + +def register(engine_path: str or pathlib.Path = None, + project_path: str or pathlib.Path = None, + gem_path: str or pathlib.Path = None, + external_subdir_path: str or pathlib.Path = None, + template_path: str or pathlib.Path = None, + restricted_path: str or pathlib.Path = None, + repo_uri: str or pathlib.Path = None, + default_engines_folder: str or pathlib.Path = None, + default_projects_folder: str or pathlib.Path = None, + default_gems_folder: str or pathlib.Path = None, + default_templates_folder: str or pathlib.Path = None, + default_restricted_folder: str or pathlib.Path = None, + external_subdir_engine_path: pathlib.Path = None, + external_subdir_project_path: pathlib.Path = None, + remove: bool = False, + force: bool = False + ) -> int: + """ + Adds/Updates entries to the ~/.o3de/o3de_manifest.json + + :param engine_path: if engine folder is supplied the path will be added to the engine if it can, if not global + :param project_path: project folder + :param gem_path: gem folder + :param external_subdir_path: external subdirectory + :param template_path: template folder + :param restricted_path: restricted folder + :param repo_uri: repo uri + :param default_engines_folder: default engines folder + :param default_projects_folder: default projects folder + :param default_gems_folder: default gems folder + :param default_templates_folder: default templates folder + :param default_restricted_folder: default restricted code folder + :param external_subdir_engine_path: Path to the engine to use when registering an external subdirectory. + The registration occurs in the engine.json file in this case + :param external_subdir_engine_path: Path to the project to use when registering an external subdirectory. + The registrations occurs in the project.json in this case + :param remove: add/remove the entries + :param force: force update of the engine_path for specified "engine_name" from the engine.json file + + :return: 0 for success or non 0 failure code + """ + + json_data = manifest.load_o3de_manifest() + + result = 0 + + # do anything that could require a engine context first + if isinstance(project_path, str) or isinstance(project_path, pathlib.PurePath): + if not project_path: + logger.error(f'Project path cannot be empty.') + return 1 + result = register_project_path(json_data, project_path, remove, engine_path) + + elif isinstance(gem_path, str) or isinstance(gem_path, pathlib.PurePath): + if not gem_path: + logger.error(f'Gem path cannot be empty.') + return 1 + result = register_gem_path(json_data, gem_path, remove, + external_subdir_engine_path, external_subdir_project_path) + elif isinstance(external_subdir_path, str) or isinstance(external_subdir_path, pathlib.PurePath): + if not external_subdir_path: + logger.error(f'External Subdirectory path is None.') + return 1 + result = register_external_subdirectory(json_data, external_subdir_path, remove, + external_subdir_engine_path, external_subdir_project_path) + + elif isinstance(template_path, str) or isinstance(template_path, pathlib.PurePath): + if not template_path: + logger.error(f'Template path cannot be empty.') + return 1 + result = register_template_path(json_data, template_path, remove, engine_path) + + elif isinstance(restricted_path, str) or isinstance(restricted_path, pathlib.PurePath): + if not restricted_path: + logger.error(f'Restricted path cannot be empty.') + return 1 + result = register_restricted_path(json_data, restricted_path, remove, engine_path) + + elif isinstance(repo_uri, str) or isinstance(repo_uri, pathlib.PurePath): + if not repo_uri: + logger.error(f'Repo URI cannot be empty.') + return 1 + result = register_repo(json_data, repo_uri, remove) + + elif isinstance(default_engines_folder, str) or isinstance(default_engines_folder, pathlib.PurePath): + result = register_default_engines_folder(json_data, default_engines_folder, remove) + + elif isinstance(default_projects_folder, str) or isinstance(default_projects_folder, pathlib.PurePath): + result = register_default_projects_folder(json_data, default_projects_folder, remove) + + elif isinstance(default_gems_folder, str) or isinstance(default_gems_folder, pathlib.PurePath): + result = register_default_gems_folder(json_data, default_gems_folder, remove) + + elif isinstance(default_templates_folder, str) or isinstance(default_templates_folder, pathlib.PurePath): + result = register_default_templates_folder(json_data, default_templates_folder, remove) + + elif isinstance(default_restricted_folder, str) or isinstance(default_restricted_folder, pathlib.PurePath): + result = register_default_restricted_folder(json_data, default_restricted_folder, remove) + + # engine is done LAST + # Now that everything that could have an engine context is done, if the engine is supplied that means this is + # registering the engine itself + elif isinstance(engine_path, str) or isinstance(engine_path, pathlib.PurePath): + if not engine_path: + logger.error(f'Engine path cannot be empty.') + return 1 + result = register_engine_path(json_data, engine_path, remove, force) + + if not result: + manifest.save_o3de_manifest(json_data) + + return result + + +def remove_invalid_o3de_objects() -> None: + json_data = manifest.load_o3de_manifest() + + for engine_object in json_data['engines']: + engine_path = engine_object['path'] + if not validation.valid_o3de_engine_json(pathlib.Path(engine_path).resolve() / 'engine.json'): + logger.warn(f"Engine path {engine_path} is invalid.") + register(engine_path=engine_path, remove=True) + + for project in json_data['projects']: + if not validation.valid_o3de_project_json(pathlib.Path(project).resolve() / 'project.json'): + logger.warn(f"Project path {project} is invalid.") + register(project_path=project, remove=True) + + for gem in json_data['gems']: + if not validation.valid_o3de_gem_json(pathlib.Path(gem).resolve() / 'gem.json'): + logger.warn(f"Gem path {gem} is invalid.") + register(gem_path=gem, remove=True) + + for external in json_data['external_subdirectories']: + external = pathlib.Path(external).resolve() + if not external.is_dir(): + logger.warn(f"External subdirectory {external} is invalid.") + register(engine_path=engine_path, external_subdir_path=external, remove=True) + + for template in json_data['templates']: + if not validation.valid_o3de_template_json(pathlib.Path(template).resolve() / 'template.json'): + logger.warn(f"Template path {template} is invalid.") + register(template_path=template, remove=True) + + for restricted in json_data['restricted']: + if not validation.valid_o3de_restricted_json(pathlib.Path(restricted).resolve() / 'restricted.json'): + logger.warn(f"Restricted path {restricted} is invalid.") + register(restricted_path=restricted, remove=True) + + default_engines_folder = pathlib.Path(json_data['default_engines_folder']).resolve() + if not default_engines_folder.is_dir(): + new_default_engines_folder = manifest.get_o3de_folder() / 'Engines' + new_default_engines_folder.mkdir(parents=True, exist_ok=True) + logger.warn( + f"Default engines folder {default_engines_folder} is invalid. Set default {new_default_engines_folder}") + register(default_engines_folder=new_default_engines_folder.as_posix()) + + default_projects_folder = pathlib.Path(json_data['default_projects_folder']).resolve() + if not default_projects_folder.is_dir(): + new_default_projects_folder = manifest.get_o3de_folder() / 'Projects' + new_default_projects_folder.mkdir(parents=True, exist_ok=True) + logger.warn( + f"Default projects folder {default_projects_folder} is invalid. Set default {new_default_projects_folder}") + register(default_projects_folder=new_default_projects_folder.as_posix()) + + default_gems_folder = pathlib.Path(json_data['default_gems_folder']).resolve() + if not default_gems_folder.is_dir(): + new_default_gems_folder = manifest.get_o3de_folder() / 'Gems' + new_default_gems_folder.mkdir(parents=True, exist_ok=True) + logger.warn(f"Default gems folder {default_gems_folder} is invalid." + f" Set default {new_default_gems_folder}") + register(default_gems_folder=new_default_gems_folder.as_posix()) + + default_templates_folder = pathlib.Path(json_data['default_templates_folder']).resolve() + if not default_templates_folder.is_dir(): + new_default_templates_folder = manifest.get_o3de_folder() / 'Templates' + new_default_templates_folder.mkdir(parents=True, exist_ok=True) + logger.warn( + f"Default templates folder {default_templates_folder} is invalid." + f" Set default {new_default_templates_folder}") + register(default_templates_folder=new_default_templates_folder.as_posix()) + + default_restricted_folder = pathlib.Path(json_data['default_restricted_folder']).resolve() + if not default_restricted_folder.is_dir(): + default_restricted_folder = manifest.get_o3de_folder() / 'Restricted' + default_restricted_folder.mkdir(parents=True, exist_ok=True) + logger.warn( + f"Default restricted folder {default_restricted_folder} is invalid." + f" Set default {default_restricted_folder}") + register(default_restricted_folder=default_restricted_folder.as_posix()) + + +def _run_register(args: argparse) -> int: + if args.override_home_folder: + manifest.override_home_folder = args.override_home_folder + + if args.update: + remove_invalid_o3de_objects() + return repo.refresh_repos() + elif args.this_engine: + ret_val = register(engine_path=manifest.get_this_engine_path(), force=args.force) + error_code = register_shipped_engine_o3de_objects(force=args.force) + if error_code: + ret_val = error_code + return ret_val + elif args.all_engines_path: + return register_all_engines_in_folder(args.all_engines_path, args.remove, args.force) + elif args.all_projects_path: + return register_all_projects_in_folder(args.all_projects_path, args.remove) + elif args.all_gems_path: + return register_all_gems_in_folder(args.all_gems_path, args.remove) + elif args.all_templates_path: + return register_all_templates_in_folder(args.all_templates_path, args.remove) + elif args.all_restricted_path: + return register_all_restricted_in_folder(args.all_restricted_path, args.remove) + elif args.all_repo_uri: + return register_all_repos_in_folder(args.all_restricted_path, args.remove) + else: + return register(engine_path=args.engine_path, + project_path=args.project_path, + gem_path=args.gem_path, + external_subdir_path=args.external_subdirectory, + template_path=args.template_path, + restricted_path=args.restricted_path, + repo_uri=args.repo_uri, + default_engines_folder=args.default_engines_folder, + default_projects_folder=args.default_projects_folder, + default_gems_folder=args.default_gems_folder, + default_templates_folder=args.default_templates_folder, + default_restricted_folder=args.default_restricted_folder, + external_subdir_engine_path=args.external_subdirectory_engine_path, + external_subdir_project_path=args.external_subdirectory_project_path, + remove=args.remove, + force=args.force) + + +def add_parser_args(parser): + """ + add_parser_args is called to add arguments to each command such that it can be + invoked locally or added by a central python file. + Ex. Directly run from this file alone with: python register.py --engine-path "C:/o3de" + :param parser: the caller passes an argparse parser like instance to this method + """ + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('--this-engine', action='store_true', required=False, + default=False, + help='Registers the engine this script is running from.') + group.add_argument('-ep', '--engine-path', type=str, required=False, + help='Engine path to register/remove.') + group.add_argument('-pp', '--project-path', type=str, required=False, + help='Project path to register/remove.') + group.add_argument('-gp', '--gem-path', type=str, required=False, + help='Gem path to register/remove.') + group.add_argument('-es', '--external-subdirectory', type=str, required=False, + help='External subdirectory path to register/remove.') + group.add_argument('-tp', '--template-path', type=str, required=False, + help='Template path to register/remove.') + group.add_argument('-rp', '--restricted-path', type=str, required=False, + help='A restricted folder to register/remove.') + group.add_argument('-ru', '--repo-uri', type=str, required=False, + help='A repo uri to register/remove.') + group.add_argument('-aep', '--all-engines-path', type=str, required=False, + help='All engines under this folder to register/remove.') + group.add_argument('-app', '--all-projects-path', type=str, required=False, + help='All projects under this folder to register/remove.') + group.add_argument('-agp', '--all-gems-path', type=str, required=False, + help='All gems under this folder to register/remove.') + group.add_argument('-atp', '--all-templates-path', type=str, required=False, + help='All templates under this folder to register/remove.') + group.add_argument('-arp', '--all-restricted-path', type=str, required=False, + help='All templates under this folder to register/remove.') + group.add_argument('-aru', '--all-repo-uri', type=str, required=False, + help='All repos under this folder to register/remove.') + group.add_argument('-def', '--default-engines-folder', type=str, required=False, + help='The default engines folder to register/remove.') + group.add_argument('-dpf', '--default-projects-folder', type=str, required=False, + help='The default projects folder to register/remove.') + group.add_argument('-dgf', '--default-gems-folder', type=str, required=False, + help='The default gems folder to register/remove.') + group.add_argument('-dtf', '--default-templates-folder', type=str, required=False, + help='The default templates folder to register/remove.') + group.add_argument('-drf', '--default-restricted-folder', type=str, required=False, + help='The default restricted folder to register/remove.') + group.add_argument('-u', '--update', action='store_true', required=False, + default=False, + help='Refresh the repo cache.') + + parser.add_argument('-ohf', '--override-home-folder', type=str, required=False, + help='By default the home folder is the user folder, override it to this folder.') + parser.add_argument('-r', '--remove', action='store_true', required=False, + default=False, + help='Remove entry.') + parser.add_argument('-f', '--force', action='store_true', default=False, + help='For the update of the registration field being modified.') + + external_subdir_group = parser.add_argument_group(title='external-subdirectory', + description='path arguments to use with the --external-subdirectory option') + external_subdir_path_group = external_subdir_group.add_mutually_exclusive_group() + external_subdir_path_group.add_argument('-esep', '--external-subdirectory-engine-path', type=pathlib.Path, + help='If supplied, registers the external subdirectory with the engine.json at' \ + ' the engine-path location') + external_subdir_path_group.add_argument('-espp', '--external-subdirectory-project-path', type=pathlib.Path) + parser.set_defaults(func=_run_register) + + +def add_args(subparsers) -> None: + """ + add_args is called to add subparsers arguments to each command such that it can be + a central python file such as o3de.py. + It can be run from the o3de.py script as follows + call add_args and execute: python o3de.py register --engine-path "C:/o3de" + :param subparsers: the caller instantiates subparsers and passes it in here + """ + register_subparser = subparsers.add_parser('register') + add_parser_args(register_subparser) + + +def main(): + """ + Runs register.py script as standalone script + """ + # parse the command line args + the_parser = argparse.ArgumentParser() + + # add subparsers + + # add args to the parser + add_parser_args(the_parser) + + # parse args + the_args = the_parser.parse_args() + + # run + ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1 + + # return + sys.exit(ret) + + +if __name__ == "__main__": + main() diff --git a/scripts/o3de/o3de/repo.py b/scripts/o3de/o3de/repo.py new file mode 100644 index 0000000000..c6b4874b6a --- /dev/null +++ b/scripts/o3de/o3de/repo.py @@ -0,0 +1,160 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import json +import logging +import pathlib +import shutil +import urllib.parse +import urllib.request + +from o3de import manifest, utils, validation + +logger = logging.getLogger() +logging.basicConfig() + + +def process_add_o3de_repo(file_name: str or pathlib.Path, + repo_set: set) -> int: + file_name = pathlib.Path(file_name).resolve() + if not validation.valid_o3de_repo_json(file_name): + return 1 + + cache_folder = manifest.get_o3de_cache_folder() + + with file_name.open('r') as f: + try: + repo_data = json.load(f) + except json.JSONDecodeError as e: + logger.error(f'{file_name} failed to load: {str(e)}') + return 1 + + for o3de_object_uris, manifest_json in [(repo_data['engines'], 'engine.json'), + (repo_data['projects'], 'project.json'), + (repo_data['gems'], 'gem.json'), + (repo_data['template'], 'template.json'), + (repo_data['restricted'], 'restricted.json')]: + for o3de_object_uri in o3de_object_uris: + manifest_json_uri = f'{o3de_object_uri}/{manifest_json}' + manifest_json_sha256 = hashlib.sha256(manifest_json_uri.encode()) + cache_file = cache_folder / str(manifest_json_sha256.hexdigest() + '.json') + if not cache_file.is_file(): + parsed_uri = urllib.parse.urlparse(manifest_json_uri) + download_file_result = utils.download_file(parsed_uri, cache_file) + if download_file_result != 0: + return download_file_result + + repo_set |= repo_data['repos'] + return 0 + + +def refresh_repos() -> int: + json_data = manifest.load_o3de_manifest() + + # clear the cache + cache_folder = manifest.get_o3de_cache_folder() + shutil.rmtree(cache_folder) + cache_folder = manifest.get_o3de_cache_folder() # will recreate it + + result = 0 + + # set will stop circular references + repo_set = set() + + for repo_uri in json_data['repos']: + if repo_uri not in repo_set: + repo_set.add(repo_uri) + + repo_uri = f'{repo_uri}/repo.json' + repo_sha256 = hashlib.sha256(repo_uri.encode()) + cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json') + if not cache_file.is_file(): + parsed_uri = urllib.parse.urlparse(repo_uri) + download_file_result = utils.download_file(parsed_uri, cache_file) + if download_file_result != 0: + return download_file_result + + if not validation.valid_o3de_repo_json(cache_file): + logger.error(f'Repo json {repo_uri} is not valid.') + cache_file.unlink() + return 1 + + last_failure = process_add_o3de_repo(cache_file, repo_set) + if last_failure: + result = last_failure + + return result + + +def search_repo(repo_json_data: dict, + engine_name: str = None, + project_name: str = None, + gem_name: str = None, + template_name: str = None, + restricted_name: str = None) -> dict or None: + + if isinstance(engine_name, str) or isinstance(engine_name, pathlib.PurePath): + o3de_object_uris = repo_json_data['engines'] + manifest_json = 'engine.json' + json_key = 'engine_name' + search_func = lambda: None if manifest_json_data.get(json_key, '') == engine_name else manifest_json_data + elif isinstance(project_name, str) or isinstance(project_name, pathlib.PurePath): + o3de_object_uris = repo_json_data['projects'] + manifest_json = 'project.json' + json_key = 'project_name' + search_func = lambda: None if manifest_json_data.get(json_key, '') == project_name else manifest_json_data + elif isinstance(gem_name, str) or isinstance(gem_name, pathlib.PurePath): + o3de_object_uris = repo_json_data['gems'] + manifest_json = 'gem.json' + json_key = 'gem_name' + search_func = lambda: None if manifest_json_data.get(json_key, '') == gem_name else manifest_json_data + elif isinstance(template_name, str) or isinstance(template_name, pathlib.PurePath): + o3de_object_uris = repo_json_data['template'] + manifest_json = 'template.json' + json_key = 'template_name' + search_func = lambda: None if manifest_json_data.get(json_key, '') == template_name_name else manifest_json_data + elif isinstance(restricted_name, str) or isinstance(restricted_name, pathlib.PurePath): + o3de_object_uris = repo_json_data['restricted'] + manifest_json = 'restricted.json' + json_key = 'restricted_name' + search_func = lambda: None if manifest_json_data.get(json_key, '') == restricted_name else manifest_json_data + else: + return None + + o3de_object = search_o3de_object(manifest_json, o3de_object_uris, search_func) + if o3de_object: + return o3de_object + + # recurse into the repos object to search for the o3de object + o3de_object_uris = repo_json_data['repos'] + manifest_json = 'repo.json' + search_func = lambda: search_repo(manifest_json, engine_name, project_name, gem_name, template_name) + return search_o3de_object(manifest_json, o3de_object_uris, search_func) + + +def search_o3de_object(manifest_json, o3de_object_uris, search_func): + # Search for the o3de object based on the supplied object name in the current repo + cache_folder = manifest.get_o3de_cache_folder() + for o3de_object_uri in o3de_object_uris: + manifest_json_uri = f'{o3de_object_uri}/{manifest_json}' + manifest_json_sha256 = hashlib.sha256(manifest_json_uri.encode()) + cache_file = cache_folder / str(manifest_json_sha256.hexdigest() + '.json') + if cache_file.is_file(): + with cache_file.open('r') as f: + try: + manifest_json_data = json.load(f) + except json.JSONDecodeError as e: + logger.warn(f'{cache_file} failed to load: {str(e)}') + else: + result_json_data = search_func() + if result_json_data: + return result_json_data + return None diff --git a/cmake/Tools/preview.png b/scripts/o3de/o3de/resources/preview.png similarity index 100% rename from cmake/Tools/preview.png rename to scripts/o3de/o3de/resources/preview.png diff --git a/scripts/o3de/o3de/sha256.py b/scripts/o3de/o3de/sha256.py new file mode 100644 index 0000000000..db0a1fe834 --- /dev/null +++ b/scripts/o3de/o3de/sha256.py @@ -0,0 +1,117 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import argparse +import json +import logging +import hashlib +import pathlib +import sys + +from o3de import utils + +logger = logging.getLogger() +logging.basicConfig() + + +def sha256(file_path: str or pathlib.Path, + json_path: str or pathlib.Path = None) -> int: + if not file_path: + logger.error(f'File path cannot be empty.') + return 1 + file_path = pathlib.Path(file_path).resolve() + if not file_path.is_file(): + logger.error(f'File path {file_path} does not exist.') + return 1 + + if json_path: + json_path = pathlib.Path(json_path).resolve() + if not json_path.is_file(): + logger.error(f'Json path {json_path} does not exist.') + return 1 + + sha256 = hashlib.sha256(file_path.open('rb').read()).hexdigest() + + if json_path: + with json_path.open('r') as s: + try: + json_data = json.load(s) + except json.JSONDecodeError as e: + logger.error(f'Failed to read Json path {json_path}: {str(e)}') + return 1 + json_data.update({"sha256": sha256}) + utils.backup_file(json_path) + with json_path.open('w') as s: + try: + s.write(json.dumps(json_data, indent=4) + '\n') + except OSError as e: + logger.error(f'Failed to write Json path {json_path}: {str(e)}') + return 1 + else: + print(sha256) + return 0 + + +def _run_sha256(args: argparse) -> int: + return sha256(args.file_path, + args.json_path) + + +def add_parser_args(parser): + """ + add_parser_args is called to add arguments to each command such that it can be + invoked locally or added by a central python file. + Ex. Directly run from this file alone with: python sha256.py --file-path "C:/TestGem" + :param parser: the caller passes an argparse parser like instance to this method + """ + parser.add_argument('-f', '--file-path', type=str, required=True, + help='The path to the file you want to sha256.') + parser.add_argument('-j', '--json-path', type=str, required=False, + help='optional path to an o3de json file to add the "sha256" element to.') + parser.set_defaults(func=_run_sha256) + + +def add_args(subparsers) -> None: + """ + add_args is called to add subparsers arguments to each command such that it can be + a central python file such as o3de.py. + It can be run from the o3de.py script as follows + call add_args and execute: python o3de.py sha256 --file-path "C:/TestGem" + :param subparsers: the caller instantiates subparsers and passes it in here + """ + sha256_subparser = subparsers.add_parser('sha256') + add_parser_args(sha256_subparser) + + +def main(): + """ + Runs sha256.py script as standalone script + """ + # parse the command line args + the_parser = argparse.ArgumentParser() + + # add subparsers + + # add args to the parser + add_parser_args(the_parser) + + # parse args + the_args = the_parser.parse_args() + + # run + ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1 + + # return + sys.exit(ret) + + +if __name__ == "__main__": + main() diff --git a/scripts/o3de/o3de/utils.py b/scripts/o3de/o3de/utils.py new file mode 100755 index 0000000000..4330de25b8 --- /dev/null +++ b/scripts/o3de/o3de/utils.py @@ -0,0 +1,112 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +""" +This file contains utility functions +""" + +import uuid +import pathlib +import shutil +import urllib.request + +def validate_identifier(identifier: str) -> bool: + """ + Determine if the identifier supplied is valid. + :param identifier: the name which needs to to checked + :return: bool: if the identifier is valid or not + """ + if not identifier: + return False + elif len(identifier) > 64: + return False + elif not identifier[0].isalpha(): + return False + else: + for character in identifier: + if not (character.isalnum() or character == '_' or character == '-'): + return False + return True + + +def validate_uuid4(uuid_string: str) -> bool: + """ + Determine if the uuid supplied is valid. + :param uuid_string: the uuid which needs to to checked + :return: bool: if the uuid is valid or not + """ + try: + val = uuid.UUID(uuid_string, version=4) + except ValueError: + return False + return str(val) == uuid_string + + +def backup_file(file_name: str or pathlib.Path) -> None: + index = 0 + renamed = False + while not renamed: + backup_file_name = pathlib.Path(str(file_name) + '.bak' + str(index)).resolve() + index += 1 + if not backup_file_name.is_file(): + file_name = pathlib.Path(file_name).resolve() + file_name.rename(backup_file_name) + if backup_file_name.is_file(): + renamed = True + + +def backup_folder(folder: str or pathlib.Path) -> None: + index = 0 + renamed = False + while not renamed: + backup_folder_name = pathlib.Path(str(folder) + '.bak' + str(index)).resolve() + index += 1 + if not backup_folder_name.is_dir(): + folder = pathlib.Path(folder).resolve() + folder.rename(backup_folder_name) + if backup_folder_name.is_dir(): + renamed = True + + +def download_file(parsed_uri, download_path: pathlib.Path) -> int: + """ + :param parsed_uri: uniform resource identifier to zip file to download + :param download_path: location path on disk to download file + """ + if download_path.is_file(): + logger.warn(f'File already downloaded to {download_path}.') + elif parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']: + with urllib.request.urlopen(url) as s: + with download_path.open('wb') as f: + shutil.copyfileobj(s, f) + else: + origin_file = pathlib.Path(url).resolve() + if not origin_file.is_file(): + return 1 + shutil.copy(origin_file, download_path) + + return 0 + + +def download_zip_file(parsed_uri, download_zip_path: pathlib.Path) -> int: + """ + :param parsed_uri: uniform resource identifier to zip file to download + :param download_zip_path: path to output zip file + """ + download_file_result = download_file(parsed_uri, download_zip_path) + if download_file_result != 0: + return download_file_result + + if not zipfile.is_zipfile(download_zip_path): + logger.error(f"File zip {download_zip_path} is invalid.") + download_zip_path.unlink() + return 1 + + return 0 \ No newline at end of file diff --git a/scripts/o3de/o3de/validation.py b/scripts/o3de/o3de/validation.py new file mode 100644 index 0000000000..721b7eae09 --- /dev/null +++ b/scripts/o3de/o3de/validation.py @@ -0,0 +1,102 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +""" +This file validating o3de object json files +""" +import json +import pathlib + +def valid_o3de_json_dict(json_data: dict, key: str) -> bool: + return key in json_data + + +def valid_o3de_repo_json(file_name: str or pathlib.Path) -> bool: + file_name = pathlib.Path(file_name).resolve() + if not file_name.is_file(): + return False + + with file_name.open('r') as f: + try: + json_data = json.load(f) + test = json_data['repo_name'] + test = json_data['origin'] + except (json.JSONDecodeError, KeyError) as e: + return False + + return True + + +def valid_o3de_engine_json(file_name: str or pathlib.Path) -> bool: + file_name = pathlib.Path(file_name).resolve() + if not file_name.is_file(): + return False + + with file_name.open('r') as f: + try: + json_data = json.load(f) + test = json_data['engine_name'] + except (json.JSONDecodeError, KeyError) as e: + return False + return True + + +def valid_o3de_project_json(file_name: str or pathlib.Path) -> bool: + file_name = pathlib.Path(file_name).resolve() + if not file_name.is_file(): + return False + + with file_name.open('r') as f: + try: + json_data = json.load(f) + test = json_data['project_name'] + except (json.JSONDecodeError, KeyError) as e: + return False + return True + + +def valid_o3de_gem_json(file_name: str or pathlib.Path) -> bool: + file_name = pathlib.Path(file_name).resolve() + if not file_name.is_file(): + return False + + with file_name.open('r') as f: + try: + json_data = json.load(f) + test = json_data['gem_name'] + except (json.JSONDecodeError, KeyError) as e: + return False + return True + + +def valid_o3de_template_json(file_name: str or pathlib.Path) -> bool: + file_name = pathlib.Path(file_name).resolve() + if not file_name.is_file(): + return False + with file_name.open('r') as f: + try: + json_data = json.load(f) + test = json_data['template_name'] + except (json.JSONDecodeError, KeyError) as e: + return False + return True + + +def valid_o3de_restricted_json(file_name: str or pathlib.Path) -> bool: + file_name = pathlib.Path(file_name).resolve() + if not file_name.is_file(): + return False + with file_name.open('r') as f: + try: + json_data = json.load(f) + test = json_data['restricted_name'] + except (json.JSONDecodeError, KeyError) as e: + return False + return True diff --git a/scripts/o3de/setup.py b/scripts/o3de/setup.py new file mode 100644 index 0000000000..595f477c45 --- /dev/null +++ b/scripts/o3de/setup.py @@ -0,0 +1,42 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" +import os +import platform + +from setuptools import setup, find_packages +from setuptools.command.develop import develop +from setuptools.command.build_py import build_py + +PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__)) + +PYTHON_64 = platform.architecture()[0] == '64bit' + + +if __name__ == '__main__': + if not PYTHON_64: + raise RuntimeError("32-bit Python is not a supported platform.") + + with open(os.path.join(PACKAGE_ROOT, 'README.txt')) as f: + long_description = f.read() + + setup( + name="o3de", + version="1.0.0", + description='O3DE editor Python bindings test tools', + long_description=long_description, + packages=find_packages(where='o3de', exclude=['tests']), + install_requires=[ + ], + tests_require=[ + ], + entry_points={ + }, + ) diff --git a/scripts/o3de/tests/CMakeLists.txt b/scripts/o3de/tests/CMakeLists.txt new file mode 100644 index 0000000000..0526c7740d --- /dev/null +++ b/scripts/o3de/tests/CMakeLists.txt @@ -0,0 +1,36 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +if(NOT PAL_TRAIT_BUILD_TESTS_SUPPORTED) + return() +endif() + +# Add a test to test out the o3de package `o3de.py register` command +ly_add_pytest( + NAME o3de_register + PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_register.py + TEST_SUITE smoke + EXCLUDE_TEST_RUN_TARGET_FROM_IDE +) + +ly_add_pytest( + NAME o3de_cmake + PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_cmake.py + TEST_SUITE smoke + EXCLUDE_TEST_RUN_TARGET_FROM_IDE +) + +ly_add_pytest( + NAME o3de_global_project + PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_global_project.py + TEST_SUITE smoke + EXCLUDE_TEST_RUN_TARGET_FROM_IDE +) diff --git a/scripts/o3de/tests/__init__.py b/scripts/o3de/tests/__init__.py new file mode 100644 index 0000000000..4d5680a30d --- /dev/null +++ b/scripts/o3de/tests/__init__.py @@ -0,0 +1,10 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# diff --git a/scripts/o3de/tests/unit_test_cmake.py b/scripts/o3de/tests/unit_test_cmake.py new file mode 100644 index 0000000000..e5ce17dc03 --- /dev/null +++ b/scripts/o3de/tests/unit_test_cmake.py @@ -0,0 +1,69 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import io +import json +import logging +import pytest +import pathlib +from unittest.mock import patch + +from o3de import cmake + + +class TestGetEnabledGems: + @pytest.mark.parametrize( + "enable_gems_cmake_data, expected_set", [ + pytest.param(""" + # Comment + set(ENABLED_GEMS foo bar baz) + """, set(['foo', 'bar', 'baz'])), + pytest.param(""" + # Comment + set(ENABLED_GEMS + foo + bar + baz + ) + """, set(['foo', 'bar', 'baz'])), + pytest.param(""" + # Comment + set(ENABLED_GEMS + foo + bar + baz) + """, set(['foo', 'bar', 'baz'])), + pytest.param(""" + # Comment + set(ENABLED_GEMS + foo bar + baz) + """, set(['foo', 'bar', 'baz'])), + pytest.param(""" + # Comment + set(RANDOM_VARIABLE TestGame, TestProject Test Engine) + set(ENABLED_GEMS HelloWorld IceCream + foo + baz bar + baz baz baz baz baz morebaz lessbaz + ) + Random Text + """, set(['HelloWorld', 'IceCream', 'foo', 'bar', 'baz', 'morebaz', 'lessbaz'])), + ] + ) + def test_get_enabled_gems(self, enable_gems_cmake_data, expected_set): + enabled_gems_set = set() + with patch('pathlib.Path.resolve', return_value=pathlib.Path('enabled_gems.cmake')) as pathlib_is_resolve_mock,\ + patch('pathlib.Path.is_file', return_value=True) as pathlib_is_file_mock,\ + patch('pathlib.Path.open', return_value=io.StringIO(enable_gems_cmake_data)) as pathlib_open_mock: + enabled_gems_set = cmake.get_enabled_gems(pathlib.Path('enabled_gems.cmake')) + + assert enabled_gems_set == expected_set diff --git a/cmake/Tools/unit_test_engine_template.py b/scripts/o3de/tests/unit_test_engine_template.py similarity index 100% rename from cmake/Tools/unit_test_engine_template.py rename to scripts/o3de/tests/unit_test_engine_template.py diff --git a/scripts/o3de/tests/unit_test_global_project.py b/scripts/o3de/tests/unit_test_global_project.py new file mode 100644 index 0000000000..1d3a4dd4f4 --- /dev/null +++ b/scripts/o3de/tests/unit_test_global_project.py @@ -0,0 +1,40 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import io +import json +import logging +import pytest +import pathlib +from unittest.mock import patch + +from o3de import global_project + + +logger = logging.getLogger() +logging.basicConfig() + +DEFAULT_BOOTSTRAP_SETREG = pathlib.Path('~/.o3de/Registry/bootstrap.setreg').expanduser() +PROJECT_PATH_KEY = ('Amazon', 'AzCore', 'Bootstrap', 'project_path') + +class TestSetGlobalProject: + @pytest.mark.parametrize( + "output_path, project_path, force, expected_result", [ + pytest.param(pathlib.Path('~/.o3de/Registry/bootstrap.setreg'), pathlib.Path('A:/'), False, False), + pytest.param(pathlib.Path('~/.o3de/Registry/bootstrap.setreg'), pathlib.Path('A:/'), True, True) + ] + ) + def test_set_global_project_non_existent_project_path(self, output_path, project_path, force, expected_result): + with patch('pathlib.Path.open', return_value=io.StringIO()) as pathlib_open_mock: + result = global_project.set_global_project(output_path, project_path=project_path, force=force) == 0 + + + assert result == expected_result diff --git a/scripts/o3de/tests/unit_test_register.py b/scripts/o3de/tests/unit_test_register.py new file mode 100644 index 0000000000..eb866e76d4 --- /dev/null +++ b/scripts/o3de/tests/unit_test_register.py @@ -0,0 +1,117 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import argparse +import json +import logging +import pytest +import pathlib +from unittest.mock import patch + +from o3de import register + +string_manifest_data = '{}' + +@pytest.mark.parametrize( + "engine_path, engine_name, force, expected_result", [ + pytest.param(pathlib.PurePath('D:/o3de/o3de'), "o3de", False, 0), + # Same engine_name and path should result in valid registration + pytest.param(pathlib.PurePath('D:/o3de/o3de'), "o3de", False, 0), + # Same engine_name and but different path should fail + pytest.param(pathlib.PurePath('D:/o3de/engine-path'), "o3de", False, 1), + # New engine_name should result in valid registration + pytest.param(pathlib.PurePath('D:/o3de/engine-path'), "o3de-other", False, 0), + # Same engine_name and but different path with --force should result in valid registration + pytest.param(pathlib.PurePath('F:/Open3DEngine'), "o3de", True, 0), + ] +) +def test_register_engine_path(engine_path, engine_name, force, expected_result): + parser = argparse.ArgumentParser() + + # Register the registration script subparsers with the current argument parser + register.add_parser_args(parser) + arg_list = ['--engine-path', str(engine_path)] + if force: + arg_list += ['--force'] + args = parser.parse_args(arg_list) + + def load_manifest_from_string() -> dict: + try: + manifest_json = json.loads(string_manifest_data) + except json.JSONDecodeError as err: + logging.error("Error decoding Json from Manifest file") + else: + return manifest_json + def save_manifest_to_string(manifest_json: dict) -> None: + global string_manifest_data + string_manifest_data = json.dumps(manifest_json) + + engine_json_data = {'engine_name': engine_name} + with patch('o3de.manifest.load_o3de_manifest', side_effect=load_manifest_from_string) as load_manifest_mock, \ + patch('o3de.manifest.save_o3de_manifest', side_effect=save_manifest_to_string) as save_manifest_mock, \ + patch('o3de.manifest.get_engine_json_data', return_value=engine_json_data) as engine_paths_mock, \ + patch('o3de.validation.valid_o3de_engine_json', return_value=True) as valid_engine_mock, \ + patch('pathlib.Path.is_dir', return_value=True) as pathlib_is_dir_mock: + result = register._run_register(args) + assert result == expected_result + + +@pytest.fixture(scope='class') +def init_manifest_data(request): + class ManifestData: + def __init__(self): + self.json_string = json.dumps({'default_engines_folder': '', + 'default_projects_folder': '', 'default_gems_folder': '', + 'default_templates_folder': '', 'default_restricted_folder': ''}) + + request.cls.manifest_data = ManifestData() + + +@pytest.mark.usefixtures('init_manifest_data') +class TestRegisterThisEngine: + @pytest.mark.parametrize( + "engine_path, engine_name, force, expected_result", [ + pytest.param(pathlib.PurePath('D:/o3de/o3de'), "o3de", False, 0), + pytest.param(pathlib.PurePath('F:/Open3DEngine'), "o3de", False, 1), + pytest.param(pathlib.PurePath('F:/Open3DEngine'), "o3de", True, 0) + ] + ) + def test_register_this_engine(self, engine_path, engine_name, force, expected_result): + parser = argparse.ArgumentParser() + + # Register the registration script subparsers with the current argument parser + register.add_parser_args(parser) + arg_list = ['--this-engine'] + if force: + arg_list += ['--force'] + args = parser.parse_args(arg_list) + + def load_manifest_from_string() -> dict: + try: + manifest_json = json.loads(self.manifest_data.json_string) + except json.JSONDecodeError as err: + logging.error("Error decoding Json from Manifest file") + else: + return manifest_json + def save_manifest_to_string(manifest_json: dict) -> None: + self.manifest_data.json_string = json.dumps(manifest_json) + + engine_json_data = {'engine_name': engine_name} + + with patch('o3de.manifest.load_o3de_manifest', side_effect=load_manifest_from_string) as load_manifest_mock, \ + patch('o3de.manifest.save_o3de_manifest', side_effect=save_manifest_to_string) as save_manifest_mock, \ + patch('o3de.manifest.get_engine_json_data', return_value=engine_json_data) as engine_paths_mock, \ + patch('o3de.manifest.get_this_engine_path', return_value=engine_path) as engine_paths_mock, \ + patch('o3de.validation.valid_o3de_engine_json', return_value=True) as valid_engine_mock, \ + patch('pathlib.Path.is_dir', return_value=True) as pathlib_is_dir_mock: + result = register._run_register(args) + assert result == expected_result + diff --git a/cmake/Tools/unit_test_utils.py b/scripts/o3de/tests/unit_test_utils.py similarity index 100% rename from cmake/Tools/unit_test_utils.py rename to scripts/o3de/tests/unit_test_utils.py diff --git a/scripts/project_manager/projects.py b/scripts/project_manager/projects.py index 51ffc2be91..f9f40aa4bf 100755 --- a/scripts/project_manager/projects.py +++ b/scripts/project_manager/projects.py @@ -29,11 +29,10 @@ executable_path = '' logger = logging.getLogger() logger.setLevel(logging.INFO) -from cmake.Tools import engine_template -from cmake.Tools import registration +from o3de import disable_gem, enable_gem, cmake, engine_template, manifest, register -o3de_folder = registration.get_o3de_folder() -o3de_logs_folder = registration.get_o3de_logs_folder() +o3de_folder = manifest.get_o3de_folder() +o3de_logs_folder = manifest.get_o3de_logs_folder() project_manager_log_file_path = o3de_logs_folder / "project_manager.log" log_file_handler = RotatingFileHandler(filename=project_manager_log_file_path, maxBytes=1024 * 1024, backupCount=1) formatter = logging.Formatter('%(asctime)s | %(levelname)s : %(message)s') @@ -124,7 +123,7 @@ class ProjectManagerDialog(QObject): super(ProjectManagerDialog, self).__init__(parent) self.ui_path = (pathlib.Path(__file__).parent / 'ui').resolve() - self.home_folder = registration.get_home_folder() + self.home_folder = manifest.get_home_folder() self.log_display = None self.dialog_logger = DialogLogger(self) @@ -187,12 +186,8 @@ class ProjectManagerDialog(QObject): self.remove_restricted_button = self.dialog.findChild(QPushButton, 'removeRestrictedButton') self.remove_restricted_button.clicked.connect(self.remove_restricted_handler) - self.manage_runtime_project_gem_targets_button = self.dialog.findChild(QPushButton, 'manageRuntimeGemTargetsButton') - self.manage_runtime_project_gem_targets_button.clicked.connect(self.manage_runtime_project_gem_targets_handler) - self.manage_tool_project_gem_targets_button = self.dialog.findChild(QPushButton, 'manageToolGemTargetsButton') - self.manage_tool_project_gem_targets_button.clicked.connect(self.manage_tool_project_gem_targets_handler) - self.manage_server_project_gem_targets_button = self.dialog.findChild(QPushButton, 'manageServerGemTargetsButton') - self.manage_server_project_gem_targets_button.clicked.connect(self.manage_server_project_gem_targets_handler) + self.manage_project_gem_targets_button = self.dialog.findChild(QPushButton, 'manageRuntimeGemTargetsButton') + self.manage_project_gem_targets_button.clicked.connect(self.manage_project_gem_targets_handler) self.log_display = self.dialog.findChild(QLabel, 'logDisplay') @@ -202,7 +197,7 @@ class ProjectManagerDialog(QObject): self.dialog.show() def refresh_project_list(self) -> None: - projects = registration.get_all_projects() + projects = manifest.get_all_projects() self.project_list_box.clear() for this_slot in range(len(projects)): display_name = f'{os.path.basename(os.path.normpath(projects[this_slot]))} ({projects[this_slot]})' @@ -256,7 +251,7 @@ class ProjectManagerDialog(QObject): return self.project_list_box.itemData(self.project_list_box.currentIndex(), Qt.ToolTipRole) def get_selected_project_name(self) -> str: - project_data = registration.get_project_data(project_path=self.get_selected_project_path()) + project_data = manifest.get_project_json_data(project_path=self.get_selected_project_path()) return project_data['project_name'] def create_project_handler(self): @@ -298,7 +293,7 @@ class ProjectManagerDialog(QObject): return folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Project Name", - registration.get_o3de_projects_folder().as_posix()) + manifest.get_o3de_projects_folder().as_posix()) folder_dialog.setFileMode(QFileDialog.AnyFile) folder_dialog.setOptions(QFileDialog.ShowDirsOnly) project_count = 0 @@ -314,7 +309,7 @@ class ProjectManagerDialog(QObject): if engine_template.create_project(project_path=project_folder[0], template_path=project_template_path) == 0: # Success - registration.register(project_path=project_folder[0]) + register.register(project_path=project_folder[0]) self.refresh_project_list() msg_box = QMessageBox(parent=self.dialog) msg_box.setWindowTitle("O3DE") @@ -360,7 +355,7 @@ class ProjectManagerDialog(QObject): return folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Gem Name", - registration.get_o3de_gems_folder().as_posix()) + manifest.get_o3de_gems_folder().as_posix()) folder_dialog.setFileMode(QFileDialog.AnyFile) folder_dialog.setOptions(QFileDialog.ShowDirsOnly) gem_count = 0 @@ -376,7 +371,7 @@ class ProjectManagerDialog(QObject): if engine_template.create_gem(gem_path=gem_folder[0], template_path=gem_template_path) == 0: # Success - registration.register(gem_path=gem_folder[0]) + register.register(gem_path=gem_folder[0]) msg_box = QMessageBox(parent=self.dialog) msg_box.setWindowTitle("O3DE") msg_box.setText(f"Gem {gem_folder[0]} created.") @@ -392,13 +387,13 @@ class ProjectManagerDialog(QObject): source_folder = QFileDialog.getExistingDirectory(self.dialog, "Select a Folder to make a template out of.", - registration.get_o3de_folder().as_posix()) + manifest.get_o3de_folder().as_posix()) if not source_folder: return destination_template_folder_dialog = QFileDialog(self.dialog, "Select where the template is to be created and named.", - registration.get_o3de_templates_folder().as_posix()) + manifest.get_o3de_templates_folder().as_posix()) destination_template_folder_dialog.setFileMode(QFileDialog.AnyFile) destination_template_folder_dialog.setOptions(QFileDialog.ShowDirsOnly) destination_folder = None @@ -410,7 +405,7 @@ class ProjectManagerDialog(QObject): if engine_template.create_template(source_path=source_folder, template_path=destination_folder[0]) == 0: # Success - registration.register(template_path=destination_folder[0]) + register.register(template_path=destination_folder[0]) msg_box = QMessageBox(parent=self.dialog) msg_box.setWindowTitle("O3DE") msg_box.setText(f"Template {destination_folder[0]} created.") @@ -454,7 +449,7 @@ class ProjectManagerDialog(QObject): return folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Gem Name", - registration.get_o3de_gems_folder().as_posix()) + manifest.get_o3de_gems_folder().as_posix()) folder_dialog.setFileMode(QFileDialog.AnyFile) folder_dialog.setOptions(QFileDialog.ShowDirsOnly) gem_count = 0 @@ -483,9 +478,9 @@ class ProjectManagerDialog(QObject): :return: None """ project_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Project Folder", - registration.get_o3de_projects_folder().as_posix()) + manifest.get_o3de_projects_folder().as_posix()) if project_folder: - if registration.register(project_path=project_folder) == 0: + if register.register(project_path=project_folder) == 0: # Success self.refresh_project_list() @@ -502,9 +497,9 @@ class ProjectManagerDialog(QObject): :return: None """ gem_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Gem Folder", - registration.get_o3de_gems_folder().as_posix()) + manifest.get_o3de_gems_folder().as_posix()) if gem_folder: - if registration.register(gem_path=gem_folder) == 0: + if register.register(gem_path=gem_folder) == 0: # Success msg_box = QMessageBox(parent=self.dialog) msg_box.setWindowTitle("O3DE") @@ -519,9 +514,9 @@ class ProjectManagerDialog(QObject): :return: None """ template_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Template Folder", - registration.get_o3de_templates_folder().as_posix()) + manifest.get_o3de_templates_folder().as_posix()) if template_folder: - if registration.register(template_path=template_folder) == 0: + if register.register(template_path=template_folder) == 0: # Success msg_box = QMessageBox(parent=self.dialog) msg_box.setWindowTitle("O3DE") @@ -536,9 +531,9 @@ class ProjectManagerDialog(QObject): :return: None """ restricted_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Restricted Folder", - registration.get_o3de_restricted_folder().as_posix()) + manifest.get_o3de_restricted_folder().as_posix()) if restricted_folder: - if registration.register(restricted_path=restricted_folder) == 0: + if register.register(restricted_path=restricted_folder) == 0: # Success msg_box = QMessageBox(parent=self.dialog) msg_box.setWindowTitle("O3DE") @@ -553,9 +548,9 @@ class ProjectManagerDialog(QObject): :return: None """ project_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Project Folder", - registration.get_o3de_projects_folder().as_posix()) + manifest.get_o3de_projects_folder().as_posix()) if project_folder: - if registration.register(project_path=project_folder, remove=True) == 0: + if register.register(project_path=project_folder, remove=True) == 0: # Success self.refresh_project_list() @@ -572,9 +567,9 @@ class ProjectManagerDialog(QObject): :return: None """ gem_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Gem Folder", - registration.get_o3de_gems_folder().as_posix()) + manifest.get_o3de_gems_folder().as_posix()) if gem_folder: - if registration.register(gem_path=gem_folder, remove=True) == 0: + if register.register(gem_path=gem_folder, remove=True) == 0: # Success msg_box = QMessageBox(parent=self.dialog) msg_box.setWindowTitle("O3DE") @@ -589,9 +584,9 @@ class ProjectManagerDialog(QObject): :return: None """ template_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Template Folder", - registration.get_o3de_templates_folder().as_posix()) + manifest.get_o3de_templates_folder().as_posix()) if template_folder: - if registration.register(template_path=template_folder, remove=True) == 0: + if register.register(template_path=template_folder, remove=True) == 0: # Success msg_box = QMessageBox(parent=self.dialog) msg_box.setWindowTitle("O3DE") @@ -606,9 +601,9 @@ class ProjectManagerDialog(QObject): :return: None """ restricted_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Restricted Folder", - registration.get_o3de_restricted_folder().as_posix()) + manifest.get_o3de_restricted_folder().as_posix()) if restricted_folder: - if registration.register(restricted_path=restricted_folder, remove=True) == 0: + if register.register(restricted_path=restricted_folder, remove=True) == 0: # Success msg_box = QMessageBox(parent=self.dialog) msg_box.setWindowTitle("O3DE") @@ -616,7 +611,7 @@ class ProjectManagerDialog(QObject): msg_box.exec() return - def manage_runtime_project_gem_targets_handler(self): + def manage_project_gem_targets_handler(self): """ Opens the Gem management pane. Waits for the load thread to complete if still running and displays all active gems for the current project as well as all available gems which aren't currently active. @@ -643,121 +638,26 @@ class ProjectManagerDialog(QObject): logger.error(f'Failed to load gems dialog file at {self.manage_project_gem_targets_ui_file_path.as_posix()}') return - self.manage_project_gem_targets_dialog.setWindowTitle(f"Manage Runtime Gem Targets for Project:" + self.manage_project_gem_targets_dialog.setWindowTitle(f"Manage Gems for Project:" f" {self.get_selected_project_name()}") self.add_gem_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, 'addGemTargetsButton') - self.add_gem_button.clicked.connect(self.add_runtime_project_gem_targets_handler) + self.add_gem_button.clicked.connect(self.add_project_gem_targets_handler) self.available_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView, 'availableGemTargetsList') - self.refresh_runtime_project_gem_targets_available_list() + self.refresh_project_gem_targets_available_list() self.remove_project_gem_targets_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, 'removeGemTargetsButton') - self.remove_project_gem_targets_button.clicked.connect(self.remove_runtime_project_gem_targets_handler) + self.remove_project_gem_targets_button.clicked.connect(self.remove_project_gem_targets_handler) self.enabled_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView, 'enabledGemTargetsList') - self.refresh_runtime_project_gem_targets_enabled_list() + self.refresh_project_gem_targets_enabled_list() self.manage_project_gem_targets_dialog.exec() - def manage_tool_project_gem_targets_handler(self): - """ - Opens the Gem management pane. Waits for the load thread to complete if still running and displays all - active gems for the current project as well as all available gems which aren't currently active. - :return: None - """ - - if not self.get_selected_project_path(): - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText("Please select a project") - msg_box.exec() - return - - loader = QUiLoader() - self.manage_project_gem_targets_file = QFile(self.manage_project_gem_targets_ui_file_path.as_posix()) - - if not self.manage_project_gem_targets_file: - logger.error(f'Failed to load manage gem targets UI file at {self.manage_project_gem_targets_ui_file_path}') - return - - self.manage_project_gem_targets_dialog = loader.load(self.manage_project_gem_targets_file) - - if not self.manage_project_gem_targets_dialog: - logger.error( - f'Failed to load gems dialog file at {self.manage_project_gem_targets_ui_file_path.as_posix()}') - return - - self.manage_project_gem_targets_dialog.setWindowTitle(f"Manage Tool Gem Targets for Project:" - f" {self.get_selected_project_name()}") - - self.add_gem_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, 'addGemTargetsButton') - self.add_gem_button.clicked.connect(self.add_tool_project_gem_targets_handler) - - self.available_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView, - 'availableGemTargetsList') - self.refresh_tool_project_gem_targets_available_list() - - self.remove_project_gem_targets_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, - 'removeGemTargetsButton') - self.remove_project_gem_targets_button.clicked.connect(self.remove_tool_project_gem_targets_handler) - - self.enabled_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView, - 'enabledGemTargetsList') - self.refresh_tool_project_gem_targets_enabled_list() - - self.manage_project_gem_targets_dialog.exec() - - def manage_server_project_gem_targets_handler(self): - """ - Opens the Gem management pane. Waits for the load thread to complete if still running and displays all - active gems for the current project as well as all available gems which aren't currently active. - :return: None - """ - - if not self.get_selected_project_path(): - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText("Please select a project") - msg_box.exec() - return - - loader = QUiLoader() - self.manage_project_gem_targets_file = QFile(self.manage_project_gem_targets_ui_file_path.as_posix()) - - if not self.manage_project_gem_targets_file: - logger.error(f'Failed to load manage gem targets UI file at {self.manage_project_gem_targets_ui_file_path}') - return - - self.manage_project_gem_targets_dialog = loader.load(self.manage_project_gem_targets_file) - - if not self.manage_project_gem_targets_dialog: - logger.error( - f'Failed to load gems dialog file at {self.manage_project_gem_targets_ui_file_path.as_posix()}') - return - - self.manage_project_gem_targets_dialog.setWindowTitle(f"Manage Server Gem Targets for Project:" - f" {self.get_selected_project_name()}") - - self.add_gem_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, 'addGemTargetsButton') - self.add_gem_button.clicked.connect(self.add_server_project_gem_targets_handler) - - self.available_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView, - 'availableGemTargetsList') - self.refresh_server_project_gem_targets_available_list() - - self.remove_project_gem_targets_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, - 'removeGemTargetsButton') - self.remove_project_gem_targets_button.clicked.connect(self.remove_server_project_gem_targets_handler) - - self.enabled_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView, - 'enabledGemTargetsList') - self.refresh_server_project_gem_targets_enabled_list() - - self.manage_project_gem_targets_dialog.exec() def manage_project_gem_targets_get_selected_available_gems(self) -> list: selected_items = self.available_gem_targets_list.selectionModel().selectedRows() @@ -767,185 +667,67 @@ class ProjectManagerDialog(QObject): selected_items = self.enabled_gem_targets_list.selectionModel().selectedRows() return [(self.enabled_gem_targets_list.model().data(item)) for item in selected_items] - def add_runtime_project_gem_targets_handler(self) -> None: - gem_paths = registration.get_all_gems() + def add_project_gem_targets_handler(self) -> None: + gem_paths = manifest.get_all_gems() for gem_target in self.manage_project_gem_targets_get_selected_available_gems(): for gem_path in gem_paths: - this_gems_targets = registration.get_gem_targets(gem_path=gem_path) - for this_gem_target in this_gems_targets: - if gem_target == this_gem_target: - registration.add_gem_to_project(gem_path=gem_path, - gem_target=gem_target, - project_path=self.get_selected_project_path(), - runtime_dependency=True) - self.refresh_runtime_project_gem_targets_available_list() - self.refresh_runtime_project_gem_targets_enabled_list() - return - self.refresh_runtime_project_gem_targets_available_list() - self.refresh_runtime_project_gem_targets_enabled_list() - - def remove_runtime_project_gem_targets_handler(self): - gem_paths = registration.get_all_gems() - for gem_target in self.manage_project_gem_targets_get_selected_enabled_gems(): - for gem_path in gem_paths: - this_gems_targets = registration.get_gem_targets(gem_path=gem_path) - for this_gem_target in this_gems_targets: - if gem_target == this_gem_target: - registration.remove_gem_from_project(gem_path=gem_path, - gem_target=gem_target, - project_path=self.get_selected_project_path(), - runtime_dependency=True) - self.refresh_runtime_project_gem_targets_available_list() - self.refresh_runtime_project_gem_targets_enabled_list() - return - self.refresh_runtime_project_gem_targets_available_list() - self.refresh_runtime_project_gem_targets_enabled_list() - - def add_tool_project_gem_targets_handler(self) -> None: - gem_paths = registration.get_all_gems() - for gem_target in self.manage_project_gem_targets_get_selected_available_gems(): - for gem_path in gem_paths: - this_gems_targets = registration.get_gem_targets(gem_path=gem_path) - for this_gem_target in this_gems_targets: - if gem_target == this_gem_target: - registration.add_gem_to_project(gem_path=gem_path, - gem_target=gem_target, - project_path=self.get_selected_project_path(), - tool_dependency=True) - self.refresh_tool_project_gem_targets_available_list() - self.refresh_tool_project_gem_targets_enabled_list() - return - self.refresh_tool_project_gem_targets_available_list() - self.refresh_tool_project_gem_targets_enabled_list() - - def remove_tool_project_gem_targets_handler(self): - gem_paths = registration.get_all_gems() - for gem_target in self.manage_project_gem_targets_get_selected_enabled_gems(): - for gem_path in gem_paths: - this_gems_targets = registration.get_gem_targets(gem_path=gem_path) - for this_gem_target in this_gems_targets: - if gem_target == this_gem_target: - registration.remove_gem_from_project(gem_path=gem_path, - gem_target=gem_target, - project_path=self.get_selected_project_path(), - tool_dependency=True) - self.refresh_tool_project_gem_targets_available_list() - self.refresh_tool_project_gem_targets_enabled_list() - return - self.refresh_tool_project_gem_targets_available_list() - self.refresh_tool_project_gem_targets_enabled_list() - - def add_server_project_gem_targets_handler(self) -> None: - gem_paths = registration.get_all_gems() - for gem_target in self.manage_project_gem_targets_get_selected_available_gems(): - for gem_path in gem_paths: - this_gems_targets = registration.get_gem_targets(gem_path=gem_path) - for this_gem_target in this_gems_targets: - if gem_target == this_gem_target: - registration.add_gem_to_project(gem_path=gem_path, - gem_target=gem_target, - project_path=self.get_selected_project_path(), - server_dependency=True) - self.refresh_server_project_gem_targets_available_list() - self.refresh_server_project_gem_targets_enabled_list() - return - self.refresh_server_project_gem_targets_available_list() - self.refresh_server_project_gem_targets_enabled_list() - - def remove_server_project_gem_targets_handler(self): - gem_paths = registration.get_all_gems() + enable_gem.enable_gem_in_project(gem_path=gem_path, + project_path=self.get_selected_project_path()) + self.refresh_project_gem_targets_available_list() + self.refresh_project_gem_targets_enabled_list() + return + self.refresh_project_gem_targets_available_list() + self.refresh_project_gem_targets_enabled_list() + + def remove_project_gem_targets_handler(self): + gem_paths = manifest.get_all_gems() for gem_target in self.manage_project_gem_targets_get_selected_enabled_gems(): for gem_path in gem_paths: - this_gems_targets = registration.get_gem_targets(gem_path=gem_path) - for this_gem_target in this_gems_targets: - if gem_target == this_gem_target: - registration.remove_gem_from_project(gem_path=gem_path, - gem_target=gem_target, - project_path=self.get_selected_project_path(), - server_dependency=True) - self.refresh_server_project_gem_targets_available_list() - self.refresh_server_project_gem_targets_enabled_list() - return - self.refresh_server_project_gem_targets_available_list() - self.refresh_server_project_gem_targets_enabled_list() - - def refresh_runtime_project_gem_targets_enabled_list(self) -> None: - enabled_project_gem_targets_model = QStandardItemModel() - enabled_project_gem_targets = registration.get_project_runtime_gem_targets( - project_path=self.get_selected_project_path()) - for gem_target in sorted(enabled_project_gem_targets): - model_item = QStandardItem(gem_target) - enabled_project_gem_targets_model.appendRow(model_item) - self.enabled_gem_targets_list.setModel(enabled_project_gem_targets_model) + disable_gem.disable_gem_in_project(gem_path=gem_path, + project_path=self.get_selected_project_path()) + self.refresh_project_gem_targets_available_list() + self.refresh_project_gem_targets_enabled_list() + return + self.refresh_project_gem_targets_available_list() + self.refresh_project_gem_targets_enabled_list() - def refresh_runtime_project_gem_targets_available_list(self) -> None: - available_project_gem_targets_model = QStandardItemModel() - enabled_project_gem_targets = registration.get_project_runtime_gem_targets( - project_path=self.get_selected_project_path()) - all_gem_targets = registration.get_all_gem_targets() - for gem_target in sorted(all_gem_targets): - if gem_target not in enabled_project_gem_targets: - model_item = QStandardItem(gem_target) - available_project_gem_targets_model.appendRow(model_item) - self.available_gem_targets_list.setModel(available_project_gem_targets_model) - - def refresh_tool_project_gem_targets_enabled_list(self) -> None: + def refresh_project_gem_targets_enabled_list(self) -> None: enabled_project_gem_targets_model = QStandardItemModel() - enabled_project_gem_targets = registration.get_project_tool_gem_targets( - project_path=self.get_selected_project_path()) - for gem_target in sorted(enabled_project_gem_targets): + enabled_project_gems = cmake.get_project_gems(project_path=self.get_selected_project_path()) + for gem_target in sorted(enabled_project_gems): model_item = QStandardItem(gem_target) enabled_project_gem_targets_model.appendRow(model_item) self.enabled_gem_targets_list.setModel(enabled_project_gem_targets_model) - def refresh_tool_project_gem_targets_available_list(self) -> None: - available_project_gem_targets_model = QStandardItemModel() - enabled_project_gem_targets = registration.get_project_tool_gem_targets( - project_path=self.get_selected_project_path()) - all_gem_targets = registration.get_all_gem_targets() - for gem_target in sorted(all_gem_targets): - if gem_target not in enabled_project_gem_targets: - model_item = QStandardItem(gem_target) - available_project_gem_targets_model.appendRow(model_item) - self.available_gem_targets_list.setModel(available_project_gem_targets_model) - - def refresh_server_project_gem_targets_enabled_list(self) -> None: - enabled_project_gem_targets_model = QStandardItemModel() - enabled_project_gem_targets = registration.get_project_server_gem_targets( - project_path=self.get_selected_project_path()) - for gem_target in sorted(enabled_project_gem_targets): - model_item = QStandardItem(gem_target) - enabled_project_gem_targets_model.appendRow(model_item) - self.enabled_gem_targets_list.setModel(enabled_project_gem_targets_model) - def refresh_server_project_gem_targets_available_list(self) -> None: + def refresh_project_gem_targets_available_list(self) -> None: available_project_gem_targets_model = QStandardItemModel() - enabled_project_gem_targets = registration.get_project_server_gem_targets( - project_path=self.get_selected_project_path()) - all_gem_targets = registration.get_all_gem_targets() + enabled_project_gem_targets = cmake.get_project_gems(project_path=self.get_selected_project_path()) + all_gem_targets = manifest.get_all_gems() for gem_target in sorted(all_gem_targets): if gem_target not in enabled_project_gem_targets: model_item = QStandardItem(gem_target) available_project_gem_targets_model.appendRow(model_item) self.available_gem_targets_list.setModel(available_project_gem_targets_model) + def refresh_create_project_template_list(self) -> None: self.create_project_template_model = QStandardItemModel() - for project_template_path in registration.get_project_templates(): + for project_template_path in manifest.get_project_templates(): model_item = QStandardItem(project_template_path) self.create_project_template_model.appendRow(model_item) self.create_project_template_list.setModel(self.create_project_template_model) def refresh_create_gem_template_list(self) -> None: self.create_gem_template_model = QStandardItemModel() - for gem_template_path in registration.get_gem_templates(): + for gem_template_path in manifest.get_gem_templates(): model_item = QStandardItem(gem_template_path) self.create_gem_template_model.appendRow(model_item) self.create_gem_template_list.setModel(self.create_gem_template_model) def refresh_create_from_template_list(self) -> None: self.create_from_template_model = QStandardItemModel() - for generic_template_path in registration.get_generic_templates(): + for generic_template_path in manifest.get_generic_templates(): model_item = QStandardItem(generic_template_path) self.create_from_template_model.appendRow(model_item) self.create_from_template_list.setModel(self.create_from_template_model) diff --git a/system_android_es3.cfg b/system_android_android.cfg similarity index 93% rename from system_android_es3.cfg rename to system_android_android.cfg index 46ab50f558..d5bfddeb73 100644 --- a/system_android_es3.cfg +++ b/system_android_android.cfg @@ -1,4 +1,4 @@ --- config file used when the android platform is running off 'es3' assets. +-- config file used when the android platform is running off 'android' assets. sys_float_exceptions=0 log_IncludeTime=1 sys_PakLogInvalidFileAccess=1 diff --git a/system_mac_osx_gl.cfg b/system_mac_mac.cfg similarity index 100% rename from system_mac_osx_gl.cfg rename to system_mac_mac.cfg