Merge branch 'development' of https://github.com/o3de/o3de into cgalvan/DraftStreamingImageAssetPixelAPI

This commit is contained in:
Chris Galvan
2022-01-24 16:18:38 -06:00
139 changed files with 2021 additions and 1445 deletions
@@ -12,12 +12,6 @@
################################################################################
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
# Only enable AWS automated tests on Windows
set(SUPPORTED_PLATFORMS "Windows" "Linux")
if (NOT "${PAL_PLATFORM_NAME}" IN_LIST SUPPORTED_PLATFORMS)
return()
endif()
ly_add_pytest(
NAME AutomatedTesting::AWSTests
TEST_SUITE awsi
@@ -3,7 +3,7 @@
## Prerequisites
1. Build the O3DE Editor and AutomatedTesting.GameLauncher in Profile.
2. Install the latest version of NodeJs.
3. AWS CLI is installed and configured following [Configuration and Credential File Settings](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html).
3. AWS CLI is installed and AWS crendentials are configured via [environment variables](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html) or [default profile](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html).
4. [AWS Cloud Development Kit (CDK)](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html#getting_started_install) is installed.
## Deploy CDK Applications
@@ -68,6 +68,10 @@ class AwsCredentials:
if (len(self._credentials.sections()) == 0) and (not self._credentials_file_exists):
os.remove(self._credentials_path)
return
credentials_file_dir = os.path.dirname(self._credentials_path)
if not os.path.isdir(credentials_file_dir):
os.makedirs(credentials_file_dir)
with open(self._credentials_path, 'w+') as credential_file:
self._credentials.write(credential_file)
@@ -16,7 +16,7 @@ logging.getLogger('nose').setLevel(logging.WARNING)
class AwsUtils:
def __init__(self, arn: str, session_name: str, region_name: str):
local_session = boto3.Session(profile_name='default')
local_session = boto3.Session()
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}')
@@ -6,11 +6,13 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import platform
# ARN of the IAM role to assume for retrieving temporary AWS credentials
ASSUME_ROLE_ARN = os.environ.get('ASSUME_ROLE_ARN', 'arn:aws:iam::645075835648:role/o3de-automation-tests')
# Name of the AWS project deployed by the CDK applications
AWS_PROJECT_NAME = os.environ.get('O3DE_AWS_PROJECT_NAME', 'AWSAUTO')
AWS_PROJECT_NAME = os.environ.get('O3DE_AWS_PROJECT_NAME').upper() if os.environ.get('O3DE_AWS_PROJECT_NAME') else \
(os.environ.get('BRANCH_NAME', '') + '-' + os.environ.get('PIPELINE_NAME', '') + '-' + platform.system()).upper()
# Region for the existing CloudFormation stacks used by the automation tests
AWS_REGION = os.environ.get('O3DE_AWS_DEPLOY_REGION', 'us-east-1')
# Name of the default resource mapping config file used by the automation tests
@@ -21,7 +21,7 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAutomation(EditorTestSuite):
enable_prefab_system = False
enable_prefab_system = True
@pytest.mark.test_case_id("C36525657")
class AtomEditorComponents_BloomAdded(EditorSharedTest):
@@ -120,6 +120,14 @@ class TestAutomation(EditorTestSuite):
class AtomEditorComponents_SSAOAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_SSAOAdded as test_module
@pytest.mark.test_case_id("C36529666")
class AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded as test_module
@pytest.mark.test_case_id("C36525660")
class AtomEditorComponentsLevel_DisplayMapperAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponentsLevel_DisplayMapperAdded as test_module
class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest):
from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module
@@ -26,13 +26,3 @@ class TestAutomation(EditorTestSuite):
@pytest.mark.test_case_id("C36525660")
class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module
# this test causes editor to crash when using slices. once automation transitions to prefabs it should pass
@pytest.mark.test_case_id("C36529666")
class AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded as test_module
# this test causes editor to crash when using slices. once automation transitions to prefabs it should pass
@pytest.mark.test_case_id("C36525660")
class AtomEditorComponentsLevel_DisplayMapperAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponentsLevel_DisplayMapperAdded as test_module
@@ -60,7 +60,7 @@ def AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Add Diffuse Global Illumination level component to the level entity.
@@ -86,10 +86,10 @@ def AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity():
# 4. Set Quality Level property to Low
diffuse_global_illumination_component.set_component_property_value(
AtomComponentProperties.diffuse_global_illumination('Quality Level', GLOBAL_ILLUMINATION_QUALITY['Low']))
AtomComponentProperties.diffuse_global_illumination('Quality Level'), GLOBAL_ILLUMINATION_QUALITY['Low'])
quality = diffuse_global_illumination_component.get_component_property_value(
AtomComponentProperties.diffuse_global_illumination('Quality Level'))
Report.result(diffuse_global_illumination_quality, quality == GLOBAL_ILLUMINATION_QUALITY['Low'])
Report.result(Tests.diffuse_global_illumination_quality, quality == GLOBAL_ILLUMINATION_QUALITY['Low'])
# 5. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
@@ -67,7 +67,7 @@ def AtomEditorComponentsLevel_DisplayMapper_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Add Display Mapper level component to the level entity.
@@ -102,7 +102,7 @@ def AtomEditorComponentsLevel_DisplayMapper_AddedToEntity():
display_mapper_component.set_component_property_value(
AtomComponentProperties.display_mapper('Enable LDR color grading LUT'), True)
Report.result(
Test.enable_ldr_color_grading_lut,
Tests.enable_ldr_color_grading_lut,
display_mapper_component.get_component_property_value(
AtomComponentProperties.display_mapper('Enable LDR color grading LUT')) is True)
@@ -97,7 +97,7 @@ def AtomEditorComponents_Bloom_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create an Bloom entity with no components.
@@ -170,10 +170,12 @@ def AtomEditorComponents_Bloom_AddedToEntity():
# 13. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, bloom_entity.exists())
# 14. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not bloom_entity.exists())
# 15. Look for errors and asserts.
@@ -95,7 +95,7 @@ def AtomEditorComponents_Decal_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Decal entity with no components.
@@ -162,6 +162,7 @@ def AtomEditorComponents_Decal_AddedToEntity():
# 11. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not decal_entity.exists())
# 12. Look for errors and asserts.
@@ -97,7 +97,7 @@ def AtomEditorComponents_DeferredFog_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create an Deferred Fog entity with no components.
@@ -174,10 +174,12 @@ def AtomEditorComponents_DeferredFog_AddedToEntity():
# 13. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, deferred_fog_entity.exists())
# 14. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not deferred_fog_entity.exists())
# 15. Look for errors and asserts.
@@ -107,7 +107,7 @@ def AtomEditorComponents_DepthOfField_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a DepthOfField entity with no components.
@@ -189,10 +189,12 @@ def AtomEditorComponents_DepthOfField_AddedToEntity():
# 15. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, depth_of_field_entity.exists())
# 16. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not depth_of_field_entity.exists())
# 17. Look for errors and asserts.
@@ -90,7 +90,7 @@ def AtomEditorComponents_DiffuseProbeGrid_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Diffuse Probe Grid entity with no components.
@@ -168,10 +168,12 @@ def AtomEditorComponents_DiffuseProbeGrid_AddedToEntity():
# 12. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, diffuse_probe_grid_entity.exists())
# 13. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not diffuse_probe_grid_entity.exists())
# 14. Look for errors or asserts.
@@ -95,7 +95,7 @@ def AtomEditorComponents_DirectionalLight_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Directional Light entity with no components.
@@ -168,10 +168,12 @@ def AtomEditorComponents_DirectionalLight_AddedToEntity():
# 12. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, directional_light_entity.exists())
# 13. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not directional_light_entity.exists())
# 14. Look for errors and asserts.
@@ -91,7 +91,7 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Display Mapper entity with no components.
@@ -166,10 +166,12 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
# 11. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, display_mapper_entity.exists())
# 12. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not display_mapper_entity.exists())
# 13. Look for errors and asserts.
@@ -81,7 +81,7 @@ def AtomEditorComponents_EntityReference_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create an Entity Reference entity with no components.
@@ -139,10 +139,12 @@ def AtomEditorComponents_EntityReference_AddedToEntity():
# 9. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, entity_reference_entity.exists())
# 10. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not entity_reference_entity.exists())
# 11. Look for errors and asserts.
@@ -101,7 +101,7 @@ def AtomEditorComponents_ExposureControl_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Creation of Exposure Control entity with no components.
@@ -169,10 +169,12 @@ def AtomEditorComponents_ExposureControl_AddedToEntity():
# 12. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, exposure_control_entity.exists())
# 13. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not exposure_control_entity.exists())
# 14. Look for errors and asserts.
@@ -99,7 +99,7 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Global Skylight (IBL) entity with no components.
@@ -176,10 +176,12 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity():
# 11. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, global_skylight_entity.exists())
# 12. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not global_skylight_entity.exists())
# 13. Look for errors and asserts.
@@ -82,7 +82,7 @@ def AtomEditorComponents_Grid_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Grid entity with no components.
@@ -139,10 +139,12 @@ def AtomEditorComponents_Grid_AddedToEntity():
# 9. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, grid_entity.exists())
# 10. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not grid_entity.exists())
# 11. Look for errors or asserts.
@@ -96,7 +96,7 @@ def AtomEditorComponents_HDRColorGrading_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create an HDR Color Grading entity with no components.
@@ -173,10 +173,12 @@ def AtomEditorComponents_HDRColorGrading_AddedToEntity():
# 13. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, hdr_color_grading_entity.exists())
# 14. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not hdr_color_grading_entity.exists())
# 15. Look for errors and asserts.
@@ -87,7 +87,7 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create an HDRi Skybox with no components.
@@ -158,10 +158,12 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity():
# 10. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, hdri_skybox_entity.exists())
# 11. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not hdri_skybox_entity.exists())
# 12. Look for errors or asserts.
@@ -89,7 +89,7 @@ def AtomEditorComponents_Light_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Light entity with no components.
@@ -144,10 +144,12 @@ def AtomEditorComponents_Light_AddedToEntity():
# 9. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, light_entity.exists())
# 10. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not light_entity.exists())
# 11. Look for errors asserts.
@@ -104,7 +104,7 @@ def AtomEditorComponents_LookModification_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create an Look Modification entity with no components.
@@ -192,10 +192,12 @@ def AtomEditorComponents_LookModification_AddedToEntity():
# 14. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, look_modification_entity.exists())
# 15. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not look_modification_entity.exists())
# 16. Look for errors and asserts.
@@ -102,7 +102,7 @@ def AtomEditorComponents_Material_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Material entity with no components.
@@ -184,10 +184,12 @@ def AtomEditorComponents_Material_AddedToEntity():
# 16. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, material_entity.exists())
# 17. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not material_entity.exists())
# 18. Look for errors or asserts.
@@ -87,7 +87,7 @@ def AtomEditorComponents_Mesh_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Mesh entity with no components.
@@ -151,10 +151,12 @@ def AtomEditorComponents_Mesh_AddedToEntity():
# 10. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, mesh_entity.exists())
# 11. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not mesh_entity.exists())
# 12. Look for errors or asserts.
@@ -80,7 +80,7 @@ def AtomEditorComponents_OcclusionCullingPlane_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a occlusion culling plane entity with no components.
@@ -140,10 +140,12 @@ def AtomEditorComponents_OcclusionCullingPlane_AddedToEntity():
# 9. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, occlusion_culling_plane_entity.exists())
# 10. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not occlusion_culling_plane_entity.exists())
# 11. Look for errors or asserts.
@@ -89,7 +89,7 @@ def AtomEditorComponents_PhysicalSky_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Physical Sky entity with no components.
@@ -146,10 +146,12 @@ def AtomEditorComponents_PhysicalSky_AddedToEntity():
# 9. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, physical_sky_entity.exists())
# 10. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not physical_sky_entity.exists())
# 11. Look for errors and asserts.
@@ -92,7 +92,7 @@ def AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a PostFX Gradient Weight Modifier entity with no components.
@@ -162,10 +162,12 @@ def AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity():
# 12. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, postfx_gradient_weight_entity.exists())
# 13. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not postfx_gradient_weight_entity.exists())
# 14. Look for errors or asserts.
@@ -80,7 +80,7 @@ def AtomEditorComponents_postfx_layer_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a PostFX Layer entity with no components.
@@ -137,10 +137,12 @@ def AtomEditorComponents_postfx_layer_AddedToEntity():
# 9. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, postfx_layer_entity.exists())
# 10. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not postfx_layer_entity.exists())
# 11. Look for errors or asserts.
@@ -92,7 +92,7 @@ def AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Post FX Radius Weight Modifier entity with no components.
@@ -161,10 +161,12 @@ def AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity():
# 12. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, postfx_radius_weight_entity.exists())
# 13. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not postfx_radius_weight_entity.exists())
# 14. Look for errors and asserts.
@@ -98,7 +98,7 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a PostFx Shape Weight Modifier entity with no components.
@@ -188,10 +188,12 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity():
# 15. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, postfx_shape_weight_entity.exists())
# 16. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not postfx_shape_weight_entity.exists())
# 17. Look for errors or asserts.
@@ -97,7 +97,7 @@ def AtomEditorComponents_ReflectionProbe_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Reflection Probe entity with no components.
@@ -183,10 +183,12 @@ def AtomEditorComponents_ReflectionProbe_AddedToEntity():
# 13. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, reflection_probe_entity.exists())
# 14. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not reflection_probe_entity.exists())
# 15. Look for errors or asserts.
@@ -94,7 +94,7 @@ def AtomEditorComponents_SSAO_AddedToEntity():
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a SSAO entity with no components.
@@ -163,10 +163,12 @@ def AtomEditorComponents_SSAO_AddedToEntity():
# 12. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, ssao_entity.exists())
# 13. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not ssao_entity.exists())
# 14. Look for errors and asserts.
@@ -0,0 +1,53 @@
{
"ContainerEntity": {
"Id": "Entity_[1146574390643]",
"Name": "Level",
"Components": {
"Component_[10641544592923449938]": {
"$type": "EditorInspectorComponent",
"Id": 10641544592923449938
},
"Component_[12039882709170782873]": {
"$type": "EditorOnlyEntityComponent",
"Id": 12039882709170782873
},
"Component_[12265484671603697631]": {
"$type": "EditorPendingCompositionComponent",
"Id": 12265484671603697631
},
"Component_[14126657869720434043]": {
"$type": "EditorEntitySortComponent",
"Id": 14126657869720434043
},
"Component_[15230859088967841193]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 15230859088967841193,
"Parent Entity": ""
},
"Component_[16239496886950819870]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 16239496886950819870
},
"Component_[5688118765544765547]": {
"$type": "EditorEntityIconComponent",
"Id": 5688118765544765547
},
"Component_[6545738857812235305]": {
"$type": "SelectionComponent",
"Id": 6545738857812235305
},
"Component_[7247035804068349658]": {
"$type": "EditorPrefabComponent",
"Id": 7247035804068349658
},
"Component_[9307224322037797205]": {
"$type": "EditorLockComponent",
"Id": 9307224322037797205
},
"Component_[9562516168917670048]": {
"$type": "EditorVisibilityComponent",
"Id": 9562516168917670048
}
}
}
}
@@ -0,0 +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
+1 -1
View File
@@ -101,7 +101,7 @@ void CEditorPreferencesPage_AWS::SaveSettingsRegistryFile()
return;
}
bool saved{};
[[maybe_unused]] bool saved{};
constexpr auto configurationMode =
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPath.data(), configurationMode))
+1 -1
View File
@@ -1128,7 +1128,7 @@ void SEditorSettings::SaveSettingsRegistryFile()
return;
}
bool saved{};
[[maybe_unused]] bool saved{};
constexpr auto configurationMode = AZ::IO::SystemFile::SF_OPEN_CREATE
| AZ::IO::SystemFile::SF_OPEN_CREATE_PATH
| AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
+18 -21
View File
@@ -87,29 +87,15 @@ public:
}
// Handle labels with submenus
if (auto toolLabel = qobject_cast<QLabel*>(toolWidget))
if (auto toolLabel = qobject_cast<QToolButton*>(toolWidget))
{
if (!toolLabel->isVisible())
{
// Manually turn the custom context menus into submenus
if (toolLabel->objectName() == "m_fovStaticCtrl")
if (toolLabel->menu())
{
QAction* newAction = menu->addMenu(m_viewportDlg->GetFovMenu());
newAction->setText(QString("FOV: %1").arg(toolLabel->text()));
}
else if (toolLabel->objectName() == "m_ratioStaticCtrl")
{
QAction* newAction = menu->addMenu(m_viewportDlg->GetAspectMenu());
newAction->setText(QString("Ratio: %1").arg(toolLabel->text()));
}
else if (toolLabel->objectName() == "m_sizeStaticCtrl")
{
QAction* newAction = menu->addMenu(m_viewportDlg->GetResolutionMenu());
newAction->setText(QString("%1").arg(toolLabel->text()));
}
else
{
// Don't add actions for other Labels
QAction* action = menu->addMenu(toolLabel->menu());
action->setText(toolLabel->text());
continue;
}
}
@@ -179,14 +165,25 @@ CLayoutViewPane::CLayoutViewPane(QWidget* parent)
toolbar->installEventFilter(&m_viewportTitleDlg);
toolbar->setContextMenuPolicy(Qt::CustomContextMenu);
connect(toolbar, &QWidget::customContextMenuRequested, &m_viewportTitleDlg, &QWidget::customContextMenuRequested);
setContextMenuPolicy(Qt::NoContextMenu);
if (QToolButton* expansion = AzQtComponents::ToolBar::getToolBarExpansionButton(toolbar))
{
expansion->installEventFilter(m_expanderWatcher);
}
AzQtComponents::BreadCrumbs* prefabsBreadcrumbs =
qobject_cast<AzQtComponents::BreadCrumbs*>(toolbar->findChild<QWidget*>("m_prefabFocusPath"));
QToolButton* backButton = qobject_cast<QToolButton*>(toolbar->findChild<QWidget*>("m_prefabFocusBackButton"));
AZ_Assert(prefabsBreadcrumbs, "Could not find Prefabs Breadcrumbs widget on CLayoutViewPane initialization!");
AZ_Assert(backButton, "Could not find Prefabs Breadcrumbs back button on CLayoutViewPane initialization!");
if (prefabsBreadcrumbs && backButton)
{
m_viewportTitleDlg.InitializePrefabViewportFocusPathHandler(prefabsBreadcrumbs, backButton);
}
m_id = -1;
}
@@ -745,7 +742,7 @@ namespace
void PySetActiveViewport(unsigned int viewportIndex)
{
bool success = false;
[[maybe_unused]] bool success = false;
CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout();
if (layout)
{
+18 -6
View File
@@ -383,12 +383,7 @@ void CViewportTitleDlg::OnInitDialog()
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (isPrefabSystemEnabled)
{
m_prefabViewportFocusPathHandler = new AzToolsFramework::Prefab::PrefabViewportFocusPathHandler();
m_prefabViewportFocusPathHandler->Initialize(m_ui->m_prefabFocusPath, m_ui->m_prefabFocusBackButton);
}
else
if (!isPrefabSystemEnabled)
{
m_ui->m_prefabFocusPath->setEnabled(false);
m_ui->m_prefabFocusBackButton->setEnabled(false);
@@ -397,6 +392,23 @@ void CViewportTitleDlg::OnInitDialog()
}
}
void CViewportTitleDlg::InitializePrefabViewportFocusPathHandler(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton)
{
if (m_prefabViewportFocusPathHandler != nullptr)
{
return;
}
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (isPrefabSystemEnabled)
{
m_prefabViewportFocusPathHandler = new AzToolsFramework::Prefab::PrefabViewportFocusPathHandler();
m_prefabViewportFocusPathHandler->Initialize(breadcrumbsWidget, backButton);
}
}
//////////////////////////////////////////////////////////////////////////
void CViewportTitleDlg::SetTitle(const QString& title)
{
+2
View File
@@ -70,6 +70,8 @@ public:
QMenu* const GetAspectMenu();
QMenu* const GetResolutionMenu();
void InitializePrefabViewportFocusPathHandler(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton);
Q_SIGNALS:
void ActionTriggered(int command);
+15
View File
@@ -80,6 +80,9 @@
<property name="toolTip">
<string>Camera settings</string>
</property>
<property name="text">
<string>Camera settings</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/camera.svg</normaloff>:/Menu/camera.svg</iconset>
@@ -91,6 +94,9 @@
<property name="toolTip">
<string>Debug information</string>
</property>
<property name="text">
<string>Debug information</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/debug.svg</normaloff>:/Menu/debug.svg</iconset>
@@ -105,6 +111,9 @@
<property name="toolTip">
<string>Toggle viewport helpers</string>
</property>
<property name="text">
<string>Toggle viewport helpers</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/helpers.svg</normaloff>:/Menu/helpers.svg</iconset>
@@ -119,6 +128,9 @@
<property name="toolTip">
<string>Viewport resolution</string>
</property>
<property name="text">
<string>Viewport resolution</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/resolution.svg</normaloff>:/Menu/resolution.svg</iconset>
@@ -130,6 +142,9 @@
<property name="toolTip">
<string>Other settings</string>
</property>
<property name="text">
<string>Other settings</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/menu.svg</normaloff>:/Menu/menu.svg</iconset>
@@ -25,7 +25,7 @@ namespace AZ
void Matrix3x4SetRowGeneric(Matrix3x4* thisPtr, ScriptDataContext& dc)
{
bool rowIsSet = false;
[[maybe_unused]] bool rowIsSet = false;
if (dc.GetNumArguments() >= 5)
{
if (dc.IsNumber(0))
@@ -88,7 +88,7 @@ namespace AZ
void Matrix3x4SetColumnGeneric(Matrix3x4* thisPtr, ScriptDataContext& dc)
{
bool columnIsSet = false;
[[maybe_unused]] bool columnIsSet = false;
if (dc.GetNumArguments() >= 4)
{
if (dc.IsNumber(0))
@@ -133,7 +133,7 @@ namespace AZ
void Matrix3x4SetTranslationGeneric(Matrix3x4* thisPtr, ScriptDataContext& dc)
{
bool translationIsSet = false;
[[maybe_unused]] bool translationIsSet = false;
if (dc.GetNumArguments() == 3 &&
dc.IsNumber(0) &&
+1 -1
View File
@@ -77,7 +77,7 @@ namespace AZ
// check open brace
char c = *current++;
bool has_open_brace = false;
[[maybe_unused]] bool has_open_brace = false;
if (c == '{')
{
c = *current++;
@@ -85,7 +85,7 @@ namespace AZ
NameDictionary::~NameDictionary()
{
bool leaksDetected = false;
[[maybe_unused]] bool leaksDetected = false;
for (const auto& keyValue : m_dictionary)
{
@@ -642,7 +642,7 @@ namespace UnitTest
char buffer[RandomStringBufferSize];
AZStd::sys_time_t newNameTime;
AZStd::sys_time_t existingNameTime;
[[maybe_unused]] AZStd::sys_time_t existingNameTime;
AZStd::sys_time_t stringTime;
{
@@ -195,7 +195,7 @@ namespace AzFramework::AssetBenchmark
// Console command: Add the given list of assets to the list of assets to load with BenchmarkLoadAssetList
void BenchmarkAddAssetsToList(const AZ::ConsoleCommandContainer& parameters)
{
bool allAssetsAdded = true;
[[maybe_unused]] bool allAssetsAdded = true;
for (auto& assetName : parameters)
{
@@ -24,7 +24,7 @@ namespace AzFramework
//! IntersectorBus::EventResult(result, editorContextId, &IntersectorInterface::RayIntersect, ray);
//!
//! Raycast against all entities
//! RayResultAggregator rayResult;
//! AZ::EBusReduceResult<RayResult, RayResultClosestAggregator> rayResult;
//! IntersectorBus::BroadCastResult(rayResult, &IntersectorInterface::RayIntersect, ray);
//
class IntersectorInterface
@@ -12,6 +12,8 @@
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/std/containers/span.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Render/GeometryIntersectionStructures.h>
#include <AzFramework/SurfaceData/SurfaceData.h>
namespace AzFramework
@@ -185,6 +187,11 @@ namespace AzFramework
SurfacePointRegionFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
//! Get the terrain raycast entity context id.
virtual EntityContextId GetTerrainRaycastEntityContextId() const = 0;
//! Given a ray, return the closest intersection with terrain.
virtual RenderGeometry::RayResult GetClosestIntersection(const RenderGeometry::RayRequest& ray) const = 0;
private:
// Private variations of the GetSurfacePoint API exposed to BehaviorContext that returns a value instead of
@@ -81,7 +81,7 @@ namespace UnitTest
TEST_P(Rotation, Permutation)
{
int expectedErrors = -1;
[[maybe_unused]] int expectedErrors = -1;
AZ_TEST_START_TRACE_SUPPRESSION;
// Given an orientation derived from the look at points
@@ -102,5 +102,9 @@ namespace UnitTest
ProcessSurfaceWeightsFromRegion, void(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler));
MOCK_CONST_METHOD4(
ProcessSurfacePointsFromRegion, void(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler));
MOCK_CONST_METHOD0(
GetTerrainRaycastEntityContextId, AzFramework::EntityContextId());
MOCK_CONST_METHOD1(
GetClosestIntersection, AzFramework::RenderGeometry::RayResult(const AzFramework::RenderGeometry::RayRequest&));
};
} // namespace UnitTest
@@ -134,7 +134,7 @@ namespace AzQtComponents
QColor color;
QString colorName(m_namedVariables.value(name));
bool colorSet = false;
[[maybe_unused]] bool colorSet = false;
if (QColor::isValidColor(colorName))
{
color.setNamedColor(colorName);
@@ -32,7 +32,8 @@ namespace AzQtComponents
{
if (QWidget* widget = item->widget())
{
toolbar->addWidget(widget);
QAction* action = toolbar->addWidget(widget);
action->setObjectName(widget->objectName());
}
else if (item->spacerItem())
{
@@ -177,7 +177,7 @@ namespace AzToolsFramework
auto data = index.data(AssetBrowserModel::Roles::EntryRole);
if (data.canConvert<const AssetBrowserEntry*>())
{
bool isEnabled = (option.state & QStyle::State_Enabled) != 0;
[[maybe_unused]] bool isEnabled = (option.state & QStyle::State_Enabled) != 0;
QStyle* style = option.widget ? option.widget->style() : QApplication::style();
@@ -506,7 +506,7 @@ namespace AzToolsFramework
//Remove all Links owned by the Template from TemplateToLinkIdsMap.
Template& templateToDelete = findTemplateResult->get();
const Template::Links& linkIdsToDelete = templateToDelete.GetLinks();
bool result;
[[maybe_unused]] bool result;
for (auto linkId : linkIdsToDelete)
{
result = RemoveLinkIdFromTemplateToLinkIdsMap(linkId);
@@ -774,7 +774,7 @@ namespace AzToolsFramework
}
Link& link = findLinkResult->get();
bool result;
[[maybe_unused]] bool result;
result = RemoveLinkIdFromTemplateToLinkIdsMap(linkId, link);
AZ_Assert(result,
"Prefab - PrefabSystemComponent::RemoveLink - "
@@ -926,7 +926,7 @@ namespace AzToolsFramework
const bool entityIsFromIgnoredSliceInstance = ignoreSliceInstance && ignoreSliceInstance->IsValid() && ignoreSliceInstance->GetReference()->GetSliceAsset().GetId() == instanceAddr.GetReference()->GetSliceAsset().GetId();
if (!entityIsFromIgnoredSliceInstance)
{
bool foundTargetAncestor = false;
[[maybe_unused]] bool foundTargetAncestor = false;
const AZ::SliceComponent::EntityList& entitiesInInstance = instanceAddr.GetInstance()->GetInstantiated()->m_entities;
@@ -890,6 +890,7 @@ namespace AzToolsFramework
m_actionGoToEntitiesInViewport = new QAction(tr("Find in viewport"), this);
m_actionGoToEntitiesInViewport->setShortcutContext(Qt::WidgetWithChildrenShortcut);
m_actionGoToEntitiesInViewport->setShortcut(tr("Z"));
connect(m_actionGoToEntitiesInViewport, &QAction::triggered, this, &EntityOutlinerWidget::GoToEntitiesInViewport);
addAction(m_actionGoToEntitiesInViewport);
}
@@ -73,6 +73,9 @@ namespace AzToolsFramework::Prefab
{
// Push new Path
m_breadcrumbsWidget->pushPath(m_prefabFocusPublicInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str());
// If root instance is focused, disable the back button; else enable it.
m_backButton->setEnabled(m_prefabFocusPublicInterface->GetPrefabFocusPathLength(m_editorEntityContextId) > 1);
}
} // namespace AzToolsFramework::Prefab
@@ -45,7 +45,7 @@ namespace AzToolsFramework
void PropertyEnumComboBoxCtrl::setValue(AZ::s64 value)
{
m_pComboBox->blockSignals(true);
bool indexWasFound = false;
[[maybe_unused]] bool indexWasFound = false;
for (size_t enumValIndex = 0; enumValIndex < m_enumValues.size(); enumValIndex++)
{
if (m_enumValues[enumValIndex].first == value)
@@ -7,6 +7,7 @@
*/
#include <AzFramework/Render/IntersectorInterface.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
namespace AzToolsFramework
@@ -66,15 +67,19 @@ namespace AzToolsFramework
AZ::Vector3 FindClosestPickIntersection(const AzFramework::RenderGeometry::RayRequest& rayRequest, const float defaultDistance)
{
AzFramework::RenderGeometry::RayResult renderGeometryIntersectionResult;
// attempt a ray intersection with any visible mesh or terrain and return the intersection position if successful
AZ::EBusReduceResult<AzFramework::RenderGeometry::RayResult, AzFramework::RenderGeometry::RayResultClosestAggregator> renderGeometryIntersectionResult;
AzFramework::RenderGeometry::IntersectorBus::EventResult(
renderGeometryIntersectionResult, AzToolsFramework::GetEntityContextId(),
&AzFramework::RenderGeometry::IntersectorBus::Events::RayIntersect, rayRequest);
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
renderGeometryIntersectionResult,
&AzFramework::Terrain::TerrainDataRequests::GetClosestIntersection,
rayRequest);
// attempt a ray intersection with any visible mesh and return the intersection position if successful
if (renderGeometryIntersectionResult)
if (renderGeometryIntersectionResult.value)
{
return renderGeometryIntersectionResult.m_worldPosition;
return renderGeometryIntersectionResult.value.m_worldPosition;
}
else
{
@@ -430,7 +430,7 @@ namespace UnitTest
size_t nextIndex = 1;
size_t slices = 0;
size_t liveAllocs = 0;
size_t totalAllocs = 0;
[[maybe_unused]] size_t totalAllocs = 0;
auto cb = [&liveAllocs](void*, const AZ::Debug::AllocationInfo&, unsigned char)
{
@@ -1952,8 +1952,8 @@ CarrierThread::ProcessConnections()
bool isHandshakeTimeOut = false;
bool isConnectionTimeout = false;
bool isBadTrafficConditions = false;
bool isBadPackets = false;
[[maybe_unused]] bool isBadTrafficConditions = false;
[[maybe_unused]] bool isBadPackets = false;
if (connection->m_isBadPackets)
{
isBadPackets = true;
@@ -544,7 +544,7 @@ void ApplicationManagerBase::InitConnectionManager()
EBUS_EVENT(AssetProcessor::ConnectionBus, SendPerPlatform, 0, message, QString::fromUtf8(message.m_platform.c_str()));
};
bool result = QObject::connect(GetAssetCatalog(), &AssetProcessor::AssetCatalog::SendAssetMessage, connectionAndChangeMessagesThreadContext, forwardMessageFunction, Qt::QueuedConnection);
[[maybe_unused]] bool result = QObject::connect(GetAssetCatalog(), &AssetProcessor::AssetCatalog::SendAssetMessage, connectionAndChangeMessagesThreadContext, forwardMessageFunction, Qt::QueuedConnection);
AZ_Assert(result, "Failed to connect to AssetCatalog signal");
//Application manager related stuff
@@ -19,7 +19,7 @@ namespace AssetProcessor
TNetResponse netResponse;
netRequest.m_request = request;
AZ::u32 type;
[[maybe_unused]] AZ::u32 type;
QByteArray data;
AZStd::binary_semaphore wait;
@@ -41,7 +41,7 @@ namespace O3DE::ProjectManager
o3deUserPath /= AZ::SettingsRegistryInterface::RegistryFolder;
o3deUserPath /= "ProjectManager.setreg";
bool saved = false;
[[maybe_unused]] bool saved = false;
constexpr auto configurationMode =
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
@@ -25,7 +25,7 @@ protected:
sceneCoreModule = AZ::DynamicModuleHandle::Create("SceneCore");
AZ_Assert(sceneCoreModule, "SceneBuilder unit tests failed to create SceneCore module.");
bool loaded = sceneCoreModule->Load(false);
[[maybe_unused]] bool loaded = sceneCoreModule->Load(false);
AZ_Assert(loaded, "SceneBuilder unit tests failed to load SceneCore module.");
auto init = sceneCoreModule->GetFunction<AZ::InitializeDynamicModuleFunction>(AZ::InitializeDynamicModuleFunctionName);
AZ_Assert(init, "SceneBuilder unit tests failed to find the initialization function the SceneCore module.");
@@ -223,7 +223,7 @@ namespace AWSCore
return;
}
bool saved {};
[[maybe_unused]] bool saved {};
constexpr auto configurationMode =
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPathAWSPreference.c_str(), configurationMode))
@@ -120,9 +120,9 @@ namespace AssetValidation
AZ::SimpleLcgRandom randomizer(seedValue);
int lastTick = 0;
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> heldAssets;
AZStd::size_t heldCount{ 0 };
AZ::u64 changeCount{ 0 };
AZ::u64 blockCount{ 0 };
[[maybe_unused]] AZStd::size_t heldCount{ 0 };
[[maybe_unused]] AZ::u64 changeCount{ 0 };
[[maybe_unused]] AZ::u64 blockCount{ 0 };
AZ_TracePrintf("TestChangeAssets", "Beginning run with %zu assets\n", assetList.size());
while (!forceStop && runMs < runTime)
{
@@ -537,7 +537,7 @@ namespace AZ
request.m_buffer = m_boxIndexBuffer.get();
request.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, m_boxIndices.size() * sizeof(uint16_t) };
request.m_initialData = m_boxIndices.data();
AZ::RHI::ResultCode result = m_bufferPool->InitBuffer(request);
[[maybe_unused]] AZ::RHI::ResultCode result = m_bufferPool->InitBuffer(request);
AZ_Error("DiffuseProbeGridFeatureProcessor", result == RHI::ResultCode::Success, "Failed to initialize box index buffer - error [%d]", result);
// create index buffer view
@@ -123,7 +123,7 @@ namespace AZ
// create the BLAS buffers for each sub-mesh, or re-use existing BLAS objects if they were already created.
// Note: all sub-meshes must either create new BLAS objects or re-use existing ones, otherwise it's an error (it's the same model in both cases)
// Note: the buffer is just reserved here, the BLAS is built in the RayTracingAccelerationStructurePass
bool blasInstanceFound = false;
[[maybe_unused]] bool blasInstanceFound = false;
for (uint32_t subMeshIndex = 0; subMeshIndex < mesh.m_subMeshes.size(); ++subMeshIndex)
{
SubMesh& subMesh = mesh.m_subMeshes[subMeshIndex];
@@ -397,7 +397,7 @@ namespace AZ
request.m_buffer = m_boxIndexBuffer.get();
request.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, m_boxIndices.size() * sizeof(uint16_t) };
request.m_initialData = m_boxIndices.data();
AZ::RHI::ResultCode result = m_bufferPool->InitBuffer(request);
[[maybe_unused]] AZ::RHI::ResultCode result = m_bufferPool->InitBuffer(request);
AZ_Error("ReflectionProbeFeatureProcessor", result == RHI::ResultCode::Success, "Failed to initialize box index buffer - error [%d]", result);
// create index buffer view
@@ -204,35 +204,56 @@ namespace AZ
// do the enumeration for each view, keep track of the lowest lod for each entry,
// and submit the appropriate dispatch item
//the [1][1] element of a perspective projection matrix stores cot(FovY/2) (equal to 2*nearPlaneDistance/nearPlaneHeight),
//which is used to determine the (vertical) projected size in screen space
const float yScale = viewToClip.GetElement(1, 1);
const bool isPerspective = viewToClip.GetElement(3, 3) == 0.f;
const Vector3 cameraPos = view->GetViewToWorldMatrix().GetTranslation();
const Vector3 pos = cullable.m_cullData.m_boundingSphere.GetCenter();
const float approxScreenPercentage = RPI::ModelLodUtils::ApproxScreenPercentage(
pos, cullable.m_lodData.m_lodSelectionRadius, cameraPos, yScale, isPerspective);
for (size_t lodIndex = 0; lodIndex < cullable.m_lodData.m_lods.size(); ++lodIndex)
switch (cullable.m_lodData.m_lodConfiguration.m_lodType)
{
const RPI::Cullable::LodData::Lod& lod = cullable.m_lodData.m_lods[lodIndex];
//Note that this supports overlapping lod ranges (to support cross-fading lods, for example)
if (approxScreenPercentage >= lod.m_screenCoverageMin && approxScreenPercentage <= lod.m_screenCoverageMax)
case RPI::Cullable::LodType::SpecificLod:
{
AZStd::lock_guard lock(m_dispatchItemMutex);
auto lodIndex = cullable.m_lodData.m_lodConfiguration.m_lodOverride;
m_skinningDispatches.insert(&renderProxy.m_dispatchItemsByLod[lodIndex]->GetRHIDispatchItem());
for (size_t morphTargetIndex = 0; morphTargetIndex < renderProxy.m_morphTargetDispatchItemsByLod[lodIndex].size(); morphTargetIndex++)
{
AZStd::lock_guard lock(m_dispatchItemMutex);
m_skinningDispatches.insert(&renderProxy.m_dispatchItemsByLod[lodIndex]->GetRHIDispatchItem());
for (size_t morphTargetIndex = 0; morphTargetIndex < renderProxy.m_morphTargetDispatchItemsByLod[lodIndex].size(); morphTargetIndex++)
const MorphTargetDispatchItem* dispatchItem = renderProxy.m_morphTargetDispatchItemsByLod[lodIndex][morphTargetIndex].get();
if (dispatchItem && dispatchItem->GetWeight() > AZ::Constants::FloatEpsilon)
{
const MorphTargetDispatchItem* dispatchItem = renderProxy.m_morphTargetDispatchItemsByLod[lodIndex][morphTargetIndex].get();
if (dispatchItem && dispatchItem->GetWeight() > AZ::Constants::FloatEpsilon)
m_morphTargetDispatches.insert(&dispatchItem->GetRHIDispatchItem());
}
}
}
break;
case RPI::Cullable::LodType::ScreenCoverage:
default:
//the [1][1] element of a perspective projection matrix stores cot(FovY/2) (equal to 2*nearPlaneDistance/nearPlaneHeight),
//which is used to determine the (vertical) projected size in screen space
const float yScale = viewToClip.GetElement(1, 1);
const bool isPerspective = viewToClip.GetElement(3, 3) == 0.f;
const Vector3 cameraPos = view->GetViewToWorldMatrix().GetTranslation();
const Vector3 pos = cullable.m_cullData.m_boundingSphere.GetCenter();
const float approxScreenPercentage = RPI::ModelLodUtils::ApproxScreenPercentage(
pos, cullable.m_lodData.m_lodSelectionRadius, cameraPos, yScale, isPerspective);
for (size_t lodIndex = 0; lodIndex < cullable.m_lodData.m_lods.size(); ++lodIndex)
{
const RPI::Cullable::LodData::Lod& lod = cullable.m_lodData.m_lods[lodIndex];
//Note that this supports overlapping lod ranges (to support cross-fading lods, for example)
if (approxScreenPercentage >= lod.m_screenCoverageMin && approxScreenPercentage <= lod.m_screenCoverageMax)
{
AZStd::lock_guard lock(m_dispatchItemMutex);
m_skinningDispatches.insert(&renderProxy.m_dispatchItemsByLod[lodIndex]->GetRHIDispatchItem());
for (size_t morphTargetIndex = 0; morphTargetIndex < renderProxy.m_morphTargetDispatchItemsByLod[lodIndex].size(); morphTargetIndex++)
{
m_morphTargetDispatches.insert(&dispatchItem->GetRHIDispatchItem());
const MorphTargetDispatchItem* dispatchItem = renderProxy.m_morphTargetDispatchItemsByLod[lodIndex][morphTargetIndex].get();
if (dispatchItem && dispatchItem->GetWeight() > AZ::Constants::FloatEpsilon)
{
m_morphTargetDispatches.insert(&dispatchItem->GetRHIDispatchItem());
}
}
}
}
break;
}
}
}
@@ -364,7 +364,7 @@ namespace AZ
AZ_Assert(success, "PipelineStateEntry already exists in the pending cache.");
}
ResultCode resultCode = ResultCode::InvalidArgument;
[[maybe_unused]] ResultCode resultCode = ResultCode::InvalidArgument;
// Increment the pending compile count on the global entry, which tracks how many pipeline states
// are currently being compiled across all threads.
@@ -120,7 +120,7 @@ namespace AZ
AZ::RHI::BufferInitRequest scratchBufferRequest;
scratchBufferRequest.m_buffer = buffers.m_scratchBuffer.get();
scratchBufferRequest.m_descriptor = scratchBufferDescriptor;
RHI::ResultCode resultCode = bufferPools.GetScratchBufferPool()->InitBuffer(scratchBufferRequest);
[[maybe_unused]] RHI::ResultCode resultCode = bufferPools.GetScratchBufferPool()->InitBuffer(scratchBufferRequest);
AZ_Assert(resultCode == RHI::ResultCode::Success, "failed to create BLAS scratch buffer");
BufferMemoryView* scratchMemoryView = static_cast<Buffer*>(buffers.m_scratchBuffer.get())->GetBufferMemoryView();
@@ -170,7 +170,7 @@ namespace AZ
createInfo.basePipelineHandle = nullptr;
createInfo.basePipelineIndex = 0;
VkResult result = vkCreateRayTracingPipelinesKHR(device.GetNativeDevice(), nullptr, nullptr, 1, &createInfo, nullptr, &m_pipeline);
[[maybe_unused]] VkResult result = vkCreateRayTracingPipelinesKHR(device.GetNativeDevice(), nullptr, nullptr, 1, &createInfo, nullptr, &m_pipeline);
AZ_Assert(result == VK_SUCCESS, "vkCreateRayTracingPipelinesKHR failed");
// retrieve the shader handles
@@ -43,7 +43,7 @@ namespace AZ
AZ::RHI::BufferInitRequest shaderTableBufferRequest;
shaderTableBufferRequest.m_buffer = shaderTableBuffer.get();
shaderTableBufferRequest.m_descriptor = shaderTableBufferDescriptor;
RHI::ResultCode resultCode = bufferPools.GetShaderTableBufferPool()->InitBuffer(shaderTableBufferRequest);
[[maybe_unused]] RHI::ResultCode resultCode = bufferPools.GetShaderTableBufferPool()->InitBuffer(shaderTableBufferRequest);
AZ_Assert(resultCode == RHI::ResultCode::Success, "failed to create shader table buffer");
BufferMemoryView* shaderTableMemoryView = static_cast<Buffer*>(shaderTableBuffer.get())->GetBufferMemoryView();
@@ -66,7 +66,7 @@ namespace AZ
AZ::RHI::BufferInitRequest tlasInstancesBufferRequest;
tlasInstancesBufferRequest.m_buffer = buffers.m_tlasInstancesBuffer.get();
tlasInstancesBufferRequest.m_descriptor = tlasInstancesBufferDescriptor;
RHI::ResultCode resultCode = bufferPools.GetTlasInstancesBufferPool()->InitBuffer(tlasInstancesBufferRequest);
[[maybe_unused]] RHI::ResultCode resultCode = bufferPools.GetTlasInstancesBufferPool()->InitBuffer(tlasInstancesBufferRequest);
AZ_Assert(resultCode == RHI::ResultCode::Success, "failed to create TLAS instances buffer");
BufferMemoryView* tlasInstancesMemoryView = static_cast<Buffer*>(buffers.m_tlasInstancesBuffer.get())->GetBufferMemoryView();
@@ -160,7 +160,7 @@ namespace AZ
AZ::RHI::BufferInitRequest scratchBufferRequest;
scratchBufferRequest.m_buffer = buffers.m_scratchBuffer.get();
scratchBufferRequest.m_descriptor = scratchBufferDescriptor;
RHI::ResultCode resultCode = bufferPools.GetScratchBufferPool()->InitBuffer(scratchBufferRequest);
[[maybe_unused]] RHI::ResultCode resultCode = bufferPools.GetScratchBufferPool()->InitBuffer(scratchBufferRequest);
AZ_Assert(resultCode == RHI::ResultCode::Success, "failed to create TLAS scratch buffer");
BufferMemoryView* scratchMemoryView = static_cast<Buffer*>(buffers.m_scratchBuffer.get())->GetBufferMemoryView();
@@ -9,6 +9,7 @@
#pragma once
#include <AzCore/Name/Name.h>
#include <AtomCore/std/containers/array_view.h>
namespace AZ
{
@@ -16,31 +17,38 @@ namespace AZ
{
class MaterialAsset;
//! Utility for building material property names consisting of a group name and a property sub-name.
//! Represented as "[groupName].[propertyName]".
//! The group name is optional, in which case the ID will just be "[propertyName]".
//! Utility for building material property IDs.
//! These IDs are represented like "groupA.groupB.[...].propertyName".
//! The groups are optional, in which case the full property ID will just be like "propertyName".
class MaterialPropertyId
{
public:
static bool IsValidName(AZStd::string_view name);
static bool IsValidName(const AZ::Name& name);
//! Creates a MaterialPropertyId from a full name string like "[groupName].[propertyName]" or just "[propertyName]"
//! Creates a MaterialPropertyId from a full name string like "groupA.groupB.[...].propertyName" or just "propertyName".
//! Also checks the name for validity.
static MaterialPropertyId Parse(AZStd::string_view fullPropertyId);
MaterialPropertyId() = default;
explicit MaterialPropertyId(AZStd::string_view propertyName);
MaterialPropertyId(AZStd::string_view groupName, AZStd::string_view propertyName);
MaterialPropertyId(const Name& groupName, const Name& propertyName);
explicit MaterialPropertyId(const AZStd::array_view<AZStd::string> names);
AZ_DEFAULT_COPY_MOVE(MaterialPropertyId);
const Name& GetGroupName() const;
const Name& GetPropertyName() const;
const Name& GetFullName() const;
operator const Name&() const;
//! Returns a pointer to the full name ("[groupName].[propertyName]").
//! Same as Name::GetCStr()
//! This is included for convenience so it can be used for error messages in the same way an AZ::Name is used.
const char* GetCStr() const;
//! Returns a string_view of the full name ("[groupName].[propertyName]").
//! Same as Name::GetStringView()
//! This is included for convenience so it can be used for string comparison in the same way an AZ::Name is used.
AZStd::string_view GetStringView() const;
//! Returns a hash of the full name. This is needed for compatibility with NameIdReflectionMap.
Name::Hash GetHash() const;
@@ -52,8 +60,6 @@ namespace AZ
private:
Name m_fullName;
Name m_groupName;
Name m_propertyName;
};
} // namespace RPI
@@ -27,63 +27,84 @@ namespace AZ
bool MaterialPropertyId::IsValid() const
{
const bool groupNameIsValid = m_groupName.IsEmpty() || IsValidName(m_groupName);
const bool propertyNameIsValid = IsValidName(m_propertyName);
return groupNameIsValid && propertyNameIsValid;
return !m_fullName.IsEmpty();
}
MaterialPropertyId MaterialPropertyId::Parse(AZStd::string_view fullPropertyId)
{
AZStd::vector<AZStd::string> tokens;
AzFramework::StringFunc::Tokenize(fullPropertyId.data(), tokens, '.', true, true);
AzFramework::StringFunc::Tokenize(fullPropertyId, tokens, '.', true, true);
if (tokens.size() == 1)
if (tokens.empty())
{
return MaterialPropertyId{"", tokens[0]};
AZ_Error("MaterialPropertyId", false, "Property ID is empty.", fullPropertyId.data());
return MaterialPropertyId{};
}
else if (tokens.size() == 2)
for (const auto& token : tokens)
{
return MaterialPropertyId{tokens[0], tokens[1]};
if (!IsValidName(token))
{
AZ_Error("MaterialPropertyId", false, "Property ID '%.*s' is not a valid identifier.", AZ_STRING_ARG(fullPropertyId));
return MaterialPropertyId{};
}
}
MaterialPropertyId id;
id.m_fullName = fullPropertyId;
return id;
}
MaterialPropertyId::MaterialPropertyId(AZStd::string_view propertyName)
{
if (!IsValidName(propertyName))
{
AZ_Error("MaterialPropertyId", false, "Property name '%.*s' is not a valid identifier.", AZ_STRING_ARG(propertyName));
}
else
{
AZ_Error("MaterialPropertyId", false, "Property ID '%s' is not a valid identifier.", fullPropertyId.data());
return MaterialPropertyId{};
m_fullName = propertyName;
}
}
MaterialPropertyId::MaterialPropertyId(AZStd::string_view groupName, AZStd::string_view propertyName)
: MaterialPropertyId(Name{groupName}, Name{propertyName})
{
}
MaterialPropertyId::MaterialPropertyId(const Name& groupName, const Name& propertyName)
{
AZ_Error("MaterialPropertyId", groupName.IsEmpty() || IsValidName(groupName), "Group name '%s' is not a valid identifier.", groupName.GetCStr());
AZ_Error("MaterialPropertyId", IsValidName(propertyName), "Property name '%s' is not a valid identifier.", propertyName.GetCStr());
m_groupName = groupName;
m_propertyName = propertyName;
if (groupName.IsEmpty())
if (!IsValidName(groupName))
{
m_fullName = m_propertyName.GetStringView();
AZ_Error("MaterialPropertyId", false, "Group name '%.*s' is not a valid identifier.", AZ_STRING_ARG(groupName));
}
else if (!IsValidName(propertyName))
{
AZ_Error("MaterialPropertyId", false, "Property name '%.*s' is not a valid identifier.", AZ_STRING_ARG(propertyName));
}
else
{
m_fullName = AZStd::string::format("%s.%s", m_groupName.GetCStr(), m_propertyName.GetCStr());
m_fullName = AZStd::string::format("%.*s.%.*s", AZ_STRING_ARG(groupName), AZ_STRING_ARG(propertyName));
}
}
const Name& MaterialPropertyId::GetGroupName() const
MaterialPropertyId::MaterialPropertyId(const Name& groupName, const Name& propertyName)
: MaterialPropertyId(groupName.GetStringView(), propertyName.GetStringView())
{
return m_groupName;
}
MaterialPropertyId::MaterialPropertyId(const AZStd::array_view<AZStd::string> names)
{
for (const auto& name : names)
{
if (!IsValidName(name))
{
AZ_Error("MaterialPropertyId", false, "'%s' is not a valid identifier.", name.c_str());
return;
}
}
AZStd::string fullName; // m_fullName is a Name, not a string, so we have to join into a local variable temporarily.
AzFramework::StringFunc::Join(fullName, names.begin(), names.end(), ".");
m_fullName = fullName;
}
const Name& MaterialPropertyId::GetPropertyName() const
{
return m_propertyName;
}
const Name& MaterialPropertyId::GetFullName() const
MaterialPropertyId::operator const Name&() const
{
return m_fullName;
}
@@ -92,6 +113,11 @@ namespace AZ
{
return m_fullName.GetCStr();
}
AZStd::string_view MaterialPropertyId::GetStringView() const
{
return m_fullName.GetStringView();
}
Name::Hash MaterialPropertyId::GetHash() const
{
@@ -340,16 +340,16 @@ namespace AZ
if (result == MaterialUtils::GetImageAssetResult::Missing)
{
materialAssetCreator.ReportWarning(
"Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(),
"Material property '%s': Could not find the image '%s'", propertyId.GetCStr(),
property.second.m_value.GetValue<AZStd::string>().data());
}
imageAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad);
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
materialAssetCreator.SetPropertyValue(propertyId, imageAsset);
}
else
{
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.second.m_value);
materialAssetCreator.SetPropertyValue(propertyId, property.second.m_value);
}
}
}
@@ -140,7 +140,7 @@ namespace AZ
{
if (action.m_operation == "rename")
{
if (action.m_renameFrom == propertyId.GetFullName().GetStringView())
if (action.m_renameFrom == propertyId.GetStringView())
{
propertyId = MaterialPropertyId::Parse(action.m_renameTo);
renamed = true;
@@ -177,14 +177,19 @@ namespace AZ
// Do the search again with the new names
groupIter = m_propertyLayout.m_properties.find(propertyId.GetGroupName().GetStringView());
if (groupIter != m_propertyLayout.m_properties.end())
AZStd::vector<AZStd::string> tokens;
AZ::StringFunc::Tokenize(propertyId.GetStringView(), tokens, ".", true, true);
if (tokens.size() == 2)
{
for (const PropertyDefinition& property : groupIter->second)
groupIter = m_propertyLayout.m_properties.find(tokens[0]);
if (groupIter != m_propertyLayout.m_properties.end())
{
if (property.m_name == propertyId.GetPropertyName().GetStringView())
for (const PropertyDefinition& property : groupIter->second)
{
return &property;
if (property.m_name == tokens[1])
{
return &property;
}
}
}
}
@@ -400,7 +405,7 @@ namespace AZ
continue;
}
materialTypeAssetCreator.BeginMaterialProperty(propertyId.GetFullName(), property.m_dataType);
materialTypeAssetCreator.BeginMaterialProperty(propertyId, property.m_dataType);
if (property.m_dataType == MaterialPropertyDataType::Enum)
{
@@ -454,18 +459,18 @@ namespace AZ
if (result == MaterialUtils::GetImageAssetResult::Missing)
{
materialTypeAssetCreator.ReportError(
"Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(),
"Material property '%s': Could not find the image '%s'", propertyId.GetCStr(),
property.m_value.GetValue<AZStd::string>().data());
}
else
{
materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
materialTypeAssetCreator.SetPropertyValue(propertyId, imageAsset);
}
}
break;
case MaterialPropertyDataType::Enum:
{
MaterialPropertyIndex propertyIndex = materialTypeAssetCreator.GetMaterialPropertiesLayout()->FindPropertyIndex(propertyId.GetFullName());
MaterialPropertyIndex propertyIndex = materialTypeAssetCreator.GetMaterialPropertiesLayout()->FindPropertyIndex(propertyId);
const MaterialPropertyDescriptor* propertyDescriptor = materialTypeAssetCreator.GetMaterialPropertiesLayout()->GetPropertyDescriptor(propertyIndex);
AZ::Name enumName = AZ::Name(property.m_value.GetValue<AZStd::string>());
@@ -476,12 +481,12 @@ namespace AZ
}
else
{
materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), enumValue);
materialTypeAssetCreator.SetPropertyValue(propertyId, enumValue);
}
}
break;
default:
materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.m_value);
materialTypeAssetCreator.SetPropertyValue(propertyId, property.m_value);
break;
}
}
@@ -322,7 +322,7 @@ namespace AZ
void Scene::RemoveRenderPipeline(const RenderPipelineId& pipelineId)
{
bool removed = false;
[[maybe_unused]] bool removed = false;
for (auto it = m_pipelines.begin(); it != m_pipelines.end(); ++it)
{
if (pipelineId == (*it)->GetId())
@@ -0,0 +1,131 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzTest/AzTest.h>
#include <Common/RPITestFixture.h>
#include <Common/ErrorMessageFinder.h>
#include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
namespace UnitTest
{
using namespace AZ;
using namespace RPI;
class MaterialPropertyIdTests
: public RPITestFixture
{
};
TEST_F(MaterialPropertyIdTests, TestConstructWithPropertyName)
{
MaterialPropertyId id{"color"};
EXPECT_TRUE(id.IsValid());
EXPECT_STREQ(id.GetCStr(), "color");
AZ::Name idCastedToName = id;
EXPECT_EQ(idCastedToName, AZ::Name{"color"});
}
TEST_F(MaterialPropertyIdTests, TestConstructWithPropertyName_BadName)
{
ErrorMessageFinder errorMessageFinder;
errorMessageFinder.AddExpectedErrorMessage("not a valid identifier");
MaterialPropertyId id{"color?"};
EXPECT_FALSE(id.IsValid());
errorMessageFinder.CheckExpectedErrorsFound();
}
TEST_F(MaterialPropertyIdTests, TestConstructWithTwoNames)
{
MaterialPropertyId id{"baseColor", "factor"};
EXPECT_TRUE(id.IsValid());
EXPECT_STREQ(id.GetCStr(), "baseColor.factor");
AZ::Name idCastedToName = id;
EXPECT_EQ(idCastedToName, AZ::Name{"baseColor.factor"});
}
TEST_F(MaterialPropertyIdTests, TestConstructWithTwoNames_BadGroupName)
{
ErrorMessageFinder errorMessageFinder;
errorMessageFinder.AddExpectedErrorMessage("not a valid identifier");
MaterialPropertyId id{"layer1.baseColor", "factor"};
EXPECT_FALSE(id.IsValid());
errorMessageFinder.CheckExpectedErrorsFound();
}
TEST_F(MaterialPropertyIdTests, TestConstructWithTwoNames_BadPropertyName)
{
ErrorMessageFinder errorMessageFinder;
errorMessageFinder.AddExpectedErrorMessage("not a valid identifier");
MaterialPropertyId id{"baseColor", ".factor"};
EXPECT_FALSE(id.IsValid());
errorMessageFinder.CheckExpectedErrorsFound();
}
TEST_F(MaterialPropertyIdTests, TestConstructWithMultipleNames)
{
AZStd::vector<AZStd::string> names{"layer1", "clearCoat", "normal", "factor"};
MaterialPropertyId id{names};
EXPECT_TRUE(id.IsValid());
EXPECT_STREQ(id.GetCStr(), "layer1.clearCoat.normal.factor");
AZ::Name idCastedToName = id;
EXPECT_EQ(idCastedToName, AZ::Name{"layer1.clearCoat.normal.factor"});
}
TEST_F(MaterialPropertyIdTests, TestConstructWithMultipleNames_BadName)
{
ErrorMessageFinder errorMessageFinder;
errorMessageFinder.AddExpectedErrorMessage("not a valid identifier");
AZStd::vector<AZStd::string> names{"layer1", "clear-coat", "normal", "factor"};
MaterialPropertyId id{names};
EXPECT_FALSE(id.IsValid());
errorMessageFinder.CheckExpectedErrorsFound();
}
TEST_F(MaterialPropertyIdTests, TestParse)
{
MaterialPropertyId id = MaterialPropertyId::Parse("layer1.clearCoat.normal.factor");
EXPECT_TRUE(id.IsValid());
EXPECT_STREQ(id.GetCStr(), "layer1.clearCoat.normal.factor");
AZ::Name idCastedToName = id;
EXPECT_EQ(idCastedToName, AZ::Name{"layer1.clearCoat.normal.factor"});
}
TEST_F(MaterialPropertyIdTests, TestParse_BadName)
{
ErrorMessageFinder errorMessageFinder;
errorMessageFinder.AddExpectedErrorMessage("not a valid identifier");
MaterialPropertyId id = MaterialPropertyId::Parse("layer1.clearCoat.normal,factor");
EXPECT_FALSE(id.IsValid());
errorMessageFinder.CheckExpectedErrorsFound();
}
TEST_F(MaterialPropertyIdTests, TestNameValidity)
{
EXPECT_TRUE(MaterialPropertyId::IsValidName("a"));
EXPECT_TRUE(MaterialPropertyId::IsValidName("z"));
EXPECT_TRUE(MaterialPropertyId::IsValidName("A"));
EXPECT_TRUE(MaterialPropertyId::IsValidName("Z"));
EXPECT_TRUE(MaterialPropertyId::IsValidName("_"));
EXPECT_TRUE(MaterialPropertyId::IsValidName("m_layer10bazBAZ"));
EXPECT_FALSE(MaterialPropertyId::IsValidName(""));
EXPECT_FALSE(MaterialPropertyId::IsValidName("1layer"));
EXPECT_FALSE(MaterialPropertyId::IsValidName("base-color"));
EXPECT_FALSE(MaterialPropertyId::IsValidName("base.color"));
EXPECT_FALSE(MaterialPropertyId::IsValidName("base/color"));
}
}
@@ -905,7 +905,7 @@ namespace UnitTest
JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson);
auto materialAssetResult = material.CreateMaterialAsset(Uuid::CreateRandom(), "test.material", AZ::RPI::MaterialAssetProcessingMode::PreBake);
EXPECT_TRUE(materialAssetResult);
MaterialPropertyIndex propertyIndex = materialAssetResult.GetValue()->GetMaterialPropertiesLayout()->FindPropertyIndex(MaterialPropertyId{groupName, propertyName}.GetFullName());
MaterialPropertyIndex propertyIndex = materialAssetResult.GetValue()->GetMaterialPropertiesLayout()->FindPropertyIndex(MaterialPropertyId{groupName, propertyName});
CheckSimilar(expectedFinalValue, materialAssetResult.GetValue()->GetPropertyValues()[propertyIndex.GetIndex()].GetValue<PropertyTypeT>());
}
@@ -37,6 +37,7 @@ set(FILES
Tests/Material/MaterialSourceDataTests.cpp
Tests/Material/MaterialFunctorTests.cpp
Tests/Material/MaterialFunctorSourceDataSerializerTests.cpp
Tests/Material/MaterialPropertyIdTests.cpp
Tests/Material/MaterialPropertyValueSourceDataTests.cpp
Tests/Material/MaterialTests.cpp
Tests/Model/ModelTests.cpp
@@ -0,0 +1,69 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/std/containers/vector.h>
#endif
#include <QDialog>
#include <QString>
class QListWidgetItem;
namespace Ui
{
class AssetGridDialog;
}
namespace AtomToolsFramework
{
//! Widget for managing and selecting from a library of assets
class AssetGridDialog : public QDialog
{
Q_OBJECT
public:
struct SelectableAsset
{
AZ::Data::AssetId m_assetId;
QString m_title;
};
using SelectableAssetVector = AZStd::vector<SelectableAsset>;
AssetGridDialog(
const QString& title,
const SelectableAssetVector& selectableAssets,
const AZ::Data::AssetId& selectedAsset,
const QSize& tileSize,
QWidget* parent = nullptr);
~AssetGridDialog();
Q_SIGNALS:
void AssetSelected(const AZ::Data::AssetId& assetId);
private:
AZ_DISABLE_COPY_MOVE(AssetGridDialog);
QListWidgetItem* CreateListItem(const SelectableAsset& selectableAsset);
void SetupAssetList();
void SetupSearchWidget();
void SetupDialogButtons();
void ApplySearchFilter();
void ShowSearchMenu(const QPoint& pos);
void SelectCurrentAsset();
void SelectInitialAsset();
QSize m_tileSize;
AZ::Data::AssetId m_initialSelectedAsset;
QScopedPointer<Ui::AssetGridDialog> m_ui;
};
} // namespace AtomToolsFramework
@@ -0,0 +1,179 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AssetGridDialog/ui_AssetGridDialog.h>
#include <AtomToolsFramework/AssetGridDialog/AssetGridDialog.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzQtComponents/Components/Widgets/ElidingLabel.h>
#include <AzQtComponents/Components/Widgets/LineEdit.h>
#include <AzQtComponents/Components/Widgets/Text.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <AzToolsFramework/Thumbnails/ThumbnailWidget.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QVBoxLayout>
namespace AtomToolsFramework
{
AssetGridDialog::AssetGridDialog(
const QString& title,
const SelectableAssetVector& selectableAssets,
const AZ::Data::AssetId& selectedAsset,
const QSize& tileSize,
QWidget* parent)
: QDialog(parent)
, m_tileSize(tileSize)
, m_initialSelectedAsset(selectedAsset)
, m_ui(new Ui::AssetGridDialog)
{
m_ui->setupUi(this);
QSignalBlocker signalBlocker(this);
setWindowTitle(title);
SetupAssetList();
SetupSearchWidget();
SetupDialogButtons();
setModal(true);
QListWidgetItem* selectedItem = nullptr;
for (const auto& selectableAsset : selectableAssets)
{
QListWidgetItem* item = CreateListItem(selectableAsset);
if (!selectedItem || m_initialSelectedAsset == selectableAsset.m_assetId)
{
selectedItem = item;
}
}
m_ui->m_assetList->sortItems();
if (selectedItem)
{
m_ui->m_assetList->setCurrentItem(selectedItem);
m_ui->m_assetList->scrollToItem(selectedItem);
}
}
AssetGridDialog::~AssetGridDialog()
{
}
QListWidgetItem* AssetGridDialog::CreateListItem(const SelectableAsset& selectableAsset)
{
const int itemBorder = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/AtomToolsFramework/AssetGridDialog/ItemBorder", 4));
const int itemSpacing = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/AtomToolsFramework/AssetGridDialog/ItemSpacing", 10));
const int headerHeight = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/AtomToolsFramework/AssetGridDialog/HeaderHeight", 15));
const QSize gridSize = m_ui->m_assetList->gridSize();
m_ui->m_assetList->setGridSize(QSize(
AZStd::max(gridSize.width(), m_tileSize.width() + itemSpacing),
AZStd::max(gridSize.height(), m_tileSize.height() + itemSpacing + headerHeight)));
QListWidgetItem* item = new QListWidgetItem(m_ui->m_assetList);
item->setData(Qt::DisplayRole, selectableAsset.m_title);
item->setData(Qt::UserRole, QString(selectableAsset.m_assetId.ToString<AZStd::string>().c_str()));
item->setSizeHint(m_tileSize + QSize(itemBorder, itemBorder + headerHeight));
m_ui->m_assetList->addItem(item);
QWidget* itemWidget = new QWidget(m_ui->m_assetList);
itemWidget->setLayout(new QVBoxLayout(itemWidget));
itemWidget->layout()->setSpacing(0);
itemWidget->layout()->setMargin(0);
AzQtComponents::ElidingLabel* header = new AzQtComponents::ElidingLabel(itemWidget);
header->setText(selectableAsset.m_title);
header->setFixedSize(QSize(m_tileSize.width(), headerHeight));
header->setMargin(0);
header->setStyleSheet("background-color: rgb(35, 35, 35)");
AzQtComponents::Text::addPrimaryStyle(header);
AzQtComponents::Text::addLabelStyle(header);
itemWidget->layout()->addWidget(header);
AzToolsFramework::Thumbnailer::ThumbnailWidget* thumbnail = new AzToolsFramework::Thumbnailer::ThumbnailWidget(itemWidget);
thumbnail->setFixedSize(m_tileSize);
thumbnail->SetThumbnailKey(
MAKE_TKEY(AzToolsFramework::AssetBrowser::ProductThumbnailKey, selectableAsset.m_assetId),
AzToolsFramework::Thumbnailer::ThumbnailContext::DefaultContext);
thumbnail->updateGeometry();
itemWidget->layout()->addWidget(thumbnail);
m_ui->m_assetList->setItemWidget(item, itemWidget);
return item;
}
void AssetGridDialog::SetupAssetList()
{
m_ui->m_assetList->setFlow(QListView::LeftToRight);
m_ui->m_assetList->setResizeMode(QListView::Adjust);
m_ui->m_assetList->setGridSize(QSize(0, 0));
m_ui->m_assetList->setWrapping(true);
QObject::connect(m_ui->m_assetList, &QListWidget::currentItemChanged, [this](){ SelectCurrentAsset(); });
}
void AssetGridDialog::SetupSearchWidget()
{
m_ui->m_searchWidget->setReadOnly(false);
m_ui->m_searchWidget->setContextMenuPolicy(Qt::CustomContextMenu);
AzQtComponents::LineEdit::applySearchStyle(m_ui->m_searchWidget);
connect(m_ui->m_searchWidget, &QLineEdit::textChanged, this, [this](){ ApplySearchFilter(); });
connect(m_ui->m_searchWidget, &QWidget::customContextMenuRequested, this, [this](const QPoint& pos){ ShowSearchMenu(pos); });
}
void AssetGridDialog::SetupDialogButtons()
{
connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_ui->m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(this, &QDialog::rejected, this, [this](){ SelectInitialAsset(); });
}
void AssetGridDialog::ApplySearchFilter()
{
for (int index = 0; index < m_ui->m_assetList->count(); ++index)
{
QListWidgetItem* item = m_ui->m_assetList->item(index);
const QString& title = item->data(Qt::DisplayRole).toString();
const QString filter = m_ui->m_searchWidget->text();
item->setHidden(!filter.isEmpty() && !title.contains(filter, Qt::CaseInsensitive));
}
}
void AssetGridDialog::ShowSearchMenu(const QPoint& pos)
{
QScopedPointer<QMenu> menu(m_ui->m_searchWidget->createStandardContextMenu());
menu->setStyleSheet("background-color: #333333");
menu->exec(m_ui->m_searchWidget->mapToGlobal(pos));
}
void AssetGridDialog::SelectCurrentAsset()
{
auto item = m_ui->m_assetList->currentItem();
if (item)
{
AZ::Data::AssetId assetId = AZ::Data::AssetId::CreateString(item->data(Qt::UserRole).toString().toUtf8().constData());
emit AssetSelected(assetId);
}
}
void AssetGridDialog::SelectInitialAsset()
{
emit AssetSelected(m_initialSelectedAsset);
}
} // namespace AtomToolsFramework
#include <AtomToolsFramework/AssetGridDialog/moc_AssetGridDialog.cpp>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PresetBrowserDialog</class>
<widget class="QWidget" name="PresetBrowserDialog">
<class>AssetGridDialog</class>
<widget class="QWidget" name="AssetGridDialog">
<property name="geometry">
<rect>
<x>0</x>
@@ -26,7 +26,7 @@
<widget class="QLineEdit" name="m_searchWidget"/>
</item>
<item>
<widget class="QListWidget" name="m_presetList"/>
<widget class="QListWidget" name="m_assetList"/>
</item>
</layout>
</item>
@@ -9,6 +9,7 @@
set(FILES
Include/AtomToolsFramework/Application/AtomToolsApplication.h
Include/AtomToolsFramework/AssetBrowser/AtomToolsAssetBrowser.h
Include/AtomToolsFramework/AssetGridDialog/AssetGridDialog.h
Include/AtomToolsFramework/Communication/LocalServer.h
Include/AtomToolsFramework/Communication/LocalSocket.h
Include/AtomToolsFramework/Debug/TraceRecorder.h
@@ -41,6 +42,8 @@ set(FILES
Source/AssetBrowser/AtomToolsAssetBrowser.cpp
Source/AssetBrowser/AtomToolsAssetBrowser.qrc
Source/AssetBrowser/AtomToolsAssetBrowser.ui
Source/AssetGridDialog/AssetGridDialog.cpp
Source/AssetGridDialog/AssetGridDialog.ui
Source/Communication/LocalServer.cpp
Source/Communication/LocalSocket.cpp
Source/Debug/TraceRecorder.cpp
@@ -589,15 +589,15 @@ namespace MaterialEditor
const MaterialPropertyId propertyId(groupName, propertyName);
const auto it = m_properties.find(propertyId.GetFullName());
const auto it = m_properties.find(propertyId);
if (it != m_properties.end() && propertyFilter(it->second))
{
MaterialPropertyValue propertyValue = AtomToolsFramework::ConvertToRuntimeType(it->second.GetValue());
if (propertyValue.IsValid())
{
if (!AtomToolsFramework::ConvertToExportFormat(exportPath, propertyId.GetFullName(), propertyDefinition, propertyValue))
if (!AtomToolsFramework::ConvertToExportFormat(exportPath, propertyId, propertyDefinition, propertyValue))
{
AZ_Error("MaterialDocument", false, "Material document property could not be converted: '%s' in '%s'.", propertyId.GetFullName().GetCStr(), m_absolutePath.c_str());
AZ_Error("MaterialDocument", false, "Material document property could not be converted: '%s' in '%s'.", propertyId.GetCStr(), m_absolutePath.c_str());
result = false;
return false;
}
@@ -783,7 +783,7 @@ namespace MaterialEditor
AtomToolsFramework::DynamicPropertyConfig propertyConfig;
// Assign id before conversion so it can be used in dynamic description
propertyConfig.m_id = MaterialPropertyId(groupName, propertyName).GetCStr();
propertyConfig.m_id = MaterialPropertyId(groupName, propertyName);
const auto& propertyIndex = m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyConfig.m_id);
const bool propertyIndexInBounds = propertyIndex.IsValid() && propertyIndex.GetIndex() < m_materialAsset->GetPropertyValues().size();
@@ -854,7 +854,7 @@ namespace MaterialEditor
propertyConfig = {};
propertyConfig.m_dataType = AtomToolsFramework::DynamicPropertyType::String;
propertyConfig.m_id = MaterialPropertyId(UvGroupName, shaderInput).GetCStr();
propertyConfig.m_id = MaterialPropertyId(UvGroupName, shaderInput);
propertyConfig.m_name = shaderInput;
propertyConfig.m_displayName = shaderInput;
propertyConfig.m_groupName = "UV Sets";
@@ -1,462 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#undef RC_INVOKED
#include <AzCore/Component/Component.h>
#include <AzCore/Component/Entity.h>
#include <AzFramework/Components/NonUniformScaleComponent.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AtomCore/Instance/InstanceDatabase.h>
#include <Atom/RPI.Public/Image/StreamingImage.h>
#include <Atom/RPI.Public/Material/Material.h>
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/WindowContext.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <Atom/Component/DebugCamera/CameraComponent.h>
#include <Atom/Component/DebugCamera/NoClipControllerComponent.h>
#include <Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h>
#include <Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcessing/PostProcessingConstants.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/Feature/Utils/ModelPreset.h>
#include <AtomLyIntegration/CommonFeatures/Grid/GridComponentConfig.h>
#include <AtomLyIntegration/CommonFeatures/Grid/GridComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/ImageBasedLights/ImageBasedLightComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/ImageBasedLights/ImageBasedLightComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlBus.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/PostFxLayerComponentConstants.h>
#include <Document/MaterialDocumentRequestBus.h>
#include <Viewport/MaterialViewportRenderer.h>
#include <Viewport/MaterialViewportRequestBus.h>
#include <Viewport/MaterialViewportSettings.h>
#include <Viewport/PerformanceMonitorRequestBus.h>
namespace MaterialEditor
{
static constexpr float DepthNear = 0.01f;
MaterialViewportRenderer::MaterialViewportRenderer(AZStd::shared_ptr<AZ::RPI::WindowContext> windowContext)
: m_windowContext(windowContext)
, m_viewportController(AZStd::make_shared<MaterialEditorViewportInputController>())
{
using namespace AZ;
using namespace Data;
// Create and register a scene with all available feature processors
AZ::RPI::SceneDescriptor sceneDesc;
sceneDesc.m_nameId = AZ::Name("MaterialViewport");
m_scene = AZ::RPI::Scene::CreateScene(sceneDesc);
m_scene->EnableAllFeatureProcessors();
// Bind m_defaultScene to the GameEntityContext's AzFramework::Scene
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "MaterialViewportRenderer was unable to get the scene system during construction.");
AZStd::shared_ptr<AzFramework::Scene> mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName);
// This should never happen unless scene creation has changed.
AZ_Assert(mainScene, "Main scenes missing during system component initialization");
mainScene->SetSubsystem(m_scene);
// Create a render pipeline from the specified asset for the window context and add the pipeline to the scene
AZ::Data::Asset<AZ::RPI::AnyAsset> pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(m_defaultPipelineAssetPath.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error);
m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineAsset, *m_windowContext.get());
pipelineAsset.Release();
m_scene->AddRenderPipeline(m_renderPipeline);
// As part of our initialization we need to create the BRDF texture generation pipeline
AZ::RPI::RenderPipelineDescriptor pipelineDesc;
pipelineDesc.m_mainViewTagName = "MainCamera";
pipelineDesc.m_name = "BRDFTexturePipeline";
pipelineDesc.m_rootPassTemplate = "BRDFTexturePipeline";
pipelineDesc.m_executeOnce = true;
AZ::RPI::RenderPipelinePtr brdfTexturePipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc);
m_scene->AddRenderPipeline(brdfTexturePipeline);
// Currently the scene has to be activated after render pipeline was added so some feature processors (i.e. imgui) can be initialized properly
// with pipeline's pass information.
m_scene->Activate();
AZ::RPI::RPISystemInterface::Get()->RegisterScene(m_scene);
AzFramework::EntityContextId entityContextId;
AzFramework::GameEntityContextRequestBus::BroadcastResult(entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
// Configure camera
AzFramework::EntityContextRequestBus::EventResult(m_cameraEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "Cameraentity");
AZ_Assert(m_cameraEntity != nullptr, "Failed to create camera entity.");
//Add debug camera and controller components
AZ::Debug::CameraComponentConfig cameraConfig(m_windowContext);
cameraConfig.m_fovY = AZ::Constants::HalfPi;
cameraConfig.m_depthNear = DepthNear;
m_cameraComponent = m_cameraEntity->CreateComponent(azrtti_typeid<AZ::Debug::CameraComponent>());
m_cameraComponent->SetConfiguration(cameraConfig);
m_cameraEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_cameraEntity->Activate();
// Connect camera to pipeline's default view after camera entity activated
m_renderPipeline->SetDefaultViewFromEntity(m_cameraEntity->GetId());
// Configure tone mapper
AzFramework::EntityContextRequestBus::EventResult(m_postProcessEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "postProcessEntity");
AZ_Assert(m_postProcessEntity != nullptr, "Failed to create post process entity.");
m_postProcessEntity->CreateComponent(AZ::Render::PostFxLayerComponentTypeId);
m_postProcessEntity->CreateComponent(AZ::Render::ExposureControlComponentTypeId);
m_postProcessEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_postProcessEntity->Activate();
// Init directional light processor
m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::DirectionalLightFeatureProcessorInterface>();
// Init display mapper processor
m_displayMapperFeatureProcessor = m_scene->GetFeatureProcessor<Render::DisplayMapperFeatureProcessorInterface>();
// Init Skybox
m_skyboxFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::SkyBoxFeatureProcessorInterface>();
m_skyboxFeatureProcessor->Enable(true);
m_skyboxFeatureProcessor->SetSkyboxMode(AZ::Render::SkyBoxMode::Cubemap);
// Create IBL
AzFramework::EntityContextRequestBus::EventResult(m_iblEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "IblEntity");
AZ_Assert(m_iblEntity != nullptr, "Failed to create ibl entity.");
m_iblEntity->CreateComponent(Render::ImageBasedLightComponentTypeId);
m_iblEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_iblEntity->Activate();
// Create model
AzFramework::EntityContextRequestBus::EventResult(m_modelEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportModel");
AZ_Assert(m_modelEntity != nullptr, "Failed to create model entity.");
m_modelEntity->CreateComponent(AZ::Render::MeshComponentTypeId);
m_modelEntity->CreateComponent(AZ::Render::MaterialComponentTypeId);
m_modelEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_modelEntity->Activate();
// Create shadow catcher
AzFramework::EntityContextRequestBus::EventResult(m_shadowCatcherEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportShadowCatcher");
AZ_Assert(m_shadowCatcherEntity != nullptr, "Failed to create shadow catcher entity.");
m_shadowCatcherEntity->CreateComponent(AZ::Render::MeshComponentTypeId);
m_shadowCatcherEntity->CreateComponent(AZ::Render::MaterialComponentTypeId);
m_shadowCatcherEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_shadowCatcherEntity->CreateComponent(azrtti_typeid<AzFramework::NonUniformScaleComponent>());
m_shadowCatcherEntity->Activate();
AZ::NonUniformScaleRequestBus::Event(m_shadowCatcherEntity->GetId(), &AZ::NonUniformScaleRequests::SetScale, AZ::Vector3{ 100, 100, 1.0 });
AZ::Data::AssetId shadowCatcherModelAssetId = RPI::AssetUtils::GetAssetIdForProductPath("materialeditor/viewportmodels/plane_1x1.azmodel", RPI::AssetUtils::TraceLevel::Error);
AZ::Render::MeshComponentRequestBus::Event(m_shadowCatcherEntity->GetId(),
&AZ::Render::MeshComponentRequestBus::Events::SetModelAssetId, shadowCatcherModelAssetId);
auto shadowCatcherMaterialAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::MaterialAsset>("materials/special/shadowcatcher.azmaterial", RPI::AssetUtils::TraceLevel::Error);
if (shadowCatcherMaterialAsset)
{
m_shadowCatcherOpacityPropertyIndex = shadowCatcherMaterialAsset->GetMaterialTypeAsset()->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name{ "settings.opacity" });
AZ_Error("MaterialViewportRenderer", m_shadowCatcherOpacityPropertyIndex.IsValid(), "Could not find opacity property");
m_shadowCatcherMaterial = AZ::RPI::Material::Create(shadowCatcherMaterialAsset);
AZ_Error("MaterialViewportRenderer", m_shadowCatcherMaterial != nullptr, "Could not create shadow catcher material.");
AZ::Render::MaterialAssignmentMap shadowCatcherMaterials;
auto& shadowCatcherMaterialAssignment = shadowCatcherMaterials[AZ::Render::DefaultMaterialAssignmentId];
shadowCatcherMaterialAssignment.m_materialInstance = m_shadowCatcherMaterial;
shadowCatcherMaterialAssignment.m_materialInstancePreCreated = true;
AZ::Render::MaterialComponentRequestBus::Event(m_shadowCatcherEntity->GetId(),
&AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides, shadowCatcherMaterials);
}
// Create grid
AzFramework::EntityContextRequestBus::EventResult(m_gridEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportGrid");
AZ_Assert(m_gridEntity != nullptr, "Failed to create grid entity.");
AZ::Render::GridComponentConfig gridConfig;
gridConfig.m_gridSize = 4.0f;
gridConfig.m_axisColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
gridConfig.m_primaryColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
gridConfig.m_secondaryColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
auto gridComponent = m_gridEntity->CreateComponent(AZ::Render::GridComponentTypeId);
gridComponent->SetConfiguration(gridConfig);
m_gridEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_gridEntity->Activate();
OnDocumentOpened(AZ::Uuid::CreateNull());
// Attempt to apply the default lighting preset
AZ::Render::LightingPresetPtr lightingPreset;
MaterialViewportRequestBus::BroadcastResult(lightingPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
OnLightingPresetSelected(lightingPreset);
// Attempt to apply the default model preset
AZ::Render::ModelPresetPtr modelPreset;
MaterialViewportRequestBus::BroadcastResult(modelPreset, &MaterialViewportRequestBus::Events::GetModelPresetSelection);
OnModelPresetSelected(modelPreset);
m_viewportController->Init(m_cameraEntity->GetId(), m_modelEntity->GetId(), m_iblEntity->GetId());
// Apply user settinngs restored since last run
AZStd::intrusive_ptr<MaterialViewportSettings> viewportSettings =
AZ::UserSettings::CreateFind<MaterialViewportSettings>(AZ::Crc32("MaterialViewportSettings"), AZ::UserSettings::CT_GLOBAL);
OnGridEnabledChanged(viewportSettings->m_enableGrid);
OnShadowCatcherEnabledChanged(viewportSettings->m_enableShadowCatcher);
OnAlternateSkyboxEnabledChanged(viewportSettings->m_enableAlternateSkybox);
OnFieldOfViewChanged(viewportSettings->m_fieldOfView);
OnDisplayMapperOperationTypeChanged(viewportSettings->m_displayMapperOperationType);
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect();
MaterialViewportNotificationBus::Handler::BusConnect();
AZ::TickBus::Handler::BusConnect();
AZ::TransformNotificationBus::MultiHandler::BusConnect(m_cameraEntity->GetId());
}
MaterialViewportRenderer::~MaterialViewportRenderer()
{
AZ::TransformNotificationBus::MultiHandler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
MaterialViewportNotificationBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusDisconnect();
AzFramework::EntityContextId entityContextId;
AzFramework::GameEntityContextRequestBus::BroadcastResult(entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_iblEntity);
m_iblEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_modelEntity);
m_modelEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_shadowCatcherEntity);
m_shadowCatcherEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_gridEntity);
m_gridEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_cameraEntity);
m_cameraEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_postProcessEntity);
m_postProcessEntity = nullptr;
for (DirectionalLightHandle& handle : m_lightHandles)
{
m_directionalLightFeatureProcessor->ReleaseLight(handle);
}
m_lightHandles.clear();
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "MaterialViewportRenderer was unable to get the scene system during destruction.");
AZStd::shared_ptr<AzFramework::Scene> mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName);
// This should never happen unless scene creation has changed.
AZ_Assert(mainScene, "Main scenes missing during system component destruction");
mainScene->UnsetSubsystem(m_scene);
m_swapChainPass = nullptr;
AZ::RPI::RPISystemInterface::Get()->UnregisterScene(m_scene);
m_scene = nullptr;
}
AZStd::shared_ptr<MaterialEditorViewportInputController> MaterialViewportRenderer::GetController()
{
return m_viewportController;
}
void MaterialViewportRenderer::OnDocumentOpened(const AZ::Uuid& documentId)
{
AZ::Data::Instance<AZ::RPI::Material> materialInstance;
MaterialDocumentRequestBus::EventResult(materialInstance, documentId, &MaterialDocumentRequestBus::Events::GetInstance);
AZ::Render::MaterialAssignmentMap materials;
auto& materialAssignment = materials[AZ::Render::DefaultMaterialAssignmentId];
materialAssignment.m_materialInstance = materialInstance;
materialAssignment.m_materialInstancePreCreated = true;
AZ::Render::MaterialComponentRequestBus::Event(m_modelEntity->GetId(),
&AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides, materials);
}
void MaterialViewportRenderer::OnLightingPresetSelected(AZ::Render::LightingPresetPtr preset)
{
if (!preset)
{
return;
}
AZ::Render::ImageBasedLightFeatureProcessorInterface* iblFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::ImageBasedLightFeatureProcessorInterface>();
AZ::Render::PostProcessFeatureProcessorInterface* postProcessFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::PostProcessFeatureProcessorInterface>();
AZ::Render::ExposureControlSettingsInterface* exposureControlSettingInterface = postProcessFeatureProcessor->GetOrCreateSettingsInterface(m_postProcessEntity->GetId())->GetOrCreateExposureControlSettingsInterface();
Camera::Configuration cameraConfig;
Camera::CameraRequestBus::EventResult(cameraConfig, m_cameraEntity->GetId(), &Camera::CameraRequestBus::Events::GetCameraConfiguration);
bool enableAlternateSkybox = false;
MaterialViewportRequestBus::BroadcastResult(enableAlternateSkybox, &MaterialViewportRequestBus::Events::GetAlternateSkyboxEnabled);
preset->ApplyLightingPreset(
iblFeatureProcessor,
m_skyboxFeatureProcessor,
exposureControlSettingInterface,
m_directionalLightFeatureProcessor,
cameraConfig,
m_lightHandles,
m_shadowCatcherMaterial,
m_shadowCatcherOpacityPropertyIndex,
enableAlternateSkybox);
}
void MaterialViewportRenderer::OnLightingPresetChanged(AZ::Render::LightingPresetPtr preset)
{
AZ::Render::LightingPresetPtr selectedPreset;
MaterialViewportRequestBus::BroadcastResult(selectedPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
if (selectedPreset == preset)
{
OnLightingPresetSelected(preset);
}
}
void MaterialViewportRenderer::OnModelPresetSelected(AZ::Render::ModelPresetPtr preset)
{
if (!preset)
{
return;
}
if (!preset->m_modelAsset.GetId().IsValid())
{
AZ_Warning("MaterialViewportRenderer", false, "Attempting to set invalid model for preset: '%s'\n.", preset->m_displayName.c_str());
return;
}
if (preset->m_modelAsset.GetId() == m_modelAssetId)
{
return;
}
AZ::Render::MeshComponentRequestBus::Event(m_modelEntity->GetId(),
&AZ::Render::MeshComponentRequestBus::Events::SetModelAsset, preset->m_modelAsset);
m_modelAssetId = preset->m_modelAsset.GetId();
AZ::Data::AssetBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusConnect(m_modelAssetId);
}
void MaterialViewportRenderer::OnModelPresetChanged(AZ::Render::ModelPresetPtr preset)
{
AZ::Render::ModelPresetPtr selectedPreset;
MaterialViewportRequestBus::BroadcastResult(selectedPreset, &MaterialViewportRequestBus::Events::GetModelPresetSelection);
if (selectedPreset == preset)
{
OnModelPresetSelected(preset);
}
}
void MaterialViewportRenderer::OnShadowCatcherEnabledChanged(bool enable)
{
AZ::Render::MeshComponentRequestBus::Event(m_shadowCatcherEntity->GetId(), &AZ::Render::MeshComponentRequestBus::Events::SetVisibility, enable);
}
void MaterialViewportRenderer::OnGridEnabledChanged(bool enable)
{
if (m_gridEntity)
{
if (enable && m_gridEntity->GetState() == AZ::Entity::State::Init)
{
m_gridEntity->Activate();
}
else if (!enable && m_gridEntity->GetState() == AZ::Entity::State::Active)
{
m_gridEntity->Deactivate();
}
}
}
void MaterialViewportRenderer::OnAlternateSkyboxEnabledChanged(bool enable)
{
AZ_UNUSED(enable);
AZ::Render::LightingPresetPtr selectedPreset;
MaterialViewportRequestBus::BroadcastResult(selectedPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
OnLightingPresetSelected(selectedPreset);
}
void MaterialViewportRenderer::OnFieldOfViewChanged(float fieldOfView)
{
MaterialEditorViewportInputControllerRequestBus::Broadcast(&MaterialEditorViewportInputControllerRequestBus::Handler::SetFieldOfView, fieldOfView);
}
void MaterialViewportRenderer::OnDisplayMapperOperationTypeChanged(AZ::Render::DisplayMapperOperationType operationType)
{
AZ::Render::DisplayMapperConfigurationDescriptor desc;
desc.m_operationType = operationType;
m_displayMapperFeatureProcessor->RegisterDisplayMapperConfiguration(desc);
}
void MaterialViewportRenderer::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
if (m_modelAssetId == asset.GetId())
{
MaterialEditorViewportInputControllerRequestBus::Broadcast(&MaterialEditorViewportInputControllerRequestBus::Handler::Reset);
AZ::Data::AssetBus::Handler::BusDisconnect(asset.GetId());
}
}
void MaterialViewportRenderer::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
m_renderPipeline->AddToRenderTickOnce();
PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::GatherMetrics);
if (m_shadowCatcherMaterial)
{
// Compile the m_shadowCatcherMaterial in OnTick because changes can only be compiled once per frame.
// This is ignored when a compile isn't needed.
m_shadowCatcherMaterial->Compile();
}
}
void MaterialViewportRenderer::OnTransformChanged(const AZ::Transform&, const AZ::Transform&)
{
const AZ::EntityId* currentBusId = AZ::TransformNotificationBus::GetCurrentBusId();
if (m_cameraEntity && currentBusId && *currentBusId == m_cameraEntity->GetId() && m_directionalLightFeatureProcessor)
{
auto transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(
transform,
m_cameraEntity->GetId(),
&AZ::TransformBus::Events::GetWorldTM);
for (const DirectionalLightHandle& id : m_lightHandles)
{
m_directionalLightFeatureProcessor->SetCameraTransform(
id, transform);
}
}
}
}
@@ -1,114 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/RPI.Public/Base.h>
#include <AtomCore/Instance/Instance.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/TransformBus.h>
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
#include <Viewport/MaterialViewportNotificationBus.h>
namespace AZ
{
namespace Render
{
class DisplayMapperFeatureProcessorInterface;
}
class Entity;
class Component;
namespace RPI
{
class SwapChainPass;
class WindowContext;
}
}
namespace MaterialEditor
{
//! Provides backend logic for MaterialViewport
//! Sets up a scene, camera, loads the model, and applies texture
class MaterialViewportRenderer
: public AZ::Data::AssetBus::Handler
, public AZ::TickBus::Handler
, public AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler
, public MaterialViewportNotificationBus::Handler
, public AZ::TransformNotificationBus::MultiHandler
{
public:
AZ_CLASS_ALLOCATOR(MaterialViewportRenderer, AZ::SystemAllocator, 0);
MaterialViewportRenderer(AZStd::shared_ptr<AZ::RPI::WindowContext> windowContext);
~MaterialViewportRenderer();
AZStd::shared_ptr<MaterialEditorViewportInputController> GetController();
private:
// AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler interface overrides...
void OnDocumentOpened(const AZ::Uuid& documentId) override;
// MaterialViewportNotificationBus::Handler interface overrides...
void OnLightingPresetSelected(AZ::Render::LightingPresetPtr preset) override;
void OnLightingPresetChanged(AZ::Render::LightingPresetPtr preset) override;
void OnModelPresetSelected(AZ::Render::ModelPresetPtr preset) override;
void OnModelPresetChanged(AZ::Render::ModelPresetPtr preset) override;
void OnShadowCatcherEnabledChanged(bool enable) override;
void OnGridEnabledChanged(bool enable) override;
void OnAlternateSkyboxEnabledChanged(bool enable) override;
void OnFieldOfViewChanged(float fieldOfView) override;
void OnDisplayMapperOperationTypeChanged(AZ::Render::DisplayMapperOperationType operationType) override;
// AZ::Data::AssetBus::Handler interface overrides...
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
// AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
// AZ::TransformNotificationBus::MultiHandler overrides...
void OnTransformChanged(const AZ::Transform&, const AZ::Transform&) override;
using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle;
AZ::Data::Instance<AZ::RPI::SwapChainPass> m_swapChainPass;
AZStd::string m_defaultPipelineAssetPath = "passes/MainRenderPipeline.azasset";
AZStd::shared_ptr<AZ::RPI::WindowContext> m_windowContext;
AZ::RPI::RenderPipelinePtr m_renderPipeline;
AZ::RPI::ScenePtr m_scene;
AZ::Render::DirectionalLightFeatureProcessorInterface* m_directionalLightFeatureProcessor = nullptr;
AZ::Render::DisplayMapperFeatureProcessorInterface* m_displayMapperFeatureProcessor = nullptr;
AZ::Entity* m_cameraEntity = nullptr;
AZ::Component* m_cameraComponent = nullptr;
bool m_cameraNeedsFullReset = true;
AZ::Entity* m_postProcessEntity = nullptr;
AZ::Entity* m_modelEntity = nullptr;
AZ::Data::AssetId m_modelAssetId;
AZ::Entity* m_gridEntity = nullptr;
AZ::Entity* m_shadowCatcherEntity = nullptr;
AZ::Data::Instance<AZ::RPI::Material> m_shadowCatcherMaterial;
AZ::RPI::MaterialPropertyIndex m_shadowCatcherOpacityPropertyIndex;
AZStd::vector<DirectionalLightHandle> m_lightHandles;
AZ::Entity* m_iblEntity = nullptr;
AZ::Render::SkyBoxFeatureProcessorInterface* m_skyboxFeatureProcessor = nullptr;
AZStd::shared_ptr<MaterialEditorViewportInputController> m_viewportController;
};
} // namespace MaterialEditor
@@ -6,29 +6,65 @@
*
*/
#undef RC_INVOKED
#include <Atom/Component/DebugCamera/CameraComponent.h>
#include <Atom/Component/DebugCamera/NoClipControllerComponent.h>
#include <Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h>
#include <Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcessing/PostProcessingConstants.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/Feature/Utils/ModelPreset.h>
#include <Atom/RHI/Device.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/Image/StreamingImage.h>
#include <Atom/RPI.Public/Material/Material.h>
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/ViewportContext.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <Atom/RPI.Public/WindowContext.h>
#include <Viewport/MaterialViewportRenderer.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AtomLyIntegration/CommonFeatures/Grid/GridComponentConfig.h>
#include <AtomLyIntegration/CommonFeatures/Grid/GridComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/ImageBasedLights/ImageBasedLightComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/ImageBasedLights/ImageBasedLightComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlBus.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/PostFxLayerComponentConstants.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/Entity.h>
#include <AzFramework/Components/NonUniformScaleComponent.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Viewport/ViewportControllerList.h>
#include <Document/MaterialDocumentRequestBus.h>
#include <Viewport/MaterialViewportRequestBus.h>
#include <Viewport/MaterialViewportSettings.h>
#include <Viewport/MaterialViewportWidget.h>
#include <Viewport/PerformanceMonitorRequestBus.h>
#include <Viewport/ui_MaterialViewportWidget.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QWindow>
#include "Viewport/ui_MaterialViewportWidget.h"
AZ_POP_DISABLE_WARNING
#include <AzFramework/Viewport/ViewportControllerList.h>
namespace MaterialEditor
{
static constexpr float DepthNear = 0.01f;
MaterialViewportWidget::MaterialViewportWidget(QWidget* parent)
: AtomToolsFramework::RenderViewportWidget(parent)
, m_ui(new Ui::MaterialViewportWidget)
, m_viewportController(AZStd::make_shared<MaterialEditorViewportInputController>())
{
m_ui->setupUi(this);
@@ -38,7 +74,415 @@ namespace MaterialEditor
const AZ::Name defaultContextName = viewportContextManager->GetDefaultViewportContextName();
viewportContextManager->RenameViewportContext(GetViewportContext(), defaultContextName);
m_renderer = AZStd::make_unique<MaterialViewportRenderer>(GetViewportContext()->GetWindowContext());
GetControllerList()->Add(m_renderer->GetController());
// Create and register a scene with all available feature processors
AZ::RPI::SceneDescriptor sceneDesc;
sceneDesc.m_nameId = AZ::Name("MaterialViewport");
m_scene = AZ::RPI::Scene::CreateScene(sceneDesc);
m_scene->EnableAllFeatureProcessors();
// Bind m_defaultScene to the GameEntityContext's AzFramework::Scene
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "MaterialViewportWidget was unable to get the scene system during construction.");
AZStd::shared_ptr<AzFramework::Scene> mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName);
// This should never happen unless scene creation has changed.
AZ_Assert(mainScene, "Main scenes missing during system component initialization");
mainScene->SetSubsystem(m_scene);
// Create a render pipeline from the specified asset for the window context and add the pipeline to the scene
AZ::Data::Asset<AZ::RPI::AnyAsset> pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(
m_defaultPipelineAssetPath.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error);
m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineAsset, *GetViewportContext()->GetWindowContext().get());
pipelineAsset.Release();
m_scene->AddRenderPipeline(m_renderPipeline);
// As part of our initialization we need to create the BRDF texture generation pipeline
AZ::RPI::RenderPipelineDescriptor pipelineDesc;
pipelineDesc.m_mainViewTagName = "MainCamera";
pipelineDesc.m_name = "BRDFTexturePipeline";
pipelineDesc.m_rootPassTemplate = "BRDFTexturePipeline";
pipelineDesc.m_executeOnce = true;
AZ::RPI::RenderPipelinePtr brdfTexturePipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc);
m_scene->AddRenderPipeline(brdfTexturePipeline);
// Currently the scene has to be activated after render pipeline was added so some feature processors (i.e. imgui) can be
// initialized properly with pipeline's pass information.
m_scene->Activate();
AZ::RPI::RPISystemInterface::Get()->RegisterScene(m_scene);
AzFramework::EntityContextId entityContextId;
AzFramework::GameEntityContextRequestBus::BroadcastResult(
entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
// Configure camera
AzFramework::EntityContextRequestBus::EventResult(
m_cameraEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "Cameraentity");
AZ_Assert(m_cameraEntity != nullptr, "Failed to create camera entity.");
// Add debug camera and controller components
AZ::Debug::CameraComponentConfig cameraConfig(GetViewportContext()->GetWindowContext());
cameraConfig.m_fovY = AZ::Constants::HalfPi;
cameraConfig.m_depthNear = DepthNear;
m_cameraComponent = m_cameraEntity->CreateComponent(azrtti_typeid<AZ::Debug::CameraComponent>());
m_cameraComponent->SetConfiguration(cameraConfig);
m_cameraEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_cameraEntity->Activate();
// Connect camera to pipeline's default view after camera entity activated
m_renderPipeline->SetDefaultViewFromEntity(m_cameraEntity->GetId());
// Configure tone mapper
AzFramework::EntityContextRequestBus::EventResult(
m_postProcessEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "postProcessEntity");
AZ_Assert(m_postProcessEntity != nullptr, "Failed to create post process entity.");
m_postProcessEntity->CreateComponent(AZ::Render::PostFxLayerComponentTypeId);
m_postProcessEntity->CreateComponent(AZ::Render::ExposureControlComponentTypeId);
m_postProcessEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_postProcessEntity->Activate();
// Init directional light processor
m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::DirectionalLightFeatureProcessorInterface>();
// Init display mapper processor
m_displayMapperFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::DisplayMapperFeatureProcessorInterface>();
// Init Skybox
m_skyboxFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::SkyBoxFeatureProcessorInterface>();
m_skyboxFeatureProcessor->Enable(true);
m_skyboxFeatureProcessor->SetSkyboxMode(AZ::Render::SkyBoxMode::Cubemap);
// Create IBL
AzFramework::EntityContextRequestBus::EventResult(
m_iblEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "IblEntity");
AZ_Assert(m_iblEntity != nullptr, "Failed to create ibl entity.");
m_iblEntity->CreateComponent(AZ::Render::ImageBasedLightComponentTypeId);
m_iblEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_iblEntity->Activate();
// Create model
AzFramework::EntityContextRequestBus::EventResult(
m_modelEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportModel");
AZ_Assert(m_modelEntity != nullptr, "Failed to create model entity.");
m_modelEntity->CreateComponent(AZ::Render::MeshComponentTypeId);
m_modelEntity->CreateComponent(AZ::Render::MaterialComponentTypeId);
m_modelEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_modelEntity->Activate();
// Create shadow catcher
AzFramework::EntityContextRequestBus::EventResult(
m_shadowCatcherEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportShadowCatcher");
AZ_Assert(m_shadowCatcherEntity != nullptr, "Failed to create shadow catcher entity.");
m_shadowCatcherEntity->CreateComponent(AZ::Render::MeshComponentTypeId);
m_shadowCatcherEntity->CreateComponent(AZ::Render::MaterialComponentTypeId);
m_shadowCatcherEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_shadowCatcherEntity->CreateComponent(azrtti_typeid<AzFramework::NonUniformScaleComponent>());
m_shadowCatcherEntity->Activate();
AZ::NonUniformScaleRequestBus::Event(
m_shadowCatcherEntity->GetId(), &AZ::NonUniformScaleRequests::SetScale, AZ::Vector3{ 100, 100, 1.0 });
AZ::Data::AssetId shadowCatcherModelAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(
"materialeditor/viewportmodels/plane_1x1.azmodel", AZ::RPI::AssetUtils::TraceLevel::Error);
AZ::Render::MeshComponentRequestBus::Event(
m_shadowCatcherEntity->GetId(), &AZ::Render::MeshComponentRequestBus::Events::SetModelAssetId, shadowCatcherModelAssetId);
auto shadowCatcherMaterialAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::MaterialAsset>(
"materials/special/shadowcatcher.azmaterial", AZ::RPI::AssetUtils::TraceLevel::Error);
if (shadowCatcherMaterialAsset)
{
m_shadowCatcherOpacityPropertyIndex =
shadowCatcherMaterialAsset->GetMaterialTypeAsset()->GetMaterialPropertiesLayout()->FindPropertyIndex(
AZ::Name{ "settings.opacity" });
AZ_Error("MaterialViewportWidget", m_shadowCatcherOpacityPropertyIndex.IsValid(), "Could not find opacity property");
m_shadowCatcherMaterial = AZ::RPI::Material::Create(shadowCatcherMaterialAsset);
AZ_Error("MaterialViewportWidget", m_shadowCatcherMaterial != nullptr, "Could not create shadow catcher material.");
AZ::Render::MaterialAssignmentMap shadowCatcherMaterials;
auto& shadowCatcherMaterialAssignment = shadowCatcherMaterials[AZ::Render::DefaultMaterialAssignmentId];
shadowCatcherMaterialAssignment.m_materialInstance = m_shadowCatcherMaterial;
shadowCatcherMaterialAssignment.m_materialInstancePreCreated = true;
AZ::Render::MaterialComponentRequestBus::Event(
m_shadowCatcherEntity->GetId(), &AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides,
shadowCatcherMaterials);
}
// Create grid
AzFramework::EntityContextRequestBus::EventResult(
m_gridEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportGrid");
AZ_Assert(m_gridEntity != nullptr, "Failed to create grid entity.");
AZ::Render::GridComponentConfig gridConfig;
gridConfig.m_gridSize = 4.0f;
gridConfig.m_axisColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
gridConfig.m_primaryColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
gridConfig.m_secondaryColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
auto gridComponent = m_gridEntity->CreateComponent(AZ::Render::GridComponentTypeId);
gridComponent->SetConfiguration(gridConfig);
m_gridEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_gridEntity->Activate();
OnDocumentOpened(AZ::Uuid::CreateNull());
// Attempt to apply the default lighting preset
AZ::Render::LightingPresetPtr lightingPreset;
MaterialViewportRequestBus::BroadcastResult(lightingPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
OnLightingPresetSelected(lightingPreset);
// Attempt to apply the default model preset
AZ::Render::ModelPresetPtr modelPreset;
MaterialViewportRequestBus::BroadcastResult(modelPreset, &MaterialViewportRequestBus::Events::GetModelPresetSelection);
OnModelPresetSelected(modelPreset);
m_viewportController->Init(m_cameraEntity->GetId(), m_modelEntity->GetId(), m_iblEntity->GetId());
// Apply user settinngs restored since last run
AZStd::intrusive_ptr<MaterialViewportSettings> viewportSettings =
AZ::UserSettings::CreateFind<MaterialViewportSettings>(AZ::Crc32("MaterialViewportSettings"), AZ::UserSettings::CT_GLOBAL);
OnGridEnabledChanged(viewportSettings->m_enableGrid);
OnShadowCatcherEnabledChanged(viewportSettings->m_enableShadowCatcher);
OnAlternateSkyboxEnabledChanged(viewportSettings->m_enableAlternateSkybox);
OnFieldOfViewChanged(viewportSettings->m_fieldOfView);
OnDisplayMapperOperationTypeChanged(viewportSettings->m_displayMapperOperationType);
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect();
MaterialViewportNotificationBus::Handler::BusConnect();
AZ::TickBus::Handler::BusConnect();
AZ::TransformNotificationBus::MultiHandler::BusConnect(m_cameraEntity->GetId());
GetControllerList()->Add(m_viewportController);
}
MaterialViewportWidget::~MaterialViewportWidget()
{
AZ::TransformNotificationBus::MultiHandler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
MaterialViewportNotificationBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusDisconnect();
AzFramework::EntityContextId entityContextId;
AzFramework::GameEntityContextRequestBus::BroadcastResult(
entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_iblEntity);
m_iblEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_modelEntity);
m_modelEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_shadowCatcherEntity);
m_shadowCatcherEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_gridEntity);
m_gridEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_cameraEntity);
m_cameraEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_postProcessEntity);
m_postProcessEntity = nullptr;
for (DirectionalLightHandle& handle : m_lightHandles)
{
m_directionalLightFeatureProcessor->ReleaseLight(handle);
}
m_lightHandles.clear();
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "MaterialViewportWidget was unable to get the scene system during destruction.");
AZStd::shared_ptr<AzFramework::Scene> mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName);
// This should never happen unless scene creation has changed.
AZ_Assert(mainScene, "Main scenes missing during system component destruction");
mainScene->UnsetSubsystem(m_scene);
m_swapChainPass = nullptr;
AZ::RPI::RPISystemInterface::Get()->UnregisterScene(m_scene);
m_scene = nullptr;
}
void MaterialViewportWidget::OnDocumentOpened(const AZ::Uuid& documentId)
{
AZ::Data::Instance<AZ::RPI::Material> materialInstance;
MaterialDocumentRequestBus::EventResult(materialInstance, documentId, &MaterialDocumentRequestBus::Events::GetInstance);
AZ::Render::MaterialAssignmentMap materials;
auto& materialAssignment = materials[AZ::Render::DefaultMaterialAssignmentId];
materialAssignment.m_materialInstance = materialInstance;
materialAssignment.m_materialInstancePreCreated = true;
AZ::Render::MaterialComponentRequestBus::Event(
m_modelEntity->GetId(), &AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides, materials);
}
void MaterialViewportWidget::OnLightingPresetSelected(AZ::Render::LightingPresetPtr preset)
{
if (!preset)
{
return;
}
AZ::Render::ImageBasedLightFeatureProcessorInterface* iblFeatureProcessor =
m_scene->GetFeatureProcessor<AZ::Render::ImageBasedLightFeatureProcessorInterface>();
AZ::Render::PostProcessFeatureProcessorInterface* postProcessFeatureProcessor =
m_scene->GetFeatureProcessor<AZ::Render::PostProcessFeatureProcessorInterface>();
AZ::Render::ExposureControlSettingsInterface* exposureControlSettingInterface =
postProcessFeatureProcessor->GetOrCreateSettingsInterface(m_postProcessEntity->GetId())
->GetOrCreateExposureControlSettingsInterface();
Camera::Configuration cameraConfig;
Camera::CameraRequestBus::EventResult(
cameraConfig, m_cameraEntity->GetId(), &Camera::CameraRequestBus::Events::GetCameraConfiguration);
bool enableAlternateSkybox = false;
MaterialViewportRequestBus::BroadcastResult(enableAlternateSkybox, &MaterialViewportRequestBus::Events::GetAlternateSkyboxEnabled);
preset->ApplyLightingPreset(
iblFeatureProcessor, m_skyboxFeatureProcessor, exposureControlSettingInterface, m_directionalLightFeatureProcessor,
cameraConfig, m_lightHandles, m_shadowCatcherMaterial, m_shadowCatcherOpacityPropertyIndex, enableAlternateSkybox);
}
void MaterialViewportWidget::OnLightingPresetChanged(AZ::Render::LightingPresetPtr preset)
{
AZ::Render::LightingPresetPtr selectedPreset;
MaterialViewportRequestBus::BroadcastResult(selectedPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
if (selectedPreset == preset)
{
OnLightingPresetSelected(preset);
}
}
void MaterialViewportWidget::OnModelPresetSelected(AZ::Render::ModelPresetPtr preset)
{
if (!preset)
{
return;
}
if (!preset->m_modelAsset.GetId().IsValid())
{
AZ_Warning(
"MaterialViewportWidget", false, "Attempting to set invalid model for preset: '%s'\n.", preset->m_displayName.c_str());
return;
}
if (preset->m_modelAsset.GetId() == m_modelAssetId)
{
return;
}
AZ::Render::MeshComponentRequestBus::Event(
m_modelEntity->GetId(), &AZ::Render::MeshComponentRequestBus::Events::SetModelAsset, preset->m_modelAsset);
m_modelAssetId = preset->m_modelAsset.GetId();
AZ::Data::AssetBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusConnect(m_modelAssetId);
}
void MaterialViewportWidget::OnModelPresetChanged(AZ::Render::ModelPresetPtr preset)
{
AZ::Render::ModelPresetPtr selectedPreset;
MaterialViewportRequestBus::BroadcastResult(selectedPreset, &MaterialViewportRequestBus::Events::GetModelPresetSelection);
if (selectedPreset == preset)
{
OnModelPresetSelected(preset);
}
}
void MaterialViewportWidget::OnShadowCatcherEnabledChanged(bool enable)
{
AZ::Render::MeshComponentRequestBus::Event(
m_shadowCatcherEntity->GetId(), &AZ::Render::MeshComponentRequestBus::Events::SetVisibility, enable);
}
void MaterialViewportWidget::OnGridEnabledChanged(bool enable)
{
if (m_gridEntity)
{
if (enable && m_gridEntity->GetState() == AZ::Entity::State::Init)
{
m_gridEntity->Activate();
}
else if (!enable && m_gridEntity->GetState() == AZ::Entity::State::Active)
{
m_gridEntity->Deactivate();
}
}
}
void MaterialViewportWidget::OnAlternateSkyboxEnabledChanged(bool enable)
{
AZ_UNUSED(enable);
AZ::Render::LightingPresetPtr selectedPreset;
MaterialViewportRequestBus::BroadcastResult(selectedPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
OnLightingPresetSelected(selectedPreset);
}
void MaterialViewportWidget::OnFieldOfViewChanged(float fieldOfView)
{
MaterialEditorViewportInputControllerRequestBus::Broadcast(
&MaterialEditorViewportInputControllerRequestBus::Handler::SetFieldOfView, fieldOfView);
}
void MaterialViewportWidget::OnDisplayMapperOperationTypeChanged(AZ::Render::DisplayMapperOperationType operationType)
{
AZ::Render::DisplayMapperConfigurationDescriptor desc;
desc.m_operationType = operationType;
m_displayMapperFeatureProcessor->RegisterDisplayMapperConfiguration(desc);
}
void MaterialViewportWidget::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
if (m_modelAssetId == asset.GetId())
{
MaterialEditorViewportInputControllerRequestBus::Broadcast(&MaterialEditorViewportInputControllerRequestBus::Handler::Reset);
AZ::Data::AssetBus::Handler::BusDisconnect(asset.GetId());
}
}
void MaterialViewportWidget::OnTick(float deltaTime, AZ::ScriptTimePoint time)
{
AtomToolsFramework::RenderViewportWidget::OnTick(deltaTime, time);
m_renderPipeline->AddToRenderTickOnce();
PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::GatherMetrics);
if (m_shadowCatcherMaterial)
{
// Compile the m_shadowCatcherMaterial in OnTick because changes can only be compiled once per frame.
// This is ignored when a compile isn't needed.
m_shadowCatcherMaterial->Compile();
}
}
void MaterialViewportWidget::OnTransformChanged(const AZ::Transform&, const AZ::Transform&)
{
const AZ::EntityId* currentBusId = AZ::TransformNotificationBus::GetCurrentBusId();
if (m_cameraEntity && currentBusId && *currentBusId == m_cameraEntity->GetId() && m_directionalLightFeatureProcessor)
{
auto transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(transform, m_cameraEntity->GetId(), &AZ::TransformBus::Events::GetWorldTM);
for (const DirectionalLightHandle& id : m_lightHandles)
{
m_directionalLightFeatureProcessor->SetCameraTransform(id, transform);
}
}
}
} // namespace MaterialEditor
@@ -5,42 +5,115 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/RPI.Public/Base.h>
#include <AtomCore/Instance/Instance.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
#include <AzCore/Component/TransformBus.h>
#include <AzFramework/Windowing/WindowBus.h>
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
#include <Viewport/MaterialViewportNotificationBus.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QWidget>
AZ_POP_DISABLE_WARNING
#endif
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
namespace AZ
{
namespace Render
{
class DisplayMapperFeatureProcessorInterface;
}
class Entity;
class Component;
namespace RPI
{
class SwapChainPass;
class WindowContext;
} // namespace RPI
} // namespace AZ
namespace Ui
{
class MaterialViewportWidget;
}
namespace AZ
{
namespace RPI
{
class WindowContext;
}
}
namespace MaterialEditor
{
class MaterialViewportRenderer;
class MaterialViewportWidget
: public AtomToolsFramework::RenderViewportWidget
, public AZ::Data::AssetBus::Handler
, public AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler
, public MaterialViewportNotificationBus::Handler
, public AZ::TransformNotificationBus::MultiHandler
{
public:
MaterialViewportWidget(QWidget* parent = nullptr);
~MaterialViewportWidget();
private:
// AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler interface overrides...
void OnDocumentOpened(const AZ::Uuid& documentId) override;
// MaterialViewportNotificationBus::Handler interface overrides...
void OnLightingPresetSelected(AZ::Render::LightingPresetPtr preset) override;
void OnLightingPresetChanged(AZ::Render::LightingPresetPtr preset) override;
void OnModelPresetSelected(AZ::Render::ModelPresetPtr preset) override;
void OnModelPresetChanged(AZ::Render::ModelPresetPtr preset) override;
void OnShadowCatcherEnabledChanged(bool enable) override;
void OnGridEnabledChanged(bool enable) override;
void OnAlternateSkyboxEnabledChanged(bool enable) override;
void OnFieldOfViewChanged(float fieldOfView) override;
void OnDisplayMapperOperationTypeChanged(AZ::Render::DisplayMapperOperationType operationType) override;
// AZ::Data::AssetBus::Handler interface overrides...
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
// AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
// AZ::TransformNotificationBus::MultiHandler overrides...
void OnTransformChanged(const AZ::Transform&, const AZ::Transform&) override;
using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle;
AZ::Data::Instance<AZ::RPI::SwapChainPass> m_swapChainPass;
AZStd::string m_defaultPipelineAssetPath = "passes/MainRenderPipeline.azasset";
AZ::RPI::RenderPipelinePtr m_renderPipeline;
AZ::RPI::ScenePtr m_scene;
AZ::Render::DirectionalLightFeatureProcessorInterface* m_directionalLightFeatureProcessor = {};
AZ::Render::DisplayMapperFeatureProcessorInterface* m_displayMapperFeatureProcessor = {};
AZ::Entity* m_cameraEntity = {};
AZ::Component* m_cameraComponent = {};
AZ::Entity* m_postProcessEntity = {};
AZ::Entity* m_modelEntity = {};
AZ::Data::AssetId m_modelAssetId;
AZ::Entity* m_gridEntity = {};
AZ::Entity* m_shadowCatcherEntity = {};
AZ::Data::Instance<AZ::RPI::Material> m_shadowCatcherMaterial;
AZ::RPI::MaterialPropertyIndex m_shadowCatcherOpacityPropertyIndex;
AZStd::vector<DirectionalLightHandle> m_lightHandles;
AZ::Entity* m_iblEntity = {};
AZ::Render::SkyBoxFeatureProcessorInterface* m_skyboxFeatureProcessor = {};
AZStd::shared_ptr<MaterialEditorViewportInputController> m_viewportController;
QScopedPointer<Ui::MaterialViewportWidget> m_ui;
AZStd::unique_ptr<MaterialViewportRenderer> m_renderer;
};
} // namespace MaterialEditor
@@ -1,23 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Window/HelpDialog/HelpDialog.h>
namespace MaterialEditor
{
HelpDialog::HelpDialog(QWidget* parent)
: QDialog(parent)
, m_ui(new Ui::HelpDialogWidget)
{
m_ui->setupUi(this);
}
HelpDialog::~HelpDialog() = default;
} // namespace MaterialEditor
#include <Window/HelpDialog/moc_HelpDialog.cpp>
@@ -1,38 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/Memory/SystemAllocator.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QDialog>
#include <Window/HelpDialog/ui_HelpDialog.h>
AZ_POP_DISABLE_WARNING
#endif
namespace Ui
{
class HelpDialogWidget;
}
namespace MaterialEditor
{
//! Displays help for Material Editor
class HelpDialog
: public QDialog
{
Q_OBJECT
public:
HelpDialog(QWidget* parent = nullptr);
~HelpDialog();
QScopedPointer<Ui::HelpDialogWidget> m_ui;
};
} // namespace MaterialEditor
@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>HelpDialogWidget</class>
<widget class="QDialog" name="HelpDialogWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>270</width>
<height>210</height>
</rect>
</property>
<property name="windowTitle">
<string>Material Editor Help</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="m_helpText">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Material Editor Controls&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;LMB&lt;/span&gt; - pan camera&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;RMB&lt;/span&gt; or &lt;span style=&quot; font-weight:600;&quot;&gt;Alt+LMB&lt;/span&gt; - orbit camera around target&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;MMB&lt;/span&gt; or &lt;span style=&quot; font-weight:600;&quot;&gt;Alt+MMB&lt;/span&gt; - move camera on its xy plane&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Alt+RMB&lt;/span&gt; or &lt;span style=&quot; font-weight:600;&quot;&gt;LMB+RMB&lt;/span&gt; - dolly camera on its z axis&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Ctrl+LMB&lt;/span&gt; - rotate model&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Shift+LMB&lt;/span&gt; - rotate environment&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>HelpDialogWidget</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>HelpDialogWidget</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
@@ -16,7 +16,6 @@
#include <Document/MaterialDocumentRequestBus.h>
#include <Viewport/MaterialViewportWidget.h>
#include <Window/CreateMaterialDialog/CreateMaterialDialog.h>
#include <Window/HelpDialog/HelpDialog.h>
#include <Window/MaterialEditorWindow.h>
#include <Window/MaterialEditorWindowSettings.h>
#include <Window/MaterialInspector/MaterialInspector.h>
@@ -30,6 +29,7 @@ AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnin
#include <QCloseEvent>
#include <QDesktopServices>
#include <QFileDialog>
#include <QMessageBox>
#include <QUrl>
#include <QWindow>
AZ_POP_DISABLE_WARNING
@@ -178,8 +178,22 @@ namespace MaterialEditor
void MaterialEditorWindow::OpenHelp()
{
HelpDialog dialog(this);
dialog.exec();
QMessageBox::information(
this, windowTitle(),
R"(<html><head/><body>
<p><h3><u>Material Editor Controls</u></h3></p>
<p><b>LMB</b> - pan camera</p>
<p><b>RMB</b> or <b>Alt+LMB</b> - orbit camera around target</p>
<p><b>MMB</b> or <b>Alt+MMB</b> - move camera on its xy plane</p>
<p><b>Alt+RMB</b> or <b>LMB+RMB</b> - dolly camera on its z axis</p>
<p><b>Ctrl+LMB</b> - rotate model</p>
<p><b>Shift+LMB</b> - rotate environment</p>
</body></html>)");
}
void MaterialEditorWindow::OpenAbout()
{
QMessageBox::about(this, windowTitle(), QApplication::applicationName());
}
void MaterialEditorWindow::closeEvent(QCloseEvent* closeEvent)
@@ -44,6 +44,7 @@ namespace MaterialEditor
bool GetOpenDocumentParams(AZStd::string& openPath) override;
void OpenSettings() override;
void OpenHelp() override;
void OpenAbout() override;
void closeEvent(QCloseEvent* closeEvent) override;
@@ -152,7 +152,7 @@ namespace MaterialEditor
AtomToolsFramework::DynamicProperty property;
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(
property, m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetProperty,
AZ::RPI::MaterialPropertyId(groupName, uvNamePair.m_shaderInput.ToString()).GetFullName());
AZ::RPI::MaterialPropertyId(groupName, uvNamePair.m_shaderInput.ToString()));
group.m_properties.push_back(property);
property.SetValue(property.GetConfig().m_parentValue);
@@ -189,7 +189,7 @@ namespace MaterialEditor
AtomToolsFramework::DynamicProperty property;
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(
property, m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetProperty,
AZ::RPI::MaterialPropertyId(groupName, propertyDefinition.m_name).GetFullName());
AZ::RPI::MaterialPropertyId(groupName, propertyDefinition.m_name));
group.m_properties.push_back(property);
}
}

Some files were not shown because too many files have changed in this diff Show More