diff --git a/AutomatedTesting/Gem/AssetProcessorGemConfig.setreg b/AutomatedTesting/Gem/AssetProcessorGemConfig.setreg
index 043774c9cc..d4de26a2dc 100644
--- a/AutomatedTesting/Gem/AssetProcessorGemConfig.setreg
+++ b/AutomatedTesting/Gem/AssetProcessorGemConfig.setreg
@@ -3,19 +3,19 @@
"AssetProcessor": {
"Settings": {
"Exclude PythonTest Benchmark Settings Assets": {
- "pattern": ".*\\\\/PythonTests\\\\/.*benchmarksettings"
+ "pattern": "(^|.+/)PythonTests/.*benchmarksettings"
},
"Exclude fbx_tests": {
- "pattern": ".*\\\\/fbx_tests\\\\/assets\\\\/.*"
+ "pattern": "(^|.+/)fbx_tests/assets(/.+)$"
},
"Exclude wwise_bank_dependency_tests": {
- "pattern": ".*\\\\/wwise_bank_dependency_tests\\\\/assets\\\\/.*"
+ "pattern": "(^|.+/)wwise_bank_dependency_tests/assets(/.+)$"
},
"Exclude AssetProcessorTestAssets": {
- "pattern": ".*\\\\/asset_processor_tests\\\\/assets\\\\/.*"
+ "pattern": "(^|.+/)asset_processor_tests/assets(/.+)$"
},
"Exclude Restricted AssetProcessorTestAssets": {
- "pattern": ".*\\\\/asset_processor_tests\\\\/restricted\\\\/.*"
+ "pattern": "(^|.+/)asset_processor_tests/restricted(/.+)$"
}
}
}
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py
index c29a391be4..45298ed563 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py
@@ -26,6 +26,10 @@ class TestAutomation(EditorTestSuite):
class AtomEditorComponents_DirectionalLightAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_DirectionalLightAdded as test_module
+ @pytest.mark.test_case_id("C36525660")
+ class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module
+
@pytest.mark.test_case_id("C32078121")
class AtomEditorComponents_ExposureControlAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_ExposureControlAdded as test_module
@@ -33,47 +37,47 @@ class TestAutomation(EditorTestSuite):
@pytest.mark.test_case_id("C32078115")
class AtomEditorComponents_GlobalSkylightIBLAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_GlobalSkylightIBLAdded as test_module
+
+ @pytest.mark.test_case_id("C32078122")
+ class AtomEditorComponents_GridAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_GridAdded as test_module
+
+ @pytest.mark.test_case_id("C32078117")
+ class AtomEditorComponents_LightAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_LightAdded as test_module
+
+ @pytest.mark.test_case_id("C32078123")
+ class AtomEditorComponents_MaterialAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_MaterialAdded as test_module
+
+ @pytest.mark.test_case_id("C32078124")
+ class AtomEditorComponents_MeshAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_MeshAdded as test_module
@pytest.mark.test_case_id("C32078125")
class AtomEditorComponents_PhysicalSkyAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_PhysicalSkyAdded as test_module
+ @pytest.mark.test_case_id("C36525664")
+ class AtomEditorComponents_PostFXGradientWeightModifierAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded as test_module
+
+ @pytest.mark.test_case_id("C32078127")
+ class AtomEditorComponents_PostFXLayerAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_PostFXLayerAdded as test_module
+
@pytest.mark.test_case_id("C32078131")
class AtomEditorComponents_PostFXRadiusWeightModifierAdded(EditorSharedTest):
from Atom.tests import (
hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded as test_module)
- @pytest.mark.test_case_id("C32078117")
- class AtomEditorComponents_LightAdded(EditorSharedTest):
- from Atom.tests import hydra_AtomEditorComponents_LightAdded as test_module
-
- @pytest.mark.test_case_id("C36525660")
- class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest):
- from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module
+ @pytest.mark.test_case_id("C36525665")
+ class AtomEditorComponents_PostFXShapeWeightModifierAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded as test_module
@pytest.mark.test_case_id("C32078128")
class AtomEditorComponents_ReflectionProbeAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_ReflectionProbeAdded as test_module
- @pytest.mark.test_case_id("C32078124")
- class AtomEditorComponents_MeshAdded(EditorSharedTest):
- from Atom.tests import hydra_AtomEditorComponents_MeshAdded as test_module
-
- @pytest.mark.test_case_id("C32078123")
- class AtomEditorComponents_MaterialAdded(EditorSharedTest):
- from Atom.tests import hydra_AtomEditorComponents_MaterialAdded as test_module
-
- @pytest.mark.test_case_id("C32078127")
- class AtomEditorComponents_PostFXLayerAdded(EditorSharedTest):
- from Atom.tests import hydra_AtomEditorComponents_PostFXLayerAdded as test_module
-
- @pytest.mark.test_case_id("C36525665")
- class AtomEditorComponents_PostFXShapeWeightModifierAdded(EditorSharedTest):
- from Atom.tests import hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded as test_module
-
- @pytest.mark.test_case_id("C36525664")
- class AtomEditorComponents_PostFXGradientWeightModifierAdded(EditorSharedTest):
- from Atom.tests import hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded as test_module
-
class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest):
from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py
index fa02b75fe2..e840cf3cf1 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py
@@ -5,25 +5,52 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
-# fmt: off
class Tests:
- camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created")
- camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity")
- camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component")
- creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed")
- creation_redo = ("REDO Entity creation success", "REDO Entity creation failed")
- decal_creation = ("Decal Entity successfully created", "Decal Entity failed to be created")
- decal_component = ("Entity has a Decal component", "Entity failed to find Decal component")
- material_property_set = ("Material property set on Decal component", "Couldn't set Material property on Decal component")
- enter_game_mode = ("Entered game mode", "Failed to enter game mode")
- exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
- is_visible = ("Entity is visible", "Entity was not visible")
- is_hidden = ("Entity is hidden", "Entity was not hidden")
- entity_deleted = ("Entity deleted", "Entity was not deleted")
- deletion_undo = ("UNDO deletion success", "UNDO deletion failed")
- deletion_redo = ("REDO deletion success", "REDO deletion failed")
- no_error_occurred = ("No errors detected", "Errors were detected")
-# fmt: on
+ camera_creation = (
+ "Camera Entity successfully created",
+ "Camera Entity failed to be created")
+ camera_component_added = (
+ "Camera component was added to entity",
+ "Camera component failed to be added to entity")
+ camera_component_check = (
+ "Entity has a Camera component",
+ "Entity failed to find Camera component")
+ creation_undo = (
+ "UNDO Entity creation success",
+ "UNDO Entity creation failed")
+ creation_redo = (
+ "REDO Entity creation success",
+ "REDO Entity creation failed")
+ decal_creation = (
+ "Decal Entity successfully created",
+ "Decal Entity failed to be created")
+ decal_component = (
+ "Entity has a Decal component",
+ "Entity failed to find Decal component")
+ material_property_set = (
+ "Material property set on Decal component",
+ "Couldn't set Material property on Decal component")
+ enter_game_mode = (
+ "Entered game mode",
+ "Failed to enter game mode")
+ exit_game_mode = (
+ "Exited game mode",
+ "Couldn't exit game mode")
+ is_visible = (
+ "Entity is visible",
+ "Entity was not visible")
+ is_hidden = (
+ "Entity is hidden",
+ "Entity was not hidden")
+ entity_deleted = (
+ "Entity deleted",
+ "Entity was not deleted")
+ deletion_undo = (
+ "UNDO deletion success",
+ "UNDO deletion failed")
+ deletion_redo = (
+ "REDO deletion success",
+ "REDO deletion failed")
def AtomEditorComponents_Decal_AddedToEntity():
@@ -51,35 +78,33 @@ def AtomEditorComponents_Decal_AddedToEntity():
9) Delete Decal entity.
10) UNDO deletion.
11) REDO deletion.
- 12) Look for errors.
+ 12) Look for errors and asserts.
:return: None
"""
import os
- import azlmbr.asset as asset
- import azlmbr.bus as bus
import azlmbr.legacy.general as general
- import azlmbr.math as math
+ from editor_python_test_tools.asset_utils import Asset
from editor_python_test_tools.editor_entity_utils import EditorEntity
- from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
+ from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
- helper.init_idle()
- helper.open_level("", "Base")
+ TestHelper.init_idle()
+ TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Create a Decal entity with no components.
- decal_name = "Decal"
- decal_entity = EditorEntity.create_editor_entity_at(math.Vector3(512.0, 512.0, 34.0), decal_name)
+ decal_entity = EditorEntity.create_editor_entity(AtomComponentProperties.decal())
Report.critical_result(Tests.decal_creation, decal_entity.exists())
# 2. Add Decal component to Decal entity.
- decal_component = decal_entity.add_component(decal_name)
- Report.critical_result(Tests.decal_component, decal_entity.has_component(decal_name))
+ decal_component = decal_entity.add_component(AtomComponentProperties.decal())
+ Report.critical_result(Tests.decal_component, decal_entity.has_component(AtomComponentProperties.decal()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -106,9 +131,9 @@ def AtomEditorComponents_Decal_AddedToEntity():
Report.result(Tests.creation_redo, decal_entity.exists())
# 5. Enter/Exit game mode.
- helper.enter_game_mode(Tests.enter_game_mode)
+ TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
- helper.exit_game_mode(Tests.exit_game_mode)
+ TestHelper.exit_game_mode(Tests.exit_game_mode)
# 6. Test IsHidden.
decal_entity.set_visibility_state(False)
@@ -120,13 +145,11 @@ def AtomEditorComponents_Decal_AddedToEntity():
Report.result(Tests.is_visible, decal_entity.is_visible() is True)
# 8. Set Material property on Decal component.
- decal_material_property_path = "Controller|Configuration|Material"
- decal_material_asset_path = os.path.join("AutomatedTesting", "Materials", "basic_grey.material")
- decal_material_asset = asset.AssetCatalogRequestBus(
- bus.Broadcast, "GetAssetIdByPath", decal_material_asset_path, math.Uuid(), False)
- decal_component.set_component_property_value(decal_material_property_path, decal_material_asset)
- get_material_property = decal_component.get_component_property_value(decal_material_property_path)
- Report.result(Tests.material_property_set, get_material_property == decal_material_asset)
+ decal_material_asset_path = os.path.join("materials", "basic_grey.azmaterial")
+ decal_material_asset = Asset.find_asset_by_path(decal_material_asset_path, False)
+ decal_component.set_component_property_value(AtomComponentProperties.decal('Material'), decal_material_asset.id)
+ get_material_property = decal_component.get_component_property_value(AtomComponentProperties.decal('Material'))
+ Report.result(Tests.material_property_set, get_material_property == decal_material_asset.id)
# 9. Delete Decal entity.
decal_entity.delete()
@@ -141,9 +164,12 @@ def AtomEditorComponents_Decal_AddedToEntity():
general.redo()
Report.result(Tests.deletion_redo, not decal_entity.exists())
- # 12. Look for errors.
- helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0)
- Report.result(Tests.no_error_occurred, not error_tracer.has_errors)
+ # 12. Look for errors and asserts.
+ TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+ for error_info in error_tracer.errors:
+ Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
+ for assert_info in error_tracer.asserts:
+ Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py
index 80284902ea..e2c5f9c77d 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py
@@ -5,28 +5,61 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
-# fmt: off
class Tests:
- camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created")
- camera_component_added = ("Camera component was added to Camera entity", "Camera component failed to be added to entity")
- camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component")
- camera_property_set = ("DepthOfField Entity set Camera Entity", "DepthOfField Entity could not set Camera Entity")
- creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed")
- creation_redo = ("REDO Entity creation success", "REDO Entity creation failed")
- depth_of_field_creation = ("DepthOfField Entity successfully created", "DepthOfField Entity failed to be created")
- depth_of_field_component = ("Entity has a DepthOfField component", "Entity failed to find DepthOfField component")
- depth_of_field_disabled = ("DepthOfField component disabled", "DepthOfField component was not disabled.")
- post_fx_component = ("Entity has a Post FX Layer component", "Entity did not have a Post FX Layer component")
- depth_of_field_enabled = ("DepthOfField component enabled", "DepthOfField component was not enabled.")
- enter_game_mode = ("Entered game mode", "Failed to enter game mode")
- exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
- is_visible = ("Entity is visible", "Entity was not visible")
- is_hidden = ("Entity is hidden", "Entity was not hidden")
- entity_deleted = ("Entity deleted", "Entity was not deleted")
- deletion_undo = ("UNDO deletion success", "UNDO deletion failed")
- deletion_redo = ("REDO deletion success", "REDO deletion failed")
- no_error_occurred = ("No errors detected", "Errors were detected")
-# fmt: on
+ camera_creation = (
+ "Camera Entity successfully created",
+ "Camera Entity failed to be created")
+ camera_component_added = (
+ "Camera component was added to Camera entity",
+ "Camera component failed to be added to entity")
+ camera_component_check = (
+ "Entity has a Camera component",
+ "Entity failed to find Camera component")
+ camera_property_set = (
+ "DepthOfField Entity set Camera Entity",
+ "DepthOfField Entity could not set Camera Entity")
+ creation_undo = (
+ "UNDO Entity creation success",
+ "UNDO Entity creation failed")
+ creation_redo = (
+ "REDO Entity creation success",
+ "REDO Entity creation failed")
+ depth_of_field_creation = (
+ "DepthOfField Entity successfully created",
+ "DepthOfField Entity failed to be created")
+ depth_of_field_component = (
+ "Entity has a DepthOfField component",
+ "Entity failed to find DepthOfField component")
+ depth_of_field_disabled = (
+ "DepthOfField component disabled",
+ "DepthOfField component was not disabled.")
+ post_fx_component = (
+ "Entity has a Post FX Layer component",
+ "Entity did not have a Post FX Layer component")
+ depth_of_field_enabled = (
+ "DepthOfField component enabled",
+ "DepthOfField component was not enabled.")
+ enter_game_mode = (
+ "Entered game mode",
+ "Failed to enter game mode")
+ exit_game_mode = (
+ "Exited game mode",
+ "Couldn't exit game mode")
+ is_visible = (
+ "Entity is visible",
+ "Entity was not visible")
+ is_hidden = (
+ "Entity is hidden",
+ "Entity was not hidden")
+ entity_deleted = (
+ "Entity deleted",
+ "Entity was not deleted")
+ deletion_undo = (
+ "UNDO deletion success",
+ "UNDO deletion failed")
+ deletion_redo = (
+ "REDO deletion success",
+ "REDO deletion failed")
def AtomEditorComponents_DepthOfField_AddedToEntity():
@@ -59,33 +92,32 @@ def AtomEditorComponents_DepthOfField_AddedToEntity():
14) Delete DepthOfField entity.
15) UNDO deletion.
16) REDO deletion.
- 17) Look for errors.
+ 17) Look for errors and asserts.
:return: None
"""
import azlmbr.legacy.general as general
- import azlmbr.math as math
from editor_python_test_tools.editor_entity_utils import EditorEntity
- from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
+ from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
- helper.init_idle()
- helper.open_level("", "Base")
+ TestHelper.init_idle()
+ TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Create a DepthOfField entity with no components.
- depth_of_field_name = "DepthOfField"
- depth_of_field_entity = EditorEntity.create_editor_entity_at(
- math.Vector3(512.0, 512.0, 34.0), depth_of_field_name)
+ depth_of_field_entity = EditorEntity.create_editor_entity(AtomComponentProperties.depth_of_field())
Report.critical_result(Tests.depth_of_field_creation, depth_of_field_entity.exists())
# 2. Add a DepthOfField component to DepthOfField entity.
- depth_of_field_component = depth_of_field_entity.add_component(depth_of_field_name)
- Report.critical_result(Tests.depth_of_field_component, depth_of_field_entity.has_component(depth_of_field_name))
+ depth_of_field_component = depth_of_field_entity.add_component(AtomComponentProperties.depth_of_field())
+ Report.critical_result(Tests.depth_of_field_component,
+ depth_of_field_entity.has_component(AtomComponentProperties.depth_of_field()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -115,17 +147,16 @@ def AtomEditorComponents_DepthOfField_AddedToEntity():
Report.result(Tests.depth_of_field_disabled, not depth_of_field_component.is_enabled())
# 6. Add Post FX Layer component since it is required by the DepthOfField component.
- post_fx_layer = "PostFX Layer"
- depth_of_field_entity.add_component(post_fx_layer)
- Report.result(Tests.post_fx_component, depth_of_field_entity.has_component(post_fx_layer))
+ depth_of_field_entity.add_component(AtomComponentProperties.postfx_layer())
+ Report.result(Tests.post_fx_component, depth_of_field_entity.has_component(AtomComponentProperties.postfx_layer()))
# 7. Verify DepthOfField component is enabled.
Report.result(Tests.depth_of_field_enabled, depth_of_field_component.is_enabled())
# 8. Enter/Exit game mode.
- helper.enter_game_mode(Tests.enter_game_mode)
+ TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
- helper.exit_game_mode(Tests.exit_game_mode)
+ TestHelper.exit_game_mode(Tests.exit_game_mode)
# 9. Test IsHidden.
depth_of_field_entity.set_visibility_state(False)
@@ -137,19 +168,20 @@ def AtomEditorComponents_DepthOfField_AddedToEntity():
Report.result(Tests.is_visible, depth_of_field_entity.is_visible() is True)
# 11. Add Camera entity.
- camera_name = "Camera"
- camera_entity = EditorEntity.create_editor_entity_at(math.Vector3(512.0, 512.0, 34.0), camera_name)
+ camera_entity = EditorEntity.create_editor_entity(AtomComponentProperties.camera())
Report.result(Tests.camera_creation, camera_entity.exists())
# 12. Add Camera component to Camera entity.
- camera_entity.add_component(camera_name)
- Report.result(Tests.camera_component_added, camera_entity.has_component(camera_name))
+ camera_entity.add_component(AtomComponentProperties.camera())
+ Report.result(Tests.camera_component_added, camera_entity.has_component(AtomComponentProperties.camera()))
# 13. Set the DepthOfField components's Camera Entity to the newly created Camera entity.
- depth_of_field_camera_property_path = "Controller|Configuration|Camera Entity"
- depth_of_field_component.set_component_property_value(depth_of_field_camera_property_path, camera_entity.id)
- camera_entity_set = depth_of_field_component.get_component_property_value(depth_of_field_camera_property_path)
- Report.result(Tests.camera_property_set, camera_entity.id == camera_entity_set)
+ depth_of_field_component.set_component_property_value(
+ AtomComponentProperties.depth_of_field('Camera Entity'), camera_entity.id)
+ Report.result(
+ Tests.camera_property_set,
+ camera_entity.id == depth_of_field_component.get_component_property_value(
+ AtomComponentProperties.depth_of_field('Camera Entity')))
# 14. Delete DepthOfField entity.
depth_of_field_entity.delete()
@@ -163,9 +195,12 @@ def AtomEditorComponents_DepthOfField_AddedToEntity():
general.redo()
Report.result(Tests.deletion_redo, not depth_of_field_entity.exists())
- # 17. Look for errors.
- helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0)
- Report.result(Tests.no_error_occurred, not error_tracer.has_errors)
+ # 17. Look for errors and asserts.
+ TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+ for error_info in error_tracer.errors:
+ Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
+ for assert_info in error_tracer.asserts:
+ Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py
index 048e132df4..f3ffc0f366 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py
@@ -5,25 +5,52 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
-# fmt: off
class Tests:
- camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created")
- camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity")
- camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component")
- creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed")
- creation_redo = ("REDO Entity creation success", "REDO Entity creation failed")
- directional_light_creation = ("Directional Light Entity successfully created", "Directional Light Entity failed to be created")
- directional_light_component = ("Entity has a Directional Light component", "Entity failed to find Directional Light component")
- shadow_camera_check = ("Directional Light component Shadow camera set", "Directional Light component Shadow camera was not set")
- enter_game_mode = ("Entered game mode", "Failed to enter game mode")
- exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
- is_visible = ("Entity is visible", "Entity was not visible")
- is_hidden = ("Entity is hidden", "Entity was not hidden")
- entity_deleted = ("Entity deleted", "Entity was not deleted")
- deletion_undo = ("UNDO deletion success", "UNDO deletion failed")
- deletion_redo = ("REDO deletion success", "REDO deletion failed")
- no_error_occurred = ("No errors detected", "Errors were detected")
-# fmt: on
+ camera_creation = (
+ "Camera Entity successfully created",
+ "Camera Entity failed to be created")
+ camera_component_added = (
+ "Camera component was added to entity",
+ "Camera component failed to be added to entity")
+ camera_component_check = (
+ "Entity has a Camera component",
+ "Entity failed to find Camera component")
+ creation_undo = (
+ "UNDO Entity creation success",
+ "UNDO Entity creation failed")
+ creation_redo = (
+ "REDO Entity creation success",
+ "REDO Entity creation failed")
+ directional_light_creation = (
+ "Directional Light Entity successfully created",
+ "Directional Light Entity failed to be created")
+ directional_light_component = (
+ "Entity has a Directional Light component",
+ "Entity failed to find Directional Light component")
+ shadow_camera_check = (
+ "Directional Light component Shadow camera set",
+ "Directional Light component Shadow camera was not set")
+ enter_game_mode = (
+ "Entered game mode",
+ "Failed to enter game mode")
+ exit_game_mode = (
+ "Exited game mode",
+ "Couldn't exit game mode")
+ is_visible = (
+ "Entity is visible",
+ "Entity was not visible")
+ is_hidden = (
+ "Entity is hidden",
+ "Entity was not hidden")
+ entity_deleted = (
+ "Entity deleted",
+ "Entity was not deleted")
+ deletion_undo = (
+ "UNDO deletion success",
+ "UNDO deletion failed")
+ deletion_redo = (
+ "REDO deletion success",
+ "REDO deletion failed")
def AtomEditorComponents_DirectionalLight_AddedToEntity():
@@ -53,34 +80,33 @@ def AtomEditorComponents_DirectionalLight_AddedToEntity():
11) Delete Directional Light entity.
12) UNDO deletion.
13) REDO deletion.
- 14) Look for errors.
+ 14) Look for errors and asserts.
:return: None
"""
import azlmbr.legacy.general as general
- import azlmbr.math as math
from editor_python_test_tools.editor_entity_utils import EditorEntity
- from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
+ from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
- helper.init_idle()
- helper.open_level("", "Base")
+ TestHelper.init_idle()
+ TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Create a Directional Light entity with no components.
- directional_light_name = "Directional Light"
- directional_light_entity = EditorEntity.create_editor_entity_at(
- math.Vector3(512.0, 512.0, 34.0), directional_light_name)
+ directional_light_entity = EditorEntity.create_editor_entity(AtomComponentProperties.directional_light())
Report.critical_result(Tests.directional_light_creation, directional_light_entity.exists())
# 2. Add Directional Light component to Directional Light entity.
- directional_light_component = directional_light_entity.add_component(directional_light_name)
+ directional_light_component = directional_light_entity.add_component(AtomComponentProperties.directional_light())
Report.critical_result(
- Tests.directional_light_component, directional_light_entity.has_component(directional_light_name))
+ Tests.directional_light_component,
+ directional_light_entity.has_component(AtomComponentProperties.directional_light()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -107,9 +133,9 @@ def AtomEditorComponents_DirectionalLight_AddedToEntity():
Report.result(Tests.creation_redo, directional_light_entity.exists())
# 5. Enter/Exit game mode.
- helper.enter_game_mode(Tests.enter_game_mode)
+ TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
- helper.exit_game_mode(Tests.exit_game_mode)
+ TestHelper.exit_game_mode(Tests.exit_game_mode)
# 6. Test IsHidden.
directional_light_entity.set_visibility_state(False)
@@ -121,19 +147,20 @@ def AtomEditorComponents_DirectionalLight_AddedToEntity():
Report.result(Tests.is_visible, directional_light_entity.is_visible() is True)
# 8. Add Camera entity.
- camera_name = "Camera"
- camera_entity = EditorEntity.create_editor_entity_at(math.Vector3(512.0, 512.0, 34.0), camera_name)
+ camera_entity = EditorEntity.create_editor_entity(AtomComponentProperties.camera())
Report.result(Tests.camera_creation, camera_entity.exists())
# 9. Add Camera component to Camera entity.
- camera_entity.add_component(camera_name)
- Report.result(Tests.camera_component_added, camera_entity.has_component(camera_name))
+ camera_entity.add_component(AtomComponentProperties.camera())
+ Report.result(Tests.camera_component_added, camera_entity.has_component(AtomComponentProperties.camera()))
# 10. Set the Directional Light component property Shadow|Camera to the Camera entity.
- shadow_camera_property_path = "Controller|Configuration|Shadow|Camera"
- directional_light_component.set_component_property_value(shadow_camera_property_path, camera_entity.id)
- shadow_camera_set = directional_light_component.get_component_property_value(shadow_camera_property_path)
- Report.result(Tests.shadow_camera_check, camera_entity.id == shadow_camera_set)
+ directional_light_component.set_component_property_value(
+ AtomComponentProperties.directional_light('Camera'), camera_entity.id)
+ Report.result(
+ Tests.shadow_camera_check,
+ camera_entity.id == directional_light_component.get_component_property_value(
+ AtomComponentProperties.directional_light('Camera')))
# 11. Delete DirectionalLight entity.
directional_light_entity.delete()
@@ -147,9 +174,12 @@ def AtomEditorComponents_DirectionalLight_AddedToEntity():
general.redo()
Report.result(Tests.deletion_redo, not directional_light_entity.exists())
- # 14. Look for errors.
- helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0)
- Report.result(Tests.no_error_occurred, not error_tracer.has_errors)
+ # 14. Look for errors and asserts.
+ TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+ for error_info in error_tracer.errors:
+ Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
+ for assert_info in error_tracer.asserts:
+ Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py
index 39d7acf4f4..f8881bfa2a 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py
@@ -5,24 +5,49 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
-# fmt: off
class Tests:
- camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created")
- camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity")
- camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component")
- creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed")
- creation_redo = ("REDO Entity creation success", "REDO Entity creation failed")
- display_mapper_creation = ("Display Mapper Entity successfully created", "Display Mapper Entity failed to be created")
- display_mapper_component = ("Entity has a Display Mapper component", "Entity failed to find Display Mapper component")
- enter_game_mode = ("Entered game mode", "Failed to enter game mode")
- exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
- is_visible = ("Entity is visible", "Entity was not visible")
- is_hidden = ("Entity is hidden", "Entity was not hidden")
- entity_deleted = ("Entity deleted", "Entity was not deleted")
- deletion_undo = ("UNDO deletion success", "UNDO deletion failed")
- deletion_redo = ("REDO deletion success", "REDO deletion failed")
- no_error_occurred = ("No errors detected", "Errors were detected")
-# fmt: on
+ camera_creation = (
+ "Camera Entity successfully created",
+ "Camera Entity failed to be created")
+ camera_component_added = (
+ "Camera component was added to entity",
+ "Camera component failed to be added to entity")
+ camera_component_check = (
+ "Entity has a Camera component",
+ "Entity failed to find Camera component")
+ creation_undo = (
+ "UNDO Entity creation success",
+ "UNDO Entity creation failed")
+ creation_redo = (
+ "REDO Entity creation success",
+ "REDO Entity creation failed")
+ display_mapper_creation = (
+ "Display Mapper Entity successfully created",
+ "Display Mapper Entity failed to be created")
+ display_mapper_component = (
+ "Entity has a Display Mapper component",
+ "Entity failed to find Display Mapper component")
+ enter_game_mode = (
+ "Entered game mode",
+ "Failed to enter game mode")
+ exit_game_mode = (
+ "Exited game mode",
+ "Couldn't exit game mode")
+ is_visible = (
+ "Entity is visible",
+ "Entity was not visible")
+ is_hidden = (
+ "Entity is hidden",
+ "Entity was not hidden")
+ entity_deleted = (
+ "Entity deleted",
+ "Entity was not deleted")
+ deletion_undo = (
+ "UNDO deletion success",
+ "UNDO deletion failed")
+ deletion_redo = (
+ "REDO deletion success",
+ "REDO deletion failed")
def AtomEditorComponents_DisplayMapper_AddedToEntity():
@@ -49,33 +74,33 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
8) Delete Display Mapper entity.
9) UNDO deletion.
10) REDO deletion.
- 11) Look for errors.
+ 11) Look for errors and asserts.
:return: None
"""
import azlmbr.legacy.general as general
- import azlmbr.math as math
from editor_python_test_tools.editor_entity_utils import EditorEntity
- from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
+ from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
- helper.init_idle()
- helper.open_level("", "Base")
+ TestHelper.init_idle()
+ TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Create a Display Mapper entity with no components.
- display_mapper = "Display Mapper"
- display_mapper_entity = EditorEntity.create_editor_entity_at(
- math.Vector3(512.0, 512.0, 34.0), f"{display_mapper}")
+ display_mapper_entity = EditorEntity.create_editor_entity(AtomComponentProperties.display_mapper())
Report.critical_result(Tests.display_mapper_creation, display_mapper_entity.exists())
# 2. Add Display Mapper component to Display Mapper entity.
- display_mapper_entity.add_component(display_mapper)
- Report.critical_result(Tests.display_mapper_component, display_mapper_entity.has_component(display_mapper))
+ display_mapper_entity.add_component(AtomComponentProperties.display_mapper())
+ Report.critical_result(
+ Tests.display_mapper_component,
+ display_mapper_entity.has_component(AtomComponentProperties.display_mapper()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -102,9 +127,9 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
Report.result(Tests.creation_redo, display_mapper_entity.exists())
# 5. Enter/Exit game mode.
- helper.enter_game_mode(Tests.enter_game_mode)
+ TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
- helper.exit_game_mode(Tests.exit_game_mode)
+ TestHelper.exit_game_mode(Tests.exit_game_mode)
# 6. Test IsHidden.
display_mapper_entity.set_visibility_state(False)
@@ -127,9 +152,12 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
general.redo()
Report.result(Tests.deletion_redo, not display_mapper_entity.exists())
- # 11. Look for errors.
- helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0)
- Report.result(Tests.no_error_occurred, not error_tracer.has_errors)
+ # 11. Look for errors and asserts.
+ TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+ for error_info in error_tracer.errors:
+ Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
+ for assert_info in error_tracer.asserts:
+ Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py
index 23a84435f7..6fa9660539 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py
@@ -5,25 +5,58 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
-# fmt: off
class Tests:
- camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created")
- camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity")
- camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component")
- creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed")
- creation_redo = ("REDO Entity creation success", "REDO Entity creation failed")
- exposure_control_creation = ("ExposureControl Entity successfully created", "ExposureControl Entity failed to be created")
- exposure_control_component = ("Entity has a Exposure Control component", "Entity failed to find Exposure Control component")
- post_fx_component = ("Entity has a Post FX Layer component", "Entity did not have a Post FX Layer component")
- enter_game_mode = ("Entered game mode", "Failed to enter game mode")
- exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
- is_visible = ("Entity is visible", "Entity was not visible")
- is_hidden = ("Entity is hidden", "Entity was not hidden")
- entity_deleted = ("Entity deleted", "Entity was not deleted")
- deletion_undo = ("UNDO deletion success", "UNDO deletion failed")
- deletion_redo = ("REDO deletion success", "REDO deletion failed")
- no_error_occurred = ("No errors detected", "Errors were detected")
-# fmt: on
+ camera_creation = (
+ "Camera Entity successfully created",
+ "Camera Entity failed to be created")
+ camera_component_added = (
+ "Camera component was added to entity",
+ "Camera component failed to be added to entity")
+ camera_component_check = (
+ "Entity has a Camera component",
+ "Entity failed to find Camera component")
+ creation_undo = (
+ "UNDO Entity creation success",
+ "UNDO Entity creation failed")
+ creation_redo = (
+ "REDO Entity creation success",
+ "REDO Entity creation failed")
+ exposure_control_creation = (
+ "ExposureControl Entity successfully created",
+ "ExposureControl Entity failed to be created")
+ exposure_control_component = (
+ "Entity has a Exposure Control component",
+ "Entity failed to find Exposure Control component")
+ exposure_control_disabled = (
+ "DepthOfField component disabled",
+ "DepthOfField component was not disabled.")
+ post_fx_component = (
+ "Entity has a Post FX Layer component",
+ "Entity did not have a Post FX Layer component")
+ exposure_control_enabled = (
+ "DepthOfField component enabled",
+ "DepthOfField component was not enabled.")
+ enter_game_mode = (
+ "Entered game mode",
+ "Failed to enter game mode")
+ exit_game_mode = (
+ "Exited game mode",
+ "Couldn't exit game mode")
+ is_visible = (
+ "Entity is visible",
+ "Entity was not visible")
+ is_hidden = (
+ "Entity is hidden",
+ "Entity was not hidden")
+ entity_deleted = (
+ "Entity deleted",
+ "Entity was not deleted")
+ deletion_undo = (
+ "UNDO deletion success",
+ "UNDO deletion failed")
+ deletion_redo = (
+ "REDO deletion success",
+ "REDO deletion failed")
def AtomEditorComponents_ExposureControl_AddedToEntity():
@@ -44,41 +77,42 @@ def AtomEditorComponents_ExposureControl_AddedToEntity():
2) Add Exposure Control component to Exposure Control entity.
3) UNDO the entity creation and component addition.
4) REDO the entity creation and component addition.
- 5) Enter/Exit game mode.
- 6) Test IsHidden.
- 7) Test IsVisible.
- 8) Add Post FX Layer component.
- 9) Delete Exposure Control entity.
- 10) UNDO deletion.
- 11) REDO deletion.
- 12) Look for errors.
+ 5) Verify Exposure Control component not enabled.
+ 6) Add Post FX Layer component since it is required by the Exposure Control component.
+ 7) Verify Exposure Control component is enabled.
+ 8) Enter/Exit game mode.
+ 9) Test IsHidden.
+ 10) Test IsVisible.
+ 11) Delete Exposure Control entity.
+ 12) UNDO deletion.
+ 13) REDO deletion.
+ 14) Look for errors and asserts.
:return: None
"""
import azlmbr.legacy.general as general
- import azlmbr.math as math
from editor_python_test_tools.editor_entity_utils import EditorEntity
- from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
+ from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
- helper.init_idle()
- helper.open_level("", "Base")
+ TestHelper.init_idle()
+ TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Creation of Exposure Control entity with no components.
- exposure_control_name = "Exposure Control"
- exposure_control_entity = EditorEntity.create_editor_entity_at(
- math.Vector3(512.0, 512.0, 34.0), f"{exposure_control_name}")
+ exposure_control_entity = EditorEntity.create_editor_entity(AtomComponentProperties.exposure_control())
Report.critical_result(Tests.exposure_control_creation, exposure_control_entity.exists())
# 2. Add Exposure Control component to Exposure Control entity.
- exposure_control_entity.add_component(exposure_control_name)
+ exposure_control_component = exposure_control_entity.add_component(AtomComponentProperties.exposure_control())
Report.critical_result(
- Tests.exposure_control_component, exposure_control_entity.has_component(exposure_control_name))
+ Tests.exposure_control_component,
+ exposure_control_entity.has_component(AtomComponentProperties.exposure_control()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -104,40 +138,49 @@ def AtomEditorComponents_ExposureControl_AddedToEntity():
general.idle_wait_frames(1)
Report.result(Tests.creation_redo, exposure_control_entity.exists())
- # 5. Enter/Exit game mode.
- helper.enter_game_mode(Tests.enter_game_mode)
- general.idle_wait_frames(1)
- helper.exit_game_mode(Tests.exit_game_mode)
+ # 5. Verify Exposure Control component not enabled.
+ Report.result(Tests.exposure_control_disabled, not exposure_control_component.is_enabled())
- # 6. Test IsHidden.
+ # 6. Add Post FX Layer component since it is required by the Exposure Control component.
+ exposure_control_entity.add_component(AtomComponentProperties.postfx_layer())
+ Report.result(Tests.post_fx_component,
+ exposure_control_entity.has_component(AtomComponentProperties.postfx_layer()))
+
+ # 7. Verify Exposure Control component is enabled.
+ Report.result(Tests.exposure_control_enabled, exposure_control_component.is_enabled())
+
+ # 8. Enter/Exit game mode.
+ TestHelper.enter_game_mode(Tests.enter_game_mode)
+ general.idle_wait_frames(1)
+ TestHelper.exit_game_mode(Tests.exit_game_mode)
+
+ # 9. Test IsHidden.
exposure_control_entity.set_visibility_state(False)
Report.result(Tests.is_hidden, exposure_control_entity.is_hidden() is True)
- # 7. Test IsVisible.
+ # 10. Test IsVisible.
exposure_control_entity.set_visibility_state(True)
general.idle_wait_frames(1)
Report.result(Tests.is_visible, exposure_control_entity.is_visible() is True)
- # 8. Add Post FX Layer component.
- post_fx_layer_name = "PostFX Layer"
- exposure_control_entity.add_component(post_fx_layer_name)
- Report.result(Tests.post_fx_component, exposure_control_entity.has_component(post_fx_layer_name))
-
- # 9. Delete ExposureControl entity.
+ # 11. Delete ExposureControl entity.
exposure_control_entity.delete()
Report.result(Tests.entity_deleted, not exposure_control_entity.exists())
- # 10. UNDO deletion.
+ # 12. UNDO deletion.
general.undo()
Report.result(Tests.deletion_undo, exposure_control_entity.exists())
- # 11. REDO deletion.
+ # 13. REDO deletion.
general.redo()
Report.result(Tests.deletion_redo, not exposure_control_entity.exists())
- # 12. Look for errors.
- helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0)
- Report.result(Tests.no_error_occurred, not error_tracer.has_errors)
+ # 14. Look for errors and asserts.
+ TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+ for error_info in error_tracer.errors:
+ Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
+ for assert_info in error_tracer.asserts:
+ Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py
index cc891f5929..db8f2daaee 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py
@@ -5,26 +5,55 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
-# fmt: off
class Tests:
- camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created")
- camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity")
- camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component")
- creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed")
- creation_redo = ("REDO Entity creation success", "REDO Entity creation failed")
- global_skylight_creation = ("Global Skylight (IBL) Entity successfully created", "Global Skylight (IBL) Entity failed to be created")
- global_skylight_component = ("Entity has a Global Skylight (IBL) component", "Entity failed to find Global Skylight (IBL) component")
- diffuse_image_set = ("Entity has the Diffuse Image set", "Entity did not the Diffuse Image set")
- specular_image_set = ("Entity has the Specular Image set", "Entity did not the Specular Image set")
- enter_game_mode = ("Entered game mode", "Failed to enter game mode")
- exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
- is_visible = ("Entity is visible", "Entity was not visible")
- is_hidden = ("Entity is hidden", "Entity was not hidden")
- entity_deleted = ("Entity deleted", "Entity was not deleted")
- deletion_undo = ("UNDO deletion success", "UNDO deletion failed")
- deletion_redo = ("REDO deletion success", "REDO deletion failed")
- no_error_occurred = ("No errors detected", "Errors were detected")
-# fmt: on
+ camera_creation = (
+ "Camera Entity successfully created",
+ "Camera Entity failed to be created")
+ camera_component_added = (
+ "Camera component was added to entity",
+ "Camera component failed to be added to entity")
+ camera_component_check = (
+ "Entity has a Camera component",
+ "Entity failed to find Camera component")
+ creation_undo = (
+ "UNDO Entity creation success",
+ "UNDO Entity creation failed")
+ creation_redo = (
+ "REDO Entity creation success",
+ "REDO Entity creation failed")
+ global_skylight_creation = (
+ "Global Skylight (IBL) Entity successfully created",
+ "Global Skylight (IBL) Entity failed to be created")
+ global_skylight_component = (
+ "Entity has a Global Skylight (IBL) component",
+ "Entity failed to find Global Skylight (IBL) component")
+ diffuse_image_set = (
+ "Entity has the Diffuse Image set",
+ "Entity did not the Diffuse Image set")
+ specular_image_set = (
+ "Entity has the Specular Image set",
+ "Entity did not the Specular Image set")
+ enter_game_mode = (
+ "Entered game mode",
+ "Failed to enter game mode")
+ exit_game_mode = (
+ "Exited game mode",
+ "Couldn't exit game mode")
+ is_visible = (
+ "Entity is visible",
+ "Entity was not visible")
+ is_hidden = (
+ "Entity is hidden",
+ "Entity was not hidden")
+ entity_deleted = (
+ "Entity deleted",
+ "Entity was not deleted")
+ deletion_undo = (
+ "UNDO deletion success",
+ "UNDO deletion failed")
+ deletion_redo = (
+ "REDO deletion success",
+ "REDO deletion failed")
def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity():
@@ -60,29 +89,28 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity():
import os
import azlmbr.legacy.general as general
- import azlmbr.math as math
from editor_python_test_tools.asset_utils import Asset
from editor_python_test_tools.editor_entity_utils import EditorEntity
- from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
+ from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
- helper.init_idle()
- helper.open_level("", "Base")
+ TestHelper.init_idle()
+ TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Create a Global Skylight (IBL) entity with no components.
- global_skylight_name = "Global Skylight (IBL)"
- global_skylight_entity = EditorEntity.create_editor_entity_at(
- math.Vector3(512.0, 512.0, 34.0), global_skylight_name)
+ global_skylight_entity = EditorEntity.create_editor_entity(AtomComponentProperties.global_skylight())
Report.critical_result(Tests.global_skylight_creation, global_skylight_entity.exists())
# 2. Add Global Skylight (IBL) component to Global Skylight (IBL) entity.
- global_skylight_component = global_skylight_entity.add_component(global_skylight_name)
+ global_skylight_component = global_skylight_entity.add_component(AtomComponentProperties.global_skylight())
Report.critical_result(
- Tests.global_skylight_component, global_skylight_entity.has_component(global_skylight_name))
+ Tests.global_skylight_component,
+ global_skylight_entity.has_component(AtomComponentProperties.global_skylight()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -109,9 +137,9 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity():
Report.result(Tests.creation_redo, global_skylight_entity.exists())
# 5. Enter/Exit game mode.
- helper.enter_game_mode(Tests.enter_game_mode)
+ TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
- helper.exit_game_mode(Tests.exit_game_mode)
+ TestHelper.exit_game_mode(Tests.exit_game_mode)
# 6. Test IsHidden.
global_skylight_entity.set_visibility_state(False)
@@ -123,24 +151,24 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity():
Report.result(Tests.is_visible, global_skylight_entity.is_visible() is True)
# 8. Set the Diffuse Image asset on the Global Skylight (IBL) entity.
- global_skylight_diffuse_image_property = "Controller|Configuration|Diffuse Image"
diffuse_image_path = os.path.join("LightingPresets", "greenwich_park_02_4k_iblskyboxcm.exr.streamingimage")
diffuse_image_asset = Asset.find_asset_by_path(diffuse_image_path, False)
global_skylight_component.set_component_property_value(
- global_skylight_diffuse_image_property, diffuse_image_asset.id)
- diffuse_image_set = global_skylight_component.get_component_property_value(
- global_skylight_diffuse_image_property)
- Report.result(Tests.diffuse_image_set, diffuse_image_set == diffuse_image_asset.id)
+ AtomComponentProperties.global_skylight('Diffuse Image'), diffuse_image_asset.id)
+ Report.result(
+ Tests.diffuse_image_set,
+ diffuse_image_asset.id == global_skylight_component.get_component_property_value(
+ AtomComponentProperties.global_skylight('Diffuse Image')))
# 9. Set the Specular Image asset on the Global Light (IBL) entity.
- global_skylight_specular_image_property = "Controller|Configuration|Specular Image"
specular_image_path = os.path.join("LightingPresets", "greenwich_park_02_4k_iblskyboxcm.exr.streamingimage")
specular_image_asset = Asset.find_asset_by_path(specular_image_path, False)
global_skylight_component.set_component_property_value(
- global_skylight_specular_image_property, specular_image_asset.id)
- specular_image_added = global_skylight_component.get_component_property_value(
- global_skylight_specular_image_property)
- Report.result(Tests.specular_image_set, specular_image_added == specular_image_asset.id)
+ AtomComponentProperties.global_skylight('Specular Image'), specular_image_asset.id)
+ Report.result(
+ Tests.specular_image_set,
+ specular_image_asset.id == global_skylight_component.get_component_property_value(
+ AtomComponentProperties.global_skylight('Specular Image')))
# 10. Delete Global Skylight (IBL) entity.
global_skylight_entity.delete()
@@ -154,9 +182,12 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity():
general.redo()
Report.result(Tests.deletion_redo, not global_skylight_entity.exists())
- # 13. Look for errors.
- helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0)
- Report.result(Tests.no_error_occurred, not error_tracer.has_errors)
+ # 13. Look for errors and asserts.
+ TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+ for error_info in error_tracer.errors:
+ Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
+ for assert_info in error_tracer.asserts:
+ Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py
new file mode 100644
index 0000000000..a77a1f50a4
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py
@@ -0,0 +1,158 @@
+"""
+Copyright (c) Contributors to the Open 3D Engine Project.
+For complete copyright and license terms please see the LICENSE at the root of this distribution.
+
+SPDX-License-Identifier: Apache-2.0 OR MIT
+"""
+
+class Tests:
+ creation_undo = (
+ "UNDO Entity creation success",
+ "UNDO Entity creation failed")
+ creation_redo = (
+ "REDO Entity creation success",
+ "REDO Entity creation failed")
+ grid_entity_creation = (
+ "Grid Entity successfully created",
+ "Grid Entity failed to be created")
+ grid_component_added = (
+ "Entity has a Grid component",
+ "Entity failed to find Grid component")
+ enter_game_mode = (
+ "Entered game mode",
+ "Failed to enter game mode")
+ exit_game_mode = (
+ "Exited game mode",
+ "Couldn't exit game mode")
+ is_visible = (
+ "Entity is visible",
+ "Entity was not visible")
+ is_hidden = (
+ "Entity is hidden",
+ "Entity was not hidden")
+ entity_deleted = (
+ "Entity deleted",
+ "Entity was not deleted")
+ deletion_undo = (
+ "UNDO deletion success",
+ "UNDO deletion failed")
+ deletion_redo = (
+ "REDO deletion success",
+ "REDO deletion failed")
+
+
+def AtomEditorComponents_Grid_AddedToEntity():
+ """
+ Summary:
+ Tests the Grid component can be added to an entity and has the expected functionality.
+
+ Test setup:
+ - Wait for Editor idle loop.
+ - Open the "Base" level.
+
+ Expected Behavior:
+ The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
+ Creation and deletion undo/redo should also work.
+
+ Test Steps:
+ 1) Create a Grid entity with no components.
+ 2) Add a Grid component to Grid entity.
+ 3) UNDO the entity creation and component addition.
+ 4) REDO the entity creation and component addition.
+ 5) Enter/Exit game mode.
+ 6) Test IsHidden.
+ 7) Test IsVisible.
+ 8) Delete Grid entity.
+ 9) UNDO deletion.
+ 10) REDO deletion.
+ 11) Look for errors.
+
+ :return: None
+ """
+
+ import os
+
+ import azlmbr.legacy.general as general
+
+ from editor_python_test_tools.editor_entity_utils import EditorEntity
+ from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
+
+ with Tracer() as error_tracer:
+ # 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")
+
+ # Test steps begin.
+ # 1. Create a Grid entity with no components.
+ grid_entity = EditorEntity.create_editor_entity(AtomComponentProperties.grid())
+ Report.critical_result(Tests.grid_entity_creation, grid_entity.exists())
+
+ # 2. Add a Grid component to Grid entity.
+ grid_component = grid_entity.add_component(AtomComponentProperties.grid())
+ Report.critical_result(
+ Tests.grid_component_added,
+ grid_entity.has_component(AtomComponentProperties.grid()))
+
+ # 3. UNDO the entity creation and component addition.
+ # -> UNDO component addition.
+ general.undo()
+ # -> UNDO naming entity.
+ general.undo()
+ # -> UNDO selecting entity.
+ general.undo()
+ # -> UNDO entity creation.
+ general.undo()
+ general.idle_wait_frames(1)
+ Report.result(Tests.creation_undo, not grid_entity.exists())
+
+ # 4. REDO the entity creation and component addition.
+ # -> REDO entity creation.
+ general.redo()
+ # -> REDO selecting entity.
+ general.redo()
+ # -> REDO naming entity.
+ general.redo()
+ # -> REDO component addition.
+ general.redo()
+ general.idle_wait_frames(1)
+ Report.result(Tests.creation_redo, grid_entity.exists())
+
+ # 5. Enter/Exit game mode.
+ TestHelper.enter_game_mode(Tests.enter_game_mode)
+ general.idle_wait_frames(1)
+ TestHelper.exit_game_mode(Tests.exit_game_mode)
+
+ # 6. Test IsHidden.
+ grid_entity.set_visibility_state(False)
+ Report.result(Tests.is_hidden, grid_entity.is_hidden() is True)
+
+ # 7. Test IsVisible.
+ grid_entity.set_visibility_state(True)
+ general.idle_wait_frames(1)
+ Report.result(Tests.is_visible, grid_entity.is_visible() is True)
+
+ # 8. Delete Grid entity.
+ grid_entity.delete()
+ Report.result(Tests.entity_deleted, not grid_entity.exists())
+
+ # 9. UNDO deletion.
+ general.undo()
+ Report.result(Tests.deletion_undo, grid_entity.exists())
+
+ # 10. REDO deletion.
+ general.redo()
+ Report.result(Tests.deletion_redo, not grid_entity.exists())
+
+ # 11. Look for errors or asserts.
+ TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+ for error_info in error_tracer.errors:
+ Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
+ for assert_info in error_tracer.asserts:
+ Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
+
+
+if __name__ == "__main__":
+ from editor_python_test_tools.utils import Report
+ Report.start_test(AtomEditorComponents_Grid_AddedToEntity)
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py
index 8b1432f1f7..249671556d 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py
@@ -5,24 +5,49 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
-# fmt: off
class Tests:
- camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created")
- camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity")
- camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component")
- creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed")
- creation_redo = ("REDO Entity creation success", "REDO Entity creation failed")
- light_creation = ("Light Entity successfully created", "Light Entity failed to be created")
- light_component = ("Entity has a Light component", "Entity failed to find Light component")
- enter_game_mode = ("Entered game mode", "Failed to enter game mode")
- exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
- is_visible = ("Entity is visible", "Entity was not visible")
- is_hidden = ("Entity is hidden", "Entity was not hidden")
- entity_deleted = ("Entity deleted", "Entity was not deleted")
- deletion_undo = ("UNDO deletion success", "UNDO deletion failed")
- deletion_redo = ("REDO deletion success", "REDO deletion failed")
- no_error_occurred = ("No errors detected", "Errors were detected")
-# fmt: on
+ camera_creation = (
+ "Camera Entity successfully created",
+ "Camera Entity failed to be created")
+ camera_component_added = (
+ "Camera component was added to entity",
+ "Camera component failed to be added to entity")
+ camera_component_check = (
+ "Entity has a Camera component",
+ "Entity failed to find Camera component")
+ creation_undo = (
+ "UNDO Entity creation success",
+ "UNDO Entity creation failed")
+ creation_redo = (
+ "REDO Entity creation success",
+ "REDO Entity creation failed")
+ light_creation = (
+ "Light Entity successfully created",
+ "Light Entity failed to be created")
+ light_component = (
+ "Entity has a Light component",
+ "Entity failed to find Light component")
+ enter_game_mode = (
+ "Entered game mode",
+ "Failed to enter game mode")
+ exit_game_mode = (
+ "Exited game mode",
+ "Couldn't exit game mode")
+ is_visible = (
+ "Entity is visible",
+ "Entity was not visible")
+ is_hidden = (
+ "Entity is hidden",
+ "Entity was not hidden")
+ entity_deleted = (
+ "Entity deleted",
+ "Entity was not deleted")
+ deletion_undo = (
+ "UNDO deletion success",
+ "UNDO deletion failed")
+ deletion_redo = (
+ "REDO deletion success",
+ "REDO deletion failed")
def AtomEditorComponents_Light_AddedToEntity():
@@ -55,26 +80,25 @@ def AtomEditorComponents_Light_AddedToEntity():
"""
import azlmbr.legacy.general as general
- import azlmbr.math as math
from editor_python_test_tools.editor_entity_utils import EditorEntity
- from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
+ from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
- helper.init_idle()
- helper.open_level("", "Base")
+ TestHelper.init_idle()
+ TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Create a Light entity with no components.
- light_name = "Light"
- light_entity = EditorEntity.create_editor_entity_at(math.Vector3(512.0, 512.0, 34.0), light_name)
+ light_entity = EditorEntity.create_editor_entity(AtomComponentProperties.light())
Report.critical_result(Tests.light_creation, light_entity.exists())
# 2. Add Light component to the Light entity.
- light_entity.add_component(light_name)
- Report.critical_result(Tests.light_component, light_entity.has_component(light_name))
+ light_component = light_entity.add_component(AtomComponentProperties.light())
+ Report.critical_result(Tests.light_component, light_entity.has_component(AtomComponentProperties.light()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -101,9 +125,9 @@ def AtomEditorComponents_Light_AddedToEntity():
Report.result(Tests.creation_redo, light_entity.exists())
# 5. Enter/Exit game mode.
- helper.enter_game_mode(Tests.enter_game_mode)
+ TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
- helper.exit_game_mode(Tests.exit_game_mode)
+ TestHelper.exit_game_mode(Tests.exit_game_mode)
# 6. Test IsHidden.
light_entity.set_visibility_state(False)
@@ -126,9 +150,12 @@ def AtomEditorComponents_Light_AddedToEntity():
general.redo()
Report.result(Tests.deletion_redo, not light_entity.exists())
- # 11. Look for errors.
- helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0)
- Report.result(Tests.no_error_occurred, not error_tracer.has_errors)
+ # 11. Look for errors asserts.
+ TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+ for error_info in error_tracer.errors:
+ Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
+ for assert_info in error_tracer.asserts:
+ Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py
index 32cd5471f2..c613bb23e7 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MaterialAdded.py
@@ -96,6 +96,7 @@ def AtomEditorComponents_Material_AddedToEntity():
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
@@ -105,15 +106,14 @@ def AtomEditorComponents_Material_AddedToEntity():
# Test steps begin.
# 1. Create a Material entity with no components.
- material_name = "Material"
- material_entity = EditorEntity.create_editor_entity(material_name)
+ material_entity = EditorEntity.create_editor_entity(AtomComponentProperties.material())
Report.critical_result(Tests.material_creation, material_entity.exists())
# 2. Add a Material component to Material entity.
- material_component = material_entity.add_component(material_name)
+ material_component = material_entity.add_component(AtomComponentProperties.material())
Report.critical_result(
Tests.material_component,
- material_entity.has_component(material_name))
+ material_entity.has_component(AtomComponentProperties.material()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -143,9 +143,8 @@ def AtomEditorComponents_Material_AddedToEntity():
Report.result(Tests.material_disabled, not material_component.is_enabled())
# 6. Add Actor component since it is required by the Material component.
- actor_name = "Actor"
- material_entity.add_component(actor_name)
- Report.result(Tests.actor_component, material_entity.has_component(actor_name))
+ material_entity.add_component(AtomComponentProperties.actor())
+ Report.result(Tests.actor_component, material_entity.has_component(AtomComponentProperties.actor()))
# 7. Verify Material component is enabled.
Report.result(Tests.material_enabled, material_component.is_enabled())
@@ -153,15 +152,14 @@ def AtomEditorComponents_Material_AddedToEntity():
# 8. UNDO component addition.
general.undo()
general.idle_wait_frames(1)
- Report.result(Tests.actor_undo, not material_entity.has_component(actor_name))
+ Report.result(Tests.actor_undo, not material_entity.has_component(AtomComponentProperties.actor()))
# 9. Verify Material component not enabled.
Report.result(Tests.material_disabled, not material_component.is_enabled())
# 10. Add Mesh component since it is required by the Material component.
- mesh_name = "Mesh"
- material_entity.add_component(mesh_name)
- Report.result(Tests.mesh_component, material_entity.has_component(mesh_name))
+ material_entity.add_component(AtomComponentProperties.mesh())
+ Report.result(Tests.mesh_component, material_entity.has_component(AtomComponentProperties.mesh()))
# 11. Verify Material component is enabled.
Report.result(Tests.material_enabled, material_component.is_enabled())
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py
index 82d3b89309..9d56753961 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py
@@ -81,7 +81,7 @@ def AtomEditorComponents_Mesh_AddedToEntity():
from editor_python_test_tools.asset_utils import Asset
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper
- from Atom.atom_utils.atom_constants import AtomComponentProperties as Atom
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
@@ -91,14 +91,14 @@ def AtomEditorComponents_Mesh_AddedToEntity():
# Test steps begin.
# 1. Create a Mesh entity with no components.
- mesh_entity = EditorEntity.create_editor_entity(Atom.mesh())
+ mesh_entity = EditorEntity.create_editor_entity(AtomComponentProperties.mesh())
Report.critical_result(Tests.mesh_entity_creation, mesh_entity.exists())
# 2. Add a Mesh component to Mesh entity.
- mesh_component = mesh_entity.add_component(Atom.mesh())
+ mesh_component = mesh_entity.add_component(AtomComponentProperties.mesh())
Report.critical_result(
Tests.mesh_component_added,
- mesh_entity.has_component(Atom.mesh()))
+ mesh_entity.has_component(AtomComponentProperties.mesh()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -127,9 +127,9 @@ def AtomEditorComponents_Mesh_AddedToEntity():
# 5. Set Mesh component asset property
model_path = os.path.join('Objects', 'shaderball', 'shaderball_default_1m.azmodel')
model = Asset.find_asset_by_path(model_path)
- mesh_component.set_component_property_value(Atom.mesh('Mesh Asset'), model.id)
+ mesh_component.set_component_property_value(AtomComponentProperties.mesh('Mesh Asset'), model.id)
Report.result(Tests.mesh_asset_specified,
- mesh_component.get_component_property_value(Atom.mesh('Mesh Asset')) == model.id)
+ mesh_component.get_component_property_value(AtomComponentProperties.mesh('Mesh Asset')) == model.id)
# 6. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py
index 04441d5b2c..fa8c626016 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py
@@ -5,24 +5,49 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
-# fmt: off
class Tests:
- camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created")
- camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity")
- camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component")
- creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed")
- creation_redo = ("REDO Entity creation success", "REDO Entity creation failed")
- physical_sky_creation = ("Physical Sky Entity successfully created", "Physical Sky Entity failed to be created")
- physical_sky_component = ("Entity has a Physical Sky component", "Entity failed to find Physical Sky component")
- enter_game_mode = ("Entered game mode", "Failed to enter game mode")
- exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
- is_visible = ("Entity is visible", "Entity was not visible")
- is_hidden = ("Entity is hidden", "Entity was not hidden")
- entity_deleted = ("Entity deleted", "Entity was not deleted")
- deletion_undo = ("UNDO deletion success", "UNDO deletion failed")
- deletion_redo = ("REDO deletion success", "REDO deletion failed")
- no_error_occurred = ("No errors detected", "Errors were detected")
-# fmt: on
+ camera_creation = (
+ "Camera Entity successfully created",
+ "Camera Entity failed to be created")
+ camera_component_added = (
+ "Camera component was added to entity",
+ "Camera component failed to be added to entity")
+ camera_component_check = (
+ "Entity has a Camera component",
+ "Entity failed to find Camera component")
+ creation_undo = (
+ "UNDO Entity creation success",
+ "UNDO Entity creation failed")
+ creation_redo = (
+ "REDO Entity creation success",
+ "REDO Entity creation failed")
+ physical_sky_creation = (
+ "Physical Sky Entity successfully created",
+ "Physical Sky Entity failed to be created")
+ physical_sky_component = (
+ "Entity has a Physical Sky component",
+ "Entity failed to find Physical Sky component")
+ enter_game_mode = (
+ "Entered game mode",
+ "Failed to enter game mode")
+ exit_game_mode = (
+ "Exited game mode",
+ "Couldn't exit game mode")
+ is_visible = (
+ "Entity is visible",
+ "Entity was not visible")
+ is_hidden = (
+ "Entity is hidden",
+ "Entity was not hidden")
+ entity_deleted = (
+ "Entity deleted",
+ "Entity was not deleted")
+ deletion_undo = (
+ "UNDO deletion success",
+ "UNDO deletion failed")
+ deletion_redo = (
+ "REDO deletion success",
+ "REDO deletion failed")
def AtomEditorComponents_PhysicalSky_AddedToEntity():
@@ -49,32 +74,33 @@ def AtomEditorComponents_PhysicalSky_AddedToEntity():
8) Delete Physical Sky entity.
9) UNDO deletion.
10) REDO deletion.
- 11) Look for errors.
+ 11) Look for errors and asserts.
:return: None
"""
import azlmbr.legacy.general as general
- import azlmbr.math as math
from editor_python_test_tools.editor_entity_utils import EditorEntity
- from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
+ from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
- helper.init_idle()
- helper.open_level("", "Base")
+ TestHelper.init_idle()
+ TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Create a Physical Sky entity with no components.
- physical_sky_name = "Physical Sky"
- physical_sky_entity = EditorEntity.create_editor_entity_at(math.Vector3(512.0, 512.0, 34.0), physical_sky_name)
+ physical_sky_entity = EditorEntity.create_editor_entity(AtomComponentProperties.physical_sky())
Report.critical_result(Tests.physical_sky_creation, physical_sky_entity.exists())
# 2. Add Physical Sky component to Physical Sky entity.
- physical_sky_entity.add_component(physical_sky_name)
- Report.critical_result(Tests.physical_sky_component, physical_sky_entity.has_component(physical_sky_name))
+ physical_sky_component = physical_sky_entity.add_component(AtomComponentProperties.physical_sky())
+ Report.critical_result(
+ Tests.physical_sky_component,
+ physical_sky_entity.has_component(AtomComponentProperties.physical_sky()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -101,9 +127,9 @@ def AtomEditorComponents_PhysicalSky_AddedToEntity():
Report.result(Tests.creation_redo, physical_sky_entity.exists())
# 5. Enter/Exit game mode.
- helper.enter_game_mode(Tests.enter_game_mode)
+ TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
- helper.exit_game_mode(Tests.exit_game_mode)
+ TestHelper.exit_game_mode(Tests.exit_game_mode)
# 6. Test IsHidden.
physical_sky_entity.set_visibility_state(False)
@@ -126,9 +152,12 @@ def AtomEditorComponents_PhysicalSky_AddedToEntity():
general.redo()
Report.result(Tests.deletion_redo, not physical_sky_entity.exists())
- # 11. Look for errors.
- helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0)
- Report.result(Tests.no_error_occurred, not error_tracer.has_errors)
+ # 11. Look for errors and asserts.
+ TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+ for error_info in error_tracer.errors:
+ Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
+ for assert_info in error_tracer.asserts:
+ Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py
index d38e96739d..6e4ae2d9ac 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded.py
@@ -86,6 +86,7 @@ def AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity():
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
@@ -95,15 +96,15 @@ def AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity():
# Test steps begin.
# 1. Create a PostFX Gradient Weight Modifier entity with no components.
- postfx_gradient_weight_name = "PostFX Gradient Weight Modifier"
- postfx_gradient_weight_entity = EditorEntity.create_editor_entity(postfx_gradient_weight_name)
+ postfx_gradient_weight_entity = EditorEntity.create_editor_entity(AtomComponentProperties.postfx_gradient())
Report.critical_result(Tests.postfx_gradient_weight_creation, postfx_gradient_weight_entity.exists())
# 2. Add a PostFX Gradient Weight Modifier component to PostFX Gradient Weight Modifier entity.
- postfx_gradient_weight_component = postfx_gradient_weight_entity.add_component(postfx_gradient_weight_name)
+ postfx_gradient_weight_component = postfx_gradient_weight_entity.add_component(
+ AtomComponentProperties.postfx_gradient())
Report.critical_result(
Tests.postfx_gradient_weight_component,
- postfx_gradient_weight_entity.has_component(postfx_gradient_weight_name))
+ postfx_gradient_weight_entity.has_component(AtomComponentProperties.postfx_gradient()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -133,9 +134,10 @@ def AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity():
Report.result(Tests.postfx_gradient_weight_disabled, not postfx_gradient_weight_component.is_enabled())
# 6. Add PostFX Layer component since it is required by the PostFX Gradient Weight Modifier component.
- postfx_layer_name = "PostFX Layer"
- postfx_gradient_weight_entity.add_component(postfx_layer_name)
- Report.result(Tests.postfx_layer_component, postfx_gradient_weight_entity.has_component(postfx_layer_name))
+ postfx_gradient_weight_entity.add_component(AtomComponentProperties.postfx_layer())
+ Report.result(
+ Tests.postfx_layer_component,
+ postfx_gradient_weight_entity.has_component(AtomComponentProperties.postfx_layer()))
# 7. Verify PostFX Gradient Weight Modifier component is enabled.
Report.result(Tests.postfx_gradient_weight_enabled, postfx_gradient_weight_component.is_enabled())
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py
index 8c2dee416b..a81864f379 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py
@@ -76,6 +76,7 @@ def AtomEditorComponents_postfx_layer_AddedToEntity():
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
@@ -85,13 +86,14 @@ def AtomEditorComponents_postfx_layer_AddedToEntity():
# Test steps begin.
# 1. Create a PostFX Layer entity with no components.
- postfx_layer_name = "PostFX Layer"
- postfx_layer_entity = EditorEntity.create_editor_entity(postfx_layer_name)
+ postfx_layer_entity = EditorEntity.create_editor_entity(AtomComponentProperties.postfx_layer())
Report.critical_result(Tests.postfx_layer_entity_creation, postfx_layer_entity.exists())
# 2. Add a PostFX Layer component to PostFX Layer entity.
- postfx_layer_component = postfx_layer_entity.add_component(postfx_layer_name)
- Report.critical_result(Tests.postfx_layer_component_added, postfx_layer_entity.has_component(postfx_layer_name))
+ postfx_layer_component = postfx_layer_entity.add_component(AtomComponentProperties.postfx_layer())
+ Report.critical_result(
+ Tests.postfx_layer_component_added,
+ postfx_layer_entity.has_component(AtomComponentProperties.postfx_layer()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py
index 8914ab9e7e..228ddfcdc5 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py
@@ -5,24 +5,49 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
-# fmt: off
class Tests:
- camera_creation = ("Camera Entity successfully created", "Camera Entity failed to be created")
- camera_component_added = ("Camera component was added to entity", "Camera component failed to be added to entity")
- camera_component_check = ("Entity has a Camera component", "Entity failed to find Camera component")
- creation_undo = ("UNDO Entity creation success", "UNDO Entity creation failed")
- creation_redo = ("REDO Entity creation success", "REDO Entity creation failed")
- postfx_radius_weight_creation = ("PostFX Radius Weight Modifier Entity successfully created", "PostFX Radius Weight Modifier Entity failed to be created")
- postfx_radius_weight_component = ("Entity has a PostFX Radius Weight Modifier component", "Entity failed to find PostFX Radius Weight Modifier component")
- enter_game_mode = ("Entered game mode", "Failed to enter game mode")
- exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
- is_visible = ("Entity is visible", "Entity was not visible")
- is_hidden = ("Entity is hidden", "Entity was not hidden")
- entity_deleted = ("Entity deleted", "Entity was not deleted")
- deletion_undo = ("UNDO deletion success", "UNDO deletion failed")
- deletion_redo = ("REDO deletion success", "REDO deletion failed")
- no_error_occurred = ("No errors detected", "Errors were detected")
-# fmt: on
+ creation_undo = (
+ "UNDO Entity creation success",
+ "UNDO Entity creation failed")
+ creation_redo = (
+ "REDO Entity creation success",
+ "REDO Entity creation failed")
+ postfx_radius_weight_creation = (
+ "PostFX Radius Weight Modifier Entity successfully created",
+ "PostFX Radius Weight Modifier Entity failed to be created")
+ postfx_radius_weight_component = (
+ "Entity has a PostFX Radius Weight Modifier component",
+ "Entity failed to find PostFX Radius Weight Modifier component")
+ postfx_radius_weight_disabled = (
+ "PostFX Radius Weight Modifier component disabled",
+ "PostFX Radius Weight Modifier component was not disabled.")
+ postfx_layer_component = (
+ "Entity has a PostFX Layer component",
+ "Entity did not have an PostFX Layer component")
+ postfx_radius_weight_enabled = (
+ "PostFX Radius Weight Modifier component enabled",
+ "PostFX Radius Weight Modifier component was not enabled.")
+ enter_game_mode = (
+ "Entered game mode",
+ "Failed to enter game mode")
+ exit_game_mode = (
+ "Exited game mode",
+ "Couldn't exit game mode")
+ is_visible = (
+ "Entity is visible",
+ "Entity was not visible")
+ is_hidden = (
+ "Entity is hidden",
+ "Entity was not hidden")
+ entity_deleted = (
+ "Entity deleted",
+ "Entity was not deleted")
+ deletion_undo = (
+ "UNDO deletion success",
+ "UNDO deletion failed")
+ deletion_redo = (
+ "REDO deletion success",
+ "REDO deletion failed")
def AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity():
@@ -43,40 +68,42 @@ def AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity():
2) Add Post FX Radius Weight Modifier component to Post FX Radius Weight Modifier entity.
3) UNDO the entity creation and component addition.
4) REDO the entity creation and component addition.
- 5) Enter/Exit game mode.
- 6) Test IsHidden.
- 7) Test IsVisible.
- 8) Delete PostFX Radius Weight Modifier entity.
- 9) UNDO deletion.
- 10) REDO deletion.
- 11) Look for errors.
+ 5) Verify PostFX Radius Weight Modifier component not enabled.
+ 6) Add PostFX Layer component since it is required by the PostFX Radius Weight Modifier component.
+ 7) Verify PostFX Radius Weight Modifier component is enabled.
+ 8) Enter/Exit game mode.
+ 9) Test IsHidden.
+ 10) Test IsVisible.
+ 11) Delete PostFX Radius Weight Modifier entity.
+ 12) UNDO deletion.
+ 13) REDO deletion.
+ 14) Look for errors.
:return: None
"""
import azlmbr.legacy.general as general
- import azlmbr.math as math
from editor_python_test_tools.editor_entity_utils import EditorEntity
- from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
+ from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
- helper.init_idle()
- helper.open_level("", "Base")
+ TestHelper.init_idle()
+ TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Create a Post FX Radius Weight Modifier entity with no components.
- postfx_radius_weight_name = "PostFX Radius Weight Modifier"
- postfx_radius_weight_entity = EditorEntity.create_editor_entity_at(
- math.Vector3(512.0, 512.0, 34.0), postfx_radius_weight_name)
+ postfx_radius_weight_entity = EditorEntity.create_editor_entity(AtomComponentProperties.postfx_radius())
Report.critical_result(Tests.postfx_radius_weight_creation, postfx_radius_weight_entity.exists())
# 2. Add Post FX Radius Weight Modifier component to Post FX Radius Weight Modifier entity.
- postfx_radius_weight_entity.add_component(postfx_radius_weight_name)
+ postfx_radius_component = postfx_radius_weight_entity.add_component(AtomComponentProperties.postfx_radius())
Report.critical_result(
- Tests.postfx_radius_weight_component, postfx_radius_weight_entity.has_component(postfx_radius_weight_name))
+ Tests.postfx_radius_weight_component,
+ postfx_radius_weight_entity.has_component(AtomComponentProperties.postfx_radius()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -102,35 +129,50 @@ def AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity():
general.idle_wait_frames(1)
Report.result(Tests.creation_redo, postfx_radius_weight_entity.exists())
- # 5. Enter/Exit game mode.
- helper.enter_game_mode(Tests.enter_game_mode)
- general.idle_wait_frames(1)
- helper.exit_game_mode(Tests.exit_game_mode)
+ # 5. Verify PostFX Radius Weight Modifier component not enabled.
+ Report.result(Tests.postfx_radius_weight_disabled, not postfx_radius_component.is_enabled())
- # 6. Test IsHidden.
+ # 6. Add PostFX Layer component since it is required by the PostFX Radius Weight Modifier component.
+ postfx_radius_weight_entity.add_component(AtomComponentProperties.postfx_layer())
+ Report.result(
+ Tests.postfx_layer_component,
+ postfx_radius_weight_entity.has_component(AtomComponentProperties.postfx_layer()))
+
+ # 7. Verify PostFX Radius Weight Modifier component is enabled.
+ Report.result(Tests.postfx_radius_weight_enabled, postfx_radius_component.is_enabled())
+
+ # 8. Enter/Exit game mode.
+ TestHelper.enter_game_mode(Tests.enter_game_mode)
+ general.idle_wait_frames(1)
+ TestHelper.exit_game_mode(Tests.exit_game_mode)
+
+ # 9. Test IsHidden.
postfx_radius_weight_entity.set_visibility_state(False)
Report.result(Tests.is_hidden, postfx_radius_weight_entity.is_hidden() is True)
- # 7. Test IsVisible.
+ # 10. Test IsVisible.
postfx_radius_weight_entity.set_visibility_state(True)
general.idle_wait_frames(1)
Report.result(Tests.is_visible, postfx_radius_weight_entity.is_visible() is True)
- # 8. Delete PostFX Radius Weight Modifier entity.
+ # 11. Delete PostFX Radius Weight Modifier entity.
postfx_radius_weight_entity.delete()
Report.result(Tests.entity_deleted, not postfx_radius_weight_entity.exists())
- # 9. UNDO deletion.
+ # 12. UNDO deletion.
general.undo()
Report.result(Tests.deletion_undo, postfx_radius_weight_entity.exists())
- # 10. REDO deletion.
+ # 13. REDO deletion.
general.redo()
Report.result(Tests.deletion_redo, not postfx_radius_weight_entity.exists())
- # 11. Look for errors.
- helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0)
- Report.result(Tests.no_error_occurred, not error_tracer.has_errors)
+ # 14. Look for errors and asserts.
+ TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+ for error_info in error_tracer.errors:
+ Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
+ for assert_info in error_tracer.asserts:
+ Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py
index 4c98bcd130..0a37ac6bf7 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py
@@ -92,6 +92,7 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity():
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
@@ -101,15 +102,14 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity():
# Test steps begin.
# 1. Create a PostFx Shape Weight Modifier entity with no components.
- postfx_shape_weight_name = "PostFX Shape Weight Modifier"
- postfx_shape_weight_entity = EditorEntity.create_editor_entity(postfx_shape_weight_name)
+ postfx_shape_weight_entity = EditorEntity.create_editor_entity(AtomComponentProperties.postfx_shape())
Report.critical_result(Tests.postfx_shape_weight_creation, postfx_shape_weight_entity.exists())
# 2. Add a PostFx Shape Weight Modifier component to PostFx Shape Weight Modifier entity.
- postfx_shape_weight_component = postfx_shape_weight_entity.add_component(postfx_shape_weight_name)
+ postfx_shape_weight_component = postfx_shape_weight_entity.add_component(AtomComponentProperties.postfx_shape())
Report.critical_result(
Tests.postfx_shape_weight_component,
- postfx_shape_weight_entity.has_component(postfx_shape_weight_name))
+ postfx_shape_weight_entity.has_component(AtomComponentProperties.postfx_shape()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -139,16 +139,16 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity():
Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled())
# 6. Add PostFX Layer component since it is required by the PostFx Shape Weight Modifier component.
- postfx_layer_name = "PostFX Layer"
- postfx_shape_weight_entity.add_component(postfx_layer_name)
- Report.result(Tests.postfx_layer_component, postfx_shape_weight_entity.has_component(postfx_layer_name))
+ postfx_shape_weight_entity.add_component(AtomComponentProperties.postfx_layer())
+ Report.result(
+ Tests.postfx_layer_component,
+ postfx_shape_weight_entity.has_component(AtomComponentProperties.postfx_layer()))
# 7. Verify PostFx Shape Weight Modifier component is NOT enabled since it also requires a shape.
Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled())
# 8. Add a required shape looping over a list and checking if it enables PostFX Shape Weight Modifier.
- for shape in ['Axis Aligned Box Shape', 'Box Shape', 'Capsule Shape', 'Compound Shape', 'Cylinder Shape',
- 'Disk Shape', 'Polygon Prism Shape', 'Quad Shape', 'Sphere Shape', 'Vegetation Reference Shape']:
+ for shape in AtomComponentProperties.postfx_shape('shapes'):
postfx_shape_weight_entity.add_component(shape)
test_shape = (
f"Entity has a {shape} component",
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py
index 9b13eb2c7e..70adf9143a 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py
@@ -87,30 +87,28 @@ def AtomEditorComponents_ReflectionProbe_AddedToEntity():
"""
import azlmbr.legacy.general as general
- import azlmbr.math as math
import azlmbr.render as render
from editor_python_test_tools.editor_entity_utils import EditorEntity
- from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
+ from editor_python_test_tools.utils import Report, Tracer, TestHelper
+ from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
- helper.init_idle()
- helper.open_level("", "Base")
+ TestHelper.init_idle()
+ TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Create a Reflection Probe entity with no components.
- reflection_probe_name = "Reflection Probe"
- reflection_probe_entity = EditorEntity.create_editor_entity_at(
- math.Vector3(512.0, 512.0, 34.0), reflection_probe_name)
+ reflection_probe_entity = EditorEntity.create_editor_entity(AtomComponentProperties.reflection_probe())
Report.critical_result(Tests.reflection_probe_creation, reflection_probe_entity.exists())
# 2. Add a Reflection Probe component to Reflection Probe entity.
- reflection_probe_component = reflection_probe_entity.add_component(reflection_probe_name)
+ reflection_probe_component = reflection_probe_entity.add_component(AtomComponentProperties.reflection_probe())
Report.critical_result(
Tests.reflection_probe_component,
- reflection_probe_entity.has_component(reflection_probe_name))
+ reflection_probe_entity.has_component(AtomComponentProperties.reflection_probe()))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
@@ -139,18 +137,27 @@ def AtomEditorComponents_ReflectionProbe_AddedToEntity():
# 5. Verify Reflection Probe component not enabled.
Report.result(Tests.reflection_probe_disabled, not reflection_probe_component.is_enabled())
- # 6. Add Box Shape component since it is required by the Reflection Probe component.
- box_shape = "Box Shape"
- reflection_probe_entity.add_component(box_shape)
- Report.result(Tests.box_shape_component, reflection_probe_entity.has_component(box_shape))
+ # 6. Add Shape component since it is required by the Reflection Probe component.
+ for shape in AtomComponentProperties.reflection_probe('shapes'):
+ reflection_probe_entity.add_component(shape)
+ test_shape = (
+ f"Entity has a {shape} component",
+ f"Entity did not have a {shape} component")
+ Report.result(test_shape, reflection_probe_entity.has_component(shape))
- # 7. Verify Reflection Probe component is enabled.
- Report.result(Tests.reflection_probe_enabled, reflection_probe_component.is_enabled())
+ # 7. Check if required shape allows Reflection Probe to be enabled
+ Report.result(Tests.reflection_probe_enabled, reflection_probe_component.is_enabled())
+
+ # Undo to remove each added shape except the last one and verify Reflection Probe is not enabled.
+ if not (shape == AtomComponentProperties.reflection_probe('shapes')[-1]):
+ general.undo()
+ TestHelper.wait_for_condition(lambda: not reflection_probe_entity.has_component(shape), 1.0)
+ Report.result(Tests.reflection_probe_disabled, not reflection_probe_component.is_enabled())
# 8. Enter/Exit game mode.
- helper.enter_game_mode(Tests.enter_game_mode)
+ TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
- helper.exit_game_mode(Tests.exit_game_mode)
+ TestHelper.exit_game_mode(Tests.exit_game_mode)
# 9. Test IsHidden.
reflection_probe_entity.set_visibility_state(False)
@@ -165,8 +172,9 @@ def AtomEditorComponents_ReflectionProbe_AddedToEntity():
render.EditorReflectionProbeBus(azlmbr.bus.Event, "BakeReflectionProbe", reflection_probe_entity.id)
Report.result(
Tests.reflection_map_generated,
- helper.wait_for_condition(
- lambda: reflection_probe_component.get_component_property_value("Cubemap|Baked Cubemap Path") != "",
+ TestHelper.wait_for_condition(
+ lambda: reflection_probe_component.get_component_property_value(
+ AtomComponentProperties.reflection_probe('Baked Cubemap Path')) != "",
20.0))
# 12. Delete Reflection Probe entity.
@@ -182,7 +190,7 @@ def AtomEditorComponents_ReflectionProbe_AddedToEntity():
Report.result(Tests.deletion_redo, not reflection_probe_entity.exists())
# 15. Look for errors or asserts.
- helper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+ TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in error_tracer.asserts:
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_fast_scan_setting_backup_fixture.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_fast_scan_setting_backup_fixture.py
index 4a096ca3fe..c224289cf4 100755
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_fast_scan_setting_backup_fixture.py
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_fast_scan_setting_backup_fixture.py
@@ -29,6 +29,9 @@ def ap_fast_scan_setting_backup_fixture(request, workspace) -> PlatformSetting:
if workspace.asset_processor_platform == 'mac':
pytest.skip("Mac plist file editing not implemented yet")
+ if workspace.asset_processor_platform == 'linux':
+ pytest.skip("Linux system settings not implemented yet")
+
key = fast_scan_key
subkey = fast_scan_subkey
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py
index ee8e177bbb..264f690534 100755
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py
@@ -79,7 +79,7 @@ class TestsAssetProcessorBatch_DependenycyTests(object):
env = ap_setup_fixture
BATCH_LOG_PATH = env["ap_batch_log_file"]
asset_processor.create_temp_asset_root()
- asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "engine_dependencies.xml"))
+ asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "Engine_Dependencies.xml"))
asset_processor.add_scan_folder(os.path.join("Assets", "Engine"))
asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "Libs", "MaterialEffects", "surfacetypes.xml"))
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/Jack_Idle_Aim_ZUp.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/SceneDebug/Jack_Idle_Aim_ZUp.dbgsg
similarity index 99%
rename from AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/Jack_Idle_Aim_ZUp.dbgsg
rename to AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/SceneDebug/Jack_Idle_Aim_ZUp.dbgsg
index 8b0132a448..c8c5c8697b 100644
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/Jack_Idle_Aim_ZUp.dbgsg
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/SceneDebug/Jack_Idle_Aim_ZUp.dbgsg
@@ -242,7 +242,7 @@ Node Type: BoneData
BasisX: < 1.000000, -0.000000, 0.000000>
BasisY: < 0.000000, 1.000000, 0.000000>
BasisZ: <-0.000000, -0.000000, 1.000000>
- Transl: < 0.152547, 0.043345, 0.090955>
+ Transl: < 0.152547, 0.043345, 0.090954>
Node Name: animation
Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.animation
@@ -544,7 +544,7 @@ Node Type: BoneData
Node Name: animation
Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_upArmRoll.animation
Node Type: AnimationData
- KeyFrames: Count 195. Hash: 15529789169672670472
+ KeyFrames: Count 195. Hash: 8781707605519483934
TimeStepBetweenFrames: 0.033333
Node Name: transform
@@ -710,7 +710,7 @@ Node Type: BoneData
BasisX: < 0.514369, 0.855813, 0.054857>
BasisY: < 0.088153, 0.010863, -0.996047>
BasisZ: <-0.853026, 0.517172, -0.069855>
- Transl: <-0.247306, -0.062325, 0.878373>
+ Transl: <-0.247306, -0.062325, 0.878372>
Node Name: animation
Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_loArmRoll.animation
@@ -857,7 +857,7 @@ Node Type: BoneData
BasisX: < 0.329257, 0.944038, -0.019538>
BasisY: < 0.465563, -0.180309, -0.866452>
BasisZ: <-0.821487, 0.276189, -0.498877>
- Transl: <-0.255124, -0.049696, 0.794467>
+ Transl: <-0.255124, -0.049696, 0.794466>
Node Name: animation
Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.l_shldr.l_upArm.l_loArm.l_hand.l_metacarpal.animation
@@ -939,7 +939,6 @@ Node Type: AnimationData
Node Name: transform
Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_index1.transform
-
Node Type: TransformData
Matrix:
BasisX: < 0.939162, 0.133704, -0.316383>
@@ -954,7 +953,7 @@ Node Type: BoneData
BasisX: <-0.102387, -0.418082, -0.902621>
BasisY: < 0.928150, 0.286271, -0.237880>
BasisZ: < 0.357847, -0.862123, 0.358732>
- Transl: < 0.187367, 0.698324, 1.467209>
+ Transl: < 0.187367, 0.698323, 1.467209>
Node Name: animation
Node Path: RootNode.jack_root.Bip01__pelvis.spine1.spine2.spine3.r_shldr.r_upArm.r_loArm.r_hand.r_mid1.animation
@@ -1513,3 +1512,4 @@ Node Type: TransformData
BasisY: < 0.000000, 0.229519, -0.973304>
BasisZ: < 0.000000, 0.973304, 0.229519>
Transl: < 0.000000, -0.023770, 0.000000>
+
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/SceneDebug/jack_idle_aim_zup.dbgsg.xml b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/SceneDebug/jack_idle_aim_zup.dbgsg.xml
new file mode 100644
index 0000000000..caaf3810fe
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/Motion/SceneDebug/jack_idle_aim_zup.dbgsg.xml
@@ -0,0 +1,3223 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshMultipleMaterials/single_mesh_multiple_materials.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshMultipleMaterials/SceneDebug/single_mesh_multiple_materials.dbgsg
similarity index 100%
rename from AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshMultipleMaterials/single_mesh_multiple_materials.dbgsg
rename to AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshMultipleMaterials/SceneDebug/single_mesh_multiple_materials.dbgsg
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshMultipleMaterials/SceneDebug/single_mesh_multiple_materials.dbgsg.xml b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshMultipleMaterials/SceneDebug/single_mesh_multiple_materials.dbgsg.xml
new file mode 100644
index 0000000000..80b80fd67c
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshMultipleMaterials/SceneDebug/single_mesh_multiple_materials.dbgsg.xml
@@ -0,0 +1,849 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshOneMaterial/onemeshonematerial.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshOneMaterial/SceneDebug/onemeshonematerial.dbgsg
similarity index 100%
rename from AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshOneMaterial/onemeshonematerial.dbgsg
rename to AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshOneMaterial/SceneDebug/onemeshonematerial.dbgsg
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshOneMaterial/SceneDebug/onemeshonematerial.dbgsg.xml b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshOneMaterial/SceneDebug/onemeshonematerial.dbgsg.xml
new file mode 100644
index 0000000000..87cc4fe260
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/OneMeshOneMaterial/SceneDebug/onemeshonematerial.dbgsg.xml
@@ -0,0 +1,519 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/SceneDebug/shaderball.dbgsg
similarity index 100%
rename from AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.dbgsg
rename to AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/SceneDebug/shaderball.dbgsg
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/SceneDebug/shaderball.dbgsg.xml b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/SceneDebug/shaderball.dbgsg.xml
new file mode 100644
index 0000000000..acff9e5f02
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/SceneDebug/shaderball.dbgsg.xml
@@ -0,0 +1,9491 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingLOD/lodtest.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingLOD/SceneDebug/lodtest.dbgsg
similarity index 100%
rename from AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingLOD/lodtest.dbgsg
rename to AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingLOD/SceneDebug/lodtest.dbgsg
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingLOD/SceneDebug/lodtest.dbgsg.xml b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingLOD/SceneDebug/lodtest.dbgsg.xml
new file mode 100644
index 0000000000..d1af4198b5
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingLOD/SceneDebug/lodtest.dbgsg.xml
@@ -0,0 +1,2007 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingPhysics/physicstest.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingPhysics/SceneDebug/physicstest.dbgsg
similarity index 100%
rename from AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingPhysics/physicstest.dbgsg
rename to AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingPhysics/SceneDebug/physicstest.dbgsg
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingPhysics/SceneDebug/physicstest.dbgsg.xml b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingPhysics/SceneDebug/physicstest.dbgsg.xml
new file mode 100644
index 0000000000..b22eb14fc6
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/SoftNamingPhysics/SceneDebug/physicstest.dbgsg.xml
@@ -0,0 +1,817 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshLinkedMaterials/multiple_mesh_linked_materials.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshLinkedMaterials/SceneDebug/multiple_mesh_linked_materials.dbgsg
similarity index 100%
rename from AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshLinkedMaterials/multiple_mesh_linked_materials.dbgsg
rename to AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshLinkedMaterials/SceneDebug/multiple_mesh_linked_materials.dbgsg
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshLinkedMaterials/SceneDebug/multiple_mesh_linked_materials.dbgsg.xml b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshLinkedMaterials/SceneDebug/multiple_mesh_linked_materials.dbgsg.xml
new file mode 100644
index 0000000000..3eaf018fb6
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshLinkedMaterials/SceneDebug/multiple_mesh_linked_materials.dbgsg.xml
@@ -0,0 +1,1345 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshOneMaterial/multiple_mesh_one_material.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshOneMaterial/SceneDebug/multiple_mesh_one_material.dbgsg
similarity index 100%
rename from AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshOneMaterial/multiple_mesh_one_material.dbgsg
rename to AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshOneMaterial/SceneDebug/multiple_mesh_one_material.dbgsg
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshOneMaterial/SceneDebug/multiple_mesh_one_material.dbgsg.xml b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshOneMaterial/SceneDebug/multiple_mesh_one_material.dbgsg.xml
new file mode 100644
index 0000000000..39ac33f654
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshOneMaterial/SceneDebug/multiple_mesh_one_material.dbgsg.xml
@@ -0,0 +1,1015 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/multiple_mesh_multiple_material.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/SceneDebug/multiple_mesh_multiple_material.dbgsg
similarity index 100%
rename from AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/multiple_mesh_multiple_material.dbgsg
rename to AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/SceneDebug/multiple_mesh_multiple_material.dbgsg
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/SceneDebug/multiple_mesh_multiple_material.dbgsg.xml b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/SceneDebug/multiple_mesh_multiple_material.dbgsg.xml
new file mode 100644
index 0000000000..c41c1414a4
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/SceneDebug/multiple_mesh_multiple_material.dbgsg.xml
@@ -0,0 +1,1015 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/multiple_mesh_multiple_material_override.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/SceneDebug/multiple_mesh_multiple_material_override.dbgsg
similarity index 100%
rename from AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/multiple_mesh_multiple_material_override.dbgsg
rename to AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/SceneDebug/multiple_mesh_multiple_material_override.dbgsg
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/SceneDebug/multiple_mesh_multiple_material_override.dbgsg.xml b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/SceneDebug/multiple_mesh_multiple_material_override.dbgsg.xml
new file mode 100644
index 0000000000..01167ec0ee
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/TwoMeshTwoMaterial/SceneDebug/multiple_mesh_multiple_material_override.dbgsg.xml
@@ -0,0 +1,817 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/VertexColor/vertexcolor.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/VertexColor/SceneDebug/vertexcolor.dbgsg
similarity index 100%
rename from AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/VertexColor/vertexcolor.dbgsg
rename to AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/VertexColor/SceneDebug/vertexcolor.dbgsg
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/VertexColor/SceneDebug/vertexcolor.dbgsg.xml b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/VertexColor/SceneDebug/vertexcolor.dbgsg.xml
new file mode 100644
index 0000000000..f5caa70d63
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/VertexColor/SceneDebug/vertexcolor.dbgsg.xml
@@ -0,0 +1,576 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py
index 5965db57b1..c28dfa64f2 100755
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py
@@ -73,7 +73,12 @@ blackbox_fbx_tests = [
asset_db_utils.DBProduct(
product_name='onemeshonematerial/onemeshonematerial.dbgsg',
sub_id=1918494907,
- asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b')
+ asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'),
+ asset_db_utils.DBProduct(
+ product_name='onemeshonematerial/onemeshonematerial.dbgsg.xml',
+ sub_id=556355570,
+ asset_type=b'51f376140d774f369ac67ed70a0ac868'
+ )
]
),
]
@@ -105,7 +110,12 @@ blackbox_fbx_tests = [
asset_db_utils.DBProduct(
product_name='softnaminglod/lodtest.dbgsg',
sub_id=-632012261,
- asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b')
+ asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'),
+ asset_db_utils.DBProduct(
+ product_name='softnaminglod/lodtest.dbgsg.xml',
+ sub_id=-2036095434,
+ asset_type=b'51f376140d774f369ac67ed70a0ac868'
+ )
]
),
]
@@ -139,11 +149,16 @@ blackbox_fbx_tests = [
sub_id=-740411732,
asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'
),
+ asset_db_utils.DBProduct(
+ product_name='softnamingphysics/physicstest.dbgsg.xml',
+ sub_id=330338417,
+ asset_type=b'51f376140d774f369ac67ed70a0ac868'
+ ),
asset_db_utils.DBProduct(
product_name="softnamingphysics/physicstest.pxmesh",
sub_id=640975857,
asset_type=b"7a2871b95eab4de0a901b0d2c6920ddb"
- ),
+ )
]
),
]
@@ -171,7 +186,11 @@ blackbox_fbx_tests = [
asset_db_utils.DBProduct(
product_name='twomeshonematerial/multiple_mesh_one_material.dbgsg',
sub_id=2077268018,
- asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b')
+ asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'),
+ asset_db_utils.DBProduct(
+ product_name='twomeshonematerial/multiple_mesh_one_material.dbgsg.xml',
+ sub_id=1321067730,
+ asset_type=b'51f376140d774f369ac67ed70a0ac868')
]
),
]
@@ -204,6 +223,11 @@ blackbox_fbx_tests = [
product_name='twomeshlinkedmaterials/multiple_mesh_linked_materials.dbgsg',
sub_id=-1898461950,
asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'
+ ),
+ asset_db_utils.DBProduct(
+ product_name='twomeshlinkedmaterials/multiple_mesh_linked_materials.dbgsg.xml',
+ sub_id=-772341513,
+ asset_type=b'51f376140d774f369ac67ed70a0ac868'
)
]
),
@@ -236,7 +260,12 @@ blackbox_fbx_tests = [
asset_db_utils.DBProduct(
product_name='onemeshmultiplematerials/single_mesh_multiple_materials.dbgsg',
sub_id=-262822238,
- asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b')
+ asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'),
+ asset_db_utils.DBProduct(
+ product_name='onemeshmultiplematerials/single_mesh_multiple_materials.dbgsg.xml',
+ sub_id=1462358160,
+ asset_type=b'51f376140d774f369ac67ed70a0ac868'
+ )
]
),
]
@@ -266,7 +295,12 @@ blackbox_fbx_tests = [
asset_db_utils.DBProduct(
product_name='vertexcolor/vertexcolor.dbgsg',
sub_id=-1543877170,
- asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b')
+ asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'),
+ asset_db_utils.DBProduct(
+ product_name='vertexcolor/vertexcolor.dbgsg.xml',
+ sub_id=1743516586,
+ asset_type=b'51f376140d774f369ac67ed70a0ac868'
+ )
]
),
]
@@ -297,6 +331,11 @@ blackbox_fbx_tests = [
product_name='motion/jack_idle_aim_zup.dbgsg',
sub_id=-517610290,
asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'),
+ asset_db_utils.DBProduct(
+ product_name='motion/jack_idle_aim_zup.dbgsg.xml',
+ sub_id=-817863914,
+ asset_type=b'51f376140d774f369ac67ed70a0ac868'
+ ),
asset_db_utils.DBProduct(
product_name='motion/jack_idle_aim_zup.motion',
sub_id=186392073,
@@ -329,6 +368,10 @@ blackbox_fbx_tests = [
product_name='shaderball/shaderball.dbgsg',
sub_id=-1607815784,
asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'),
+ asset_db_utils.DBProduct(
+ product_name='shaderball/shaderball.dbgsg.xml',
+ sub_id=-1153118555,
+ asset_type=b'51f376140d774f369ac67ed70a0ac868'),
]
),
]
@@ -361,7 +404,12 @@ blackbox_fbx_special_tests = [
asset_db_utils.DBProduct(
product_name='twomeshtwomaterial/multiple_mesh_multiple_material.dbgsg',
sub_id=896980093,
- asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b')
+ asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'),
+ asset_db_utils.DBProduct(
+ product_name='twomeshtwomaterial/multiple_mesh_multiple_material.dbgsg.xml',
+ sub_id=-1556988544,
+ asset_type=b'51f376140d774f369ac67ed70a0ac868'
+ )
]
),
]
@@ -382,7 +430,12 @@ blackbox_fbx_special_tests = [
asset_db_utils.DBProduct(
product_name='twomeshtwomaterial/multiple_mesh_multiple_material.dbgsg',
sub_id=896980093,
- asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b')
+ asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'),
+ asset_db_utils.DBProduct(
+ product_name='twomeshtwomaterial/multiple_mesh_multiple_material.dbgsg.xml',
+ sub_id=-1556988544,
+ asset_type=b'51f376140d774f369ac67ed70a0ac868'
+ )
]
),
]
@@ -454,6 +507,19 @@ class TestsFBX_AllPlatforms(object):
product.product_name = job.platform + "/" \
+ product.product_name
+ def compare_scene_debug_file(self, asset_processor, expected_file_path, actual_file_path):
+ debug_graph_path = os.path.join(asset_processor.project_test_cache_folder(), actual_file_path)
+ expected_debug_graph_path = os.path.join(asset_processor.project_test_source_folder(), "SceneDebug", expected_file_path)
+
+ logger.info(f"Parsing scene graph: {debug_graph_path}")
+ with open(debug_graph_path, "r") as scene_file:
+ actual_lines = scene_file.readlines()
+
+ logger.info(f"Parsing scene graph: {expected_debug_graph_path}")
+ with open(expected_debug_graph_path, "r") as scene_file:
+ expected_lines = scene_file.readlines()
+
+ assert utils.compare_lists(actual_lines, expected_lines), "Scene mismatch"
def run_fbx_test(self, workspace, ap_setup_fixture, asset_processor,
project, blackbox_params: BlackboxAssetTest, overrideAsset=False):
"""
@@ -509,19 +575,12 @@ class TestsFBX_AllPlatforms(object):
scene_debug_file = blackbox_params.override_scene_debug_file if overrideAsset \
else blackbox_params.scene_debug_file
- debug_graph_path = os.path.join(asset_processor.project_test_cache_folder(),
- blackbox_params.scene_debug_file)
- expected_debug_graph_path = os.path.join(asset_processor.project_test_source_folder(), scene_debug_file)
+ self.compare_scene_debug_file(asset_processor, scene_debug_file, blackbox_params.scene_debug_file)
- logger.info(f"Parsing scene graph: {debug_graph_path}")
- with open(debug_graph_path, "r") as scene_file:
- actual_lines = scene_file.readlines()
-
- logger.info(f"Parsing scene graph: {expected_debug_graph_path}")
- with open(expected_debug_graph_path, "r") as scene_file:
- expected_lines = scene_file.readlines()
-
- assert utils.compare_lists(actual_lines, expected_lines), "Scene mismatch"
+ # Run again for the .dbgsg.xml file
+ self.compare_scene_debug_file(asset_processor,
+ scene_debug_file + ".xml",
+ blackbox_params.scene_debug_file + ".xml")
# Check that each given source asset resulted in the expected jobs and products.
self.populateAssetInfo(workspace, project, assetsToValidate)
diff --git a/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AzTestRunner_Works.py b/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AzTestRunner_Works.py
index b269c9e131..df755d5d11 100644
--- a/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AzTestRunner_Works.py
+++ b/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AzTestRunner_Works.py
@@ -13,18 +13,26 @@ import os
import pytest
import subprocess
+import ly_test_tools
+
@pytest.mark.SUITE_smoke
class TestCLIToolAzTestRunnerWorks(object):
- def test_CLITool_AzTestRunner_Works(self, build_directory):
+ def test_CLITool_AzTestRunner_ListSelfTests(self, build_directory):
file_path = os.path.join(build_directory, "AzTestRunner")
help_message = "OKAY Symbol found: AzRunUnitTests"
- # Launch AzTestRunner
+
+ if ly_test_tools.WINDOWS:
+ target_lib = "AzTestRunner.Tests"
+ else:
+ target_lib = "libAzTestRunner.Tests"
+
+ # Launch AzTestRunner, load self-tests, print test names
output = subprocess.run(
- [file_path, "AzTestRunner.Tests", "AzRunUnitTests", "--gtest_list_tests"], capture_output=True, timeout=10
+ [file_path, target_lib, "AzRunUnitTests", "--gtest_list_tests"], capture_output=True, timeout=10
)
assert (
len(output.stderr) == 0 and output.returncode == 0
), f"Error occurred while launching {file_path}: {output.stderr}"
# Verify help message
- assert help_message in str(output.stdout), f"Help Message: {help_message} is not present"
+ assert help_message in str(output.stdout), f"Help Message: '{help_message}' unexpectedly not present"
diff --git a/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.mm b/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.mm
index a7f59b7ac8..c664d13c79 100644
--- a/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.mm
+++ b/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.mm
@@ -9,7 +9,7 @@
#import
#include "EditorDefs.h"
-#include "QtEditorApplication.h"
+#include "QtEditorApplication_mac.h"
// AzFramework
#include
diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
index 41e950a058..5b849dcbe7 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
@@ -1788,6 +1788,11 @@ AZStd::string SandboxIntegrationManager::GetComponentEditorIcon(const AZ::Uuid&
return iconPath;
}
+AZStd::string SandboxIntegrationManager::GetComponentTypeEditorIcon(const AZ::Uuid& componentType)
+{
+ return GetComponentEditorIcon(componentType, nullptr);
+}
+
AZStd::string SandboxIntegrationManager::GetComponentIconPath(const AZ::Uuid& componentType,
AZ::Crc32 componentIconAttrib, AZ::Component* component)
{
diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h
index 40962409a3..9afa944438 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h
@@ -233,6 +233,7 @@ private:
}
AZStd::string GetComponentEditorIcon(const AZ::Uuid& componentType, AZ::Component* component) override;
+ AZStd::string GetComponentTypeEditorIcon(const AZ::Uuid& componentType) override;
AZStd::string GetComponentIconPath(const AZ::Uuid& componentType, AZ::Crc32 componentIconAttrib, AZ::Component* component) override;
//////////////////////////////////////////////////////////////////////////
diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/ComponentPalette/ComponentDataModel.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/ComponentPalette/ComponentDataModel.cpp
index cd0fba350f..dcae6abfeb 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/ComponentPalette/ComponentDataModel.cpp
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/ComponentPalette/ComponentDataModel.cpp
@@ -139,7 +139,7 @@ ComponentDataModel::ComponentDataModel(QObject* parent)
if (element.m_elementId == AZ::Edit::ClassElements::EditorData)
{
AZStd::string iconPath;
- EBUS_EVENT_RESULT(iconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, classData->m_typeId, nullptr);
+ AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, classData->m_typeId);
if (!iconPath.empty())
{
m_componentIcons[classData->m_typeId] = QIcon(iconPath.c_str());
diff --git a/Code/Editor/TrackView/TrackViewNodes.cpp b/Code/Editor/TrackView/TrackViewNodes.cpp
index 2a2d584e46..db65abf9d2 100644
--- a/Code/Editor/TrackView/TrackViewNodes.cpp
+++ b/Code/Editor/TrackView/TrackViewNodes.cpp
@@ -408,7 +408,7 @@ CTrackViewNodesCtrl::CTrackViewNodesCtrl(QWidget* hParentWnd, CTrackViewDialog*
serializeContext->EnumerateDerived([this](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) -> bool
{
AZStd::string iconPath;
- EBUS_EVENT_RESULT(iconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, classData->m_typeId, nullptr);
+ AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, classData->m_typeId);
if (!iconPath.empty())
{
m_componentTypeToIconMap[classData->m_typeId] = QIcon(iconPath.c_str());
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
index dabf02e260..df8db79db0 100644
--- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
@@ -215,11 +215,6 @@ namespace AZ
m_oldProjectPath = newProjectPath;
// Merge the project.json file into settings registry under ProjectSettingsRootKey path.
- AZ::IO::FixedMaxPath projectMetadataFile{ AZ::SettingsRegistryMergeUtils::FindEngineRoot(m_registry) / newProjectPath };
- projectMetadataFile /= "project.json";
- m_registry.MergeSettingsFile(projectMetadataFile.Native(),
- AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey);
-
// Update all the runtime file paths based on the new "project_path" value.
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry);
}
diff --git a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp
new file mode 100644
index 0000000000..5d66bb6ac5
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+
+namespace AZ::DOM
+{
+ const char* VisitorError::CodeToString(VisitorErrorCode code)
+ {
+ switch (code)
+ {
+ case VisitorErrorCode::UnsupportedOperation:
+ return "operation not supported";
+ case VisitorErrorCode::InvalidData:
+ return "invalid data specified";
+ case VisitorErrorCode::InternalError:
+ return "internal error";
+ default:
+ return "unknown error";
+ }
+ }
+
+ VisitorError::VisitorError(VisitorErrorCode code)
+ : m_code(code)
+ {
+ }
+
+ VisitorError::VisitorError(VisitorErrorCode code, AZStd::string additionalInfo)
+ : m_code(code)
+ , m_additionalInfo(AZStd::move(additionalInfo))
+ {
+ }
+
+ VisitorErrorCode VisitorError::GetCode() const
+ {
+ return m_code;
+ }
+
+ const AZStd::string& VisitorError::GetAdditionalInfo() const
+ {
+ return m_additionalInfo;
+ }
+
+ AZStd::string VisitorError::FormatVisitorErrorMessage() const
+ {
+ if (m_additionalInfo.empty())
+ {
+ return AZStd::string::format("VisitorError: %s.", CodeToString(m_code));
+ }
+ return AZStd::string::format("VisitorError: %s. %s.", CodeToString(m_code), m_additionalInfo.c_str());
+ }
+
+ Visitor::Result Visitor::VisitorFailure(VisitorErrorCode code)
+ {
+ return AZ::Failure(VisitorError(code));
+ }
+
+ Visitor::Result Visitor::VisitorFailure(VisitorErrorCode code, AZStd::string additionalInfo)
+ {
+ return AZ::Failure(VisitorError(code, AZStd::move(additionalInfo)));
+ }
+
+ Visitor::Result Visitor::VisitorFailure(VisitorError error)
+ {
+ return AZ::Failure(error);
+ }
+
+ Visitor::Result Visitor::VisitorSuccess()
+ {
+ return AZ::Success();
+ }
+
+ Visitor::Result Visitor::Null()
+ {
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::Bool([[maybe_unused]] bool value)
+ {
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::Int64([[maybe_unused]] AZ::s64 value)
+ {
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::Uint64([[maybe_unused]] AZ::u64 value)
+ {
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::Double([[maybe_unused]] double value)
+ {
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::String([[maybe_unused]] AZStd::string_view value, [[maybe_unused]] Lifetime lifetime)
+ {
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::OpaqueValue([[maybe_unused]] const OpaqueType& value, [[maybe_unused]] Lifetime lifetime)
+ {
+ if (!SupportsOpaqueValues())
+ {
+ return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Opaque values are not supported by this visitor");
+ }
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::RawValue([[maybe_unused]] AZStd::string_view value, [[maybe_unused]] Lifetime lifetime)
+ {
+ if (!SupportsRawValues())
+ {
+ return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Raw values are not supported by this visitor");
+ }
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::StartObject()
+ {
+ if (!SupportsObjects())
+ {
+ return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Objects are not supported by this visitor");
+ }
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::EndObject([[maybe_unused]] AZ::u64 attributeCount)
+ {
+ if (!SupportsObjects())
+ {
+ return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Objects are not supported by this visitor");
+ }
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::Key([[maybe_unused]] AZ::Name key)
+ {
+ if (!SupportsObjects() && !SupportsNodes())
+ {
+ return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Keys are not supported by this visitor");
+ }
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::RawKey(AZStd::string_view key, [[maybe_unused]] Lifetime lifetime)
+ {
+ if (!SupportsRawKeys())
+ {
+ return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Raw keys are not supported by this visitor");
+ }
+ return Key(AZ::Name(key));
+ }
+
+ Visitor::Result Visitor::StartArray()
+ {
+ if (!SupportsArrays())
+ {
+ return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Arrays are not supported by this visitor");
+ }
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::EndArray([[maybe_unused]] AZ::u64 elementCount)
+ {
+ if (!SupportsArrays())
+ {
+ return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Arrays are not supported by this visitor");
+ }
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::StartNode([[maybe_unused]] AZ::Name name)
+ {
+ if (!SupportsNodes())
+ {
+ return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Nodes are not supported by this visitor");
+ }
+ return VisitorSuccess();
+ }
+
+ Visitor::Result Visitor::RawStartNode(AZStd::string_view name, [[maybe_unused]] Lifetime lifetime)
+ {
+ return StartNode(AZ::Name(name));
+ }
+
+ Visitor::Result Visitor::EndNode([[maybe_unused]] AZ::u64 attributeCount, [[maybe_unused]] AZ::u64 elementCount)
+ {
+ if (!SupportsNodes())
+ {
+ return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Nodes are not supported by this visitor");
+ }
+ return VisitorSuccess();
+ }
+
+ VisitorFlags Visitor::GetVisitorFlags() const
+ {
+ // By default support raw keys (promoting them to AZ::Name) and support Array / Object / Node
+ // We leave Opaque type support and Raw Values to more specialized, implementation-specific cases
+ return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects | VisitorFlags::SupportsNodes;
+ }
+
+ bool Visitor::SupportsRawValues() const
+ {
+ return (GetVisitorFlags() & VisitorFlags::SupportsRawValues) != VisitorFlags::Null;
+ }
+
+ bool Visitor::SupportsRawKeys() const
+ {
+ return (GetVisitorFlags() & VisitorFlags::SupportsRawKeys) != VisitorFlags::Null;
+ }
+
+ bool Visitor::SupportsObjects() const
+ {
+ return (GetVisitorFlags() & VisitorFlags::SupportsObjects) != VisitorFlags::Null;
+ }
+
+ bool Visitor::SupportsArrays() const
+ {
+ return (GetVisitorFlags() & VisitorFlags::SupportsArrays) != VisitorFlags::Null;
+ }
+
+ bool Visitor::SupportsNodes() const
+ {
+ return (GetVisitorFlags() & VisitorFlags::SupportsNodes) != VisitorFlags::Null;
+ }
+
+ bool Visitor::SupportsOpaqueValues() const
+ {
+ return (GetVisitorFlags() & VisitorFlags::SupportsOpaqueValues) != VisitorFlags::Null;
+ }
+} // namespace AZ::DOM
diff --git a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h
new file mode 100644
index 0000000000..584cfce4ed
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+namespace AZ::DOM
+{
+ //
+ // Lifetime enum
+ //
+ //! Specifies the period in which a reference value will still be alive and safe to read.
+ enum class Lifetime
+ {
+ //! Specifies that the value is safe to read and will remain so indefinitely.
+ //! This implies that the value will not be mutated for the duration of this storage.
+ Persistent,
+ //! Specifies that the value may change or be deallocated, and must be copied to be safely stored.
+ Temporary,
+ };
+
+ //
+ // VisitorErrorCode enum
+ //
+ //! Error code specifying the reason a Visitor operation failed.
+ enum class VisitorErrorCode
+ {
+ //! Set when a Visitor doesn't have an implementation for a given attribute type.
+ //! A pure-JSON serializer might reject a Node attribute, for example, and serialization visitors
+ //! can forbid non-serializable Opaque types.
+ UnsupportedOperation,
+ //! Set when a Visitor has received malformed or invalid data.
+ //! Potential sources include mismatching Begin/End call pairs or invalid attribute or element counts
+ //! being sent to End methods.
+ InvalidData,
+ //! The Visitor failed for some other reason not caused by invalid input.
+ //! If returning a custom error with this code, it's preferrable to also provide supplemental info
+ //! in the form of an explanatory string.
+ InternalError
+ };
+
+ //
+ // VisitorError class
+ //
+ //! Details of the reason for failure within a VisitorInterface operation.
+ class VisitorError final
+ {
+ public:
+ explicit VisitorError(VisitorErrorCode code);
+ VisitorError(VisitorErrorCode code, AZStd::string additionalInfo);
+
+ //! Gets the error code associated with this error.
+ VisitorErrorCode GetCode() const;
+ //! Gets a supplemental error info string from the error.
+ //! Returns an empty string if no additional information was provided to the error.
+ const AZStd::string& GetAdditionalInfo() const;
+ //! Provides a formatted, human-readable error description that can be used for logging purposes.
+ AZStd::string FormatVisitorErrorMessage() const;
+
+ //! Helper method, translates a VisitorErrorCode to a human readable string.
+ static const char* CodeToString(VisitorErrorCode code);
+
+ private:
+ VisitorErrorCode m_code;
+ AZStd::string m_additionalInfo;
+ };
+
+ //! A type alias for opaque DOM types that aren't meant to be serializable.
+ //! /see VisitorInterface::OpaqueValue
+ using OpaqueType = AZStd::any;
+
+ //
+ // VisitorFlags enum
+ //
+ //! Flags representning capabilities of a \ref Visitor.
+ enum class VisitorFlags : AZ::u16
+ {
+ //! No flags are set. This can be used in conjunction with bitwise operators to check a flag.
+ Null = 0,
+ //! If set, this Visitor interface supports raw strings in place of specific value types.
+ //! Visitors with this flag accept RawValue calls in lieu of more specific value calls such as Int64 or String.
+ SupportsRawValues = (1 << 1),
+ //! If set, this Visitor interface supports raw strings in place of Name types for keys and Node names.
+ //! Visitors with this flag accept RawKey and RawStartNode in lieu of Key and StartNode calls.
+ SupportsRawKeys = (1 << 2),
+ //! If set, this Visitor interface supports Object types described via BeginObject and EndObject.
+ SupportsObjects = (1 << 3),
+ //! If set, this Visitor interface supports Array types described via BeginArray and EndArray.
+ SupportsArrays = (1 << 4),
+ //! If set, this Visitor interface supports Node types described BeginNode and EndNode.
+ SupportsNodes = (1 << 4),
+ //! If set, this Visitor interface supports opaque values described via OpaqueValue.
+ SupportsOpaqueValues = (1 << 5),
+ };
+
+ AZ_DEFINE_ENUM_BITWISE_OPERATORS(VisitorFlags);
+
+ //
+ // Visitor class
+ //
+ //! An interface for performing operations on elements of a generic DOM (Document Object Model).
+ //! A Document Object Model is defined here as a tree structure comprised of one of the following values:
+ //! - Primitives: plain data types, including
+ //! - \ref Int64: 64 bit signed integer
+ //! - \ref Uint64: 64 bit unsigned integer
+ //! - \ref Bool: boolean value
+ //! - \ref Double: 64 bit double precision float
+ //! - \ref Null: sentinel "empty" type with no value representation
+ //! - \ref String: UTF8 encoded string
+ //! - \ref Object: an ordered container of key/value pairs where keys are AZ::Names and values may be any DOM type
+ //! (including Object)
+ //! - \ref Array: an ordered container of values, in which values are any DOM value type (including Array)
+ //! - \ref Node: a container
+ //! - \ref OpaqueValue: An arbitrary value stored in an AZStd::any. This is a non-serializable representation of an
+ //! entry useful for in-memory options. This is intended to be used as an intermediate value over the course of DOM
+ //! transformation and as a proxy to pass through types of which the DOM has no knowledge to other systems.
+ //!
+ //! Opaque values are rejected by the default VisitorInterface implementation.
+ //!
+ //! Care should be ensured that DOMs representing opaque types are only visited by consumers that understand them.
+ class Visitor
+ {
+ public:
+ virtual ~Visitor() = default;
+
+ //! The result of a Visitor operation.
+ //! A failure indicates a non-recoverable issue and signals that no further visit calls may be made in the
+ //! current state.
+ using Result = AZ::Outcome;
+
+ //! Returns a set of flags representing the operations this Visitor supports.
+ //! The base implementation supports raw keys (\see VisitorFlags::SupportsRawKeys) and
+ //! arrays (\see VisitorFlags::SupportsArrays), objects (\see VisitorFlags::SupportsObjects), and
+ //! nodes (\see VisitorFlags::SupportsNodes).
+ //! Raw (\see VisitorFlags::SupportsRawValues) and opaque values (\see VisitorFlags::SupportsOpaqueValues)
+ //! are disallowed by default, as their handling is intended to be implementation-specific.
+ virtual VisitorFlags GetVisitorFlags() const;
+ //! /see VisitorFlags::SupportsRawValues
+ bool SupportsRawValues() const;
+ //! /see VisitorFlags::SupportsRawKeys
+ bool SupportsRawKeys() const;
+ //! /see VisitorFlags::SupportsObjects
+ bool SupportsObjects() const;
+ //! /see VisitorFlags::SupportsArrays
+ bool SupportsArrays() const;
+ //! /see VisitorFlags::SupportsNodes
+ bool SupportsNodes() const;
+ //! /see VisitorFlags::SupportsOpaqueValues
+ bool SupportsOpaqueValues() const;
+
+ //! Operates on an empty null value.
+ virtual Result Null();
+ //! Operates on a bool value.
+ virtual Result Bool(bool value);
+ //! Operates on a signed, 64 bit integer value.
+ virtual Result Int64(AZ::s64 value);
+ //! Operates on an unsigned, 64 bit integer value.
+ virtual Result Uint64(AZ::u64 value);
+ //! Operates on a double precision, 64 bit floating point value.
+ virtual Result Double(double value);
+ //! Operates on a string value. As strings are a reference type.
+ //! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy.
+ virtual Result String(AZStd::string_view value, Lifetime lifetime);
+ //! Operates on an opaque value. As opaque values are a reference type, storage semantics are provided to
+ //! indicate where the value may be stored persistently or requires a copy.
+ //! The base implementation of OpaqueValue rejects the operation, as opaque values are meant for special
+ //! cases with specific implementations, not generic usage.
+ //! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy.
+ virtual Result OpaqueValue(const OpaqueType& value, Lifetime lifetime);
+ //! Operates on a raw value encoded as a UTF-8 string that hasn't had its type deduced.
+ //! Visitors that support raw values (\see VisitorFlags::SupportsRawValues) may parse the raw value and
+ //! forward it to the corresponding value call or calls of their choice.
+ //! The base implementation of RawValue rejects the operation, as raw values are meant to be handled on
+ //! a per-implementation basis.
+ virtual Result RawValue(AZStd::string_view value, Lifetime lifetime);
+
+ //! Operates on an Object.
+ //! Callers may make any number of Key calls, followed by calls representing a value (including a nested
+ //! StartObject call) and then must call EndObject.
+ virtual Result StartObject();
+ //! Finishes operating on an Object.
+ //! Callers must provide the number of attributes that were provided to the object, i.e. the number of key
+ //! and value calls made within the direct context of this object (but not any nested objects / nodes).
+ virtual Result EndObject(AZ::u64 attributeCount);
+
+ //! Specifies a key for a key/value pair.
+ //! Key must be called subsequent to a call to \ref StartObject or \ref StartNode and immediately followed by
+ //! calls representing the key's associated value.
+ virtual Result Key(AZ::Name key);
+ //! Specifies a key for a key/value pair using a raw string instead of \ref AZ::Name.
+ //! \see Key
+ virtual Result RawKey(AZStd::string_view key, Lifetime lifetime);
+
+ //! Operates on an Array.
+ //! Callers may make any number of subsequent value calls to represent the elements of the array, and then must
+ //! call EndArray.
+ virtual Result StartArray();
+ //! Finishes operating on an Array.
+ //! Callers must provide the number of elements that were provided to the array, i.e. the number of value calls
+ //! made within the direct context of this array (but not any nested arrays / nodes).
+ virtual Result EndArray(AZ::u64 elementCount);
+
+ //! Operates on a Node.
+ //! Callers may make any number of Key calls followed by value calls or value calls not prefixed with a Key
+ //! call, and then must call EndNode. See \ref StartObject and \ref StartArray as Node types combine the
+ //! functionality of both structures into a named Node structure.
+ virtual Result StartNode(AZ::Name name);
+ //! Operates on a Node using a raw string instead of \ref AZ::Name.
+ //! \see StartNode
+ virtual Result RawStartNode(AZStd::string_view name, Lifetime lifetime);
+ //! Finishes operating on a Node.
+ //! Callers must provide both the number of attributes the were provided and the number of elements that were
+ //! provided to the node, attributes being values prefaced by a call to Key.
+ virtual Result EndNode(AZ::u64 attributeCount, AZ::u64 elementCount);
+
+ protected:
+ Visitor() = default;
+
+ //! Helper method, constructs a failure \ref Result with the specified code.
+ static Result VisitorFailure(VisitorErrorCode code);
+ //! Helper method, constructs a failure \ref Result with the specified code and supplemental info.
+ static Result VisitorFailure(VisitorErrorCode code, AZStd::string additionalInfo);
+ //! Helper method, constructs a failure \ref Result with the specified error.
+ static Result VisitorFailure(VisitorError error);
+ //! Helper method, constructs a success \ref Result.
+ static Result VisitorSuccess();
+ };
+} // namespace AZ::DOM
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
index 36f66312d8..5458a3fadf 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
@@ -634,12 +634,18 @@ namespace AZ::SettingsRegistryMergeUtils
}
// Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name.
- auto projectNameKey =
- AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey)
+ constexpr auto projectNameKey =
+ FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey)
+ "/project_name";
- AZ::SettingsRegistryInterface::FixedValueString projectName;
- if (!registry.Get(projectName, projectNameKey))
+ // Read the project name from the project.json file if it exists
+ if (AZ::IO::FixedMaxPath projectJsonPath = normalizedProjectPath / "project.json";
+ AZ::IO::SystemFile::Exists(projectJsonPath.c_str()))
+ {
+ registry.MergeSettingsFile(projectJsonPath.Native(),
+ AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey);
+ }
+ if (FixedValueString projectName; !registry.Get(projectName, projectNameKey))
{
projectName = path.Filename().Native();
registry.Set(projectNameKey, projectName);
diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake
index 3cd702e588..a5cc3fdcd4 100644
--- a/Code/Framework/AzCore/AzCore/azcore_files.cmake
+++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake
@@ -125,6 +125,8 @@ set(FILES
Debug/TraceMessagesDrillerBus.h
Debug/TraceReflection.cpp
Debug/TraceReflection.h
+ DOM/DomVisitor.cpp
+ DOM/DomVisitor.h
Driller/DefaultStringPool.h
Driller/Driller.cpp
Driller/Driller.h
diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp
index 79a6333612..c7663dc1ab 100644
--- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp
+++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp
@@ -258,10 +258,17 @@ namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////////
void XcbNativeWindow::SetWindowTitle(const AZStd::string& title)
{
+ // Set the title of both the window and the task bar by using
+ // a buffer to hold the title twice, separated by a null-terminator
+ auto doubleTitleSize = (title.size() + 1) * 2;
+ AZStd::string doubleTitle(doubleTitleSize, '\0');
+ azstrncpy(doubleTitle.data(), doubleTitleSize, title.c_str(), title.size());
+ azstrncpy(&doubleTitle.data()[title.size() + 1], title.size(), title.c_str(), title.size());
+
xcb_void_cookie_t xcbCheckResult;
xcbCheckResult = xcb_change_property(
- m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, static_cast(title.size()),
- title.c_str());
+ m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, static_cast(doubleTitle.size()),
+ doubleTitle.c_str());
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set window title.");
}
diff --git a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp
index 0cce93d751..6957844452 100644
--- a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp
+++ b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp
@@ -82,7 +82,7 @@ namespace AzGameFramework
// Used the lowercase the platform name since the bootstrap.game...setreg is being loaded
// from the asset cache root where all the files are in lowercased from regardless of the filesystem case-sensitivity
- static constexpr char filename[] = "bootstrap.game." AZ_BUILD_CONFIGURATION_TYPE "." AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER ".setreg";
+ static constexpr char filename[] = "bootstrap.game." AZ_BUILD_CONFIGURATION_TYPE ".setreg";
AZ::IO::FixedMaxPath cacheRootPath;
if (registry.Get(cacheRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp
index f79f355ccb..8831bef89c 100644
--- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp
+++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp
@@ -22,6 +22,7 @@ namespace AzQtComponents
, m_closeOnClick(true)
, m_ui(new Ui::ToastNotification())
, m_fadeAnimation(nullptr)
+ , m_configuration(toastConfiguration)
{
setProperty("HasNoWindowDecorations", true);
@@ -80,7 +81,13 @@ namespace AzQtComponents
}
ToastNotification::~ToastNotification()
- {
+ {
+ }
+
+ bool ToastNotification::IsDuplicate(const ToastConfiguration& toastConfiguration)
+ {
+ return toastConfiguration.m_title == m_configuration.m_title
+ && toastConfiguration.m_description == m_configuration.m_description;
}
void ToastNotification::paintEvent(QPaintEvent* event)
diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h
index 7f2701a803..4343f37df4 100644
--- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h
+++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h
@@ -45,6 +45,8 @@ namespace AzQtComponents
void ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint);
void UpdatePosition(const QPoint& screenPosition, const QPointF& anchorPoint);
+
+ bool IsDuplicate(const ToastConfiguration& toastConfiguration);
// QDialog
void showEvent(QShowEvent* showEvent) override;
@@ -64,7 +66,7 @@ namespace AzQtComponents
private:
QPropertyAnimation* m_fadeAnimation;
-
+ ToastConfiguration m_configuration;
bool m_closeOnClick;
QTimer m_lifeSpan;
uint32_t m_borderRadius = 0;
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h
index 9e29b9813c..fd4196a296 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h
@@ -826,6 +826,10 @@ namespace AzToolsFramework
/// Path will be empty if component should have no icon.
virtual AZStd::string GetComponentEditorIcon(const AZ::Uuid& /*componentType*/, AZ::Component* /*component*/) { return AZStd::string(); }
+ //! Return path to icon for component type.
+ //! Path will be empty if component type should have no icon.
+ virtual AZStd::string GetComponentTypeEditorIcon(const AZ::Uuid& /*componentType*/) { return AZStd::string(); }
+
/**
* Return the icon image path based on the component type and where it is used.
* \param componentType component type
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h
index 966b9f8478..2d25dbbafc 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h
@@ -43,6 +43,8 @@ namespace AzToolsFramework
};
//! Provides a bus to notify when the different editor modes are entered/exit.
+ //! @note The editor modes are not discrete states but rather each progression of mode retain the active the parent
+ //! mode that the new mode progressed from.
class ViewportEditorModeNotifications : public AZ::EBusTraits
{
public:
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.cpp
index ce50fc7e00..6a926b89fd 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.cpp
@@ -9,9 +9,27 @@
#include
#include
+#include
namespace AzToolsFramework
{
+ static bool AreEntitiesValidForDuplication(const EntityIdList& entityIds)
+ {
+ for (AZ::EntityId entityId : entityIds)
+ {
+ if (GetEntityById(entityId) == nullptr)
+ {
+ AZ_Error(
+ "Entity", false,
+ "Entity with id '%llu' is not found. This can happen when you try to duplicate the entity before it is created. Please "
+ "ensure entities are created before trying to duplicate them.",
+ static_cast(entityId));
+ return false;
+ }
+ }
+ return true;
+ }
+
void EditorEntityManager::Start()
{
m_prefabPublicInterface = AZ::Interface::Get();
@@ -62,7 +80,11 @@ namespace AzToolsFramework
EntityIdList selectedEntities;
ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities);
- m_prefabPublicInterface->DuplicateEntitiesInInstance(selectedEntities);
+ if (AreEntitiesValidForDuplication(selectedEntities))
+ {
+ m_prefabPublicInterface->DuplicateEntitiesInInstance(selectedEntities);
+ }
+
}
void EditorEntityManager::DuplicateEntityById(AZ::EntityId entityId)
@@ -72,7 +94,10 @@ namespace AzToolsFramework
void EditorEntityManager::DuplicateEntities(const EntityIdList& entities)
{
- m_prefabPublicInterface->DuplicateEntitiesInInstance(entities);
+ if (AreEntitiesValidForDuplication(entities))
+ {
+ m_prefabPublicInterface->DuplicateEntitiesInInstance(entities);
+ }
}
}
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.h
index 5786cc3fbc..c4311de672 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.h
@@ -34,5 +34,4 @@ namespace AzToolsFramework
private:
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
};
-
}
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp
index 3f254581dd..67563aaf69 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp
@@ -176,7 +176,7 @@ namespace AzToolsFramework
, public AZ::BehaviorEBusHandler
{
AZ_EBUS_BEHAVIOR_BINDER(ToolsApplicationNotificationBusHandler, "{7EB67956-FF86-461A-91E2-7B08279CFACF}", AZ::SystemAllocator,
- EntityRegistered, EntityDeregistered);
+ EntityRegistered, EntityDeregistered, AfterEntitySelectionChanged);
void EntityRegistered(AZ::EntityId entityId) override
{
@@ -187,6 +187,11 @@ namespace AzToolsFramework
{
Call(FN_EntityDeregistered, entityId);
}
+
+ void AfterEntitySelectionChanged(const EntityIdList& newlySelectedEntities, const EntityIdList& newlyDeselectedEntities) override
+ {
+ Call(FN_AfterEntitySelectionChanged, newlySelectedEntities, newlyDeselectedEntities);
+ }
};
struct ViewPaneCallbackBusHandler final
@@ -410,6 +415,7 @@ namespace AzToolsFramework
->Handler()
->Event("EntityRegistered", &ToolsApplicationEvents::EntityRegistered)
->Event("EntityDeregistered", &ToolsApplicationEvents::EntityDeregistered)
+ ->Event("AfterEntitySelectionChanged", &ToolsApplicationEvents::AfterEntitySelectionChanged)
;
behaviorContext->Class()
@@ -428,6 +434,7 @@ namespace AzToolsFramework
->Attribute(AZ::Script::Attributes::Module, "editor")
->Event("RegisterCustomViewPane", &EditorRequests::RegisterCustomViewPane)
->Event("UnregisterViewPane", &EditorRequests::UnregisterViewPane)
+ ->Event("GetComponentTypeEditorIcon", &EditorRequests::GetComponentTypeEditorIcon)
;
behaviorContext->EBus("EditorEventBus")
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp
index d976c91c3e..a6a80e58d3 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp
@@ -1110,7 +1110,8 @@ namespace AzToolsFramework
// Select the duplicated entities/instances
auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances");
selectionUndo->SetParent(undoBatch.GetUndoBatch());
- ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds);
+ ToolsApplicationRequestBus::Broadcast(
+ &ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds);
}
return AZ::Success(AZStd::move(duplicatedEntityAndInstanceIds));
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp
index b6f5b41b65..888554d065 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp
@@ -90,7 +90,7 @@ namespace AzToolsFramework
}
AZStd::string componentIconPath;
- EBUS_EVENT_RESULT(componentIconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, componentClass->m_typeId, nullptr);
+ AzToolsFramework::EditorRequestBus::BroadcastResult(componentIconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, componentClass->m_typeId);
componentIconTable[componentClass] = QString::fromUtf8(componentIconPath.c_str());
}
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp
index e039230783..a88711a9ed 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp
@@ -63,6 +63,12 @@ namespace AzToolsFramework
ToastId ToastNotificationsView::ShowToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration)
{
+ // reject duplicate messages
+ if (m_rejectDuplicates && DuplicateNotificationInQueue(toastConfiguration))
+ {
+ return ToastId();
+ }
+
ToastId toastId = CreateToastNotification(toastConfiguration);
m_queuedNotifications.emplace_back(toastId);
@@ -70,10 +76,28 @@ namespace AzToolsFramework
{
DisplayQueuedNotification();
}
+ else if (m_queuedNotifications.size() >= m_maxQueuedNotifications)
+ {
+ // hiding the active toast will cause the next toast to be displayed
+ HideToastNotification(m_activeNotification);
+ }
return toastId;
}
+ bool ToastNotificationsView::DuplicateNotificationInQueue(const AzQtComponents::ToastConfiguration& toastConfiguration)
+ {
+ for (auto iter : m_notifications)
+ {
+ if (iter.second && iter.second->IsDuplicate(toastConfiguration))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
ToastId ToastNotificationsView::ShowToastAtCursor(const AzQtComponents::ToastConfiguration& toastConfiguration)
{
ToastId toastId = CreateToastNotification(toastConfiguration);
@@ -187,4 +211,14 @@ namespace AzToolsFramework
{
m_anchorPoint = anchorPoint;
}
+
+ void ToastNotificationsView::SetMaxQueuedNotifications(AZ::u32 maxQueuedNotifications)
+ {
+ m_maxQueuedNotifications = maxQueuedNotifications;
+ }
+
+ void ToastNotificationsView::SetRejectDuplicates(bool rejectDuplicates)
+ {
+ m_rejectDuplicates = rejectDuplicates;
+ }
}
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h
index e13f129467..c64ce00f4c 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h
@@ -52,10 +52,13 @@ namespace AzToolsFramework
void SetOffset(const QPoint& offset);
void SetAnchorPoint(const QPointF& anchorPoint);
+ void SetMaxQueuedNotifications(AZ::u32 maxQueuedNotifications);
+ void SetRejectDuplicates(bool rejectDuplicates);
private:
ToastId CreateToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration);
void DisplayQueuedNotification();
+ bool DuplicateNotificationInQueue(const AzQtComponents::ToastConfiguration& toastConfiguration);
QPoint GetGlobalPoint();
ToastId m_activeNotification;
@@ -64,5 +67,7 @@ namespace AzToolsFramework
QPoint m_offset = QPoint(10, 10);
QPointF m_anchorPoint = QPointF(1, 0);
+ AZ::u32 m_maxQueuedNotifications = 5;
+ bool m_rejectDuplicates = true;
};
} // AzToolsFramework
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp
index d94ced392c..0609f8113d 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp
@@ -72,7 +72,7 @@ namespace AzToolsFramework
void EntityOutlinerTreeView::leaveEvent([[maybe_unused]] QEvent* event)
{
- m_mousePosition = QPoint();
+ m_mousePosition = QPoint(-1, -1);
m_currentHoveredIndex = QModelIndex();
update();
}
@@ -200,7 +200,7 @@ namespace AzToolsFramework
const bool isEnabled = (this->model()->flags(index) & Qt::ItemIsEnabled);
const bool isSelected = selectionModel()->isSelected(index);
- const bool isHovered = (index == indexAt(m_mousePosition)) && isEnabled;
+ const bool isHovered = (index == indexAt(m_mousePosition).siblingAtColumn(0)) && isEnabled;
// Paint the branch Selection/Hover Rect
PaintBranchSelectionHoverRect(painter, rect, isSelected, isHovered);
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp
index 3e97f967fc..d6e9cac754 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp
@@ -153,6 +153,9 @@ namespace AzToolsFramework
{
initEntityOutlinerWidgetResources();
+ m_editorEntityUiInterface = AZ::Interface::Get();
+ AZ_Assert(m_editorEntityUiInterface != nullptr, "EntityOutlinerWidget requires a EditorEntityUiInterface instance on Initialize.");
+
m_gui = new Ui::EntityOutlinerWidgetUI();
m_gui->setupUi(this);
@@ -282,12 +285,6 @@ namespace AzToolsFramework
m_listModel->Initialize();
- m_editorEntityUiInterface = AZ::Interface::Get();
-
- AZ_Assert(
- m_editorEntityUiInterface != nullptr,
- "EntityOutlinerWidget requires a EditorEntityUiInterface instance on Initialize.");
-
EditorPickModeNotificationBus::Handler::BusConnect(GetEntityContextId());
EntityHighlightMessages::Bus::Handler::BusConnect();
EntityOutlinerModelNotificationBus::Handler::BusConnect();
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.cpp
index 91d2359ca6..48dec6f225 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.cpp
@@ -583,7 +583,7 @@ namespace AzToolsFramework
}
AZStd::string iconPath;
- EBUS_EVENT_RESULT(iconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, componentType, const_cast(&componentInstance));
+ AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentEditorIcon, componentType, const_cast(&componentInstance));
GetHeader()->SetIcon(QIcon(iconPath.c_str()));
bool isExpanded = true;
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp
index 43e2a6793f..5155de3087 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp
@@ -427,8 +427,8 @@ namespace AzToolsFramework::ViewportUi::Internal
void ViewportUiDisplay::PositionUiOverlayOverRenderViewport()
{
QPoint offset = m_renderOverlay->mapToGlobal(QPoint());
- m_uiMainWindow.move(offset);
- m_uiOverlay.setFixedSize(m_renderOverlay->width(), m_renderOverlay->height());
+ m_uiMainWindow.setGeometry(offset.x(), offset.y(), m_renderOverlay->width(), m_renderOverlay->height());
+ m_uiOverlay.setGeometry(m_uiMainWindow.rect());
UpdateUiOverlayGeometry();
}
diff --git a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp
index db6a0b8571..85b65f3edf 100644
--- a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp
+++ b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
#include
#include
@@ -187,10 +188,13 @@ namespace UnitTest
ASSERT_NE(m_viewportEditorModeTracker, nullptr);
m_viewportEditorModes = m_viewportEditorModeTracker->GetViewportEditorModes({AzToolsFramework::GetEntityContextId()});
ASSERT_NE(m_viewportEditorModes, nullptr);
+ m_focusModeInterface = AZ::Interface::Get();
+ ASSERT_NE(m_focusModeInterface, nullptr);
}
ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr;
const ViewportEditorModesInterface* m_viewportEditorModes = nullptr;
+ AzToolsFramework::FocusModeInterface* m_focusModeInterface = nullptr;
};
TEST_F(ViewportEditorModesTestsFixture, NumberOfEditorModesIsEqualTo4)
@@ -522,32 +526,48 @@ namespace UnitTest
}
TEST_F(
- ViewportEditorModeTrackerIntegrationTestFixture, EnteringComponentModeAfterInitialStateHasViewportEditorModesDefaultAndComponentModeActive)
+ ViewportEditorModeTrackerIntegrationTestFixture,
+ EnteringComponentModeAfterInitialStateHasViewportEditorModesDefaultAndComponentModeActive)
{
// When component mode is entered
AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
&AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode,
AZStd::vector{});
- bool inComponentMode = false;
- AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult(
- inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode);
-
// Expect to be in component mode
- EXPECT_TRUE(inComponentMode);
+ EXPECT_TRUE(AzToolsFramework::ComponentModeFramework::InComponentMode());
// Expect the default and component viewport editor modes to be active
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default));
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component));
-
- // Do not expect the pick and focus viewport editor modes to be active
EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick));
EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus));
}
TEST_F(
ViewportEditorModeTrackerIntegrationTestFixture,
- EnteringEditorPickEntitySelectionAfterInitialStateHasOnlyViewportEditorModePickModeActive)
+ ExitingComponentModeAfterEnteringFrominitialStateHasViewportEditorModesDefaultActive)
+ {
+ // When component mode is entered and exited
+ AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
+ &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode,
+ AZStd::vector{});
+
+ EXPECT_TRUE(AzToolsFramework::ComponentModeFramework::InComponentMode());
+
+ AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
+ &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::EndComponentMode);
+
+ // Expect to not be in component mode
+ EXPECT_FALSE(AzToolsFramework::ComponentModeFramework::InComponentMode());
+
+ // Expect only the default viewport editor mode to be active
+ ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default);
+ }
+
+ TEST_F(
+ ViewportEditorModeTrackerIntegrationTestFixture,
+ EnteringEditorPickEntitySelectionAfterInitialStateHasOnlyViewportEditorModePickActive)
{
// When entering pick mode
using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
@@ -563,6 +583,96 @@ namespace UnitTest
ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Pick);
}
- // FocusMode integration tests will follow (LYN-6995)
+ TEST_F(
+ ViewportEditorModeTrackerIntegrationTestFixture,
+ EnteringEditorDefaultEntitySelectionFromEditorPickEntitySelectionHasOnlyViewportEditorModeDefaultActive)
+ {
+ // When pick mode is entered and exited
+ using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
+ EditorInteractionSystemViewportSelectionRequestBus::Event(
+ AzToolsFramework::GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler,
+ [](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache,
+ [[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
+ {
+ return AZStd::make_unique(entityDataCache, viewportEditorModeTracker);
+ });
+ EditorInteractionSystemViewportSelectionRequestBus::Event(
+ AzToolsFramework::GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler,
+ [](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache,
+ [[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
+ {
+ return AZStd::make_unique(entityDataCache, viewportEditorModeTracker);
+ });
+
+ // Expect only the default viewport editor mode to be active
+ ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default);
+ }
+
+ TEST_F(ViewportEditorModeTrackerIntegrationTestFixture, EnteringFocusModeAfterInitialStateHasViewportEditorModeDefaultAndPickActive)
+ {
+ // When entering focus mode
+ m_focusModeInterface->SetFocusRoot(AZ::EntityId{ 1 });
+
+ // Expect the default and focus viewport editor modes to be active
+ EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default));
+ EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus));
+ EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick));
+ EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component));
+ }
+
+ TEST_F(
+ ViewportEditorModeTrackerIntegrationTestFixture,
+ ExitingFocusModeAfterEnteringFromInitialStateHasOnlyViewportEditorModeDefaultActive)
+ {
+ // When entering and leaving focus mode
+ m_focusModeInterface->SetFocusRoot(AZ::EntityId{ 1 });
+ m_focusModeInterface->SetFocusRoot(AZ::EntityId());
+
+ // Expect only the default mode to be active
+ ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default);
+ }
+
+ TEST_F(ViewportEditorModeTrackerIntegrationTestFixture, EnteringComponentModeFromFocusModeStateHasViewportEditorModeDefaultAndFocusAndComponentActive)
+ {
+ // When entering component mode from focus mode
+ m_focusModeInterface->SetFocusRoot(AZ::EntityId{ 1 });
+ AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
+ &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode,
+ AZStd::vector{});
+
+ // Expect to be in component mode
+ EXPECT_TRUE(AzToolsFramework::ComponentModeFramework::InComponentMode());
+
+ // Expect the default, focus and component viewport editor modes to be active
+ EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default));
+ EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus));
+ EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component));
+ EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick));
+ }
+
+ TEST_F(
+ ViewportEditorModeTrackerIntegrationTestFixture,
+ ExitingComponentModeAfterEnteringFromFocusModeHasViewportEditorModeDefaultAndFocusActive)
+ {
+ // When entering and leaving component mode from focus mode
+ m_focusModeInterface->SetFocusRoot(AZ::EntityId{ 1 });
+ AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
+ &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode,
+ AZStd::vector{});
+
+ EXPECT_TRUE(AzToolsFramework::ComponentModeFramework::InComponentMode());
+
+ AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
+ &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::EndComponentMode);
+
+ // Expect to not be in component mode
+ EXPECT_FALSE(AzToolsFramework::ComponentModeFramework::InComponentMode());
+
+ // Expect the default and focus viewport editor modes to be active
+ EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default));
+ EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus));
+ EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component));
+ EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick));
+ }
} // namespace UnitTest
diff --git a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h
index c85c45dd3e..5a57fa1e72 100644
--- a/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h
+++ b/Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h
@@ -10,12 +10,13 @@
#pragma once
#include
+#include
+#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
diff --git a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp
index c65ab24aed..6f15fa5ea2 100644
--- a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp
+++ b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp
@@ -259,6 +259,11 @@ namespace AssetProcessor
scratchBuffer.reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer;
AZStd::fixed_vector platformCodes;
AzFramework::PlatformHelper::AppendPlatformCodeNames(platformCodes, request.m_platformInfo.m_identifier);
+ AZ_Assert(platformCodes.size() <= 1, "A one-to-one mapping of asset type platform identifier"
+ " to platform codename is required in the SettingsRegistryBuilder."
+ " The bootstrap.game is now only produced per build configuration and doesn't take into account"
+ " different platforms names");
+
const AZStd::string& assetPlatformIdentifier = request.m_jobDescription.GetPlatformIdentifier();
// Determines the suffix that will be used for the launcher based on processing server vs non-server assets
const char* launcherType = assetPlatformIdentifier != AzFramework::PlatformHelper::GetPlatformName(AzFramework::PlatformId::SERVER)
@@ -293,9 +298,10 @@ namespace AssetProcessor
outputBuffer.Reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer;
SettingsExporter exporter(outputBuffer, excludes);
- for (AZStd::string_view platform : platformCodes)
+ if (!platformCodes.empty())
{
- AZ::u32 productSubID = static_cast(AZStd::hash{}(platform)); // Deliberately ignoring half the bits.
+ AZStd::string_view platform = platformCodes.front();
+ constexpr AZ::u32 productSubID = 0;
for (size_t i = 0; i < AZStd::size(specializations); ++i)
{
const AZ::SettingsRegistryInterface::Specializations& specialization = specializations[i];
@@ -337,7 +343,7 @@ namespace AssetProcessor
// The purpose of this section is to copy the Gem's SourcePaths from the Global Settings Registry
// the local SettingsRegistry. The reason this is needed is so that the call to
// `MergeSettingsToRegistry_GemRegistries` below is able to locate each gem's "/Registry" folder
- // that will be merged into the bootstrap.game...setreg file
+ // that will be merged into the bootstrap.game..setreg file
// This is used by the GameLauncher applications to read from a single merged .setreg file
// containing the settings needed to run a game/simulation without have access to the source code base registry
AZStd::vector gemInfos;
@@ -408,8 +414,6 @@ namespace AssetProcessor
}
outputPath += specialization.GetSpecialization(0); // Append configuration
- outputPath += '.';
- outputPath += platform;
outputPath += ".setreg";
AZ::IO::SystemFile file;
diff --git a/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp
index 2a1c4e4755..46f801223a 100644
--- a/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp
+++ b/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp
@@ -281,7 +281,7 @@ namespace AssetProcessor
ExcludeAssetRecognizer excludeRecogniser;
excludeRecogniser.m_name = "backup";
- excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/savebackup\\/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
+ excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("(^|.+/)savebackup/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
config.AddExcludeRecognizer(excludeRecogniser);
}
diff --git a/Code/Tools/AssetProcessor/native/tests/assetscanner/AssetScannerTests.cpp b/Code/Tools/AssetProcessor/native/tests/assetscanner/AssetScannerTests.cpp
index 7bf9eb3cc5..9b4c4e4f40 100644
--- a/Code/Tools/AssetProcessor/native/tests/assetscanner/AssetScannerTests.cpp
+++ b/Code/Tools/AssetProcessor/native/tests/assetscanner/AssetScannerTests.cpp
@@ -123,7 +123,7 @@ namespace AssetProcessor
ExcludeAssetRecognizer excludeRecogniser;
excludeRecogniser.m_name = "backup";
// we are excluding all the files in the folder but not the folder itself
- excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/subfolder2\\/aaa\\/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
+ excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("(^|[^/]+/)aaa/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
m_platformConfig.get()->AddExcludeRecognizer(excludeRecogniser);
m_assetScanner.get()->StartScan();
@@ -144,7 +144,7 @@ namespace AssetProcessor
ExcludeAssetRecognizer excludeRecogniser;
excludeRecogniser.m_name = "backup";
// we are excluding the complete folder here
- excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/subfolder2\\/aaa", AssetBuilderSDK::AssetBuilderPattern::Regex);
+ excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("(^|[^/]+/)aaa", AssetBuilderSDK::AssetBuilderPattern::Regex);
m_platformConfig.get()->AddExcludeRecognizer(excludeRecogniser);
m_assetScanner.get()->StartScan();
diff --git a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp
index 69397745f1..6fd05fa948 100644
--- a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp
+++ b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp
@@ -405,6 +405,8 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularExcludes)
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config;
+
+ config.AddScanFolder(ScanFolderInfo("blahblah", "Blah ScanFolder", "sf2", true, true), true);
m_absorber.Clear();
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
diff --git a/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp
index c0e2c0f1d2..9efe755f16 100644
--- a/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp
+++ b/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp
@@ -347,7 +347,7 @@ namespace AssetProcessor
ExcludeAssetRecognizer excludeRecogniser;
excludeRecogniser.m_name = "backup";
- excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/savebackup\\/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
+ excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("(^|.+/)savebackup/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
config.AddExcludeRecognizer(excludeRecogniser);
AssetProcessorManager_Test apm(&config); // note, this will 'push' the scan folders in to the db.
@@ -1791,13 +1791,8 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE((newfingerprintForPCAfterVersionChange != fingerprintForPC) || (newfingerprintForPCAfterVersionChange != newfingerprintForPC));//Fingerprints should be different
UNIT_TEST_EXPECT_TRUE((newfingerprintForANDROIDAfterVersionChange != fingerprintForANDROID) || (newfingerprintForANDROIDAfterVersionChange != newfingerprintForANDROID));//Fingerprints should be different
- //------Test for Files which are excluded
processResults.clear();
- absolutePath = AssetUtilities::NormalizeFilePath(tempPath.absoluteFilePath("subfolder3/savebackup/test.txt"));
- QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath));
- UNIT_TEST_EXPECT_FALSE(BlockUntil(idling, 3000)); //Processing a file that will be excluded should not cause assetprocessor manager to emit the onBecameIdle signal because its state should not change
- UNIT_TEST_EXPECT_TRUE(processResults.size() == 0);
-
+
// ------------- Test querying asset status -------------------
{
absolutePath = tempPath.absoluteFilePath("subfolder2/folder/ship.tiff");
diff --git a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp
index 9a60bf3110..bcadd0f103 100644
--- a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp
+++ b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp
@@ -1633,13 +1633,18 @@ namespace AssetProcessor
bool AssetProcessor::PlatformConfiguration::IsFileExcluded(QString fileName) const
{
- for (const ExcludeAssetRecognizer& excludeRecognizer : m_excludeAssetRecognizers)
+ QString relPath, scanFolderName;
+ if (ConvertToRelativePath(fileName, relPath, scanFolderName))
{
- if (excludeRecognizer.m_patternMatcher.MatchesPath(fileName.toUtf8().constData()))
+ for (const ExcludeAssetRecognizer& excludeRecognizer : m_excludeAssetRecognizers)
{
- return true;
+ if (excludeRecognizer.m_patternMatcher.MatchesPath(relPath.toUtf8().constData()))
+ {
+ return true;
+ }
}
}
+
return false;
}
diff --git a/Code/Tools/AssetProcessor/testdata/config_regular/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_regular/AssetProcessorPlatformConfig.setreg
index e4bee1b6de..11a77aefd4 100644
--- a/Code/Tools/AssetProcessor/testdata/config_regular/AssetProcessorPlatformConfig.setreg
+++ b/Code/Tools/AssetProcessor/testdata/config_regular/AssetProcessorPlatformConfig.setreg
@@ -50,10 +50,10 @@
"order": 6000
},
"Exclude HoldFiles": {
- "pattern": ".*\\\\/Levels\\\\/.*_hold\\\\/.*"
+ "pattern": "(^|.+/)Levels/.*_hold(/.*)?$"
},
"Exclude TempFiles": {
- "pattern": ".*\\\\/\\\\$tmp[0-9]*_.*"
+ "pattern": "(^|.+/)\\\\$tmp[0-9]*_.*"
},
"RC i_caf": {
"glob": "*.i_caf",
diff --git a/Code/Tools/AssetProcessor/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg
index c8fd13bf1f..a86a2168fd 100644
--- a/Code/Tools/AssetProcessor/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg
+++ b/Code/Tools/AssetProcessor/testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg
@@ -74,10 +74,10 @@
"include": "test"
},
"Exclude HoldFiles": {
- "pattern": ".*\\\\/Levels\\\\/.*_hold\\\\/.*"
+ "pattern": "(^|.+/)Levels/.*_hold(/.*)?$"
},
"Exclude TempFiles": {
- "pattern": ".*\\\\/\\\\$tmp[0-9]*_.*"
+ "pattern": "(^|.+/)\\\\$tmp[0-9]*_.*"
},
"RC i_caf": {
"glob": "*.i_caf",
diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss
index db0ec7e91a..fecdc67920 100644
--- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss
+++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss
@@ -691,6 +691,12 @@ QProgressBar::chunk {
#gemRepoAddDialogInstructionTitleLabel {
font-size:14px;
+ font-weight:bold;
+}
+
+#gemRepoAddDialogWarningLabel {
+ font-size:12px;
+ font-style:italic;
}
#addGemRepoDialog #formFrame {
diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp
index bc667db4b4..a22f41d054 100644
--- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp
+++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp
@@ -42,7 +42,9 @@ namespace O3DE::ProjectManager
m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel, m_downloadController);
vLayout->addWidget(m_headerWidget);
+ connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged);
connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo);
+ connect(m_headerWidget, &GemCatalogHeaderWidget::AddGem, this, &GemCatalogScreen::OnAddGemClicked);
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setMargin(0);
@@ -73,6 +75,7 @@ namespace O3DE::ProjectManager
m_notificationsView = AZStd::make_unique(this, AZ_CRC("GemCatalogNotificationsView"));
m_notificationsView->SetOffset(QPoint(10, 70));
+ m_notificationsView->SetMaxQueuedNotifications(1);
}
void GemCatalogScreen::ReinitForProject(const QString& projectPath)
@@ -94,48 +97,6 @@ namespace O3DE::ProjectManager
m_headerWidget->ReinitForProject();
connect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter);
- connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged);
- connect(
- m_headerWidget, &GemCatalogHeaderWidget::AddGem,
- [&]()
- {
- EngineInfo engineInfo;
- QString defaultPath;
-
- AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
- if (engineInfoResult.IsSuccess())
- {
- engineInfo = engineInfoResult.GetValue();
- defaultPath = engineInfo.m_defaultGemsFolder;
- }
-
- if (defaultPath.isEmpty())
- {
- defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
- }
-
- QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath));
- if (!directory.isEmpty())
- {
- // register the gem to the o3de_manifest.json and to the project after the user confirms
- // project creation/update
- auto registerResult = PythonBindingsInterface::Get()->RegisterGem(directory);
- if(!registerResult)
- {
- QMessageBox::critical(this, tr("Failed to add gem"), registerResult.GetError().c_str());
- }
- else
- {
- m_gemsToRegisterWithProject.insert(directory);
- AZ::Outcome gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(directory);
- if (gemInfoResult)
- {
- m_gemModel->AddGem(gemInfoResult.GetValue());
- m_gemModel->UpdateGemDependencies();
- }
- }
- }
- });
// Select the first entry after everything got correctly sized
QTimer::singleShot(200, [=]{
@@ -144,6 +105,46 @@ namespace O3DE::ProjectManager
});
}
+ void GemCatalogScreen::OnAddGemClicked()
+ {
+ EngineInfo engineInfo;
+ QString defaultPath;
+
+ AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
+ if (engineInfoResult.IsSuccess())
+ {
+ engineInfo = engineInfoResult.GetValue();
+ defaultPath = engineInfo.m_defaultGemsFolder;
+ }
+
+ if (defaultPath.isEmpty())
+ {
+ defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
+ }
+
+ QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath));
+ if (!directory.isEmpty())
+ {
+ // register the gem to the o3de_manifest.json and to the project after the user confirms
+ // project creation/update
+ auto registerResult = PythonBindingsInterface::Get()->RegisterGem(directory);
+ if(!registerResult)
+ {
+ QMessageBox::critical(this, tr("Failed to add gem"), registerResult.GetError().c_str());
+ }
+ else
+ {
+ m_gemsToRegisterWithProject.insert(directory);
+ AZ::Outcome gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(directory);
+ if (gemInfoResult)
+ {
+ m_gemModel->AddGem(gemInfoResult.GetValue());
+ m_gemModel->UpdateGemDependencies();
+ }
+ }
+ }
+ }
+
void GemCatalogScreen::OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies)
{
if (m_notificationsEnabled)
@@ -166,6 +167,10 @@ namespace O3DE::ProjectManager
{
notification += " " + tr("and") + " ";
}
+ if (added && GemModel::GetDownloadStatus(modelIndex) == GemInfo::DownloadStatus::NotDownloaded)
+ {
+ m_downloadController->AddGemDownload(GemModel::GetName(modelIndex));
+ }
}
if (numChangedDependencies == 1 )
@@ -174,7 +179,7 @@ namespace O3DE::ProjectManager
}
else if (numChangedDependencies > 1)
{
- notification += QString("%d Gem ").arg(numChangedDependencies) + tr("dependencies");
+ notification += QString("%1 Gem ").arg(numChangedDependencies) + tr("dependencies");
}
notification += " " + (added ? tr("activated") : tr("deactivated"));
diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h
index 8e9f31c710..1ade87af0c 100644
--- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h
+++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h
@@ -47,6 +47,7 @@ namespace O3DE::ProjectManager
public slots:
void OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies);
+ void OnAddGemClicked();
protected:
void hideEvent(QHideEvent* event) override;
diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp
index b425c15dee..4f737d8629 100644
--- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp
+++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp
@@ -225,21 +225,22 @@ namespace O3DE::ProjectManager
QVector elementNames;
QVector elementCounts;
const int totalGems = m_gemModel->rowCount();
- const int selectedGemTotal = m_gemModel->TotalAddedGems();
+ const int selectedGemTotal = m_gemModel->GatherGemsToBeAdded(/*includeDependencies=*/true).size();
+ const int unselectedGemTotal = m_gemModel->GatherGemsToBeRemoved(/*includeDependencies=*/true).size();
const int enabledGemTotal = m_gemModel->TotalAddedGems(/*includeDependencies=*/true);
- elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Unselected));
- elementCounts.push_back(totalGems - selectedGemTotal);
-
elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Selected));
elementCounts.push_back(selectedGemTotal);
- elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Inactive));
- elementCounts.push_back(totalGems - enabledGemTotal);
+ elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Unselected));
+ elementCounts.push_back(unselectedGemTotal);
elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Active));
elementCounts.push_back(enabledGemTotal);
+ elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Inactive));
+ elementCounts.push_back(totalGems - enabledGemTotal);
+
bool wasCollapsed = false;
if (m_statusFilter)
{
@@ -262,44 +263,51 @@ namespace O3DE::ProjectManager
const QList buttons = m_statusFilter->GetButtonGroup()->buttons();
- QAbstractButton* unselectedButton = buttons[0];
- QAbstractButton* selectedButton = buttons[1];
- unselectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Unselected);
- selectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Selected);
+ QAbstractButton* selectedButton = buttons[0];
+ QAbstractButton* unselectedButton = buttons[1];
+ selectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Selected);
+ unselectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Unselected);
auto updateGemSelection = [=]([[maybe_unused]] bool checked)
{
- if (unselectedButton->isChecked() && !selectedButton->isChecked())
- {
- m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Unselected);
- }
- else if (!unselectedButton->isChecked() && selectedButton->isChecked())
+ if (!unselectedButton->isChecked() && selectedButton->isChecked())
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Selected);
}
+ else if (unselectedButton->isChecked() && !selectedButton->isChecked())
+ {
+ m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Unselected);
+ }
else
{
- m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::NoFilter);
+ if (unselectedButton->isChecked() && selectedButton->isChecked())
+ {
+ m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Both);
+ }
+ else
+ {
+ m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::NoFilter);
+ }
}
};
connect(unselectedButton, &QAbstractButton::toggled, this, updateGemSelection);
connect(selectedButton, &QAbstractButton::toggled, this, updateGemSelection);
- QAbstractButton* inactiveButton = buttons[2];
- QAbstractButton* activeButton = buttons[3];
- inactiveButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Inactive);
- activeButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Active);
+ QAbstractButton* activeButton = buttons[2];
+ QAbstractButton* inactiveButton = buttons[3];
+ activeButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Active);
+ inactiveButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Inactive);
auto updateGemActive = [=]([[maybe_unused]] bool checked)
{
- if (inactiveButton->isChecked() && !activeButton->isChecked())
- {
- m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Inactive);
- }
- else if (!inactiveButton->isChecked() && activeButton->isChecked())
+ if (!inactiveButton->isChecked() && activeButton->isChecked())
{
m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Active);
}
+ else if (inactiveButton->isChecked() && !activeButton->isChecked())
+ {
+ m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Inactive);
+ }
else
{
m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::NoFilter);
diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp
index 199692f200..7ec45ac721 100644
--- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp
+++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp
@@ -50,11 +50,26 @@ namespace O3DE::ProjectManager
}
}
- // Gem selected
- if (m_gemSelectedFilter != GemSelected::NoFilter)
+ // Gem selected
+ if (m_gemSelectedFilter == GemSelected::Selected)
{
- const GemSelected sourceGemStatus = static_cast(GemModel::IsAdded(sourceIndex));
- if (m_gemSelectedFilter != sourceGemStatus)
+ if (!GemModel::NeedsToBeAdded(sourceIndex, true))
+ {
+ return false;
+ }
+ }
+ // Gem unselected
+ else if (m_gemSelectedFilter == GemSelected::Unselected)
+ {
+ if (!GemModel::NeedsToBeRemoved(sourceIndex, true))
+ {
+ return false;
+ }
+ }
+ // Gem selected or unselected
+ else if (m_gemSelectedFilter == GemSelected::Both)
+ {
+ if (!GemModel::NeedsToBeAdded(sourceIndex, true) && !GemModel::NeedsToBeRemoved(sourceIndex, true))
{
return false;
}
diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h
index 74b1e915eb..ab739e62f9 100644
--- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h
+++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h
@@ -29,7 +29,8 @@ namespace O3DE::ProjectManager
{
NoFilter = -1,
Unselected,
- Selected
+ Selected,
+ Both
};
enum class GemActive
{
diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.cpp
index 601c62d6e1..05a14b48d3 100644
--- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.cpp
+++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.cpp
@@ -27,6 +27,7 @@ namespace O3DE::ProjectManager
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setContentsMargins(30, 30, 25, 10);
vLayout->setSpacing(0);
+ vLayout->setAlignment(Qt::AlignTop);
setLayout(vLayout);
QLabel* instructionTitleLabel = new QLabel(tr("Enter a valid path to add a new user repository"));
@@ -41,9 +42,18 @@ namespace O3DE::ProjectManager
vLayout->addWidget(instructionContextLabel);
m_repoPath = new FormFolderBrowseEditWidget(tr("Repository Path"), "", this);
- m_repoPath->setFixedWidth(600);
+ m_repoPath->setFixedSize(QSize(600, 100));
vLayout->addWidget(m_repoPath);
+ vLayout->addSpacing(10);
+
+ QLabel* warningLabel = new QLabel(tr("Online repositories may contain files that could potentially harm your computer,"
+ " please ensure you understand the risks before downloading Gems from third-party sources."));
+ warningLabel->setObjectName("gemRepoAddDialogWarningLabel");
+ warningLabel->setWordWrap(true);
+ warningLabel->setAlignment(Qt::AlignLeft);
+ vLayout->addWidget(warningLabel);
+
vLayout->addSpacing(40);
QDialogButtonBox* dialogButtons = new QDialogButtonBox();
diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp
index 314765def0..fd8fbc970a 100644
--- a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp
+++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp
@@ -133,6 +133,11 @@ namespace O3DE::ProjectManager
return true;
}
+ else
+ {
+ // If we are already on this screen still notify we are on this screen to refresh it
+ newScreen->NotifyCurrentScreen();
+ }
}
return false;
diff --git a/Code/Tools/SceneAPI/SceneCore/DllMain.cpp b/Code/Tools/SceneAPI/SceneCore/DllMain.cpp
index ad875f5538..51f081d8fa 100644
--- a/Code/Tools/SceneAPI/SceneCore/DllMain.cpp
+++ b/Code/Tools/SceneAPI/SceneCore/DllMain.cpp
@@ -187,6 +187,7 @@ namespace AZ
// Register utilities
AZ::SceneAPI::SceneCore::PatternMatcher::Reflect(context);
+ AZ::SceneAPI::Utilities::DebugSceneGraph::Reflect(context);
}
}
diff --git a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp
index de727498fc..41a030760e 100644
--- a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp
+++ b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp
@@ -10,6 +10,7 @@
#include
#include
+#include
#include
#include
#include
@@ -20,6 +21,36 @@
namespace AZ::SceneAPI::Utilities
{
+ void DebugNode::Reflect(AZ::ReflectContext* context)
+ {
+ AZ::SerializeContext* serialize = azrtti_cast(context);
+
+ if (serialize)
+ {
+ serialize->Class()
+ ->Field("Name", &DebugNode::m_name)
+ ->Field("Path", &DebugNode::m_path)
+ ->Field("Type", &DebugNode::m_type)
+ ->Field("Data", &DebugNode::m_data);
+ }
+ }
+
+ void DebugSceneGraph::Reflect(AZ::ReflectContext* context)
+ {
+ DebugNode::Reflect(context);
+
+ AZ::SerializeContext* serialize = azrtti_cast(context);
+
+ if (serialize)
+ {
+ serialize->Class()
+ ->Field("Version", &DebugSceneGraph::m_version)
+ ->Field("ProductName", &DebugSceneGraph::m_productName)
+ ->Field("SceneName", &DebugSceneGraph::m_sceneName)
+ ->Field("Nodes", &DebugSceneGraph::m_nodes);
+ }
+ }
+
void DebugOutput::Write(const char* name, const char* data)
{
m_output += AZStd::string::format("\t%s: %s\n", name, data);
@@ -38,21 +69,29 @@ namespace AZ::SceneAPI::Utilities
void DebugOutput::Write(const char* name, const AZStd::string& data)
{
Write(name, data.c_str());
+
+ AddToNode(name, data);
}
void DebugOutput::Write(const char* name, double data)
{
m_output += AZStd::string::format("\t%s: %f\n", name, data);
+
+ AddToNode(name, data);
}
void DebugOutput::Write(const char* name, uint64_t data)
{
m_output += AZStd::string::format("\t%s: %" PRIu64 "\n", name, data);
+
+ AddToNode(name, data);
}
void DebugOutput::Write(const char* name, int64_t data)
{
m_output += AZStd::string::format("\t%s: %" PRId64 "\n", name, data);
+
+ AddToNode(name, data);
}
void DebugOutput::Write(const char* name, const DataTypes::MatrixType& data)
@@ -63,6 +102,7 @@ namespace AZ::SceneAPI::Utilities
AZ::Vector3 translation{};
data.GetBasisAndTranslation(&basisX, &basisY, &basisZ, &translation);
+ m_pauseNodeData = true;
m_output += AZStd::string::format("\t%s:\n", name);
m_output += "\t";
@@ -76,16 +116,23 @@ namespace AZ::SceneAPI::Utilities
m_output += "\t";
Write("Transl", translation);
+ m_pauseNodeData = false;
+
+ AddToNode(name, data);
}
void DebugOutput::Write(const char* name, bool data)
{
m_output += AZStd::string::format("\t%s: %s\n", name, data ? "true" : "false");
+
+ AddToNode(name, data);
}
void DebugOutput::Write(const char* name, Vector3 data)
{
m_output += AZStd::string::format("\t%s: <% f, % f, % f>\n", name, data.GetX(), data.GetY(), data.GetZ());
+
+ AddToNode(name, data);
}
void DebugOutput::Write(const char* name, AZStd::optional data)
@@ -128,6 +175,11 @@ namespace AZ::SceneAPI::Utilities
return m_output;
}
+ DebugNode DebugOutput::GetDebugNode() const
+ {
+ return m_currentNode;
+ }
+
void WriteAndLog(AZ::IO::SystemFile& dbgFile, const char* strToWrite)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "%s", strToWrite);
@@ -137,7 +189,6 @@ namespace AZ::SceneAPI::Utilities
void DebugOutput::BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr& scene, AZStd::string productName)
{
- const int debugSceneGraphVersion = 1;
AZStd::string debugSceneFile;
AzFramework::StringFunc::Path::ConstructFull(outputFolder, productName.c_str(), debugSceneFile);
@@ -147,7 +198,7 @@ namespace AZ::SceneAPI::Utilities
if (dbgFile.Open(debugSceneFile.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
{
WriteAndLog(dbgFile, AZStd::string::format("ProductName: %s", productName.c_str()).c_str());
- WriteAndLog(dbgFile, AZStd::string::format("debugSceneGraphVersion: %d", debugSceneGraphVersion).c_str());
+ WriteAndLog(dbgFile, AZStd::string::format("debugSceneGraphVersion: %d", SceneGraphVersion).c_str());
WriteAndLog(dbgFile, scene->GetName().c_str());
const AZ::SceneAPI::Containers::SceneGraph& sceneGraph = scene->GetGraph();
@@ -158,6 +209,11 @@ namespace AZ::SceneAPI::Utilities
AZ::SceneAPI::Containers::Views::BreadthFirst>(
sceneGraph, sceneGraph.GetRoot(), pairView.cbegin(), true);
+ DebugSceneGraph debugSceneGraph;
+ debugSceneGraph.m_version = SceneGraphVersion;
+ debugSceneGraph.m_productName = productName;
+ debugSceneGraph.m_sceneName = scene->GetName().c_str();
+
for (auto&& viewIt : view)
{
if (viewIt.second == nullptr)
@@ -170,20 +226,31 @@ namespace AZ::SceneAPI::Utilities
WriteAndLog(dbgFile, AZStd::string::format("Node Name: %s", viewIt.first.GetName()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("Node Path: %s", viewIt.first.GetPath()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("Node Type: %s", graphObject->RTTI_GetTypeName()).c_str());
-
- AZ::SceneAPI::Utilities::DebugOutput debugOutput;
+
+ AZ::SceneAPI::Utilities::DebugOutput debugOutput(
+ DebugNode(viewIt.first.GetName(), viewIt.first.GetPath(), graphObject->RTTI_GetTypeName()));
+
viewIt.second->GetDebugOutput(debugOutput);
if (!debugOutput.GetOutput().empty())
{
WriteAndLog(dbgFile, debugOutput.GetOutput().c_str());
}
+
+ debugSceneGraph.m_nodes.push_back(debugOutput.GetDebugNode());
}
dbgFile.Close();
+ Utils::SaveObjectToFile((debugSceneFile + ".xml").c_str(), DataStream::StreamType::ST_XML, &debugSceneGraph);
+
static const AZ::Data::AssetType dbgSceneGraphAssetType("{07F289D1-4DC7-4C40-94B4-0A53BBCB9F0B}");
productList.AddProduct(productName, AZ::Uuid::CreateName(productName.c_str()), dbgSceneGraphAssetType,
AZStd::nullopt, AZStd::nullopt);
+
+ static const AZ::Data::AssetType dbgSceneGraphXmlAssetType("{51F37614-0D77-4F36-9AC6-7ED70A0AC868}");
+ productList.AddProduct(
+ (productName + ".xml"), AZ::Uuid::CreateName((productName + ".xml").c_str()), dbgSceneGraphXmlAssetType,
+ AZStd::nullopt, AZStd::nullopt);
}
}
}
diff --git a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h
index e05d10e2fa..533fb03a67 100644
--- a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h
+++ b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
namespace AZ
{
@@ -34,15 +35,63 @@ namespace AZ
namespace AZ::SceneAPI::Utilities
{
+ constexpr int SceneGraphVersion = 1;
+
+ struct DebugNode
+ {
+ AZ_TYPE_INFO(DebugNode, "{490B9D4C-1847-46EB-BEBC-49812E104626}");
+
+ AZStd::string m_name;
+ AZStd::string m_path;
+ AZStd::string m_type;
+
+ DebugNode() = default;
+
+ DebugNode(AZStd::string name, AZStd::string path, AZStd::string type)
+ : m_name(AZStd::move(name)),
+ m_path(AZStd::move(path)),
+ m_type(AZStd::move(type))
+ {
+ }
+
+ static void Reflect(AZ::ReflectContext* context);
+
+ using DataItem = AZStd::pair;
+ AZStd::vector m_data;
+ };
+
+ struct DebugSceneGraph
+ {
+ AZ_TYPE_INFO(DebugSceneGraph, "{375F6558-5709-409F-881E-8ED575D56C92}");
+
+ int m_version = SceneGraphVersion;
+ AZStd::string m_productName;
+ AZStd::string m_sceneName;
+ AZStd::vector m_nodes;
+
+ static void Reflect(AZ::ReflectContext* context);
+ };
+
class DebugOutput
{
public:
+ DebugOutput(DebugNode node) : m_currentNode(AZStd::move(node)){}
+
template
void Write(const char* name, const AZStd::vector& data);
template
void Write(const char* name, const AZStd::vector>& data);
+ template
+ void AddToNode(const char* name, const T& data)
+ {
+ if (!m_pauseNodeData)
+ {
+ m_currentNode.m_data.emplace_back(name, AZStd::make_any>(data));
+ }
+ }
+
SCENE_CORE_API void Write(const char* name, const char* data);
SCENE_CORE_API void WriteArray(const char* name, const unsigned int* data, int size);
SCENE_CORE_API void Write(const char* name, const AZStd::string& data);
@@ -57,11 +106,15 @@ namespace AZ::SceneAPI::Utilities
SCENE_CORE_API void Write(const char* name, AZStd::optional data);
SCENE_CORE_API const AZStd::string& GetOutput() const;
+ SCENE_CORE_API DebugNode GetDebugNode() const;
SCENE_CORE_API static void BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr& scene, AZStd::string productName);
protected:
AZStd::string m_output;
+ DebugSceneGraph m_graph;
+ DebugNode m_currentNode;
+ bool m_pauseNodeData = false; // If true, don't append any data to the DebugNode. Useful when a Write function calls other Write functions
};
}
diff --git a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.inl b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.inl
index a139b4bce2..bba1d42ee9 100644
--- a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.inl
+++ b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.inl
@@ -13,7 +13,11 @@ namespace AZ::SceneAPI::Utilities
template
void DebugOutput::Write(const char* name, const AZStd::vector& data)
{
- m_output += AZStd::string::format("\t%s: Count %zu. Hash: %zu\n", name, data.size(), AZStd::hash_range(data.begin(), data.end()));
+ size_t hash = AZStd::hash_range(data.begin(), data.end());
+ m_output += AZStd::string::format("\t%s: Count %zu. Hash: %zu\n", name, data.size(), hash);
+
+ AddToNode(AZStd::string::format("%s - Count", name).c_str(), data.size());
+ AddToNode(AZStd::string::format("%s - Hash", name).c_str(), hash);
}
template
@@ -27,5 +31,9 @@ namespace AZ::SceneAPI::Utilities
}
m_output += AZStd::string::format("\t%s: Count %zu. Hash: %zu\n", name, data.size(), hash);
+
+ AddToNode(AZStd::string::format("%s - Count", name).c_str(), data.size());
+ AddToNode(AZStd::string::format("%s - Hash", name).c_str(), hash);
}
+
}
diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp
index be72de555e..bc147fe7f6 100644
--- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp
+++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp
@@ -108,7 +108,7 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithoutClientSe
MatchmakingNotificationsHandlerMock matchmakingHandlerMock;
AZ_TEST_START_TRACE_SUPPRESSION;
m_gameliftClientTicketTracker->StartPolling("ticket1", "player1");
- WaitForProcessFinish([](){ return ::UnitTest::TestRunner::Instance().m_numAssertsFailed == 1; });
+ WaitForProcessFinish([&](){ return matchmakingHandlerMock.m_numMatchError == 1; });
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
ASSERT_TRUE(matchmakingHandlerMock.m_numMatchError == 1);
ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle());
@@ -122,7 +122,7 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_MultipleCallsWithou
AZ_TEST_START_TRACE_SUPPRESSION;
m_gameliftClientTicketTracker->StartPolling("ticket1", "player1");
m_gameliftClientTicketTracker->StartPolling("ticket1", "player1");
- WaitForProcessFinish([](){ return ::UnitTest::TestRunner::Instance().m_numAssertsFailed == 1; });
+ WaitForProcessFinish([&](){ return matchmakingHandlerMock.m_numMatchError == 1; });
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
ASSERT_TRUE(matchmakingHandlerMock.m_numMatchError == 1);
ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle());
@@ -140,7 +140,7 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButWithFailedOu
MatchmakingNotificationsHandlerMock matchmakingHandlerMock;
AZ_TEST_START_TRACE_SUPPRESSION;
m_gameliftClientTicketTracker->StartPolling("ticket1", "player1");
- WaitForProcessFinish([](){ return ::UnitTest::TestRunner::Instance().m_numAssertsFailed == 1; });
+ WaitForProcessFinish([&](){ return matchmakingHandlerMock.m_numMatchError == 1; });
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
ASSERT_TRUE(matchmakingHandlerMock.m_numMatchError == 1);
ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle());
@@ -160,7 +160,7 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithMoreThanOne
MatchmakingNotificationsHandlerMock matchmakingHandlerMock;
AZ_TEST_START_TRACE_SUPPRESSION;
m_gameliftClientTicketTracker->StartPolling("ticket1", "player1");
- WaitForProcessFinish([](){ return ::UnitTest::TestRunner::Instance().m_numAssertsFailed == 1; });
+ WaitForProcessFinish([&](){ return matchmakingHandlerMock.m_numMatchError == 1; });
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
ASSERT_TRUE(matchmakingHandlerMock.m_numMatchError == 1);
ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle());
diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp
index 56f2fdec62..e41c04a0be 100644
--- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp
+++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp
@@ -23,6 +23,7 @@
#include
#include
+#include
#include
#include
@@ -91,19 +92,31 @@ namespace AZ
m_shaderAssetBuilder.BusConnect(shaderAssetBuilderDescriptor.m_busId);
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, shaderAssetBuilderDescriptor);
- // Register Shader Variant Asset Builder
- AssetBuilderSDK::AssetBuilderDesc shaderVariantAssetBuilderDescriptor;
- shaderVariantAssetBuilderDescriptor.m_name = "Shader Variant Asset Builder";
- // Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update
- // ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder".
- shaderVariantAssetBuilderDescriptor.m_version = 26; // [AZSL] Changing inlineConstant to rootConstant keyword work.
- shaderVariantAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
- shaderVariantAssetBuilderDescriptor.m_busId = azrtti_typeid();
- shaderVariantAssetBuilderDescriptor.m_createJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::CreateJobs, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
- shaderVariantAssetBuilderDescriptor.m_processJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::ProcessJob, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
+ // If, either the SettingsRegistry doesn't exist, or the property @EnableShaderVariantAssetBuilderRegistryKey is not found,
+ // the default is to enable the ShaderVariantAssetBuilder.
+ m_enableShaderVariantAssetBuilder = true;
+ auto settingsRegistry = AZ::SettingsRegistry::Get();
+ if (settingsRegistry)
+ {
+ settingsRegistry->Get(m_enableShaderVariantAssetBuilder, EnableShaderVariantAssetBuilderRegistryKey);
+ }
- m_shaderVariantAssetBuilder.BusConnect(shaderVariantAssetBuilderDescriptor.m_busId);
- AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, shaderVariantAssetBuilderDescriptor);
+ if (m_enableShaderVariantAssetBuilder)
+ {
+ // Register Shader Variant Asset Builder
+ AssetBuilderSDK::AssetBuilderDesc shaderVariantAssetBuilderDescriptor;
+ shaderVariantAssetBuilderDescriptor.m_name = "Shader Variant Asset Builder";
+ // Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update
+ // ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder".
+ shaderVariantAssetBuilderDescriptor.m_version = 26; // [AZSL] Changing inlineConstant to rootConstant keyword work.
+ shaderVariantAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
+ shaderVariantAssetBuilderDescriptor.m_busId = azrtti_typeid();
+ shaderVariantAssetBuilderDescriptor.m_createJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::CreateJobs, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
+ shaderVariantAssetBuilderDescriptor.m_processJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::ProcessJob, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
+
+ m_shaderVariantAssetBuilder.BusConnect(shaderVariantAssetBuilderDescriptor.m_busId);
+ AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, shaderVariantAssetBuilderDescriptor);
+ }
// Register Precompiled Shader Builder
AssetBuilderSDK::AssetBuilderDesc precompiledShaderBuilderDescriptor;
@@ -121,7 +134,10 @@ namespace AZ
void AzslShaderBuilderSystemComponent::Deactivate()
{
m_shaderAssetBuilder.BusDisconnect();
- m_shaderVariantAssetBuilder.BusDisconnect();
+ if (m_enableShaderVariantAssetBuilder)
+ {
+ m_shaderVariantAssetBuilder.BusDisconnect();
+ }
m_precompiledShaderBuilder.BusDisconnect();
RHI::ShaderPlatformInterfaceRegisterBus::Handler::BusDisconnect();
diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.h b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.h
index 9ff5bd8282..f502e4c329 100644
--- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.h
+++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.h
@@ -61,7 +61,17 @@ namespace AZ
private:
ShaderAssetBuilder m_shaderAssetBuilder;
+
+ // The ShaderVariantAssetBuilder can be disabled with this registry key.
+ // By default it is enabled. A user might want to disable it when doing look development
+ // work with shaders or doing lots of iterative changes to shaders. In these cases
+ // GPU performance doesn't matter at all so it is important to not waste time
+ // building ShaderVariantAssets (Other than the Root ShaderVariantAsset, of course.).
+ static constexpr char EnableShaderVariantAssetBuilderRegistryKey[] = "/O3DE/Atom/Shaders/BuildVariants";
+ bool m_enableShaderVariantAssetBuilder = true;
+
ShaderVariantAssetBuilder m_shaderVariantAssetBuilder;
+
PrecompiledShaderBuilder m_precompiledShaderBuilder;
/// Contains the ShaderPlatformInterface for all registered RHIs
diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp
index a0205efa72..89e202a4bc 100644
--- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp
+++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp
@@ -477,8 +477,8 @@ namespace AZ
preprocessorOptions.m_predefinedMacros.end(), macroDefinitionsToAdd.begin(), macroDefinitionsToAdd.end());
// Run the preprocessor.
PreprocessorData output;
- PreprocessFile(prependedAzslFilePath, output, preprocessorOptions, true, true);
- RHI::ReportErrorMessages(ShaderAssetBuilderName, output.diagnostics);
+ const bool preprocessorSuccess = PreprocessFile(prependedAzslFilePath, output, preprocessorOptions, true, true);
+ RHI::ReportMessages(ShaderAssetBuilderName, output.diagnostics, !preprocessorSuccess);
// Dump the preprocessed string as a flat AZSL file with extension .azslin, which will be given to AZSLc to generate the HLSL file.
AZStd::string superVariantAzslinStemName = shaderFileName;
if (!supervariantInfo.m_name.IsEmpty())
diff --git a/Gems/Atom/Asset/Shader/Registry/atom_shaders.setreg b/Gems/Atom/Asset/Shader/Registry/atom_shaders.setreg
new file mode 100644
index 0000000000..31d108f47a
--- /dev/null
+++ b/Gems/Atom/Asset/Shader/Registry/atom_shaders.setreg
@@ -0,0 +1,10 @@
+{
+ "O3DE": {
+ "Atom": {
+ "Shaders": {
+ "BuildVariants": true
+ }
+ }
+ }
+ }
+}
diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp
index 8f2ff264e5..3f5761270a 100644
--- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp
+++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
#include
@@ -115,7 +116,9 @@ namespace AZ
{
// GFX TODO - investigate window creation being part of the GameApplication.
- m_nativeWindow = AZStd::make_unique("O3DELauncher", AzFramework::WindowGeometry(0, 0, 1920, 1080));
+ auto projectTitle = AZ::Utils::GetProjectName();
+
+ m_nativeWindow = AZStd::make_unique(projectTitle.c_str(), AzFramework::WindowGeometry(0, 0, 1920, 1080));
AZ_Assert(m_nativeWindow, "Failed to create the game window\n");
m_nativeWindow->Activate();
diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype
index f4bcfb2673..d36213694b 100644
--- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype
+++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype
@@ -1,6 +1,14 @@
{
"description": "Material Type with properties used to define Enhanced PBR, a metallic-roughness Physically-Based Rendering (PBR) material shading model, with advanced features like subsurface scattering, transmission, and anisotropy.",
- "version": 3,
+ "version": 4,
+ "versionUpdates": [
+ {
+ "toVersion": 4,
+ "actions": [
+ {"op": "rename", "from": "opacity.doubleSided", "to": "general.doubleSided"}
+ ]
+ }
+ ],
"propertyLayout": {
"groups": [
{
@@ -92,6 +100,12 @@
],
"properties": {
"general": [
+ {
+ "name": "doubleSided",
+ "displayName": "Double-sided",
+ "description": "Whether to render back-faces or just front-faces.",
+ "type": "Bool"
+ },
{
"name": "applySpecularAA",
"displayName": "Apply Specular AA",
@@ -709,12 +723,6 @@
"name": "m_opacityFactor"
}
},
- {
- "name": "doubleSided",
- "displayName": "Double-sided",
- "description": "Whether to render back-faces or just front-faces.",
- "type": "Bool"
- },
{
"name": "alphaAffectsSpecular",
"displayName": "Alpha affects specular",
diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype
index 7527a7658a..e0b1949058 100644
--- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype
+++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype
@@ -1,6 +1,14 @@
{
"description": "Material Type with properties used to define Standard PBR, a metallic-roughness Physically-Based Rendering (PBR) material shading model.",
- "version": 3,
+ "version": 4,
+ "versionUpdates": [
+ {
+ "toVersion": 4,
+ "actions": [
+ {"op": "rename", "from": "opacity.doubleSided", "to": "general.doubleSided"}
+ ]
+ }
+ ],
"propertyLayout": {
"groups": [
{
@@ -72,6 +80,12 @@
],
"properties": {
"general": [
+ {
+ "name": "doubleSided",
+ "displayName": "Double-sided",
+ "description": "Whether to render back-faces or just front-faces.",
+ "type": "Bool"
+ },
{
"name": "applySpecularAA",
"displayName": "Apply Specular AA",
@@ -650,12 +664,6 @@
"name": "m_opacityFactor"
}
},
- {
- "name": "doubleSided",
- "displayName": "Double-sided",
- "description": "Whether to render back-faces or just front-faces.",
- "type": "Bool"
- },
{
"name": "alphaAffectsSpecular",
"displayName": "Alpha affects specular",
diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua
index 2382f3f0f0..9584698532 100644
--- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua
+++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua
@@ -10,14 +10,14 @@
----------------------------------------------------------------------------------------------------
function GetMaterialPropertyDependencies()
- return {"opacity.doubleSided"}
+ return {"general.doubleSided"}
end
ForwardPassIndex = 0
ForwardPassEdsIndex = 1
function Process(context)
- local doubleSided = context:GetMaterialPropertyValue_bool("opacity.doubleSided")
+ local doubleSided = context:GetMaterialPropertyValue_bool("general.doubleSided")
local lastShader = context:GetShaderCount() - 1;
if(doubleSided) then
diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua
index c86fbfe0b7..9315131e44 100644
--- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua
+++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua
@@ -81,7 +81,6 @@ function ProcessEditor(context)
context:SetMaterialPropertyVisibility("opacity.textureMap", mainVisibility)
context:SetMaterialPropertyVisibility("opacity.textureMapUv", mainVisibility)
context:SetMaterialPropertyVisibility("opacity.factor", mainVisibility)
- context:SetMaterialPropertyVisibility("opacity.doubleSided", mainVisibility)
if(opacityMode == OpacityMode_Blended or opacityMode == OpacityMode_TintedTransparent) then
context:SetMaterialPropertyVisibility("opacity.alphaAffectsSpecular", MaterialPropertyVisibility_Enabled)
diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/ScreenSpace/DeferredFog.shader b/Gems/Atom/Feature/Common/Assets/Shaders/ScreenSpace/DeferredFog.shader
index 5e2ca9bff5..06c991400e 100644
--- a/Gems/Atom/Feature/Common/Assets/Shaders/ScreenSpace/DeferredFog.shader
+++ b/Gems/Atom/Feature/Common/Assets/Shaders/ScreenSpace/DeferredFog.shader
@@ -23,8 +23,8 @@
"DrawList" : "forward",
"CompilerHints" : {
- "DxcDisableOptimizations" : false,
- "DxcGenerateDebugInfo" : false
+ "DisableOptimizations" : false,
+ "GenerateDebugInfo" : false
},
"ProgramSettings":
diff --git a/Gems/Atom/Feature/Common/Assets/Textures/Default/checker_basecolor.tif b/Gems/Atom/Feature/Common/Assets/Textures/Default/checker_basecolor.tif
new file mode 100644
index 0000000000..5abe5bbd49
--- /dev/null
+++ b/Gems/Atom/Feature/Common/Assets/Textures/Default/checker_basecolor.tif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:57d6744696768f9fb8a5fe5fee9aa36fee1eb87a9dbc1e60d4a35ed3c39d68e6
+size 810620
diff --git a/Gems/Atom/Feature/Common/Assets/Textures/Default/checker_uv_basecolor.png b/Gems/Atom/Feature/Common/Assets/Textures/Default/checker_uv_basecolor.png
new file mode 100644
index 0000000000..07e240baf9
--- /dev/null
+++ b/Gems/Atom/Feature/Common/Assets/Textures/Default/checker_uv_basecolor.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:513f47f6fea5105f603170a8881b7e3b1cd2c4258636d64a6399c725032b500d
+size 38689
diff --git a/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake b/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake
index 7c594fc945..7a325ca97e 100644
--- a/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake
+++ b/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake
@@ -5,12 +5,3 @@
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
-
-if(LY_MONOLITHIC_GAME) # Do not use OpenImageIO in monolithic game
- return()
-endif()
-
-set(LY_BUILD_DEPENDENCIES
- PRIVATE
- 3rdParty::ilmbase
-)
diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h
index d0960a7fde..83a868ab65 100644
--- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h
+++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h
@@ -57,12 +57,19 @@ namespace AZ
AZStd::string m_azslcAdditionalFreeArguments;
// note: if you add new sort of arguments here, don't forget to update HasDifferentAzslcArguments()
- //! DXC
- bool m_dxcDisableWarnings = false;
- bool m_dxcWarningAsError = false;
- bool m_dxcDisableOptimizations = false;
- bool m_dxcGenerateDebugInfo = false;
- uint8_t m_dxcOptimizationLevel = LevelUnset;
+ //! Remark: To the user, the following parameters are exposed without the
+ //! "Dxc" prefix because these are common options for the "main" compiler
+ //! for the given RHI. At the moment the only "main" compiler is Dxc, but in
+ //! the future AZSLc may transpile from AZSL to some other proprietary language
+ //! and in that case the "main" compiler won't be DXC
+ bool m_disableWarnings = false;
+ bool m_warningAsError = false;
+ bool m_disableOptimizations = false;
+ bool m_generateDebugInfo = false;
+ uint8_t m_optimizationLevel = LevelUnset;
+ //! "DxcAdditionalFreeArguments" keeps the "Dxc" prefix because these arguments
+ //! are specific to DXC, and it will be relevant only if DXC is the "main" compiler
+ //! for a given RHI, otherwise this parameter won't matter.
AZStd::string m_dxcAdditionalFreeArguments;
//! both
diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h
index 0624dce5f8..d648f05b46 100644
--- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h
+++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h
@@ -83,11 +83,12 @@ namespace AZ
const AZStd::string& shaderSourcePathForDebug,
const char* toolNameForLog);
- //! Reports error messages to AZ_Error and/or AZ_Warning, given a text blob that potentially contains many lines of errors and warnings.
- //! @param window Debug window name used for AZ Trace functions
- //! @param errorMessages String that may contain many lines of errors and warnings
- //! @param return true if Errors were detected and reported (Warnings don't count)
- bool ReportErrorMessages(AZStd::string_view window, AZStd::string_view errorMessages);
+ //! Reports messages with AZ_Error or AZ_Warning (See @reportAsErrors).
+ //! @param window Debug window name used for AZ Trace functions.
+ //! @param errorMessages Message string.
+ //! @param reportAsErrors If true, messages are traced with AZ_Error, otherwise AZ_Warning is used.
+ //! @returns true If the input text blob contains at least one line with the "error" string.
+ bool ReportMessages(AZStd::string_view window, AZStd::string_view errorMessages, bool reportAsErrors);
//! Converts from a RHI::ShaderHardwareStage to an RHI::ShaderStage
ShaderStage ToRHIShaderStage(ShaderHardwareStage stageType);
diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp
index ef330e7902..5411e6655e 100644
--- a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp
+++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp
@@ -33,17 +33,17 @@ namespace AZ
RegisterEnumerators(serializeContext);
serializeContext->Class()
- ->Version(2)
+ ->Version(3)
->Field("AzslcWarningLevel", &ShaderCompilerArguments::m_azslcWarningLevel)
->Field("AzslcWarningAsError", &ShaderCompilerArguments::m_azslcWarningAsError)
->Field("AzslcAdditionalFreeArguments", &ShaderCompilerArguments::m_azslcAdditionalFreeArguments)
- ->Field("DxcDisableWarnings", &ShaderCompilerArguments::m_dxcDisableWarnings)
- ->Field("DxcWarningAsError", &ShaderCompilerArguments::m_dxcWarningAsError)
- ->Field("DxcDisableOptimizations", &ShaderCompilerArguments::m_dxcDisableOptimizations)
- ->Field("DxcGenerateDebugInfo", &ShaderCompilerArguments::m_dxcGenerateDebugInfo)
- ->Field("DxcOptimizationLevel", &ShaderCompilerArguments::m_dxcOptimizationLevel)
- ->Field("DxcAdditionalFreeArguments", &ShaderCompilerArguments::m_dxcAdditionalFreeArguments)
+ ->Field("DisableWarnings", &ShaderCompilerArguments::m_disableWarnings)
+ ->Field("WarningAsError", &ShaderCompilerArguments::m_warningAsError)
+ ->Field("DisableOptimizations", &ShaderCompilerArguments::m_disableOptimizations)
+ ->Field("GenerateDebugInfo", &ShaderCompilerArguments::m_generateDebugInfo)
+ ->Field("OptimizationLevel", &ShaderCompilerArguments::m_optimizationLevel)
->Field("DefaultMatrixOrder", &ShaderCompilerArguments::m_defaultMatrixOrder)
+ ->Field("DxcAdditionalFreeArguments", &ShaderCompilerArguments::m_dxcAdditionalFreeArguments)
;
}
}
@@ -62,13 +62,13 @@ namespace AZ
}
m_azslcWarningAsError = m_azslcWarningAsError || right.m_azslcWarningAsError;
m_azslcAdditionalFreeArguments = CommandLineArgumentUtils::MergeCommandLineArguments(m_azslcAdditionalFreeArguments, right.m_azslcAdditionalFreeArguments);
- m_dxcDisableWarnings = m_dxcDisableWarnings || right.m_dxcDisableWarnings;
- m_dxcWarningAsError = m_dxcWarningAsError || right.m_dxcWarningAsError;
- m_dxcDisableOptimizations = m_dxcDisableOptimizations || right.m_dxcDisableOptimizations;
- m_dxcGenerateDebugInfo = m_dxcGenerateDebugInfo || right.m_dxcGenerateDebugInfo;
- if (right.m_dxcOptimizationLevel != LevelUnset)
+ m_disableWarnings = m_disableWarnings || right.m_disableWarnings;
+ m_warningAsError = m_warningAsError || right.m_warningAsError;
+ m_disableOptimizations = m_disableOptimizations || right.m_disableOptimizations;
+ m_generateDebugInfo = m_generateDebugInfo || right.m_generateDebugInfo;
+ if (right.m_optimizationLevel != LevelUnset)
{
- m_dxcOptimizationLevel = right.m_dxcOptimizationLevel;
+ m_optimizationLevel = right.m_optimizationLevel;
}
m_dxcAdditionalFreeArguments = CommandLineArgumentUtils::MergeCommandLineArguments(m_dxcAdditionalFreeArguments, right.m_dxcAdditionalFreeArguments);
if (right.m_defaultMatrixOrder != MatrixOrder::Default)
@@ -131,21 +131,21 @@ namespace AZ
AZStd::string ShaderCompilerArguments::MakeAdditionalDxcCommandLineString() const
{
AZStd::string arguments;
- if (m_dxcDisableWarnings)
+ if (m_disableWarnings)
{
arguments += " -no-warnings";
}
- else if (m_dxcWarningAsError)
+ else if (m_warningAsError)
{
arguments += " -WX";
}
- if (m_dxcDisableOptimizations)
+ if (m_disableOptimizations)
{
arguments += " -Od";
}
- else if (m_dxcOptimizationLevel <= 3)
+ else if (m_optimizationLevel <= 3)
{
- arguments = " -O" + AZStd::to_string(m_dxcOptimizationLevel);
+ arguments = " -O" + AZStd::to_string(m_optimizationLevel);
}
if (m_defaultMatrixOrder == MatrixOrder::Column)
{
diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp
index b4c68b3f75..dc203ef731 100644
--- a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp
+++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp
@@ -330,7 +330,7 @@ namespace AZ
// Pump one last time to make sure the streams have been flushed
pumpOuputStreams();
- const bool reportedErrors = ReportErrorMessages(toolNameForLog, errorMessages);
+ const bool reportedErrors = ReportMessages(toolNameForLog, errorMessages, exitCode != 0);
if (timedOut)
{
@@ -367,32 +367,20 @@ namespace AZ
return true;
}
- bool ReportErrorMessages([[maybe_unused]] AZStd::string_view window, AZStd::string_view errorMessages)
+ bool ReportMessages([[maybe_unused]] AZStd::string_view window, AZStd::string_view errorMessages, bool reportAsErrors)
{
- // There are more efficient ways to do this, but this approach is simple and gets us moving for now.
- AZStd::vector lines;
- AzFramework::StringFunc::Tokenize(errorMessages.data(), lines, "\n\r");
-
- bool foundErrors = false;
-
- for (auto& line : lines)
+ if (reportAsErrors)
{
- if (AZStd::string::npos != AzFramework::StringFunc::Find(line, "error"))
- {
- AZ_Error(window.data(), false, "%s", line.data());
- foundErrors = true;
- }
- else if (AZStd::string::npos != AzFramework::StringFunc::Find(line, "warning"))
- {
- AZ_Warning(window.data(), false, "%s", line.data());
- }
- else
- {
- AZ_TracePrintf(window.data(), "%s", line.data());
- }
+ AZ_Error(window.data(), false, "%.*s", aznumeric_cast(errorMessages.size()), errorMessages.data());
}
-
- return foundErrors;
+ else
+ {
+ // Using AZ_Warning instead of AZ_TracePrintf because this function is commonly
+ // used to report messages from stderr when executing applications. Applications
+ // when ran successfully, only output to stderr for errors or warnings.
+ AZ_Warning(window.data(), false, "%.*s", aznumeric_cast(errorMessages.size()), errorMessages.data());
+ }
+ return AZStd::string::npos != AzFramework::StringFunc::Find(errorMessages, "error");
}
ShaderStage ToRHIShaderStage(ShaderHardwareStage stageType)
diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp
index f30fc72ace..ee293292e1 100644
--- a/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp
+++ b/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp
@@ -114,7 +114,7 @@ namespace AZ
}
}
- if (shaderCompilerArguments.m_dxcDisableOptimizations)
+ if (shaderCompilerArguments.m_disableOptimizations)
{
// When optimizations are disabled (-Od), all resources declared in the source file are available to all stages
// (when enabled only the resources which are referenced in a stage are bound to the stage)
@@ -195,7 +195,7 @@ namespace AZ
bool ShaderPlatformInterface::BuildHasDebugInfo(const RHI::ShaderCompilerArguments& shaderCompilerArguments) const
{
- return shaderCompilerArguments.m_dxcGenerateDebugInfo;
+ return shaderCompilerArguments.m_generateDebugInfo;
}
const char* ShaderPlatformInterface::GetAzslHeader(const AssetBuilderSDK::PlatformInfo& platform) const
diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp
index 7e41e50892..d43d88a7e1 100644
--- a/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp
+++ b/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp
@@ -167,7 +167,7 @@ namespace AZ
bool ShaderPlatformInterface::BuildHasDebugInfo(const RHI::ShaderCompilerArguments& shaderCompilerArguments) const
{
- return shaderCompilerArguments.m_dxcGenerateDebugInfo;
+ return shaderCompilerArguments.m_generateDebugInfo;
}
const char* ShaderPlatformInterface::GetAzslHeader(const AssetBuilderSDK::PlatformInfo& platform) const
diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp
index 8b99b7b510..c5f1060ca3 100644
--- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp
+++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp
@@ -109,7 +109,7 @@ namespace AZ
bool ShaderPlatformInterface::BuildHasDebugInfo(const RHI::ShaderCompilerArguments& shaderCompilerArguments) const
{
- return shaderCompilerArguments.m_dxcGenerateDebugInfo;
+ return shaderCompilerArguments.m_generateDebugInfo;
}
const char* ShaderPlatformInterface::GetAzslHeader(const AssetBuilderSDK::PlatformInfo& platform) const
diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp
index 2620aa5f7b..ffa02ac3c4 100644
--- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp
+++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp
@@ -729,8 +729,7 @@ namespace AZ
void CommandList::SetStencilRef(uint8_t stencilRef)
{
- vkCmdSetStencilReference(m_nativeCommandBuffer, VK_STENCIL_FACE_FRONT_BIT, static_cast(stencilRef));
- vkCmdSetStencilReference(m_nativeCommandBuffer, VK_STENCIL_FACE_BACK_BIT, static_cast(stencilRef));
+ vkCmdSetStencilReference(m_nativeCommandBuffer, VK_STENCIL_FACE_FRONT_AND_BACK, aznumeric_cast(stencilRef));
}
void CommandList::BindPipeline(const PipelineState* pipelineState)
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h
index 085256f6d8..76bb2a6113 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h
@@ -106,6 +106,9 @@ namespace AZ
static constexpr uint32_t InvalidEnumValue = std::numeric_limits::max();
uint32_t GetEnumValue(const AZ::Name& enumName) const;
+ //! Returns the name of the enum from its index. An empty name is returned for an invalid id.
+ const AZ::Name& GetEnumName(uint32_t enumValue) const;
+
//! Returns the unique name ID of this property
const Name& GetName() const;
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantKey.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantKey.h
index 74826d9f24..e7a0672931 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantKey.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantKey.h
@@ -7,6 +7,8 @@
*/
#pragma once
+#include
+#include
#include
#include
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Pass/PassBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Pass/PassBuilder.cpp
index d5e243c687..6a0b10633e 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Pass/PassBuilder.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Pass/PassBuilder.cpp
@@ -10,8 +10,8 @@
#include
#include
-
#include
+#include
#include
#include
@@ -33,11 +33,27 @@ namespace AZ
static const char* PassAssetExtension = "pass";
}
+ namespace PassBuilderNamespace
+ {
+ enum PassDependencies
+ {
+ Shader,
+ AttachmentImage,
+ Count
+ };
+
+ static const AZStd::tuple DependencyExtensionJobKeyTable[PassDependencies::Count] =
+ {
+ {".shader", "Shader Asset"},
+ {".attimage", "Any Asset Builder"}
+ };
+ }
+
void PassBuilder::RegisterBuilder()
{
AssetBuilderSDK::AssetBuilderDesc builder;
builder.m_name = PassBuilderJobKey;
- builder.m_version = 13; // antonmic: making .pass files declare dependency on shaders they reference
+ builder.m_version = 14; // making .pass files emit product dependencies for the shaders they reference so they are picked up by the asset bundler
builder.m_busId = azrtti_typeid();
builder.m_createJobFunction = AZStd::bind(&PassBuilder::CreateJobs, this, AZStd::placeholders::_1, AZStd::placeholders::_2);
builder.m_processJobFunction = AZStd::bind(&PassBuilder::ProcessJob, this, AZStd::placeholders::_1, AZStd::placeholders::_2);
@@ -104,8 +120,27 @@ namespace AZ
}
}
+ bool SetJobKeyForExtension(const AZStd::string& filePath, FindPassReferenceAssetParams& params)
+ {
+ AZStd::string extension;
+ StringFunc::Path::GetExtension(filePath.c_str(), extension);
+ for (const auto& [dependencyExtension, jobKey] : PassBuilderNamespace::DependencyExtensionJobKeyTable)
+ {
+ if (extension == dependencyExtension)
+ {
+ params.jobKey = jobKey;
+ return true;
+ }
+ }
+
+ AZ_Error(PassBuilderName, false, "PassBuilder found a dependency with extension '%s', but does not know the corresponding job key. Add the job key for that extension to SetJobKeyForExtension in PassBuilder.cpp", extension.c_str());
+ params.jobKey = "Unknown";
+ return false;
+ }
+
// Helper function to find all assetId's and object references
- bool FindReferencedAssets(FindPassReferenceAssetParams& params, AssetBuilderSDK::JobDescriptor* job)
+ bool FindReferencedAssets(
+ FindPassReferenceAssetParams& params, AssetBuilderSDK::JobDescriptor* job, AZStd::vector* productDependencies)
{
SerializeContext::ErrorHandler errorLogger;
errorLogger.Reset();
@@ -129,8 +164,8 @@ namespace AZ
if (job != nullptr) // Create Job Phase
{
params.dependencySourceFile = path;
- bool dependencyAddedSuccessfully = AddDependency(params, job);
- success = dependencyAddedSuccessfully && success;
+ success &= SetJobKeyForExtension(path, params);
+ success &= AddDependency(params, job);
}
else // Process Job Phase
{
@@ -139,6 +174,9 @@ namespace AZ
if (assetIdOutcome)
{
assetReference->m_assetId = assetIdOutcome.GetValue();
+ productDependencies->push_back(
+ AssetBuilderSDK::ProductDependency{assetReference->m_assetId, AZ::Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad)}
+ );
}
else
{
@@ -223,9 +261,9 @@ namespace AZ
params.passAssetSourceFile = request.m_sourceFile;
params.passAssetUuid = passAssetUuid;
params.serializeContext = serializeContext;
- params.jobKey = "Shader Asset";
+ params.jobKey = "Unknown";
- if (!FindReferencedAssets(params, &job))
+ if (!FindReferencedAssets(params, &job, nullptr))
{
return;
}
@@ -287,9 +325,10 @@ namespace AZ
params.passAssetSourceFile = request.m_sourceFile;
params.passAssetUuid = passAssetUuid;
params.serializeContext = serializeContext;
- params.jobKey = "Shader Asset";
+ params.jobKey = "Unknown";
- if (!FindReferencedAssets(params, nullptr))
+ AZStd::vector productDependencies;
+ if (!FindReferencedAssets(params, nullptr, &productDependencies))
{
return;
}
@@ -313,6 +352,7 @@ namespace AZ
// --- Save output product(s) to response ---
AssetBuilderSDK::JobProduct jobProduct(destPath, PassAsset::RTTI_Type(), 0);
+ jobProduct.m_dependencies = productDependencies;
jobProduct.m_dependenciesHandled = true;
response.m_outputProducts.push_back(jobProduct);
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp
index 5aed6b2993..1467b017d5 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp
@@ -93,31 +93,28 @@ namespace AZ
// Note that the only kind of property update currently supported is rename...
+ PropertyGroupMap newPropertyGroups;
for (auto& groupPair : m_properties)
{
PropertyMap& propertyMap = groupPair.second;
- PropertyMap newPropertyMap;
-
for (auto& propertyPair : propertyMap)
{
MaterialPropertyId propertyId{groupPair.first, propertyPair.first};
+
if (materialTypeSourceData.ApplyPropertyRenames(propertyId, m_materialTypeVersion))
{
- newPropertyMap[propertyId.GetPropertyName().GetStringView()] = propertyPair.second;
changesWereApplied = true;
}
- else
- {
- newPropertyMap[propertyPair.first] = propertyPair.second;
- }
+
+ newPropertyGroups[propertyId.GetGroupName().GetStringView()][propertyId.GetPropertyName().GetStringView()] = propertyPair.second;
}
-
- propertyMap = newPropertyMap;
}
if (changesWereApplied)
{
+ m_properties = AZStd::move(newPropertyGroups);
+
AZ_Warning("MaterialSourceData", false,
"This material is based on version '%u' of '%s', but the material type is now at version '%u'. "
"Automatic updates are available. Consider updating the .material source file.",
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp
index 74250647c3..08f57c7cd3 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp
@@ -164,16 +164,14 @@ namespace AZ
const MaterialTypeSourceData::PropertyDefinition* MaterialTypeSourceData::FindProperty(AZStd::string_view groupName, AZStd::string_view propertyName, uint32_t materialTypeVersion) const
{
auto groupIter = m_propertyLayout.m_properties.find(groupName);
- if (groupIter == m_propertyLayout.m_properties.end())
+ if (groupIter != m_propertyLayout.m_properties.end())
{
- return nullptr;
- }
-
- for (const PropertyDefinition& property : groupIter->second)
- {
- if (property.m_name == propertyName)
+ for (const PropertyDefinition& property : groupIter->second)
{
- return &property;
+ if (property.m_name == propertyName)
+ {
+ return &property;
+ }
}
}
@@ -185,16 +183,14 @@ 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())
+ if (groupIter != m_propertyLayout.m_properties.end())
{
- return nullptr;
- }
-
- for (const PropertyDefinition& property : groupIter->second)
- {
- if (property.m_name == propertyId.GetPropertyName().GetStringView())
+ for (const PropertyDefinition& property : groupIter->second)
{
- return &property;
+ if (property.m_name == propertyId.GetPropertyName().GetStringView())
+ {
+ return &property;
+ }
}
}
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp
index e9d8a42641..36f4947e3d 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp
@@ -208,7 +208,7 @@ namespace AZ
return;
}
- const uint32_t originalVersion = m_materialTypeVersion;
+ [[maybe_unused]] const uint32_t originalVersion = m_materialTypeVersion;
bool changesWereApplied = false;
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp
index 700ac41003..5d0a88a6d3 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp
@@ -203,6 +203,16 @@ namespace AZ
return InvalidEnumValue;
}
+
+ const AZ::Name& MaterialPropertyDescriptor::GetEnumName(uint32_t enumValue) const
+ {
+ if (enumValue < m_enumNames.size())
+ {
+ return m_enumNames.at(enumValue);
+ }
+ static AZ::Name EmptyName = AZ::Name();
+ return EmptyName;
+ }
} // namespace RPI
} // namespace AZ
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp
index 692087d93b..84757d58ab 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp
@@ -264,7 +264,6 @@ namespace AZ
{
// When rebuilding shaders we may be in a state where the ShaderAsset and root ShaderVariantAsset have been rebuilt and reloaded, but some (or all)
// shader variants haven't been built yet. Since we want to use the latest version of the shader code, ignore the old variants and fall back to the newer root variant instead.
- AZ_Warning("ShaderAsset", false, "ShaderAsset and ShaderVariantAsset are out of sync; defaulting to root shader variant. (This is common while reloading shaders).");
return GetRootVariant(supervariantIndex);
}
}
diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp
index acce52ae8e..fa8eed35de 100644
--- a/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp
+++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp
@@ -105,6 +105,13 @@ namespace UnitTest
{"op": "rename", "from": "general.testColorNameB", "to": "general.testColorNameC"}
]
},
+ {
+ "toVersion": 6,
+ "actions": [
+ {"op": "rename", "from": "oldGroup.MyFloat", "to": "general.MyFloat"},
+ {"op": "rename", "from": "oldGroup.MyIntOldName", "to": "general.MyInt"}
+ ]
+ },
{
"toVersion": 10,
"actions": [
@@ -751,6 +758,72 @@ namespace UnitTest
material.ApplyVersionUpdates();
}
+ TEST_F(MaterialSourceDataTests, Load_MaterialTypeVersionUpdate_MovePropertiesToAnotherGroup)
+ {
+ const AZStd::string inputJson = R"(
+ {
+ "materialType": "@exefolder@/Temp/test.materialtype",
+ "materialTypeVersion": 3,
+ "properties": {
+ "oldGroup": {
+ "MyFloat": 1.2,
+ "MyIntOldName": 5
+ }
+ }
+ }
+ )";
+
+ MaterialSourceData material;
+ JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson);
+
+ EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
+ EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
+
+ // Initially, the loaded material data will match the .material file exactly. This gives us the accurate representation of
+ // what's actually saved on disk.
+
+ EXPECT_NE(material.m_properties["oldGroup"].find("MyFloat"), material.m_properties["oldGroup"].end());
+ EXPECT_NE(material.m_properties["oldGroup"].find("MyIntOldName"), material.m_properties["oldGroup"].end());
+ EXPECT_EQ(material.m_properties["general"].find("MyFloat"), material.m_properties["general"].end());
+ EXPECT_EQ(material.m_properties["general"].find("MyInt"), material.m_properties["general"].end());
+
+ float myFloat = material.m_properties["oldGroup"]["MyFloat"].m_value.GetValue();
+ EXPECT_EQ(myFloat, 1.2f);
+
+ int32_t myInt = material.m_properties["oldGroup"]["MyIntOldName"].m_value.GetValue();
+ EXPECT_EQ(myInt, 5);
+
+ EXPECT_EQ(3, material.m_materialTypeVersion);
+
+ // Then we force the material data to update to the latest material type version specification
+ ErrorMessageFinder warningFinder; // Note this finds errors and warnings, and we're looking for a warning.
+ warningFinder.AddExpectedErrorMessage("Automatic updates are available. Consider updating the .material source file");
+ warningFinder.AddExpectedErrorMessage("This material is based on version '3'");
+ warningFinder.AddExpectedErrorMessage("material type is now at version '10'");
+ material.ApplyVersionUpdates();
+ warningFinder.CheckExpectedErrorsFound();
+
+ // Now the material data should match the latest material type.
+ // Look for the property under the latest name in the material type, not the name used in the .material file.
+
+ EXPECT_EQ(material.m_properties["oldGroup"].find("MyFloat"), material.m_properties["oldGroup"].end());
+ EXPECT_EQ(material.m_properties["oldGroup"].find("MyIntOldName"), material.m_properties["oldGroup"].end());
+ EXPECT_NE(material.m_properties["general"].find("MyFloat"), material.m_properties["general"].end());
+ EXPECT_NE(material.m_properties["general"].find("MyInt"), material.m_properties["general"].end());
+
+ myFloat = material.m_properties["general"]["MyFloat"].m_value.GetValue();
+ EXPECT_EQ(myFloat, 1.2f);
+
+ myInt = material.m_properties["general"]["MyInt"].m_value.GetValue();
+ EXPECT_EQ(myInt, 5);
+
+ EXPECT_EQ(10, material.m_materialTypeVersion);
+
+ // Calling ApplyVersionUpdates() again should not report the warning again, since the material has already been updated.
+ warningFinder.Reset();
+ material.ApplyVersionUpdates();
+ }
+
TEST_F(MaterialSourceDataTests, Load_MaterialTypeVersionPartialUpdate)
{
// This case is similar to Load_MaterialTypeVersionUpdate but we start at a later
diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp
index b811e630d0..2fe7b1dbe5 100644
--- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp
+++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp
@@ -1346,7 +1346,8 @@ namespace UnitTest
{
"toVersion": 7,
"actions": [
- { "op": "rename", "from": "general.bazA", "to": "otherGroup.bazB" }
+ { "op": "rename", "from": "general.bazA", "to": "otherGroup.bazB" },
+ { "op": "rename", "from": "onlyOneProperty.bopA", "to": "otherGroup.bopB" } // This tests a group 'onlyOneProperty' that no longer exists in the material type
]
}
],
@@ -1370,6 +1371,10 @@ namespace UnitTest
{
"name": "bazB",
"type": "Float"
+ },
+ {
+ "name": "bopB",
+ "type": "Float"
}
]
}
@@ -1386,13 +1391,16 @@ namespace UnitTest
const MaterialTypeSourceData::PropertyDefinition* foo = materialType.FindProperty("general", "fooC");
const MaterialTypeSourceData::PropertyDefinition* bar = materialType.FindProperty("general", "barC");
const MaterialTypeSourceData::PropertyDefinition* baz = materialType.FindProperty("otherGroup", "bazB");
+ const MaterialTypeSourceData::PropertyDefinition* bop = materialType.FindProperty("otherGroup", "bopB");
EXPECT_TRUE(foo);
EXPECT_TRUE(bar);
EXPECT_TRUE(baz);
+ EXPECT_TRUE(bop);
EXPECT_EQ(foo->m_name, "fooC");
EXPECT_EQ(bar->m_name, "barC");
EXPECT_EQ(baz->m_name, "bazB");
+ EXPECT_EQ(bop->m_name, "bopB");
// Now try doing the property lookup using old versions of the name and make sure the same property can be found
@@ -1401,12 +1409,15 @@ namespace UnitTest
EXPECT_EQ(bar, materialType.FindProperty("general", "barA"));
EXPECT_EQ(bar, materialType.FindProperty("general", "barB"));
EXPECT_EQ(baz, materialType.FindProperty("general", "bazA"));
+ EXPECT_EQ(bop, materialType.FindProperty("onlyOneProperty", "bopA"));
EXPECT_EQ(nullptr, materialType.FindProperty("general", "fooX"));
EXPECT_EQ(nullptr, materialType.FindProperty("general", "barX"));
EXPECT_EQ(nullptr, materialType.FindProperty("general", "bazX"));
EXPECT_EQ(nullptr, materialType.FindProperty("general", "bazB"));
EXPECT_EQ(nullptr, materialType.FindProperty("otherGroup", "bazA"));
+ EXPECT_EQ(nullptr, materialType.FindProperty("onlyOneProperty", "bopB"));
+ EXPECT_EQ(nullptr, materialType.FindProperty("otherGroup", "bopA"));
}
TEST_F(MaterialTypeSourceDataTests, FindPropertyUsingOldName_Error_UnsupportedVersionUpdate)
diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_Opaque_DoubleSided.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_Opaque_DoubleSided.material
new file mode 100644
index 0000000000..a26bf6e045
--- /dev/null
+++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_Opaque_DoubleSided.material
@@ -0,0 +1,14 @@
+{
+ "description": "",
+ "materialType": "Materials/Types/StandardPBR.materialtype",
+ "parentMaterial": "",
+ "propertyLayoutVersion": 3,
+ "properties": {
+ "baseColor": {
+ "textureMap": "Textures/Default/checker_uv_basecolor.png"
+ },
+ "general": {
+ "doubleSided": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.shader b/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.shader
index 4f1c45d235..6418cc392e 100644
--- a/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.shader
+++ b/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.shader
@@ -24,7 +24,7 @@
},
"CompilerHints" : {
- "DxcDisableOptimizations" : false
+ "DisableOptimizations" : false
},
"ProgramSettings":
diff --git a/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR_ForwardPass.shader b/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR_ForwardPass.shader
index 13ba0ce547..f87a56daa2 100644
--- a/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR_ForwardPass.shader
+++ b/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR_ForwardPass.shader
@@ -24,7 +24,7 @@
},
"CompilerHints" : {
- "DxcDisableOptimizations" : false
+ "DisableOptimizations" : false
},
"ProgramSettings":
diff --git a/Gems/Atom/TestData/TestData/Objects/tube.fbx b/Gems/Atom/TestData/TestData/Objects/tube.fbx
new file mode 100644
index 0000000000..f9034e7641
--- /dev/null
+++ b/Gems/Atom/TestData/TestData/Objects/tube.fbx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b2ecc32cd3052f3cb5836c8be7bf5cba54d98f46e6a0eeac95aaef00a123411a
+size 27340
diff --git a/Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/ViewportModels/_dev_shaderball_00_basecolor.png b/Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/ViewportModels/_dev_shaderball_00_basecolor.png
index 415ca3e521..07e240baf9 100644
--- a/Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/ViewportModels/_dev_shaderball_00_basecolor.png
+++ b/Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/ViewportModels/_dev_shaderball_00_basecolor.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:93a7e033d9fb0fcac221647322bde03716643d789390f79078c4fcc37ecfd005
-size 68327
+oid sha256:513f47f6fea5105f603170a8881b7e3b1cd2c4258636d64a6399c725032b500d
+size 38689
diff --git a/Gems/AtomContent/Sponza/Registry/AssetProcessorPlatformConfig.setreg b/Gems/AtomContent/Sponza/Registry/AssetProcessorPlatformConfig.setreg
index 778d05dee9..206a0a2a87 100644
--- a/Gems/AtomContent/Sponza/Registry/AssetProcessorPlatformConfig.setreg
+++ b/Gems/AtomContent/Sponza/Registry/AssetProcessorPlatformConfig.setreg
@@ -6,7 +6,7 @@
// Sample Gems, Block source folders
// ------------------------------------------------------------------------------
"Exclude Work In Progress Folders": {
- "pattern": ".*\\\\/.[Ss]rc\\\\/.*"
+ "pattern": "(^|.+/).[Ss]rc(/.*)?$"
}
}
}
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt
index e68681315e..95331a2f3f 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt
@@ -86,6 +86,8 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
FILES_CMAKE
atomlyintegration_commonfeatures_editor_files.cmake
${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
+ PLATFORM_INCLUDE_FILES
+ ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Android/platform_android.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Android/platform_android.cmake
new file mode 100644
index 0000000000..7a325ca97e
--- /dev/null
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Android/platform_android.cmake
@@ -0,0 +1,7 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/AppleTV/platform_appletv.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/AppleTV/platform_appletv.cmake
new file mode 100644
index 0000000000..5cd1fb5a22
--- /dev/null
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/AppleTV/platform_appletv.cmake
@@ -0,0 +1,8 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Linux/platform_linux.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Linux/platform_linux.cmake
new file mode 100644
index 0000000000..7a325ca97e
--- /dev/null
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Linux/platform_linux.cmake
@@ -0,0 +1,7 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Mac/platform_mac.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Mac/platform_mac.cmake
new file mode 100644
index 0000000000..7a325ca97e
--- /dev/null
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Mac/platform_mac.cmake
@@ -0,0 +1,7 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Windows/platform_windows.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Windows/platform_windows.cmake
new file mode 100644
index 0000000000..3beda63de7
--- /dev/null
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Windows/platform_windows.cmake
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+if(NOT LY_MONOLITHIC_GAME) # Do not use OpenImageIO in monolithic game
+ set(LY_RUNTIME_DEPENDENCIES
+ 3rdParty::OpenImageIO
+ )
+endif()
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/iOS/platform_ios.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/iOS/platform_ios.cmake
new file mode 100644
index 0000000000..7a325ca97e
--- /dev/null
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/iOS/platform_ios.cmake
@@ -0,0 +1,7 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp
index eaaf04fcf2..1325455cd9 100644
--- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp
+++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp
@@ -88,6 +88,10 @@ namespace AZ::Render
{
RenderTangents(mesh, globalTM);
}
+ if (renderWireframe)
+ {
+ RenderWireframe(mesh, globalTM);
+ }
}
}
}
@@ -255,8 +259,6 @@ namespace AZ::Render
m_auxVertices.clear();
m_auxVertices.reserve(numTriangles * 2);
- m_auxColors.clear();
- m_auxColors.reserve(m_auxVertices.size());
for (uint32 triangleIndex = 0; triangleIndex < numTriangles; ++triangleIndex)
{
@@ -275,17 +277,15 @@ namespace AZ::Render
const AZ::Vector3 normalPos = (posA + posB + posC) * (1.0f / 3.0f);
m_auxVertices.emplace_back(normalPos);
- m_auxColors.emplace_back(colorFaceNormals);
m_auxVertices.emplace_back(normalPos + (normalDir * faceNormalsScale));
- m_auxColors.emplace_back(colorFaceNormals);
}
}
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast(m_auxVertices.size());
- lineArgs.m_colors = m_auxColors.data();
- lineArgs.m_colorCount = static_cast(m_auxColors.size());
+ lineArgs.m_colors = &colorFaceNormals;
+ lineArgs.m_colorCount = 1;
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
@@ -302,8 +302,6 @@ namespace AZ::Render
m_auxVertices.clear();
m_auxVertices.reserve(numVertices * 2);
- m_auxColors.clear();
- m_auxColors.reserve(m_auxVertices.size());
for (uint32 j = 0; j < numVertices; ++j)
{
@@ -312,17 +310,15 @@ namespace AZ::Render
const AZ::Vector3 normal = worldTM.TransformVector(normals[vertexIndex]).GetNormalizedSafe() * vertexNormalsScale;
m_auxVertices.emplace_back(position);
- m_auxColors.emplace_back(colorFaceNormals);
m_auxVertices.emplace_back(position + normal);
- m_auxColors.emplace_back(colorFaceNormals);
}
}
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast(m_auxVertices.size());
- lineArgs.m_colors = m_auxColors.data();
- lineArgs.m_colorCount = static_cast(m_auxColors.size());
+ lineArgs.m_colors = &colorVertexNormals;
+ lineArgs.m_colorCount = 1;
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
@@ -412,4 +408,67 @@ namespace AZ::Render
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
+
+ void AtomActorDebugDraw::RenderWireframe(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM)
+ {
+ // Check if the mesh is valid and skip the node in case it's not
+ if (!mesh)
+ {
+ return;
+ }
+
+ RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
+ if (!auxGeom)
+ {
+ return;
+ }
+
+ PrepareForMesh(mesh, worldTM);
+
+ const float scale = 0.01f;
+
+ const AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS);
+ const AZ::Color vertexColor = AZ::Color(0.8f, 0.24f, 0.88f, 1.0f);
+
+ const size_t numSubMeshes = mesh->GetNumSubMeshes();
+ for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex)
+ {
+ EMotionFX::SubMesh* subMesh = mesh->GetSubMesh(subMeshIndex);
+ const uint32 numTriangles = subMesh->GetNumPolygons();
+ const uint32 startVertex = subMesh->GetStartVertex();
+ const uint32* indices = subMesh->GetIndices();
+
+ m_auxVertices.clear();
+ m_auxVertices.reserve(numTriangles * 6);
+
+ for (uint32 triangleIndex = 0; triangleIndex < numTriangles; ++triangleIndex)
+ {
+ const uint32 triangleStartIndex = triangleIndex * 3;
+ const uint32 indexA = indices[triangleStartIndex + 0] + startVertex;
+ const uint32 indexB = indices[triangleStartIndex + 1] + startVertex;
+ const uint32 indexC = indices[triangleStartIndex + 2] + startVertex;
+
+ const AZ::Vector3 posA = m_worldSpacePositions[indexA] + normals[indexA] * scale;
+ const AZ::Vector3 posB = m_worldSpacePositions[indexB] + normals[indexB] * scale;
+ const AZ::Vector3 posC = m_worldSpacePositions[indexC] + normals[indexC] * scale;
+
+ m_auxVertices.emplace_back(posA);
+ m_auxVertices.emplace_back(posB);
+
+ m_auxVertices.emplace_back(posB);
+ m_auxVertices.emplace_back(posC);
+
+ m_auxVertices.emplace_back(posC);
+ m_auxVertices.emplace_back(posA);
+ }
+
+ RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
+ lineArgs.m_verts = m_auxVertices.data();
+ lineArgs.m_vertCount = static_cast(m_auxVertices.size());
+ lineArgs.m_colors = &vertexColor;
+ lineArgs.m_colorCount = 1;
+ lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
+ auxGeom->DrawLines(lineArgs);
+ }
+ }
} // namespace AZ::Render
diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h
index 9f8b137f13..797de16351 100644
--- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h
+++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h
@@ -43,6 +43,7 @@ namespace AZ::Render
void RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance);
void RenderNormals(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM, bool vertexNormals, bool faceNormals);
void RenderTangents(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM);
+ void RenderWireframe(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM);
EMotionFX::Mesh* m_currentMesh = nullptr; /**< A pointer to the mesh whose world space positions are in the pre-calculated positions buffer.
NULL in case we haven't pre-calculated any positions yet. */
diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp
index f6186be235..facf7a7b12 100644
--- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp
+++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp
@@ -128,10 +128,10 @@ namespace EMStudio
AZ_Assert(m_gridEntity != nullptr, "Failed to create grid entity.");
AZ::Render::GridComponentConfig gridConfig;
- gridConfig.m_gridSize = 4.0f;
+ gridConfig.m_gridSize = 20.0f;
gridConfig.m_axisColor = AZ::Color(0.5f, 0.5f, 0.5f, 1.0f);
gridConfig.m_primaryColor = AZ::Color(0.3f, 0.3f, 0.3f, 1.0f);
- gridConfig.m_secondaryColor = AZ::Color(0.5f, 0.1f, 0.1f, 1.0f);
+ gridConfig.m_secondaryColor = AZ::Color(0.5f, 0.5f, 0.5f, 1.0f);
auto gridComponent = m_gridEntity->CreateComponent(AZ::Render::GridComponentTypeId);
gridComponent->SetConfiguration(gridConfig);
diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp
index 7af5c1607a..c6e349236f 100644
--- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp
+++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp
@@ -6,10 +6,11 @@
*
*/
-#include
+#include
#include
#include
#include
+#include
#include
#include
@@ -166,6 +167,26 @@ namespace EMStudio
GetViewportContext()->SetCameraTransform(AZ::Transform::CreateLookAt(cameraPosition, targetPosition));
}
+ void AnimViewportWidget::OnTick(float deltaTime, AZ::ScriptTimePoint time)
+ {
+ RenderViewportWidget::OnTick(deltaTime, time);
+ CalculateCameraProjection();
+ }
+
+ void AnimViewportWidget::CalculateCameraProjection()
+ {
+ auto viewportContext = GetViewportContext();
+ auto windowSize = viewportContext->GetViewportSize();
+ // Prevent devided by zero
+ const float height = AZStd::max(aznumeric_cast(windowSize.m_height), 1.0f);
+ const float aspectRatio = aznumeric_cast(windowSize.m_width) / height;
+
+ AZ::Matrix4x4 viewToClipMatrix;
+ AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, AZ::Constants::HalfPi, aspectRatio, DepthNear, DepthFar, true);
+
+ viewportContext->GetDefaultView()->SetViewToClipMatrix(viewToClipMatrix);
+ }
+
void AnimViewportWidget::ToggleRenderFlag(EMotionFX::ActorRenderFlag flag)
{
m_renderFlags[flag] = !m_renderFlags[flag];
diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h
index 8aa316a8ba..e2099ea2ab 100644
--- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h
+++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h
@@ -30,6 +30,9 @@ namespace EMStudio
EMotionFX::ActorRenderFlagBitset GetRenderFlags() const;
private:
+ void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
+
+ void CalculateCameraProjection();
void SetupCameras();
void SetupCameraController();
@@ -42,6 +45,8 @@ namespace EMStudio
void ToggleRenderFlag(EMotionFX::ActorRenderFlag flag);
static constexpr float CameraDistance = 2.0f;
+ static constexpr float DepthNear = 0.01f;
+ static constexpr float DepthFar = 100.0f;
AZStd::unique_ptr m_renderer;
AZStd::shared_ptr m_rotateCamera;
diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp
index 161160e16f..74ba99c26c 100644
--- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp
+++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp
@@ -311,17 +311,17 @@ namespace AZ
m_forceClearRenderData = true;
}
- bool HairFeatureProcessor::HasHairParentPass()
+ bool HairFeatureProcessor::HasHairParentPass(RPI::RenderPipeline* renderPipeline)
{
- RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene());
+ RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, renderPipeline);
RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
- return pass;
+ return pass ? true : false;
}
void HairFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr renderPipeline)
{
// Proceed only if this is the main pipeline that contains the parent pass
- if (!HasHairParentPass())
+ if (!HasHairParentPass(renderPipeline.get()))
{
return;
}
@@ -335,7 +335,7 @@ namespace AZ
void HairFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] RPI::RenderPipeline* renderPipeline)
{
// Proceed only if this is the main pipeline that contains the parent pass
- if (!HasHairParentPass())
+ if (!HasHairParentPass(renderPipeline))
{
return;
}
@@ -347,7 +347,7 @@ namespace AZ
void HairFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline)
{
// Proceed only if this is the main pipeline that contains the parent pass
- if (!HasHairParentPass())
+ if (!HasHairParentPass(renderPipeline))
{
return;
}
@@ -623,3 +623,4 @@ namespace AZ
} // namespace Hair
} // namespace Render
} // namespace AZ
+
diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h
index f810967824..70e37a7863 100644
--- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h
+++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h
@@ -165,7 +165,7 @@ namespace AZ
void EnablePasses(bool enable);
- bool HasHairParentPass();
+ bool HasHairParentPass(RPI::RenderPipeline* renderPipeline);
//! The following will serve to register the FP in the Thumbnail system
AZStd::vector m_hairFeatureProcessorRegistryName;
diff --git a/Gems/AudioEngineWwise/AssetProcessorGemConfig.setreg b/Gems/AudioEngineWwise/AssetProcessorGemConfig.setreg
index 9b4d86791c..6d7995b230 100644
--- a/Gems/AudioEngineWwise/AssetProcessorGemConfig.setreg
+++ b/Gems/AudioEngineWwise/AssetProcessorGemConfig.setreg
@@ -3,10 +3,10 @@
"AssetProcessor": {
"Settings": {
"Exclude AudioProject": {
- "pattern": ".*\\\\/Sounds\\\\/.+_project.*"
+ "pattern": "(^|.+/)Sounds/.+_project.*"
},
"Exclude AudioTemp": {
- "pattern": ".*\\\\/Sounds\\\\/.+\\\\.(txt|xml|dat)"
+ "pattern": "(^|.+/)Sounds/.+\\\\.(txt|xml|dat)"
},
"RC audio": {
"pattern": ".*\\\\.(wav|pcm)",
diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphNodeGroupCommands.h b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphNodeGroupCommands.h
index 014d3b0398..3623bd5cd5 100644
--- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphNodeGroupCommands.h
+++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphNodeGroupCommands.h
@@ -60,7 +60,7 @@ namespace CommandSystem
const char* GetDescription() const override;
MCore::Command* Create() override
{
- return new CommandAnimGraphAdjustNodeGroup(this);
+ return aznew CommandAnimGraphAdjustNodeGroup(this);
}
static AZStd::vector GenerateNodeNameVector(EMotionFX::AnimGraph* animGraph, const AZStd::vector& nodeIDs);
diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/CommandManager.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/CommandManager.cpp
index be4626f592..2c646b3504 100644
--- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/CommandManager.cpp
+++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/CommandManager.cpp
@@ -104,7 +104,7 @@ namespace CommandSystem
RegisterCommand(new CommandMotionSetAdjustMotion());
// register node group commands
- RegisterCommand(new CommandAdjustNodeGroup());
+ RegisterCommand(aznew CommandAdjustNodeGroup());
RegisterCommand(new CommandAddNodeGroup());
RegisterCommand(new CommandRemoveNodeGroup());
@@ -134,7 +134,7 @@ namespace CommandSystem
RegisterCommand(aznew CommandAdjustTransitionCondition());
RegisterCommand(new CommandAnimGraphAddNodeGroup());
RegisterCommand(new CommandAnimGraphRemoveNodeGroup());
- RegisterCommand(new CommandAnimGraphAdjustNodeGroup());
+ RegisterCommand(aznew CommandAnimGraphAdjustNodeGroup());
RegisterCommand(new CommandAnimGraphAddGroupParameter());
RegisterCommand(new CommandAnimGraphRemoveGroupParameter());
RegisterCommand(new CommandAnimGraphAdjustGroupParameter());
diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/NodeGroupCommands.h b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/NodeGroupCommands.h
index 6c57877bd5..640a670e44 100644
--- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/NodeGroupCommands.h
+++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/NodeGroupCommands.h
@@ -63,7 +63,7 @@ namespace CommandSystem
const char* GetDescription() const override;
MCore::Command* Create() override
{
- return new CommandAdjustNodeGroup(this);
+ return aznew CommandAdjustNodeGroup(this);
}
private:
diff --git a/Gems/LyShine/Code/Editor/ViewportInteraction.cpp b/Gems/LyShine/Code/Editor/ViewportInteraction.cpp
index e6bdb4d8d5..a39a7b9cac 100644
--- a/Gems/LyShine/Code/Editor/ViewportInteraction.cpp
+++ b/Gems/LyShine/Code/Editor/ViewportInteraction.cpp
@@ -739,14 +739,32 @@ void ViewportInteraction::MouseWheelEvent(QWheelEvent* ev)
bool ViewportInteraction::KeyPressEvent(QKeyEvent* ev)
{
- if (ev->key() == Qt::Key_Space)
+ switch (ev->key())
{
+ case Qt::Key_Space:
if (!ev->isAutoRepeat())
{
ActivateSpaceBar();
}
-
return true;
+ case Qt::Key_Up:
+ Nudge(ViewportInteraction::NudgeDirection::Up,
+ (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow);
+ return true;
+ case Qt::Key_Down:
+ Nudge(ViewportInteraction::NudgeDirection::Down,
+ (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow);
+ return true;
+ case Qt::Key_Left:
+ Nudge(ViewportInteraction::NudgeDirection::Left,
+ (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow);
+ return true;
+ case Qt::Key_Right:
+ Nudge(ViewportInteraction::NudgeDirection::Right,
+ (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow);
+ return true;
+ default:
+ break;
}
return false;
diff --git a/Gems/LyShine/Code/Editor/ViewportWidget.cpp b/Gems/LyShine/Code/Editor/ViewportWidget.cpp
index e172d43b60..bf7447585c 100644
--- a/Gems/LyShine/Code/Editor/ViewportWidget.cpp
+++ b/Gems/LyShine/Code/Editor/ViewportWidget.cpp
@@ -220,6 +220,7 @@ ViewportWidget::ViewportWidget(EditorWindow* parent)
InitUiRenderer();
SetupShortcuts();
+ installEventFilter(m_editorWindow);
// Setup a timer for the maximum refresh rate we want.
// Refresh is actually triggered by interaction events and by the IdleUpdate. This avoids the UI
@@ -258,6 +259,8 @@ ViewportWidget::~ViewportWidget()
LyShinePassDataRequestBus::Handler::BusDisconnect();
AZ::RPI::ViewportContextNotificationBus::Handler::BusDisconnect();
+ removeEventFilter(m_editorWindow);
+
m_uiRenderer.reset();
// Notify LyShine that this is no longer a valid UiRenderer.
@@ -688,9 +691,9 @@ void ViewportWidget::wheelEvent(QWheelEvent* ev)
Refresh();
}
-bool ViewportWidget::event(QEvent* ev)
+bool ViewportWidget::eventFilter([[maybe_unused]] QObject* watched, QEvent* event)
{
- if (ev->type() == QEvent::ShortcutOverride)
+ if (event->type() == QEvent::ShortcutOverride)
{
// When a shortcut is matched, Qt's event processing sends out a shortcut override event
// to allow other systems to override it. If it's not overridden, then the key events
@@ -698,40 +701,48 @@ bool ViewportWidget::event(QEvent* ev)
// handler. In our case this causes a problem in preview mode for the Key_Delete event.
// So, if we are preview mode avoid treating Key_Delete as a shortcut.
- QKeyEvent* keyEvent = static_cast(ev);
+ QKeyEvent* keyEvent = static_cast(event);
int key = keyEvent->key();
// Override the space bar shortcut so that the key gets handled by the viewport's KeyPress/KeyRelease
// events when the viewport has the focus. The space bar is set up as a shortcut in order to give the
// viewport the focus and activate the space bar when another widget has the focus. Once the shortcut
// is pressed and focus is given to the viewport, the viewport takes over handling the space bar via
- // the KeyPress/KeyRelease events
- if (key == Qt::Key_Space)
+ // the KeyPress/KeyRelease events.
+ // Also ignore nudge shortcuts in edit/preview mode so that the KeyPressEvent will be sent.
+ switch (key)
{
- ev->accept();
+ case Qt::Key_Space:
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ {
+ event->accept();
return true;
}
+ default:
+ {
+ break;
+ }
+ }
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
if (editorMode == UiEditorMode::Preview)
{
- switch (key)
+ if (key == Qt::Key_Delete)
{
- case Qt::Key_Delete:
- // Ignore nudge shortcuts in preview mode so that the KeyPressEvent will be sent
- case Qt::Key_Up:
- case Qt::Key_Down:
- case Qt::Key_Left:
- case Qt::Key_Right:
- {
- ev->accept();
+ event->accept();
return true;
}
- break;
- };
}
}
-
+
+ return false;
+}
+
+bool ViewportWidget::event(QEvent* ev)
+{
bool result = RenderViewportWidget::event(ev);
return result;
}
@@ -742,8 +753,7 @@ void ViewportWidget::keyPressEvent(QKeyEvent* event)
if (editorMode == UiEditorMode::Edit)
{
// in Edit mode just send input to ViewportInteraction
- bool handled = m_viewportInteraction->KeyPressEvent(event);
- if (!handled)
+ if (!m_viewportInteraction->KeyPressEvent(event))
{
RenderViewportWidget::keyPressEvent(event);
}
@@ -1246,115 +1256,6 @@ void ViewportWidget::SetupShortcuts()
{
// Actions with shortcuts are created instead of direct shortcuts because the shortcut dispatcher only looks for matching actions
- // Create nudge shortcuts that are active across the entire UI Editor window. Any widgets (such as the spin box widget) that
- // handle the same keys and want the shortcut to be ignored need to handle that with a shortcut override event.
- // In preview mode, the nudge shortcuts are ignored via the shortcut override event. KeyPressEvents are sent instead,
- // and passed along to the canvas
-
- // Nudge up
- {
- QAction* action = new QAction("Up", this);
- action->setShortcut(QKeySequence(Qt::Key_Up));
- QObject::connect(action,
- &QAction::triggered,
- [this]()
- {
- m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Up, ViewportInteraction::NudgeSpeed::Slow);
- });
- addAction(action);
- }
-
- // Nudge up fast
- {
- QAction* action = new QAction("Up Fast", this);
- action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Up));
- QObject::connect(action,
- &QAction::triggered,
- [this]()
- {
- m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Up, ViewportInteraction::NudgeSpeed::Fast);
- });
- addAction(action);
- }
-
- // Nudge down
- {
- QAction* action = new QAction("Down", this);
- action->setShortcut(QKeySequence(Qt::Key_Down));
- QObject::connect(action,
- &QAction::triggered,
- [this]()
- {
- m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Down, ViewportInteraction::NudgeSpeed::Slow);
- });
- addAction(action);
- }
-
- // Nudge down fast
- {
- QAction* action = new QAction("Down Fast", this);
- action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Down));
- QObject::connect(action,
- &QAction::triggered,
- [this]()
- {
- m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Down, ViewportInteraction::NudgeSpeed::Fast);
- });
- addAction(action);
- }
-
- // Nudge left
- {
- QAction* action = new QAction("Left", this);
- action->setShortcut(QKeySequence(Qt::Key_Left));
- QObject::connect(action,
- &QAction::triggered,
- [this]()
- {
- m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Left, ViewportInteraction::NudgeSpeed::Slow);
- });
- addAction(action);
- }
-
- // Nudge left fast
- {
- QAction* action = new QAction("Left Fast", this);
- action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Left));
- QObject::connect(action,
- &QAction::triggered,
- [this]()
- {
- m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Left, ViewportInteraction::NudgeSpeed::Fast);
- });
- addAction(action);
- }
-
- // Nudge right
- {
- QAction* action = new QAction("Right", this);
- action->setShortcut(QKeySequence(Qt::Key_Right));
- QObject::connect(action,
- &QAction::triggered,
- [this]()
- {
- m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Right, ViewportInteraction::NudgeSpeed::Slow);
- });
- addAction(action);
- }
-
- // Nudge right fast
- {
- QAction* action = new QAction("Right Fast", this);
- action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Right));
- QObject::connect(action,
- &QAction::triggered,
- [this]()
- {
- m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Right, ViewportInteraction::NudgeSpeed::Fast);
- });
- addAction(action);
- }
-
// Give the viewport focus and activate the space bar
{
QAction* action = new QAction("Viewport Focus", this);
diff --git a/Gems/LyShine/Code/Editor/ViewportWidget.h b/Gems/LyShine/Code/Editor/ViewportWidget.h
index 620cb8fb35..722335aa93 100644
--- a/Gems/LyShine/Code/Editor/ViewportWidget.h
+++ b/Gems/LyShine/Code/Editor/ViewportWidget.h
@@ -122,12 +122,15 @@ protected:
void wheelEvent(QWheelEvent* ev) override;
//! Prevents shortcuts from interfering with preview mode.
+ bool eventFilter(QObject* watched, QEvent* event) override;
+
+ //! Handle events from Qt.
bool event(QEvent* ev) override;
- //! Key press event from Qt
+ //! Key press event from Qt.
void keyPressEvent(QKeyEvent* event) override;
- //! Key release event from Qt
+ //! Key release event from Qt.
void keyReleaseEvent(QKeyEvent* event) override;
void focusOutEvent(QFocusEvent* ev) override;
diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerEditorServerBus.h b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerEditorServerBus.h
new file mode 100644
index 0000000000..1c6d2152bb
--- /dev/null
+++ b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerEditorServerBus.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+
+namespace Multiplayer
+{
+ class MultiplayerEditorServerRequests : public AZ::EBusTraits
+ {
+ public:
+ static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+ static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+
+ //! Sends a packet that initializes a local server launched from the editor.
+ //! The editor will package the data required for loading the current editor level on the editor-server; data includes entities and asset data.
+ //! @param connection The connection to the editor-server
+ virtual void SendEditorServerLevelDataPacket(AzNetworking::IConnection* connection) = 0;
+ };
+ using MultiplayerEditorServerRequestBus = AZ::EBus;
+} // namespace Multiplayer
diff --git a/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml
index dd553a2413..b8b880d03c 100644
--- a/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml
+++ b/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml
@@ -4,13 +4,15 @@
-
-
+
+
+
+
-
+
diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp
index a30ab207b4..d2b0ca7095 100644
--- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp
+++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
#include
@@ -17,7 +18,6 @@
#include
#include
#include
-#include
#include
#include
@@ -35,13 +35,28 @@ namespace Multiplayer
m_networkEditorInterface->SetTimeoutMs(AZ::TimeMs{ 0 }); // Disable timeouts on this network interface
if (editorsv_isDedicated)
{
- uint16_t editorServerPort = DefaultServerEditorPort;
- if (auto console = AZ::Interface::Get(); console)
+ uint16_t editorsv_port = DefaultServerEditorPort;
+ const auto console = AZ::Interface::Get();
+ if (console->GetCvarValue("editorsv_port", editorsv_port) != AZ::GetValueResult::Success)
{
- console->GetCvarValue("editorsv_port", editorServerPort);
+ AZ_Assert( false,
+ "MultiplayerEditorConnection failed! Could not find the editorsv_port cvar; we may not be able to connect to the editor's port! Please update this code to use a valid cvar!")
+ }
+
+ AZ_Assert(m_networkEditorInterface, "MP Editor Network Interface was unregistered before Editor Server could start listening.")
+
+ // Check if there's already an Editor out there waiting to connect
+ const ConnectionId editorServerToEditorConnectionId = m_networkEditorInterface->Connect(IpAddress(LocalHost.data(), editorsv_port, ProtocolType::Tcp));
+
+ // If there wasn't an Editor waiting for this server to start, then assume this is an editor-server launched by hand... listen and wait for the editor to request a connection
+ if (editorServerToEditorConnectionId == InvalidConnectionId)
+ {
+ m_networkEditorInterface->Listen(editorsv_port);
+ }
+ else
+ {
+ m_networkEditorInterface->SendReliablePacket(editorServerToEditorConnectionId, MultiplayerEditorPackets::EditorServerReadyForLevelData());
}
- AZ_Assert(m_networkEditorInterface, "MP Editor Network Interface was unregistered before Editor Server could start listening.");
- m_networkEditorInterface->Listen(editorServerPort);
}
}
@@ -49,7 +64,7 @@ namespace Multiplayer
(
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const IPacketHeader& packetHeader,
- [[maybe_unused]] MultiplayerEditorPackets::EditorServerInit& packet
+ [[maybe_unused]] MultiplayerEditorPackets::EditorServerLevelData& packet
)
{
// Editor Server Init is intended for non-release targets
@@ -76,7 +91,7 @@ namespace Multiplayer
AZ::Data::AssetData* assetDatum = AZ::Utils::LoadObjectFromStream(m_byteStream, nullptr);
if (!assetDatum)
{
- AZLOG_ERROR("EditorServerInit packet contains no asset data. Asset: %s", assetHint.c_str());
+ AZLOG_ERROR("EditorServerLevelData packet contains no asset data. Asset: %s", assetHint.c_str())
return false;
}
assetSize = m_byteStream.GetCurPos() - assetSize;
@@ -105,18 +120,21 @@ namespace Multiplayer
// Load the level via the root spawnable that was registered
const AZ::CVarFixedString loadLevelString = "LoadLevel Root.spawnable";
- AZ::Interface::Get()->PerformCommand(loadLevelString.c_str());
+ const auto console = AZ::Interface::Get();
+ console->PerformCommand(loadLevelString.c_str());
// Setup the normal multiplayer connection
AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::DedicatedServer);
INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpNetworkInterfaceName));
- uint16_t serverPort = DefaultServerPort;
- if (auto console = AZ::Interface::Get(); console)
+ uint16_t sv_port = DefaultServerPort;
+ if (console->GetCvarValue("sv_port", sv_port) != AZ::GetValueResult::Success)
{
- console->GetCvarValue("sv_port", serverPort);
+ AZ_Assert(false,
+ "MultiplayerEditorConnection::HandleRequest for EditorServerLevelData failed! Could not find the sv_port cvar; we won't be able to listen on the correct port for incoming network messages! Please update this code to use a valid cvar!")
}
- networkInterface->Listen(serverPort);
+
+ networkInterface->Listen(sv_port);
AZLOG_INFO("Editor Server completed asset receive, responding to Editor...");
return connection->SendReliablePacket(MultiplayerEditorPackets::EditorServerReady());
@@ -125,6 +143,15 @@ namespace Multiplayer
return true;
}
+ bool MultiplayerEditorConnection::HandleRequest(
+ [[maybe_unused]] AzNetworking::IConnection* connection,
+ [[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader,
+ [[maybe_unused]] MultiplayerEditorPackets::EditorServerReadyForLevelData& packet)
+ {
+ MultiplayerEditorServerRequestBus::Broadcast(&MultiplayerEditorServerRequestBus::Events::SendEditorServerLevelDataPacket, connection);
+ return true;
+ }
+
bool MultiplayerEditorConnection::HandleRequest
(
[[maybe_unused]] AzNetworking::IConnection* connection,
@@ -132,23 +159,29 @@ namespace Multiplayer
[[maybe_unused]] MultiplayerEditorPackets::EditorServerReady& packet
)
{
- if (connection->GetConnectionRole() == ConnectionRole::Connector)
- {
- // Receiving this packet means Editor sync is done, disconnect
- connection->Disconnect(AzNetworking::DisconnectReason::TerminatedByClient, AzNetworking::TerminationEndpoint::Local);
+ // Receiving this packet means Editor sync is done, disconnect
+ connection->Disconnect(AzNetworking::DisconnectReason::TerminatedByClient, AzNetworking::TerminationEndpoint::Local);
+ const auto console = AZ::Interface::Get();
+ AZ::CVarFixedString editorsv_serveraddr = AZ::CVarFixedString(LocalHost);
+ uint16_t sv_port = DefaultServerEditorPort;
- if (auto console = AZ::Interface::Get(); console)
- {
- AZ::CVarFixedString remoteAddress;
- uint16_t remotePort;
- if (console->GetCvarValue("editorsv_serveraddr", remoteAddress) != AZ::GetValueResult::ConsoleVarNotFound &&
- console->GetCvarValue("sv_port", remotePort) != AZ::GetValueResult::ConsoleVarNotFound)
- {
- // Connect the Editor to the editor server for Multiplayer simulation
- AZ::Interface::Get()->Connect(remoteAddress.c_str(), remotePort);
- }
- }
+ if (console->GetCvarValue("sv_port", sv_port) != AZ::GetValueResult::Success)
+ {
+ AZ_Assert(false,
+ "MultiplayerEditorConnection::HandleRequest for EditorServerReady failed! Could not find the sv_port cvar; we may not be able to "
+ "connect to the correct port for incoming network messages! Please update this code to use a valid cvar!")
}
+
+ if (console->GetCvarValue("editorsv_serveraddr", editorsv_serveraddr) != AZ::GetValueResult::Success)
+ {
+ AZ_Assert(false,
+ "MultiplayerEditorConnection::HandleRequest for EditorServerReady failed! Could not find the editorsv_serveraddr cvar; we may not be able to "
+ "connect to the correct port for incoming network messages! Please update this code to use a valid cvar!")
+ }
+
+ // Connect the Editor to the editor server for Multiplayer simulation
+ AZ::Interface::Get()->Connect(editorsv_serveraddr.c_str(), sv_port);
+
return true;
}
@@ -171,26 +204,5 @@ namespace Multiplayer
{
return MultiplayerEditorPackets::DispatchPacket(connection, packetHeader, serializer, *this);
}
-
- void MultiplayerEditorConnection::OnPacketLost([[maybe_unused]] IConnection* connection, [[maybe_unused]] PacketId packetId)
- {
- ;
- }
-
- void MultiplayerEditorConnection::OnDisconnect([[maybe_unused]] AzNetworking::IConnection* connection, [[maybe_unused]] DisconnectReason reason, [[maybe_unused]] TerminationEndpoint endpoint)
- {
- bool editorLaunch = false;
- if (auto console = AZ::Interface::Get(); console)
- {
- console->GetCvarValue("editorsv_launch", editorLaunch);
- }
-
- if (editorsv_isDedicated && editorLaunch && m_networkEditorInterface->GetConnectionSet().GetConnectionCount() == 1)
- {
- if (m_networkEditorInterface->GetPort() != 0)
- {
- m_networkEditorInterface->StopListening();
- }
- }
- }
+
}
diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h
index b93c830cd0..3828d751f0 100644
--- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h
+++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h
@@ -10,8 +10,6 @@
#include
-#include
-#include
#include
#include
#include
@@ -33,7 +31,8 @@ namespace Multiplayer
MultiplayerEditorConnection();
~MultiplayerEditorConnection() = default;
- bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerInit& packet);
+ bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerReadyForLevelData& packet);
+ bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerLevelData& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerReady& packet);
//! IConnectionListener interface
@@ -41,8 +40,8 @@ namespace Multiplayer
AzNetworking::ConnectResult ValidateConnect(const AzNetworking::IpAddress& remoteAddress, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override;
void OnConnect(AzNetworking::IConnection* connection) override;
AzNetworking::PacketDispatchResult OnPacketReceived(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override;
- void OnPacketLost(AzNetworking::IConnection* connection, AzNetworking::PacketId packetId) override;
- void OnDisconnect(AzNetworking::IConnection* connection, AzNetworking::DisconnectReason reason, AzNetworking::TerminationEndpoint endpoint) override;
+ void OnPacketLost([[maybe_unused]]AzNetworking::IConnection* connection, [[maybe_unused]]AzNetworking::PacketId packetId) override {}
+ void OnDisconnect([[maybe_unused]]AzNetworking::IConnection* connection, [[maybe_unused]]AzNetworking::DisconnectReason reason, [[maybe_unused]]AzNetworking::TerminationEndpoint endpoint) override {}
//! @}
private:
diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp
index 55a05e33a7..2651981bce 100644
--- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp
+++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp
@@ -71,6 +71,7 @@ namespace Multiplayer
{
AzFramework::GameEntityContextEventBus::Handler::BusConnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
+ MultiplayerEditorServerRequestBus::Handler::BusConnect();
AZ::Interface::Get()->AddServerAcceptanceReceivedHandler(m_serverAcceptanceReceivedHandler);
}
@@ -78,6 +79,7 @@ namespace Multiplayer
{
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
AzFramework::GameEntityContextEventBus::Handler::BusDisconnect();
+ MultiplayerEditorServerRequestBus::Handler::BusDisconnect();
}
void MultiplayerEditorSystemComponent::NotifyRegisterViews()
@@ -107,8 +109,8 @@ namespace Multiplayer
m_serverProcess->TerminateProcess(0);
m_serverProcess = nullptr;
}
- INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName));
- if (editorNetworkInterface)
+
+ if (INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)))
{
editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByClient);
}
@@ -191,53 +193,49 @@ namespace Multiplayer
}
const AZ::CVarFixedString remoteAddress = editorsv_serveraddr;
- if (editorsv_launch && LocalHost == remoteAddress)
+ if (editorsv_launch)
{
+ if (LocalHost != remoteAddress)
+ {
+ AZ_Warning(
+ "MultiplayerEditor", false,
+ "Launching EditorServer skipped because incompatible cvars. editorsv_launch=true, meaning you want to launch an editor-server on this machine, but the editorsv_serveraddr is %s instead of the local address (127.0.0.1). "
+ "Please either set editorsv_launch=false and keep the remote editor-server, or set editorsv_launch=true and editorsv_serveraddr=127.0.0.1.",
+ remoteAddress.c_str())
+ return;
+ }
+
+ // Begin listening for MPEditor packets before we launch the editor-server.
+ // The editor-server will send us (the editor) an "EditorServerReadyForLevelData" packet to let us know it's ready to receive data.
+ INetworkInterface* editorNetworkInterface =
+ AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName));
+ AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect.");
+ editorNetworkInterface->Listen(editorsv_port);
+
+ // Launch the editor-server
m_serverProcess = LaunchEditorServer();
}
-
- // Spawnable library needs to be rebuilt since now we have newly registered in-memory spawnable assets
- AZ::Interface::Get()->BuildSpawnablesList();
-
- // Now that the server has launched, attempt to connect the NetworkInterface
- INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName));
- AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect.");
- m_editorConnId = editorNetworkInterface->Connect(
- AzNetworking::IpAddress(remoteAddress.c_str(), editorsv_port, AzNetworking::ProtocolType::Tcp));
-
- if (m_editorConnId == AzNetworking::InvalidConnectionId)
+ else
{
- AZ_Warning(
- "MultiplayerEditor", false,
- "Could not connect to server targeted by Editor. If using a local server, check that it's built and editorsv_launch is true.");
- return;
- }
+ // Editorsv_launch=false, so we're expecting an editor-server already exists.
+ // Connect to the editor-server and then send the EditorServerLevelData packet.
+ INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName));
+ AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect.")
+
+ m_editorConnId = editorNetworkInterface->Connect(AzNetworking::IpAddress(remoteAddress.c_str(), editorsv_port, AzNetworking::ProtocolType::Tcp));
- // Read the buffer into EditorServerInit packets until we've flushed the whole thing
- byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN);
-
- while (byteStream.GetCurPos() < byteStream.GetLength())
- {
- MultiplayerEditorPackets::EditorServerInit packet;
- auto& outBuffer = packet.ModifyAssetData();
-
- // Size the packet's buffer appropriately
- size_t readSize = outBuffer.GetCapacity();
- size_t byteStreamSize = byteStream.GetLength() - byteStream.GetCurPos();
- if (byteStreamSize < readSize)
+ if (m_editorConnId == AzNetworking::InvalidConnectionId)
{
- readSize = byteStreamSize;
+ AZ_Warning(
+ "MultiplayerEditor", false,
+ "Editor multiplayer game-mode failed! Could not connect to an editor-server. editorsv_launch is false so we're assuming you're running your own editor-server at editorsv_serveraddr(%s) on editorsv_port(%i). "
+ "Either set editorsv_launch=true so the editor launches an editor-server for you, or launch your own editor-server by hand before entering game-mode. Remember editor-servers must use editorsv_isDedicated=true.",
+ remoteAddress.c_str(),
+ static_cast < uint16_t>(editorsv_port))
+ return;
}
- outBuffer.Resize(readSize);
- byteStream.Read(readSize, outBuffer.GetBuffer());
-
- // If we've run out of buffer, mark that we're done
- if (byteStream.GetCurPos() == byteStream.GetLength())
- {
- packet.SetLastUpdate(true);
- }
- editorNetworkInterface->SendReliablePacket(m_editorConnId, packet);
+ SendEditorServerLevelDataPacket(editorNetworkInterface->GetConnectionSet().GetConnection(m_editorConnId));
}
}
}
@@ -253,4 +251,64 @@ namespace Multiplayer
// but since we're in Editor, we're already in the level.
AZ::Interface::Get()->SendReadyForEntityUpdates(true);
}
+
+ void MultiplayerEditorSystemComponent::SendEditorServerLevelDataPacket(AzNetworking::IConnection* connection)
+ {
+ const auto prefabEditorEntityOwnershipInterface = AZ::Interface::Get();
+ if (!prefabEditorEntityOwnershipInterface)
+ {
+ AZ_Error("MultiplayerEditor", prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface unavailable")
+ return;
+ }
+
+ const AZStd::vector>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData();
+
+ AZStd::vector buffer;
+ AZ::IO::ByteContainerStream byteStream(&buffer);
+
+ // Serialize Asset information and AssetData into a potentially large buffer
+ for (const auto& asset : assetData)
+ {
+ AZ::Data::AssetId assetId = asset.GetId();
+ AZStd::string assetHint = asset.GetHint();
+ auto hintSize = aznumeric_cast(assetHint.size());
+
+ byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast(&assetId));
+ byteStream.Write(sizeof(uint32_t), reinterpret_cast(&hintSize));
+ byteStream.Write(assetHint.size(), assetHint.data());
+ AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType());
+ }
+
+ // Spawnable library needs to be rebuilt since now we have newly registered in-memory spawnable assets
+ AZ::Interface::Get()->BuildSpawnablesList();
+
+ // Read the buffer into EditorServerLevelData packets until we've flushed the whole thing
+ byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN);
+
+ while (byteStream.GetCurPos() < byteStream.GetLength())
+ {
+ MultiplayerEditorPackets::EditorServerLevelData editorServerLevelDataPacket;
+ auto& outBuffer = editorServerLevelDataPacket.ModifyAssetData();
+
+ // Size the packet's buffer appropriately
+ size_t readSize = outBuffer.GetCapacity();
+ const size_t byteStreamSize = byteStream.GetLength() - byteStream.GetCurPos();
+ if (byteStreamSize < readSize)
+ {
+ readSize = byteStreamSize;
+ }
+
+ outBuffer.Resize(readSize);
+ byteStream.Read(readSize, outBuffer.GetBuffer());
+
+ // If we've run out of buffer, mark that we're done
+ if (byteStream.GetCurPos() == byteStream.GetLength())
+ {
+ editorServerLevelDataPacket.SetLastUpdate(true);
+ }
+
+ connection->SendReliablePacket(editorServerLevelDataPacket);
+ }
+ }
+
}
diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h
index 4e4c6b677f..a008afc873 100644
--- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h
+++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h
@@ -9,7 +9,7 @@
#pragma once
#include
-
+#include
#include
#include
@@ -35,6 +35,7 @@ namespace Multiplayer
, private AzFramework::GameEntityContextEventBus::Handler
, private AzToolsFramework::EditorEvents::Bus::Handler
, private IEditorNotifyListener
+ , private MultiplayerEditorServerRequestBus::Handler
{
public:
AZ_COMPONENT(MultiplayerEditorSystemComponent, "{9F335CC0-5574-4AD3-A2D8-2FAEF356946C}");
@@ -73,6 +74,11 @@ namespace Multiplayer
void OnGameEntitiesReset() override;
//! @}
+ //! MultiplayerEditorServerRequestBus::Handler
+ //! @{
+ void SendEditorServerLevelDataPacket(AzNetworking::IConnection* connection) override;
+ //! @}
+
IEditor* m_editor = nullptr;
AzFramework::ProcessWatcher* m_serverProcess = nullptr;
AzNetworking::ConnectionId m_editorConnId;
diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake
index a799278203..1376083443 100644
--- a/Gems/Multiplayer/Code/multiplayer_files.cmake
+++ b/Gems/Multiplayer/Code/multiplayer_files.cmake
@@ -13,6 +13,7 @@ set(FILES
Include/Multiplayer/MultiplayerConstants.h
Include/Multiplayer/MultiplayerStats.h
Include/Multiplayer/MultiplayerTypes.h
+ Include/Multiplayer/MultiplayerEditorServerBus.h
Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h
Include/Multiplayer/Components/MultiplayerComponent.h
Include/Multiplayer/Components/MultiplayerComponentRegistry.h
diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp
index 832145d016..59b659a0bf 100644
--- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp
+++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp
@@ -348,19 +348,7 @@ namespace PhysX
LmbrCentral::BoxShapeComponentRequestsBus::EventResult(boxDimensions, GetEntityId(),
&LmbrCentral::BoxShapeComponentRequests::GetBoxDimensions);
- if (m_shapeType != ShapeType::Box)
- {
- m_shapeConfigs.clear();
- m_shapeConfigs.emplace_back(AZStd::make_shared(boxDimensions));
-
- m_shapeType = ShapeType::Box;
- }
- else
- {
- Physics::BoxShapeConfiguration& configuration =
- static_cast(*m_shapeConfigs.back());
- configuration = Physics::BoxShapeConfiguration(boxDimensions);
- }
+ SetShapeConfig(ShapeType::Box, Physics::BoxShapeConfiguration(boxDimensions));
m_shapeConfigs.back()->m_scale = scale;
m_geometryCache.m_boxDimensions = scale * boxDimensions;
@@ -374,19 +362,7 @@ namespace PhysX
const Physics::CapsuleShapeConfiguration& capsuleShapeConfig =
Utils::ConvertFromLmbrCentralCapsuleConfig(lmbrCentralCapsuleShapeConfig);
- if (m_shapeType != ShapeType::Capsule)
- {
- m_shapeConfigs.clear();
- m_shapeConfigs.emplace_back(AZStd::make_shared(capsuleShapeConfig));
-
- m_shapeType = ShapeType::Capsule;
- }
- else
- {
- Physics::CapsuleShapeConfiguration& configuration =
- static_cast(*m_shapeConfigs.back());
- configuration = capsuleShapeConfig;
- }
+ SetShapeConfig(ShapeType::Capsule, capsuleShapeConfig);
m_shapeConfigs.back()->m_scale = scale;
const float scalarScale = scale.GetMaxElement();
@@ -400,19 +376,7 @@ namespace PhysX
LmbrCentral::SphereShapeComponentRequestsBus::EventResult(radius, GetEntityId(),
&LmbrCentral::SphereShapeComponentRequests::GetRadius);
- if (m_shapeType != ShapeType::Sphere)
- {
- m_shapeConfigs.clear();
- m_shapeConfigs.emplace_back(AZStd::make_shared(radius));
-
- m_shapeType = ShapeType::Sphere;
- }
- else
- {
- Physics::SphereShapeConfiguration& configuration =
- static_cast(*m_shapeConfigs.back());
- configuration = Physics::SphereShapeConfiguration(radius);
- }
+ SetShapeConfig(ShapeType::Sphere, Physics::SphereShapeConfiguration(radius));
m_shapeConfigs.back()->m_scale = scale;
m_geometryCache.m_radius = scale.GetMaxElement() * radius;
@@ -455,19 +419,7 @@ namespace PhysX
if (shapeConfig.has_value())
{
- if (m_shapeType != ShapeType::Cylinder)
- {
- m_shapeConfigs.clear();
- m_shapeConfigs.push_back(AZStd::make_shared(shapeConfig.value()));
-
- m_shapeType = ShapeType::Cylinder;
- }
- else
- {
- Physics::CookedMeshShapeConfiguration& configuration =
- static_cast(*m_shapeConfigs.back());
- configuration = Physics::CookedMeshShapeConfiguration(shapeConfig.value());
- }
+ SetShapeConfig(ShapeType::Cylinder, shapeConfig.value());
CreateStaticEditorCollider();
}
diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h
index 431b34b1ae..3422d4959f 100644
--- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h
+++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h
@@ -8,6 +8,7 @@
#pragma once
+#include
#include
#include
#include
@@ -90,12 +91,15 @@ namespace PhysX
void UpdateBoxConfig(const AZ::Vector3& scale);
void UpdateCapsuleConfig(const AZ::Vector3& scale);
void UpdateSphereConfig(const AZ::Vector3& scale);
+ void UpdateCylinderConfig(const AZ::Vector3& scale);
void UpdatePolygonPrismDecomposition();
void UpdatePolygonPrismDecomposition(const AZ::PolygonPrismPtr polygonPrismPtr);
- void RefreshUiProperties();
+ // Helper function to set a specific shape configuration
+ template
+ void SetShapeConfig(ShapeType shapeType, const ConfigType& shapeConfig);
- void UpdateCylinderConfig(const AZ::Vector3& scale);
+ void RefreshUiProperties();
AZ::u32 OnSubdivisionCountChange();
AZ::Crc32 SubdivisionCountVisibility();
@@ -154,4 +158,28 @@ namespace PhysX
AZ::NonUniformScaleChangedEvent::Handler m_nonUniformScaleChangedHandler; //!< Responds to changes in non-uniform scale.
AZ::Vector3 m_currentNonUniformScale = AZ::Vector3::CreateOne(); //!< Caches the current non-uniform scale.
};
+
+ template
+ void EditorShapeColliderComponent::SetShapeConfig(ShapeType shapeType, const ConfigType& shapeConfig)
+ {
+ if (m_shapeType != shapeType)
+ {
+ m_shapeConfigs.clear();
+ m_shapeType = shapeType;
+ }
+
+ if (m_shapeConfigs.empty())
+ {
+ m_shapeConfigs.emplace_back(AZStd::make_shared(shapeConfig));
+ }
+ else
+ {
+ AZ_Assert(m_shapeConfigs.back()->GetShapeType() == shapeConfig.GetShapeType(),
+ "Expected Physics shape configuration with shape type %d but found one with shape type %d.",
+ static_cast(shapeConfig.GetShapeType()), static_cast(m_shapeConfigs.back()->GetShapeType()));
+ ConfigType& configuration =
+ static_cast(*m_shapeConfigs.back());
+ configuration = shapeConfig;
+ }
+ }
} // namespace PhysX
diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp
index b4e1e768c3..3e77ef257a 100644
--- a/Gems/PhysX/Code/Source/Utils.cpp
+++ b/Gems/PhysX/Code/Source/Utils.cpp
@@ -102,7 +102,7 @@ namespace PhysX
const float scaleFactor = (maxHeightBounds <= minHeightBounds) ? 1.0f : AZStd::numeric_limits::max() / halfBounds;
const float heightScale{ 1.0f / scaleFactor };
- const uint8_t physxMaximumMaterialIndex = 0x7f;
+ [[maybe_unused]] const uint8_t physxMaximumMaterialIndex = 0x7f;
// Delete the cached heightfield object if it is there, and create a new one and save in the shape configuration
heightfieldConfig.SetCachedNativeHeightfield(nullptr);
diff --git a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp
index fce427d9d3..06ba4f3e9c 100644
--- a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp
+++ b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp
@@ -320,7 +320,7 @@ namespace PhysXEditorTests
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinderWithNullHeight_HandledGracefully)
{
- ValidateInvalidEditorShapeColliderComponentParams(0.f, 1.f);
+ ValidateInvalidEditorShapeColliderComponentParams(1.f, 0.f);
}
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinderWithNullRadiusAndNullHeight_HandledGracefully)
@@ -338,6 +338,44 @@ namespace PhysXEditorTests
ValidateInvalidEditorShapeColliderComponentParams(0.f, -1.f);
}
+ TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinderSwitchingFromNullHeightToValidHeight_HandledGracefully)
+ {
+ // create an editor entity with a shape collider component and a cylinder shape component
+ EntityPtr editorEntity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
+ editorEntity->CreateComponent();
+ editorEntity->CreateComponent(LmbrCentral::EditorCylinderShapeComponentTypeId);
+ editorEntity->Activate();
+
+ const float validRadius = 1.0f;
+ const float nullHeight = 0.0f;
+ const float validHeight = 1.0f;
+
+ LmbrCentral::CylinderShapeComponentRequestsBus::Event(editorEntity->GetId(),
+ &LmbrCentral::CylinderShapeComponentRequests::SetRadius, validRadius);
+
+ {
+ UnitTest::ErrorHandler dimensionWarningHandler("Negative or zero cylinder dimensions are invalid");
+ UnitTest::ErrorHandler colliderWarningHandler("No Collider or Shape information found when creating Rigid body");
+
+ LmbrCentral::CylinderShapeComponentRequestsBus::Event(editorEntity->GetId(),
+ &LmbrCentral::CylinderShapeComponentRequests::SetHeight, nullHeight);
+
+ EXPECT_EQ(dimensionWarningHandler.GetExpectedWarningCount(), 1);
+ EXPECT_EQ(colliderWarningHandler.GetExpectedWarningCount(), 1);
+ }
+
+ {
+ UnitTest::ErrorHandler dimensionWarningHandler("Negative or zero cylinder dimensions are invalid");
+ UnitTest::ErrorHandler colliderWarningHandler("No Collider or Shape information found when creating Rigid body");
+
+ LmbrCentral::CylinderShapeComponentRequestsBus::Event(editorEntity->GetId(),
+ &LmbrCentral::CylinderShapeComponentRequests::SetHeight, validHeight);
+
+ EXPECT_EQ(dimensionWarningHandler.GetExpectedWarningCount(), 0);
+ EXPECT_EQ(colliderWarningHandler.GetExpectedWarningCount(), 0);
+ }
+ }
+
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithBoxAndRigidBody_CorrectRuntimeComponents)
{
// create an editor entity with a shape collider component and a box shape component
diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp
index 3bc8e9bff9..af2a54f7a5 100644
--- a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp
+++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp
@@ -81,7 +81,7 @@ namespace AZ::SceneAPI::Behaviors
m_exportEventHandler.reset();
}
- AZStd::unique_ptr PrefabGroupBehavior::CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup) const
+ AZStd::unique_ptr PrefabGroupBehavior::CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup, const AZ::IO::Path& relativePath) const
{
using namespace AzToolsFramework::Prefab;
@@ -109,8 +109,12 @@ namespace AZ::SceneAPI::Behaviors
return {};
}
- // validate the PrefabDom will make a valid Prefab template instance
- auto templateId = prefabLoaderInterface->LoadTemplateFromString(sb.GetString(), prefabGroup->GetName().c_str());
+ // The originPath we pass to LoadTemplateFromString must be the relative path of the file
+ AZ::IO::Path templateName(prefabGroup->GetName());
+ templateName.ReplaceExtension(AZ::Prefab::PrefabGroupAssetHandler::s_Extension);
+ templateName = relativePath / templateName;
+
+ auto templateId = prefabLoaderInterface->LoadTemplateFromString(sb.GetString(), templateName.Native().c_str());
if (templateId == InvalidTemplateId)
{
AZ_Error("prefab", false, "PrefabGroup(%s) Could not write load template", prefabGroup->GetName().c_str());
@@ -136,22 +140,8 @@ namespace AZ::SceneAPI::Behaviors
const SceneData::PrefabGroup* prefabGroup,
const rapidjson::Document& doc) const
{
- // Retrieve source asset info so we can get a string with the relative path to the asset
- bool assetInfoResult;
- Data::AssetInfo info;
- AZStd::string watchFolder;
- AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
- assetInfoResult,
- &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath,
- context.GetScene().GetSourceFilename().c_str(),
- info,
- watchFolder);
-
- AZ::IO::FixedMaxPath assetPath(info.m_relativePath);
- assetPath.ReplaceFilename(prefabGroup->GetName().c_str());
-
AZStd::string filePath = AZ::SceneAPI::Utilities::FileUtilities::CreateOutputFileName(
- assetPath.c_str(),
+ prefabGroup->GetName().c_str(),
context.GetOutputDirectory(),
AZ::Prefab::PrefabGroupAssetHandler::s_Extension);
@@ -174,7 +164,7 @@ namespace AZ::SceneAPI::Behaviors
const auto bytesWritten = fileStream.Write(sb.GetSize(), sb.GetString());
if (bytesWritten > 1)
{
- AZ::u32 subId = AZ::Crc32(assetPath.c_str());
+ AZ::u32 subId = AZ::Crc32(prefabGroup->GetName().c_str());
context.GetProductList().AddProduct(
filePath,
context.GetScene().GetSourceGuid(),
@@ -206,9 +196,14 @@ namespace AZ::SceneAPI::Behaviors
return AZ::SceneAPI::Events::ProcessingResult::Ignored;
}
+ // Get the relative path of the source and then take just the path portion of it (no file name)
+ AZ::IO::Path relativePath = context.GetScene().GetSourceFilename();
+ relativePath = relativePath.LexicallyRelative(AZStd::string_view(context.GetScene().GetWatchFolder()));
+ relativePath = relativePath.ParentPath();
+
for (const auto* prefabGroup : prefabGroupCollection)
{
- auto result = CreateProductAssetData(prefabGroup);
+ auto result = CreateProductAssetData(prefabGroup, relativePath);
if (!result)
{
return Events::ProcessingResult::Failure;
diff --git a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h
index b57d30695a..5e787c316b 100644
--- a/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h
+++ b/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -42,7 +43,7 @@ namespace AZ::SceneAPI::Behaviors
private:
Events::ProcessingResult OnPrepareForExport(Events::PreExportEventContext& context) const;
- AZStd::unique_ptr CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup) const;
+ AZStd::unique_ptr CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup, const AZ::IO::Path& relativePath) const;
bool WriteOutProductAsset(
Events::PreExportEventContext& context,
diff --git a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype
index 01b862c4f8..235079d95e 100644
--- a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype
+++ b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype
@@ -92,13 +92,58 @@
{
"id": "heightmapImage",
"displayName": "Heightmap Image",
- "description": "Heightmap of the terrain, controlled by the runtime.",
+ "description": "Heightmap of the terrain. Controlled by the runtime.",
+ "visibility": "Hidden",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_heightmapImage"
}
},
+ {
+ "id": "detailMaterialIdImage",
+ "displayName": "Detail Material Id Image",
+ "description": "Texture containing detail material Ids and weights. Controlled by the runtime.",
+ "visibility": "Hidden",
+ "type": "Image",
+ "connection": {
+ "type": "ShaderInput",
+ "id": "m_detailMaterialIdImage"
+ }
+ },
+ {
+ "id": "detailMaterialIdCenter",
+ "displayName": "Detail Material Id Image Center",
+ "description": "The center position of the detail material Id image. Controlled by the runtime.",
+ "visibility": "Hidden",
+ "type": "Vector2",
+ "connection": {
+ "type": "ShaderInput",
+ "id": "m_detailMaterialIdImageCenter"
+ }
+ },
+ {
+ "id": "detailAabb",
+ "displayName": "Detail material bounds in 2d",
+ "description": "The 2d world space bounds of the detail id material. Controlled by the runtime.",
+ "visibility": "Hidden",
+ "type": "Vector4",
+ "connection": {
+ "type": "ShaderInput",
+ "id": "m_detailAabb"
+ }
+ },
+ {
+ "id": "detailHalfPixelUv",
+ "displayName": "Detail texture half pixel uv size",
+ "description": "Uv size of a half pixel in the detail material id texture. Controlled by the runtime.",
+ "visibility": "Hidden",
+ "type": "float",
+ "connection": {
+ "type": "ShaderInput",
+ "id": "m_detailHalfPixelUv"
+ }
+ },
{
"id": "detailTextureMultiplier",
"displayName": "Detail Texture UV Multiplier",
diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli
index 72c1af953c..dc6f65207b 100644
--- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli
+++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli
@@ -93,10 +93,15 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial
{
Texture2D m_heightmapImage;
+ Texture2D m_detailMaterialIdImage;
+ float2 m_detailMaterialIdImageCenter;
float m_detailTextureMultiplier;
float m_detailFadeDistance;
float m_detailFadeLength;
+ float4 m_detailAabb;
+ float m_detailHalfPixelUv;
+
Sampler HeightmapSampler
{
MinFilter = Linear;
@@ -117,6 +122,15 @@ ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial
MaxAnisotropy = 16;
};
+ Sampler m_detailSampler
+ {
+ AddressU = Wrap;
+ AddressV = Wrap;
+ MinFilter = Point;
+ MagFilter = Point;
+ MipFilter = Point;
+ };
+
// Base Color
float3 m_baseColor;
float m_baseColorFactor;
diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl
index 750cd2fb29..a15ad931f6 100644
--- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl
+++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
struct VSOutput
{
@@ -29,6 +30,8 @@ struct VSOutput
float2 m_uv : UV1;
};
+option bool o_debugDetailMaterialIds = false;
+
VSOutput TerrainPBR_MainPassVS(VertexInput IN)
{
VSOutput OUT;
@@ -121,6 +124,48 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN)
float3 detailColor = GetBaseColorInput(TerrainMaterialSrg::m_baseColorMap, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_baseColor.rgb, o_baseColor_useTexture);
float3 blendedColor = BlendBaseColor(lerp(detailColor, TerrainMaterialSrg::m_baseColor.rgb, detailFactor), macroColor, TerrainMaterialSrg::m_baseColorFactor, o_baseColorTextureBlendMode, o_baseColor_useTexture);
+ // ------- Debug detail materials using random colors -------
+ // This assigns a random color to each material, turns off any kind of distance fading, and draws a black line at the texture edges.
+ if (o_debugDetailMaterialIds)
+ {
+ float2 detailRegionMin = TerrainMaterialSrg::m_detailAabb.xy;
+ float2 detailRegionMax = TerrainMaterialSrg::m_detailAabb.zw;
+ float2 detailRegionUv = (surface.position.xy - detailRegionMin) / (detailRegionMax - detailRegionMin);
+ if (all(detailRegionUv > TerrainMaterialSrg::m_detailHalfPixelUv) && all(detailRegionUv < 1.0 - TerrainMaterialSrg::m_detailHalfPixelUv))
+ {
+ detailRegionUv += TerrainMaterialSrg::m_detailMaterialIdImageCenter - (0.5);
+
+ uint material1 = TerrainMaterialSrg::m_detailMaterialIdImage.GatherRed(TerrainMaterialSrg::m_detailSampler, detailRegionUv, 0).r;
+ uint material2 = TerrainMaterialSrg::m_detailMaterialIdImage.GatherGreen(TerrainMaterialSrg::m_detailSampler, detailRegionUv, 0).r;
+ float blend = float(TerrainMaterialSrg::m_detailMaterialIdImage.GatherBlue(TerrainMaterialSrg::m_detailSampler, detailRegionUv, 0).r) / 0xFF;
+
+ float3 material1Color = float3(0.1, 0.1, 0.1);
+ float3 material2Color = float3(0.1, 0.1, 0.1);
+
+ // Get a reasonably random hue for the material id
+ if (material1 != 255)
+ {
+ float hue1 = (material1 * 25043 % 256) / 256.0;
+ material1Color = HsvToRgb(float3(hue1, 1.0, 1.0));
+ }
+ if (material2 != 255)
+ {
+ float hue2 = (material2 * 25043 % 256) / 256.0;
+ material2Color = HsvToRgb(float3(hue2, 1.0, 1.0));
+ }
+
+ blendedColor = lerp(material1Color, material2Color, blend);
+ float seamBlend = 0.0;
+ const float halfLineWidth = 1.0 / 2048.0;
+ if (any(abs(detailRegionUv) % 1.0 < halfLineWidth) || any(abs(detailRegionUv) % 1.0 > 1.0 - halfLineWidth))
+ {
+ seamBlend = 1.0;
+ }
+ blendedColor = lerp(blendedColor, float3(0.0, 0.0, 0.0), seamBlend); // draw texture seams
+ blendedColor = pow(blendedColor , 2.2);
+ }
+ }
+
// ------- Specular -------
float specularF0Factor = GetSpecularInput(TerrainMaterialSrg::m_specularF0Map, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_specularF0Factor, o_specularF0_useTexture);
specularF0Factor = lerp(specularF0Factor, 0.5, detailFactor);
diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp
index d8b7308ef7..bd65cf6abc 100644
--- a/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp
+++ b/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp
@@ -32,16 +32,22 @@ namespace Terrain
AZ::EditContext* edit = serialize->GetEditContext();
if (edit)
{
- edit->Class(
- "Terrain World Component", "Data required for the terrain system to run")
+ edit->Class