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/PythonAssetBuilder/mock_asset_builder.py b/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/mock_asset_builder.py
index 443a420b61..a6e8b97b63 100644
--- a/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/mock_asset_builder.py
+++ b/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/mock_asset_builder.py
@@ -23,7 +23,7 @@ def create_jobs(request):
jobDescriptorList = []
for platformInfo in request.enabledPlatforms:
jobDesc = azlmbr.asset.builder.JobDescriptor()
- jobDesc.jobKey = jobKeyName
+ jobDesc.jobKey = f'{jobKeyName}-{platformInfo.identifier}'
jobDesc.set_platform_identifier(platformInfo.identifier)
jobDescriptorList.append(jobDesc)
@@ -38,7 +38,7 @@ def on_create_jobs(args):
return create_jobs(request)
except:
log_exception_traceback()
- # returing back a default CreateJobsResponse() records an asset error
+ # returning back a default CreateJobsResponse() records an asset error
return azlmbr.asset.builder.CreateJobsResponse()
def process_file(request):
@@ -58,6 +58,7 @@ def process_file(request):
fileOutput = open(tempFilename, "w")
fileOutput.write('{}')
fileOutput.close()
+ print(f'Wrote mock asset file: {tempFilename}')
# generate a product asset file entry
subId = binascii.crc32(mockFilename.encode())
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/Asset/AssetManagerBus.h b/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h
index f8e263d6b0..f76ea19589 100644
--- a/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h
+++ b/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h
@@ -65,8 +65,35 @@ namespace AZ
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides - Application is a singleton
- static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
- typedef AZStd::recursive_mutex MutexType;
+ static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+ using MutexType = AZStd::recursive_mutex;
+
+ static constexpr bool EnableEventQueue = true;
+ using EventQueueMutexType = AZStd::mutex;
+ struct PostThreadDispatchInvoker
+ {
+ ~PostThreadDispatchInvoker();
+ };
+
+ template
+ struct ThreadDispatchLockGuard
+ {
+ ThreadDispatchLockGuard(DispatchMutex& contextMutex)
+ : m_lock{ contextMutex }
+ {}
+ ThreadDispatchLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock)
+ : m_lock{ contextMutex, adopt_lock }
+ {}
+ ThreadDispatchLockGuard(const ThreadDispatchLockGuard&) = delete;
+ ThreadDispatchLockGuard& operator=(const ThreadDispatchLockGuard&) = delete;
+ private:
+ PostThreadDispatchInvoker m_threadPolicyInvoker;
+ using LockType = AZStd::conditional_t, AZStd::scoped_lock>;
+ LockType m_lock;
+ };
+
+ template
+ using DispatchLockGuard = ThreadDispatchLockGuard;
//////////////////////////////////////////////////////////////////////////
virtual ~AssetCatalogRequests() = default;
@@ -200,6 +227,17 @@ namespace AZ
using AssetCatalogRequestBus = AZ::EBus;
+ inline AssetCatalogRequests::PostThreadDispatchInvoker::~PostThreadDispatchInvoker()
+ {
+ if (!AssetCatalogRequestBus::IsInDispatchThisThread())
+ {
+ if (AssetCatalogRequestBus::QueuedEventCount())
+ {
+ AssetCatalogRequestBus::ExecuteQueuedEvents();
+ }
+ }
+ }
+
/*
* Events that AssetManager listens for
*/
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
index ba66d56379..df8db79db0 100644
--- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
@@ -14,6 +14,7 @@
#include
#include
+#include
#include
#include
@@ -44,8 +45,6 @@
#include
#include
-#include
-#include
#include
#include
@@ -216,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);
}
@@ -506,6 +500,16 @@ namespace AZ
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
+ // The /O3DE/Application/LifecycleEvents array contains a valid set of lifecycle events
+ // Those lifecycle events are normally read from the /Registry
+ // which isn't merged until ComponentApplication::Create invokes MergeSettingsToRegistry
+ // So pre-populate the valid lifecycle even entries
+ ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "SystemAllocatorCreated");
+ ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "SettingsRegistryAvailable");
+ ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "ConsoleAvailable");
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SystemAllocatorCreated", R"({})");
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SettingsRegistryAvailable", R"({})");
+
// Create the Module Manager
m_moduleManager = AZStd::make_unique();
@@ -520,6 +524,7 @@ namespace AZ
m_ownsConsole = true;
m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
m_settingsRegistryConsoleFunctors = AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_settingsRegistry, *m_console);
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleAvailable", R"({})");
}
}
@@ -551,6 +556,7 @@ namespace AZ
{
AZ::Interface::Unregister(m_console);
delete m_console;
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleUnavailable", R"({})");
}
m_moduleManager.reset();
@@ -558,6 +564,8 @@ namespace AZ
if (AZ::SettingsRegistry::Get() == m_settingsRegistry.get())
{
SettingsRegistry::Unregister(m_settingsRegistry.get());
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SettingsRegistryUnavailable", R"({})");
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SystemAllocatorPendingDestruction", R"({})");
}
m_settingsRegistry.reset();
@@ -672,6 +680,8 @@ namespace AZ
ReflectionEnvironment::GetReflectionManager()->Reflect(azrtti_typeid(this), [this](ReflectContext* context) {Reflect(context); });
RegisterCoreComponents();
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerAvailable", R"({})");
+
TickBus::AllowFunctionQueuing(true);
SystemTickBus::AllowFunctionQueuing(true);
@@ -691,6 +701,7 @@ namespace AZ
// Load the actual modules
LoadModules();
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsLoaded", R"({})");
// Execute user.cfg after modules have been loaded but before processing any command-line overrides
AZ::IO::FixedMaxPath platformCachePath;
@@ -756,12 +767,14 @@ namespace AZ
m_entities.rehash(0); // force free all memory
DestroyReflectionManager();
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerUnavailable", R"({})");
static_cast(m_settingsRegistry.get())->ClearNotifiers();
static_cast(m_settingsRegistry.get())->ClearMergeEvents();
// Uninit and unload any dynamic modules.
m_moduleManager->UnloadModules();
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsUnloaded", R"({})");
NameDictionary::Destroy();
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
index f2b1bb8905..6df93aff4e 100644
--- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
@@ -175,6 +175,8 @@ namespace AZ
bool m_loadDynamicModules = true;
//! Used by test fixtures to ensure reflection occurs to edit context.
bool m_createEditContext = false;
+ //! Indicates whether the AssetCatalog.xml should be loaded by default in Application::StartCommon
+ bool m_loadAssetCatalog = true;
};
ComponentApplication();
@@ -356,7 +358,7 @@ namespace AZ
/// Calculates the root directory of the engine.
void CalculateEngineRoot();
- /// Calculates the directory where the bootstrap.cfg file resides.
+ /// Deprecated: The term "AppRoot" has no meaning
void CalculateAppRoot();
template
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp
new file mode 100644
index 0000000000..533d55ed27
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+#include
+
+namespace AZ::ComponentApplicationLifecycle
+{
+ bool ValidateEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName)
+ {
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+ using Type = SettingsRegistryInterface::Type;
+ FixedValueString eventRegistrationKey{ ApplicationLifecycleEventRegistrationKey };
+ eventRegistrationKey += '/';
+ eventRegistrationKey += eventName;
+ return settingsRegistry.GetType(eventRegistrationKey) == Type::Object;
+ }
+
+ bool SignalEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName, AZStd::string_view eventValue)
+ {
+ using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+ using Format = AZ::SettingsRegistryInterface::Format;
+
+ if (!ValidateEvent(settingsRegistry, eventName))
+ {
+ AZ_Warning("ComponentApplicationLifecycle", false, R"(Cannot signal event %.*s. Name does is not a field of object "%.*s".)"
+ R"( Please make sure the entry exists in the '/Registry/application_lifecycle_events.setreg")"
+ " or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey));
+ return false;
+ }
+ auto eventRegistrationKey = FixedValueString::format("%.*s/%.*s", AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey),
+ AZ_STRING_ARG(eventName));
+
+ return settingsRegistry.MergeSettings(eventValue, Format::JsonMergePatch, eventRegistrationKey);
+ }
+
+ bool RegisterEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName)
+ {
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+ using Format = AZ::SettingsRegistryInterface::Format;
+
+ if (!ValidateEvent(settingsRegistry, eventName))
+ {
+ FixedValueString eventRegistrationKey{ ApplicationLifecycleEventRegistrationKey };
+ eventRegistrationKey += '/';
+ eventRegistrationKey += eventName;
+ return settingsRegistry.MergeSettings(R"({})", Format::JsonMergePatch, eventRegistrationKey);
+ }
+
+ return true;
+ }
+
+ bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler,
+ AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName)
+ {
+ using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+ using Type = AZ::SettingsRegistryInterface::Type;
+ using NotifyEventHandler = AZ::SettingsRegistryInterface::NotifyEventHandler;
+
+ if (!ValidateEvent(settingsRegistry, eventName))
+ {
+ AZ_Warning("ComponentApplicationLifecycle", false, R"(Cannot register event %.*s. Name does is not a field of object "%.*s".)"
+ R"( Please make sure the entry exists in the '/Registry/application_lifecycle_events.setreg")"
+ " or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey));
+ return false;
+ }
+ auto eventNameRegistrationKey = FixedValueString::format("%.*s/%.*s", AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey),
+ AZ_STRING_ARG(eventName));
+ auto lifecycleCallback = [callback = AZStd::move(callback), eventNameRegistrationKey](AZStd::string_view path, Type type)
+ {
+ if (path == eventNameRegistrationKey)
+ {
+ callback(path, type);
+ }
+ };
+
+ handler = NotifyEventHandler(AZStd::move(lifecycleCallback));
+ settingsRegistry.RegisterNotifier(handler);
+
+ return true;
+ }
+}
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h
new file mode 100644
index 0000000000..5fc6c4b2c3
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace AZ::ComponentApplicationLifecycle
+{
+ //! Root Key where lifecycle events should be registered under
+ inline constexpr AZStd::string_view ApplicationLifecycleEventRegistrationKey = "/O3DE/Runtime/Application/LifecycleEvents";
+
+
+ //! Validates that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey
+ //! @param settingsRegistry registry where @eventName will be searched
+ //! @param eventName name of key that validated that exists as an element in the ApplicationLifecycleEventRegistrationKey array
+ //! @return true if the @eventName was found in the ApplicationLifecycleEventRegistrationKey array
+ bool ValidateEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName);
+
+ //! Wrapper around setting a value underneath the ApplicationLifecycleEventRegistrationKey
+ //! It validates if the @eventName is is part of the ApplicationLifecycleEventRegistrationKey array
+ //! It then appends the @eventName to the ApplicationLifecycleEventRegistrationKey merges the @eventValue into
+ //! the SettingsRegistry at that key
+ //! NOTE: This function should only be invoked from ComponentApplication and its derived classes
+ //! @param settingsRegistry registry where eventName should be set
+ //! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to signal
+ //! @param eventValue JSON Object that will be merged into the SettingsRegistry at /
+ //! @return true if the eventValue was successfully merged at the /
+ bool SignalEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName, AZStd::string_view eventValue);
+
+ //! Register that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey
+ //! @param settingsRegistry registry where @eventName will be searched
+ //! @param eventName name of key that will be stored in the ApplicationLifecycleEventRegistrationKey array
+ //! @return true if the event passed validation or the eventName was stored in the ApplicationLifecycleEventRegistrationKey array
+ bool RegisterEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName);
+
+ //! Wrapper around registering the NotifyEventHandler with the SettingsRegistry for the specified event
+ //! It validates if the @eventName is is part of the ApplicationLifecycleEventRegistrationKey array and if
+ //! so moves the @callback into @handler and then registers the handler with the SettingsRegistry NotifyEvent
+ //! @param settingsRegistry registry where handler will be registered
+ //! @param handler handler where callback will be moved into and then registered with the SettingsRegistry
+ //! if the specified @eventName passes validation
+ //! @param callback will be moved into the handler if the specified @eventName is valid
+ //! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to register
+ //! @return true if the handler was registered with the SettingsRegistry NotifyEvent
+ bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler,
+ AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName);
+}
diff --git a/Code/Framework/AzCore/AzCore/Console/IConsole.h b/Code/Framework/AzCore/AzCore/Console/IConsole.h
index 73d17ac65a..aef163d22b 100644
--- a/Code/Framework/AzCore/AzCore/Console/IConsole.h
+++ b/Code/Framework/AzCore/AzCore/Console/IConsole.h
@@ -262,6 +262,6 @@ static constexpr AZ::ThreadSafety ConsoleThreadSafety<_TYPE, std::enable_if_t Functor##_FUNCTION(#_FUNCTION, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION)
+ inline AZ::ConsoleFunctor Functor##_FUNCTION(_NAME, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION)
#define AZ_CONSOLEFREEFUNC(...) AZ_MACRO_SPECIALIZE(AZ_CONSOLEFREEFUNC_, AZ_VA_NUM_ARGS(__VA_ARGS__), (__VA_ARGS__))
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/Module/ModuleManager.cpp b/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp
index 1fdeafa309..cbf3ce5243 100644
--- a/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp
+++ b/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp
@@ -10,16 +10,13 @@
#include
#include
-#include
-#include
#include
#include
#include
-#include
#include
+#include
+#include
#include
-#include
-#include
#include
#include
@@ -221,11 +218,16 @@ namespace AZ
}
}
+ AZStd::string componentNamesArray = R"({ "SystemComponents":[)";
+ const char* comma = "";
// For all system components, deactivate
for (auto componentIt = m_systemComponents.rbegin(); componentIt != m_systemComponents.rend(); ++componentIt)
{
ModuleEntity::DeactivateComponent(**componentIt);
+ componentNamesArray += AZStd::string::format(R"(%s"%s")", comma, (*componentIt)->RTTI_GetTypeName());
+ comma = ", ";
}
+ componentNamesArray += R"(]})";
// For all modules that we created an entity for, set them to "Init" (meaning not Activated)
for (auto& moduleData : m_ownedModules)
@@ -239,6 +241,13 @@ namespace AZ
// Since the system components have been deactivated clear out the vector.
m_systemComponents.clear();
+
+ // Signal that the System Components have deactivated
+ if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
+ {
+ AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "SystemComponentsDeactivated", componentNamesArray);
+ }
+
}
//=========================================================================
@@ -284,7 +293,11 @@ namespace AZ
{
// Split the tag list
AZStd::vector tagList;
- AZStd::tokenize(tags, ",", tagList);
+ auto TokenizeTags = [&tagList](AZStd::string_view token)
+ {
+ tagList.push_back(token);
+ };
+ AZ::StringFunc::TokenizeVisitor(tags, TokenizeTags, ',');
m_systemComponentTags.resize(tagList.size());
AZStd::transform(tagList.begin(), tagList.end(), m_systemComponentTags.begin(), [](const AZStd::string_view& tag)
@@ -737,11 +750,17 @@ namespace AZ
}
}
+ AZStd::string componentNamesArray = R"({ "SystemComponents":[)";
+ const char* comma = "";
// Activate the entities in the appropriate order
for (Component* component : componentsToActivate)
{
ModuleEntity::ActivateComponent(*component);
+
+ componentNamesArray += AZStd::string::format(R"(%s"%s")", comma, component->RTTI_GetTypeName());
+ comma = ", ";
}
+ componentNamesArray += R"(]})";
// Done activating; set state to active
for (auto& moduleData : modulesToInit)
@@ -755,5 +774,12 @@ namespace AZ
// Save the activated components for deactivation later
m_systemComponents.insert(m_systemComponents.end(), componentsToActivate.begin(), componentsToActivate.end());
+
+ // Signal that the System Components are activated
+ if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
+ {
+ AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "SystemComponentsActivated",
+ componentNamesArray);
+ }
}
} // namespace AZ
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/Settings/SettingsRegistryScriptUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp
index c32591874b..5cd36785dc 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp
@@ -15,20 +15,20 @@
namespace AZ::SettingsRegistryScriptUtils::Internal
{
- static void RegisterScriptProxyForNotify(SettingsRegistryScriptProxy& settingsRegistryProxy)
+ static void RegisterScriptProxyForNotify(SettingsRegistryInterface* settingsRegistry,
+ SettingsRegistryScriptProxy::NotifyEventProxy* notifyEventProxy)
{
- if (settingsRegistryProxy.IsValid())
+ if (settingsRegistry != nullptr)
{
- auto ForwardSettingsUpdateToProxyEvent = [&settingsRegistryProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
+ auto ForwardSettingsUpdateToProxyEvent = [notifyEventProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
- if (settingsRegistryProxy.m_notifyEventProxy)
+ if (notifyEventProxy)
{
- settingsRegistryProxy.m_notifyEventProxy->m_scriptNotifyEvent.Signal(path);
+ notifyEventProxy->m_scriptNotifyEvent.Signal(path);
}
};
// Register the forwarding function with the BehaviorContext
- settingsRegistryProxy.m_notifyEventProxy->m_settingsUpdatedHandler =
- settingsRegistryProxy.m_settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent);
+ notifyEventProxy->m_settingsUpdatedHandler = settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent);
}
}
@@ -37,7 +37,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
: m_settingsRegistry(AZStd::move(settingsRegistry))
, m_notifyEventProxy(AZStd::make_shared())
{
- RegisterScriptProxyForNotify(*this);
+ RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get());
}
// Raw AZ::SettingsRegistryInterface pointer is not owned by the proxy, so it's deleter is a no-op
@@ -45,7 +45,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
: m_settingsRegistry(settingsRegistry, [](AZ::SettingsRegistryInterface*) {})
, m_notifyEventProxy(AZStd::make_shared())
{
- RegisterScriptProxyForNotify(*this);
+ RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get());
}
// SettingsRegistryScriptProxy function that determines if the SettingsRegistry object is valid
diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake
index 41229429f2..6a9e5a29d6 100644
--- a/Code/Framework/AzCore/AzCore/azcore_files.cmake
+++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake
@@ -41,6 +41,8 @@ set(FILES
Component/ComponentApplication.cpp
Component/ComponentApplication.h
Component/ComponentApplicationBus.h
+ Component/ComponentApplicationLifecycle.cpp
+ Component/ComponentApplicationLifecycle.h
Component/ComponentBus.cpp
Component/ComponentBus.h
Component/ComponentExport.h
@@ -121,6 +123,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/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
index f8d0ea8bb1..8fb986802e 100644
--- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -120,6 +121,11 @@ namespace AzFramework
m_archiveFileIO = AZStd::make_unique(m_archive.get());
AZ::IO::FileIOBase::SetInstance(m_archiveFileIO.get());
SetFileIOAliases();
+ // The FileIOAvailable event needs to be registered here as this event is sent out
+ // before the settings registry has merged the .setreg files from the
+ // (That happens in MergeSettingsToRegistry
+ AZ::ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "FileIOAvailable");
+ AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOAvailable", R"({})");
}
if (auto nativeUI = AZ::Interface::Get(); nativeUI == nullptr)
@@ -172,6 +178,8 @@ namespace AzFramework
// Archive classes relies on the FileIOBase DirectInstance to close
// files properly
m_directFileIO.reset();
+
+ AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOUnavailable", R"({})");
}
void Application::Start(const Descriptor& descriptor, const StartupParameters& startupParameters)
@@ -196,7 +204,25 @@ namespace AzFramework
systemEntity->Activate();
AZ_Assert(systemEntity->GetState() == AZ::Entity::State::Active, "System Entity failed to activate.");
- m_isStarted = (systemEntity->GetState() == AZ::Entity::State::Active);
+
+ if (m_isStarted = (systemEntity->GetState() == AZ::Entity::State::Active); m_isStarted)
+ {
+ if (m_startupParameters.m_loadAssetCatalog)
+ {
+ // Start Monitoring Asset changes over the network and load the AssetCatalog
+ auto StartMonitoringAssetsAndLoadCatalog = [this](AZ::Data::AssetCatalogRequests* assetCatalogRequests)
+ {
+ if (AZ::IO::FixedMaxPath assetCatalogPath;
+ m_settingsRegistry->Get(assetCatalogPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
+ {
+ assetCatalogPath /= "assetcatalog.xml";
+ assetCatalogRequests->LoadCatalog(assetCatalogPath.c_str());
+ }
+ };
+ using AssetCatalogBus = AZ::Data::AssetCatalogRequestBus;
+ AssetCatalogBus::Broadcast(AZStd::move(StartMonitoringAssetsAndLoadCatalog));
+ }
+ }
}
void Application::PreModuleLoad()
@@ -210,6 +236,17 @@ namespace AzFramework
{
if (m_isStarted)
{
+ if (m_startupParameters.m_loadAssetCatalog)
+ {
+ // Stop Monitoring Assets changes
+ auto StopMonitoringAssets = [](AZ::Data::AssetCatalogRequests* assetCatalogRequests)
+ {
+ assetCatalogRequests->StopMonitoringAssets();
+ };
+ using AssetCatalogBus = AZ::Data::AssetCatalogRequestBus;
+ AssetCatalogBus::Broadcast(AZStd::move(StopMonitoringAssets));
+ }
+
ApplicationLifecycleEvents::Bus::Broadcast(&ApplicationLifecycleEvents::OnApplicationAboutToStop);
m_pimpl.reset();
diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp
index e6b8211c28..3d76bcefc4 100644
--- a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp
@@ -565,7 +565,7 @@ namespace AzFramework
if (!bytes.empty())
{
- AZStd::shared_ptr < AzFramework::AssetRegistry> prevRegistry;
+ AZStd::shared_ptr prevRegistry;
if (!m_initialized)
{
// First time initialization may have updates already processed which we want to apply
@@ -589,7 +589,6 @@ namespace AzFramework
AZ_TracePrintf("AssetCatalog", "Loaded registry containing %u assets.\n", m_registry->m_assetIdToInfo.size());
// It's currently possible in tools for us to have received updates from AP which were applied before the catalog was ready to load
- // due to CryPak and CrySystem coming online later than our components
if (!m_initialized)
{
ApplyDeltaCatalog(prevRegistry);
@@ -611,12 +610,13 @@ namespace AzFramework
// the mutex. If the listener tries to perform a blocking asset load via GetAsset() / BlockUntilLoadComplete(), the spawned asset
// thread will make a call to the AssetCatalogRequestBus and block on the held mutex. This would cause a deadlock, since the listener
// won't free the mutex until the load is complete.
- // So instead, queue the notification until the next tick, so that it doesn't occur within the AssetCatalogRequestBus mutex, and also
+ // So instead, queue the notification until after the AssetCatalogRequestBus mutex is unlocked for the current thread, and also
// so that the entire AssetCatalog initialization is complete.
- AZ::TickBus::QueueFunction([catalogRegistryString = AZStd::string(catalogRegistryFile)]()
- {
- AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str());
- });
+ auto OnCatalogLoaded = [catalogRegistryString = AZStd::string(catalogRegistryFile)]()
+ {
+ AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str());
+ };
+ AZ::Data::AssetCatalogRequestBus::QueueFunction(AZStd::move(OnCatalogLoaded));
}
}
@@ -978,6 +978,7 @@ namespace AzFramework
AZStd::lock_guard lock(m_registryMutex);
m_registry->Clear();
+ m_initialized = false;
}
diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp
index f9eefa7639..26a22f4625 100644
--- a/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp
@@ -61,6 +61,7 @@ namespace AzFramework
//=========================================================================
void AssetRegistry::Clear()
{
+ m_assetDependencies = {};
m_assetIdToInfo = AssetIdToInfoMap();
m_assetPathToId = AssetPathToIdMap();
}
diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp
index f820ee56ed..abbb9ec2f1 100644
--- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp
+++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp
@@ -10,11 +10,11 @@
#include
#include
#include
+#include
#include
#include
#include
#include
-#include
#include
#include
#include
@@ -89,19 +89,19 @@ namespace AzFramework
bool FileTagManager::Save(FileTagType fileTagType, const AZStd::string& destinationFilePath = AZStd::string())
{
AzFramework::FileTag::FileTagAsset* fileTagAsset = GetFileTagAsset(fileTagType);
- AZStd::string filePathToSave = destinationFilePath;
+ AZ::IO::Path filePathToSave = destinationFilePath;
if (filePathToSave.empty())
{
filePathToSave = FileTagQueryManager::GetDefaultFileTagFilePath(fileTagType);
}
- if (!AzFramework::StringFunc::EndsWith(filePathToSave, AzFramework::FileTag::FileTagAsset::Extension()))
+ if (!filePathToSave.Extension().Native().ends_with(AzFramework::FileTag::FileTagAsset::Extension()))
{
AZ_Error("FileTag", false, "Unable to save tag file (%s). Invalid file extension, file tag can only have (%s) extension.\n", filePathToSave.c_str(), AzFramework::FileTag::FileTagAsset::Extension());
return false;
}
- return AZ::Utils::SaveObjectToFile(filePathToSave, AZ::DataStream::StreamType::ST_XML, fileTagAsset);
+ return AZ::Utils::SaveObjectToFile(filePathToSave.Native(), AZ::DataStream::StreamType::ST_XML, fileTagAsset);
}
AZ::Outcome FileTagManager::AddTagsInternal(AZStd::string filePath, FileTagType fileTagType, AZStd::vector fileTags, AzFramework::FileTag::FilePatternType filePatternType)
@@ -239,17 +239,22 @@ namespace AzFramework
QueryFileTagsEventBus::Handler::BusDisconnect();
}
- AZStd::string FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType fileTagType)
+ AZ::IO::Path FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType fileTagType)
{
- auto destinationFilePath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / EngineAssetSourceRelPath;
+ AZ::IO::Path destinationFilePath;
+ if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
+ {
+ settingsRegistry->Get(destinationFilePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
+ }
+ destinationFilePath /= EngineAssetSourceRelPath;
destinationFilePath /= fileTagType == FileTagType::Exclude ? ExcludeFileName : IncludeFileName;
destinationFilePath.ReplaceExtension(AzFramework::FileTag::FileTagAsset::Extension());
- return destinationFilePath.String();
+ return destinationFilePath;
}
bool FileTagQueryManager::Load(const AZStd::string& filePath)
{
- AZStd::string fileToLoad = filePath;
+ AZ::IO::Path fileToLoad = filePath;
if (fileToLoad.empty())
{
fileToLoad = GetDefaultFileTagFilePath(m_fileTagType);
diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h
index be3d6e6d08..d2dee3f629 100644
--- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h
+++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
namespace AzFramework
@@ -88,7 +89,7 @@ namespace AzFramework
/////////////////////////////////////////////////////////////////////////
- static AZStd::string GetDefaultFileTagFilePath(FileTagType fileTagType);
+ static AZ::IO::Path GetDefaultFileTagFilePath(FileTagType fileTagType);
protected:
diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp b/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp
index 1e03a6df0d..1d09500415 100644
--- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp
+++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp
@@ -16,6 +16,7 @@
#include
#include
+#include
#include
namespace AzFramework
@@ -66,7 +67,8 @@ namespace AzFramework
m_excludeFileQueryManager.reset(aznew FileTagQueryManager(FileTagType::Exclude));
if (!m_excludeFileQueryManager.get()->Load())
{
- AZ_Error("FileTagQueryComponent", false, "Not able to load default exclude file (%s). Please make sure that it exists on disk.\n", FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType::Exclude).c_str());
+ AZ_Error("FileTagQueryComponent", false, "Not able to load default exclude file (%s). Please make sure that it exists on disk.\n",
+ FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType::Exclude).c_str());
}
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
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/AzFramework/Tests/AssetCatalog.cpp b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp
index 8cdc54f34f..9e5c72f74c 100644
--- a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp
+++ b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp
@@ -308,7 +308,9 @@ namespace UnitTest
registry->Set(projectPathKey, "AutomatedTesting");
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
- m_app->Start(desc);
+ AZ::ComponentApplication::StartupParameters startupParameters;
+ startupParameters.m_loadAssetCatalog = false;
+ m_app->Start(desc, startupParameters);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
diff --git a/Code/Framework/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/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp
index a5f1e29942..434a1d8303 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp
@@ -943,13 +943,15 @@ namespace AzToolsFramework
{
return false;
}
-
+
+ const int count = rowCount(parent);
AZ::EntityId newParentId = GetEntityFromIndex(parent);
- AZ::EntityId beforeEntityId = GetEntityFromIndex(index(row, 0, parent));
+ AZ::EntityId beforeEntityId = (row >= 0 && row < count) ? GetEntityFromIndex(index(row, 0, parent)) : AZ::EntityId();
EntityIdList topLevelEntityIds;
topLevelEntityIds.reserve(entityIdListContainer.m_entityIds.size());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::FindTopLevelEntityIdsInactive, entityIdListContainer.m_entityIds, topLevelEntityIds);
- if (!ReparentEntities(newParentId, topLevelEntityIds, beforeEntityId))
+ const auto appendActionForInvalid = newParentId.IsValid() && (row >= count) ? AppendEnd : AppendBeginning;
+ if (!ReparentEntities(newParentId, topLevelEntityIds, beforeEntityId, appendActionForInvalid))
{
return false;
}
@@ -1046,7 +1048,7 @@ namespace AzToolsFramework
return true;
}
- bool EntityOutlinerListModel::ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList &selectedEntityIds, const AZ::EntityId& beforeEntityId)
+ bool EntityOutlinerListModel::ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList &selectedEntityIds, const AZ::EntityId& beforeEntityId, ReparentForInvalid forInvalid)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
if (!CanReparentEntities(newParentId, selectedEntityIds))
@@ -1056,10 +1058,18 @@ namespace AzToolsFramework
m_isFilterDirty = true;
- ScopedUndoBatch undo("Reparent Entities");
//capture child entity order before re-parent operation, which will automatically add order info if not present
EntityOrderArray entityOrderArray = GetEntityChildOrder(newParentId);
+ //search for the insertion entity in the order array
+ const auto beforeEntityItr = AZStd::find(entityOrderArray.begin(), entityOrderArray.end(), beforeEntityId);
+ const bool hasInvalidIndex = beforeEntityItr == entityOrderArray.end();
+ if (hasInvalidIndex && forInvalid == None)
+ {
+ return false;
+ }
+
+ ScopedUndoBatch undo("Reparent Entities");
// The new parent is dirty due to sort change(s)
undo.MarkEntityDirty(GetEntityIdForSortInfo(newParentId));
@@ -1088,9 +1098,7 @@ namespace AzToolsFramework
}
}
- //search for the insertion entity in the order array
- auto beforeEntityItr = AZStd::find(entityOrderArray.begin(), entityOrderArray.end(), beforeEntityId);
-
+
//replace order info matching selection with bad values rather than remove to preserve layout
for (auto& id : entityOrderArray)
{
@@ -1100,17 +1108,25 @@ namespace AzToolsFramework
}
}
- if (newParentId.IsValid())
+ //if adding to a valid parent entity, insert at the found entity location or at the head/tail depending on placeAtTail flag
+ if (hasInvalidIndex)
{
- //if adding to a valid parent entity, insert at the found entity location or at the head of the container
- auto insertItr = beforeEntityItr != entityOrderArray.end() ? beforeEntityItr : entityOrderArray.begin();
- entityOrderArray.insert(insertItr, processedEntityIds.begin(), processedEntityIds.end());
- }
- else
+ switch(forInvalid)
+ {
+ case AppendEnd:
+ entityOrderArray.insert(entityOrderArray.end(), processedEntityIds.begin(), processedEntityIds.end());
+ break;
+ case AppendBeginning:
+ entityOrderArray.insert(entityOrderArray.begin(), processedEntityIds.begin(), processedEntityIds.end());
+ break;
+ default:
+ AZ_Assert(false, "Unexpected type for ReparentForInvalid");
+ break;
+ }
+ }
+ else
{
- //if adding to an invalid parent entity (the root), insert at the found entity location or at the tail of the container
- auto insertItr = beforeEntityItr != entityOrderArray.end() ? beforeEntityItr : entityOrderArray.end();
- entityOrderArray.insert(insertItr, processedEntityIds.begin(), processedEntityIds.end());
+ entityOrderArray.insert(beforeEntityItr, processedEntityIds.begin(), processedEntityIds.end());
}
//remove placeholder entity ids
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx
index 8176867038..0a46ee4850 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx
@@ -72,6 +72,13 @@ namespace AzToolsFramework
ColumnCount //!< Total number of columns
};
+ enum ReparentForInvalid
+ {
+ None, //!< For an invalid location the entity does not change location
+ AppendEnd, //!< Append Item to end of target parent list
+ AppendBeginning, //!< Append Item to the beginning of target parent list
+ };
+
// Note: the ColumnSortIndex column isn't shown, hence the -1 and the need for a separate counter.
// A wrong column count number causes refresh issues and hover mismatch on model update.
static const int VisibleColumnCount = ColumnCount - 1;
@@ -162,7 +169,7 @@ namespace AzToolsFramework
// Buffer Processing Slots - These are called using single-shot events when the buffers begin to fill.
bool CanReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds) const;
- bool ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds, const AZ::EntityId& beforeEntityId = AZ::EntityId());
+ bool ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds, const AZ::EntityId& beforeEntityId = AZ::EntityId(), ReparentForInvalid forInvalid = None);
//! Use the current filter setting and re-evaluate the filter.
void InvalidateFilter();
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/Legacy/CrySystem/IDebugCallStack.cpp b/Code/Legacy/CrySystem/IDebugCallStack.cpp
index deb9c11581..a02658d34a 100644
--- a/Code/Legacy/CrySystem/IDebugCallStack.cpp
+++ b/Code/Legacy/CrySystem/IDebugCallStack.cpp
@@ -242,7 +242,7 @@ void IDebugCallStack::WriteLineToLog(const char* format, ...)
va_end(ArgList);
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
- AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle);
+ AZ::IO::FileIOBase::GetDirectInstance()->Open("@log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle);
if (fileHandle != AZ::IO::InvalidHandle)
{
AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, szBuffer, strlen(szBuffer));
@@ -254,7 +254,7 @@ void IDebugCallStack::WriteLineToLog(const char* format, ...)
//////////////////////////////////////////////////////////////////////////
void IDebugCallStack::StartMemLog()
{
- AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle);
+ AZ::IO::FileIOBase::GetDirectInstance()->Open("@log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle);
assert(m_memAllocFileHandle != AZ::IO::InvalidHandle);
}
diff --git a/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.cpp b/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.cpp
index 45d01b9b5b..8def0a698c 100644
--- a/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.cpp
+++ b/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.cpp
@@ -61,8 +61,12 @@ namespace AssetBundler
{
AZStd::string absolutePath = filePath.toUtf8().data();
if (AZ::IO::FileIOBase::GetInstance()->Exists(absolutePath.c_str()))
- {
- AZStd::string projectName = pathToProjectNameMap.at(absolutePath);
+ {
+ AZStd::string projectName;
+ if (pathToProjectNameMap.contains(absolutePath))
+ {
+ projectName = pathToProjectNameMap.at(absolutePath);
+ }
// If a project name is already specified, then the associated file is a default file
LoadFile(absolutePath, projectName, !projectName.empty());
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/AssetManager/assetProcessorManager.cpp b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp
index fc806f6c9e..5e061491f0 100644
--- a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp
+++ b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp
@@ -3576,23 +3576,24 @@ namespace AssetProcessor
// Absolute path, just check the 1 scan folder
if (AZ::IO::PathView(encodedFileData.toUtf8().constData()).IsAbsolute())
{
- QString scanFolderName;
- if (!m_platformConfig->ConvertToRelativePath(encodedFileData, resultDatabaseSourceName, scanFolderName))
+ auto scanFolderInfo = m_platformConfig->GetScanFolderForFile(encodedFileData);
+
+ if (!m_platformConfig->ConvertToRelativePath(encodedFileData, scanFolderInfo, resultDatabaseSourceName))
{
AZ_Warning(
AssetProcessor::ConsoleChannel, false,
"'%s' does not appear to be in any input folder. Use relative paths instead.",
sourceDependency.m_sourceFileDependencyPath.c_str());
}
+ else
+ {
+ // Make an absolute path that is ScanFolderPath + Part of search path before the wildcard
+ QDir rooted(scanFolderInfo->ScanPath());
+ QString scanFolderAndKnownSubPath = rooted.absoluteFilePath(knownPathBeforeWildcard);
- auto scanFolderInfo = m_platformConfig->GetScanFolderByPath(scanFolderName);
-
- // Make an absolute path that is ScanFolderPath + Part of search path before the wildcard
- QDir rooted(scanFolderName);
- QString scanFolderAndKnownSubPath = rooted.absoluteFilePath(knownPathBeforeWildcard);
-
- resolvedDependencyList.append(m_platformConfig->FindWildcardMatches(
- scanFolderAndKnownSubPath, relativeSearch, false, scanFolderInfo->RecurseSubFolders()));
+ resolvedDependencyList.append(m_platformConfig->FindWildcardMatches(
+ scanFolderAndKnownSubPath, relativeSearch, false, scanFolderInfo->RecurseSubFolders()));
+ }
}
else // Relative path, check every scan folder
{
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/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp
index da6e995c97..0b02b168dc 100644
--- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp
+++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp
@@ -88,6 +88,7 @@ public:
friend struct DuplicateProductsTest;
friend struct DuplicateProcessTest;
friend struct AbsolutePathProductDependencyTest;
+ friend struct WildcardSourceDependencyTest;
explicit AssetProcessorManager_Test(PlatformConfiguration* config, QObject* parent = nullptr);
~AssetProcessorManager_Test() override;
@@ -4446,7 +4447,9 @@ AssetBuilderSDK::AssetBuilderDesc MockBuilderInfoHandler::CreateBuilderDesc(cons
void FingerprintTest::SetUp()
{
+ AZ_Printf("FingerprintTest", "SetUp start");
AssetProcessorManagerTest::SetUp();
+ AZ_Printf("FingerprintTest", "SetUp self");
// We don't want the mock application manager to provide builder descriptors, mockBuilderInfoHandler will provide our own
m_mockApplicationManager->BusDisconnect();
@@ -4465,18 +4468,23 @@ void FingerprintTest::SetUp()
});
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(m_absolutePath, ""));
+ AZ_Printf("FingerprintTest", "SetUp end");
}
void FingerprintTest::TearDown()
{
+ AZ_Printf("FingerprintTest", "TearDown start");
m_jobResults = AZStd::vector{};
m_mockBuilderInfoHandler = {};
+ AZ_Printf("FingerprintTest", "TearDown parent");
AssetProcessorManagerTest::TearDown();
+ AZ_Printf("FingerprintTest", "TearDown end");
}
void FingerprintTest::RunFingerprintTest(QString builderFingerprint, QString jobFingerprint, bool expectedResult)
{
+ AZ_Printf("FingerprintTest", "Fingerprint Test Start");
m_mockBuilderInfoHandler.m_builderDesc.m_analysisFingerprint = builderFingerprint.toUtf8().data();
m_mockBuilderInfoHandler.m_jobFingerprint = jobFingerprint;
QMetaObject::invokeMethod(m_assetProcessorManager.get(), "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, m_absolutePath));
@@ -4485,6 +4493,7 @@ void FingerprintTest::RunFingerprintTest(QString builderFingerprint, QString job
ASSERT_EQ(m_mockBuilderInfoHandler.m_createJobsCount, 1);
ASSERT_EQ(m_jobResults.size(), 1);
ASSERT_EQ(m_jobResults[0].m_autoFail, expectedResult);
+ AZ_Printf("FingerprintTest", "Fingerprint Test End");
}
TEST_F(FingerprintTest, FingerprintChecking_JobFingerprint_NoBuilderFingerprint)
@@ -5308,3 +5317,141 @@ TEST_F(MetadataFileTest, MetadataFile_SourceFileExtensionDifferentCase)
ASSERT_TRUE(BlockUntilIdle(5000));
ASSERT_EQ(jobDetails.m_jobEntry.m_pathRelativeToWatchFolder, relFileName);
}
+
+bool WildcardSourceDependencyTest::Test(
+ const AZStd::string& dependencyPath, AZStd::vector& resolvedPaths)
+{
+ [[maybe_unused]] QString resolvedName;
+ QStringList stringlistPaths;
+ AssetBuilderSDK::SourceFileDependency dependency(dependencyPath, AZ::Uuid::CreateNull(), AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Wildcards);
+ bool result = m_assetProcessorManager->ResolveSourceFileDependencyPath(dependency, resolvedName, stringlistPaths);
+
+ // Convert to a vector of AZStd::strings because GTest handles this type better when displaying errors
+ for (const QString& resolvedPath : stringlistPaths)
+ {
+ resolvedPaths.emplace_back(resolvedPath.toUtf8().constData());
+ }
+
+ return result;
+}
+
+void WildcardSourceDependencyTest::SetUp()
+{
+ AssetProcessorManagerTest::SetUp();
+
+ QDir tempPath(m_tempDir.path());
+
+ // Add a non-recursive scan folder. Only files directly inside of this folder should be picked up, subfolders are ignored
+ m_config->AddScanFolder(ScanFolderInfo(tempPath.filePath("no_recurse"), "no_recurse",
+ "no_recurse", false, false, m_config->GetEnabledPlatforms(), 1));
+
+ UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder1/1a.foo"));
+ UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder1/1b.foo"));
+ UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/a.foo"));
+ UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/b.foo"));
+ UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/folder/one/c.foo"));
+ UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/folder/one/d.foo"));
+
+ // Add a file that is not in a scanfolder. Should always be ignored
+ UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("not/a/scanfolder/e.foo"));
+
+ // Add a file in the non-recursive scanfolder. Since its not directly in the scan folder, it should always be ignored
+ UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("no_recurse/one/two/three/f.foo"));
+}
+
+TEST_F(WildcardSourceDependencyTest, Relative_Broad)
+{
+ // Expect all files except for the 2 invalid ones (e and f)
+ AZStd::vector resolvedPaths;
+
+ ASSERT_TRUE(Test("*.foo", resolvedPaths));
+ ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("a.foo", "b.foo", "folder/one/c.foo", "folder/one/d.foo", "1a.foo", "1b.foo"));
+}
+
+TEST_F(WildcardSourceDependencyTest, Relative_WithFolder)
+{
+ // Make sure we can filter to files under a folder
+ AZStd::vector resolvedPaths;
+
+ ASSERT_TRUE(Test("folder/*.foo", resolvedPaths));
+ ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("folder/one/c.foo", "folder/one/d.foo"));
+}
+
+TEST_F(WildcardSourceDependencyTest, Relative_WildcardPath)
+{
+ // Make sure the * wildcard works even if the full filename is given
+ AZStd::vector resolvedPaths;
+
+ ASSERT_TRUE(Test("*a.foo", resolvedPaths));
+ ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("a.foo", "1a.foo"));
+}
+
+TEST_F(WildcardSourceDependencyTest, Absolute_WithFolder)
+{
+ // Make sure we can use absolute paths to filter to files under a folder
+ AZStd::vector resolvedPaths;
+ QDir tempPath(m_tempDir.path());
+
+ ASSERT_TRUE(Test(tempPath.absoluteFilePath("subfolder2/redirected/*.foo").toUtf8().constData(), resolvedPaths));
+ ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("a.foo", "b.foo", "folder/one/c.foo", "folder/one/d.foo"));
+}
+
+TEST_F(WildcardSourceDependencyTest, Absolute_NotInScanfolder)
+{
+ // Files outside a scanfolder should not be returned even with an absolute path
+ AZStd::vector resolvedPaths;
+ QDir tempPath(m_tempDir.path());
+
+ ASSERT_TRUE(Test(tempPath.absoluteFilePath("not/a/scanfolder/*.foo").toUtf8().constData(), resolvedPaths));
+ ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
+}
+
+TEST_F(WildcardSourceDependencyTest, Relative_NotInScanfolder)
+{
+ // Files outside a scanfolder should not be returned
+ AZStd::vector resolvedPaths;
+ QDir tempPath(m_tempDir.path());
+
+ ASSERT_TRUE(Test("*/e.foo", resolvedPaths));
+ ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
+}
+
+TEST_F(WildcardSourceDependencyTest, Relative_InNonRecursiveScanfolder)
+{
+ // Files deep inside non-recursive scanfolders should not be returned
+ AZStd::vector resolvedPaths;
+ QDir tempPath(m_tempDir.path());
+
+ ASSERT_TRUE(Test("*/f.foo", resolvedPaths));
+ ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
+}
+
+TEST_F(WildcardSourceDependencyTest, Absolute_InNonRecursiveScanfolder)
+{
+ // Absolute paths to files deep inside non-recursive scanfolders should not be returned
+ AZStd::vector resolvedPaths;
+ QDir tempPath(m_tempDir.path());
+
+ ASSERT_TRUE(Test(tempPath.absoluteFilePath("one/two/three/*.foo").toUtf8().constData(), resolvedPaths));
+ ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
+}
+
+TEST_F(WildcardSourceDependencyTest, Relative_NoWildcard)
+{
+ // No wildcard results in a failure
+ AZStd::vector resolvedPaths;
+ QDir tempPath(m_tempDir.path());
+
+ ASSERT_FALSE(Test("subfolder1/1a.foo", resolvedPaths));
+ ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
+}
+
+TEST_F(WildcardSourceDependencyTest, Absolute_NoWildcard)
+{
+ // No wildcard results in a failure
+ AZStd::vector resolvedPaths;
+ QDir tempPath(m_tempDir.path());
+
+ ASSERT_FALSE(Test(tempPath.absoluteFilePath("subfolder1/1a.foo").toUtf8().constData(), resolvedPaths));
+ ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
+}
diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h
index 3443a4c519..94cc997938 100644
--- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h
+++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h
@@ -131,6 +131,14 @@ struct MultiplatformPathDependencyTest
void SetUp() override;
};
+struct WildcardSourceDependencyTest
+ : AssetProcessorManagerTest
+{
+ bool Test(const AZStd::string& dependencyPath, AZStd::vector& resolvedPaths);
+
+ void SetUp() override;
+};
+
struct MockBuilderInfoHandler
: public AssetProcessor::AssetBuilderInfoBus::Handler
{
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/unittests/UnitTestRunner.h b/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h
index c281b3050a..0c4357f84a 100644
--- a/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h
+++ b/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h
@@ -21,6 +21,7 @@
#endif
#include
+#include
//! These macros can be used for checking your unit tests,
//! you can check AssetScannerUnitTest.cpp for usage
@@ -155,6 +156,7 @@ namespace UnitTestUtils
bool OnPreWarning([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override
{
+ UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
++m_numWarningsAbsorbed;
if (m_debugMessages)
{
@@ -165,6 +167,7 @@ namespace UnitTestUtils
bool OnPreAssert([[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override
{
+ UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
++m_numAssertsAbsorbed;
if (m_debugMessages)
{
@@ -175,6 +178,7 @@ namespace UnitTestUtils
bool OnPreError([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override
{
+ UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
++m_numErrorsAbsorbed;
if (m_debugMessages)
{
@@ -183,8 +187,9 @@ namespace UnitTestUtils
return true; // I handled this, do not forward it
}
- bool OnPrintf(const char* /*window*/, const char* /*message*/) override
+ bool OnPrintf(const char* /*window*/, const char* message) override
{
+ UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
++m_numMessagesAbsorbed;
return true;
}
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/CMakeLists.txt b/Code/Tools/ProjectManager/CMakeLists.txt
index d34abcbc6c..a47ccb62c9 100644
--- a/Code/Tools/ProjectManager/CMakeLists.txt
+++ b/Code/Tools/ProjectManager/CMakeLists.txt
@@ -34,6 +34,7 @@ ly_add_target(
INCLUDE_DIRECTORIES
PRIVATE
Source
+ Platform/${PAL_PLATFORM_NAME}
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
diff --git a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake
index 11222602d5..c3acd44f9b 100644
--- a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake
+++ b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake
@@ -11,4 +11,6 @@ set(FILES
ProjectBuilderWorker_linux.cpp
ProjectUtils_linux.cpp
ProjectManagerDefs_linux.cpp
+ ProjectManager_Traits_Platform.h
+ ProjectManager_Traits_Linux.h
)
diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h
new file mode 100644
index 0000000000..7c0543361f
--- /dev/null
+++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR false
diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h
new file mode 100644
index 0000000000..97aee25507
--- /dev/null
+++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
diff --git a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake
index 54b35f0d3c..6d4d453f21 100644
--- a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake
+++ b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake
@@ -11,4 +11,6 @@ set(FILES
ProjectBuilderWorker_mac.cpp
ProjectUtils_mac.cpp
ProjectManagerDefs_mac.cpp
+ ProjectManager_Traits_Platform.h
+ ProjectManager_Traits_Mac.h
)
diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h
new file mode 100644
index 0000000000..7c0543361f
--- /dev/null
+++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR false
diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h
new file mode 100644
index 0000000000..dc77e77fd0
--- /dev/null
+++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
diff --git a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake
index d95b0d2502..22b4614ddf 100644
--- a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake
+++ b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake
@@ -11,4 +11,6 @@ set(FILES
ProjectBuilderWorker_windows.cpp
ProjectUtils_windows.cpp
ProjectManagerDefs_windows.cpp
+ ProjectManager_Traits_Platform.h
+ ProjectManager_Traits_Windows.h
)
diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h
new file mode 100644
index 0000000000..f5eac50dbc
--- /dev/null
+++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h
new file mode 100644
index 0000000000..e6422b5a77
--- /dev/null
+++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR true
diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss
index 6694168f2b..fecdc67920 100644
--- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss
+++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss
@@ -9,7 +9,7 @@ QMainWindow {
#ScreensCtrl {
min-width:1200px;
- min-height:800px;
+ min-height:700px;
}
QPushButton:focus {
@@ -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/Application.cpp b/Code/Tools/ProjectManager/Source/Application.cpp
index a7e4805ce9..29e0df3c3a 100644
--- a/Code/Tools/ProjectManager/Source/Application.cpp
+++ b/Code/Tools/ProjectManager/Source/Application.cpp
@@ -16,6 +16,7 @@
#include
#include
#include
+#include
#include
#include
@@ -194,8 +195,12 @@ namespace O3DE::ProjectManager
// set stylesheet after creating the main window or their styles won't get updated
AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:ProjectManager.qss"));
- // the decoration wrapper is intended to remember window positioning and sizing
+ // the decoration wrapper is intended to remember window positioning and sizing
+#if AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR
auto wrapper = new AzQtComponents::WindowDecorationWrapper();
+#else
+ auto wrapper = new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionDisabled);
+#endif
wrapper->setGuest(m_mainWindow.data());
// show the main window here to apply the stylesheet before restoring geometry or we
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/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp
index 6cfe072f80..d0528a9cad 100644
--- a/Code/Tools/SerializeContextTools/SliceConverter.cpp
+++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp
@@ -81,8 +81,6 @@ namespace AZ
// Load the asset catalog so that we can find any nested assets successfully. We also need to tick the tick bus
// so that the OnCatalogLoaded event gets processed now, instead of during application shutdown.
- AZ::Data::AssetCatalogRequestBus::Broadcast(
- &AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml");
application.Tick();
AZStd::string logggingScratchBuffer;
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/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