diff --git a/.gitignore b/.gitignore
index 3a9b8f6f8e..5f6172cd76 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ Editor/EditorEventLog.xml
Editor/EditorLayout.xml
**/*egg-info/**
**/*egg-link
+**/[Rr]estricted
UserSettings.xml
[Uu]ser/
FrameCapture/**
diff --git a/Assets/Editor/Prefabs/Default_Level.prefab b/Assets/Editor/Prefabs/Default_Level.prefab
index d02d669f53..0267131fa0 100644
--- a/Assets/Editor/Prefabs/Default_Level.prefab
+++ b/Assets/Editor/Prefabs/Default_Level.prefab
@@ -185,7 +185,7 @@
{
"id": {
"materialAssetId": {
- "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}",
+ "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
"subId": 803645540
}
}
@@ -197,7 +197,7 @@
"id": {
"lodIndex": 0,
"materialAssetId": {
- "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}",
+ "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
"subId": 803645540
}
}
diff --git a/Assets/Editor/Scripts/lua_symbols.py b/Assets/Editor/Scripts/lua_symbols.py
new file mode 100644
index 0000000000..b21edb41d0
--- /dev/null
+++ b/Assets/Editor/Scripts/lua_symbols.py
@@ -0,0 +1,116 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+
+# This script shows basic usage of LuaSymbolsReporterBus,
+# Which can be used to report all symbols available for
+# game scripting with Lua.
+
+import sys
+import os
+
+import azlmbr.bus as azbus
+import azlmbr.script as azscript
+import azlmbr.legacy.general as azgeneral
+
+
+def _dump_class_symbol(class_symbol: azlmbr.script.LuaClassSymbol):
+ print(f"** {class_symbol}")
+ print("Properties:")
+ for property_symbol in class_symbol.properties:
+ print(f" - {property_symbol}")
+ print("Methods:")
+ for method_symbol in class_symbol.methods:
+ print(f" - {method_symbol}")
+
+
+def _dump_lua_classes():
+ class_symbols = azscript.LuaSymbolsReporterBus(azbus.Broadcast,
+ "GetListOfClasses")
+ print("======== Classes ==========")
+ sorted_classes_by_named = sorted(class_symbols, key=lambda class_symbol: class_symbol.name)
+ for class_symbol in sorted_classes_by_named:
+ _dump_class_symbol(class_symbol)
+ print("\n\n")
+
+
+def _dump_lua_globals():
+ global_properties = azscript.LuaSymbolsReporterBus(azbus.Broadcast,
+ "GetListOfGlobalProperties")
+ print("======== Global Properties ==========")
+ sorted_properties_by_name = sorted(global_properties, key=lambda symbol: symbol.name)
+ for property_symbol in sorted_properties_by_name:
+ print(f"- {property_symbol}")
+ print("\n\n")
+ global_functions = azscript.LuaSymbolsReporterBus(azbus.Broadcast,
+ "GetListOfGlobalFunctions")
+ print("======== Global Functions ==========")
+ sorted_functions_by_name = sorted(global_functions, key=lambda symbol: symbol.name)
+ for function_symbol in sorted_functions_by_name:
+ print(f"- {function_symbol}")
+ print("\n\n")
+
+
+def _dump_lua_ebus(ebus_symbol: azlmbr.script.LuaEBusSymbol):
+ print(f">> {ebus_symbol}")
+ sorted_senders = sorted(ebus_symbol.senders, key=lambda symbol: symbol.name)
+ for sender in sorted_senders:
+ print(f" - {sender}")
+ print("\n")
+
+
+def _dump_lua_ebuses():
+ ebuses = azscript.LuaSymbolsReporterBus(azbus.Broadcast,
+ "GetListOfEBuses")
+ print("======== Ebus List ==========")
+ sorted_ebuses_by_name = sorted(ebuses, key=lambda symbol: symbol.name)
+ for ebus_symbol in sorted_ebuses_by_name:
+ _dump_lua_ebus(ebus_symbol)
+ print("\n\n")
+
+
+class WhatToDo:
+ DumpClasses = "c"
+ DumpGlobals = "g"
+ DumpEBuses = "e"
+
+if __name__ == "__main__":
+ redirecting_stdout = False
+ orig_stdout = sys.stdout
+ if len(sys.argv) > 1:
+ output_file_name = sys.argv[1]
+ if not os.path.isabs(output_file_name):
+ game_root_path = os.path.normpath(azgeneral.get_game_folder())
+ output_file_name = os.path.join(game_root_path, output_file_name)
+ try:
+ file_obj = open(output_file_name, 'wt')
+ sys.stdout = file_obj
+ redirecting_stdout = True
+ except Exception as e:
+ print(f"Failed to open {output_file_name}: {e}")
+ sys.exit(-1)
+
+ what_to_do = [action.lower() for action in sys.argv[2:]]
+
+ # If the user did not specify what to do, then let's dump
+ # all the symbols.
+ if len(what_to_do) < 1:
+ what_to_do = [WhatToDo.DumpClasses, WhatToDo.DumpGlobals, WhatToDo.DumpEBuses]
+
+ for action in what_to_do:
+ if action == WhatToDo.DumpClasses:
+ _dump_lua_classes()
+ elif action == WhatToDo.DumpGlobals:
+ _dump_lua_globals()
+ elif action == WhatToDo.DumpEBuses:
+ _dump_lua_ebuses()
+
+ if redirecting_stdout:
+ sys.stdout.close()
+ sys.stdout = orig_stdout
+ print(f" Lua Symbols Are available in: {output_file_name}")
diff --git a/Assets/Engine/Engine_Dependencies.xml b/Assets/Engine/Engine_Dependencies.xml
index 0d6541e18e..b969b3bc97 100644
--- a/Assets/Engine/Engine_Dependencies.xml
+++ b/Assets/Engine/Engine_Dependencies.xml
@@ -1,16 +1,9 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
diff --git a/Assets/Engine/SeedAssetList.seed b/Assets/Engine/SeedAssetList.seed
index aafbffbe8f..18ccd19dc3 100644
--- a/Assets/Engine/SeedAssetList.seed
+++ b/Assets/Engine/SeedAssetList.seed
@@ -1,621 +1,260 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/AutomatedTesting/Editor/Scripts/Profiler/__init__.py b/AutomatedTesting/Editor/Scripts/Profiler/__init__.py
new file mode 100644
index 0000000000..7a325ca97e
--- /dev/null
+++ b/AutomatedTesting/Editor/Scripts/Profiler/__init__.py
@@ -0,0 +1,7 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
diff --git a/AutomatedTesting/Editor/Scripts/Profiler/profiler_system_example.py b/AutomatedTesting/Editor/Scripts/Profiler/profiler_system_example.py
new file mode 100644
index 0000000000..16f161c50e
--- /dev/null
+++ b/AutomatedTesting/Editor/Scripts/Profiler/profiler_system_example.py
@@ -0,0 +1,30 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+import azlmbr.debug as debug
+
+import pathlib
+
+def test_profiler_system():
+ if not debug.g_ProfilerSystem.IsValid():
+ print('g_ProfilerSystem is INVALID')
+ return
+
+ state = 'ACTIVE' if debug.g_ProfilerSystem.IsActive() else 'INACTIVE'
+ print(f'Profiler system is currently {state}')
+
+ capture_location = pathlib.Path(debug.g_ProfilerSystem.GetCaptureLocation())
+ print(f'Capture location set to {capture_location}')
+
+ print('Capturing single frame...' )
+ capture_file = str(capture_location / 'script_capture_frame.json')
+ debug.g_ProfilerSystem.CaptureFrame(capture_file)
+
+# Invoke main function
+if __name__ == '__main__':
+ test_profiler_system()
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/Code/CMakeLists.txt b/AutomatedTesting/Gem/Code/CMakeLists.txt
index 6f55cc2764..8808507765 100644
--- a/AutomatedTesting/Gem/Code/CMakeLists.txt
+++ b/AutomatedTesting/Gem/Code/CMakeLists.txt
@@ -14,15 +14,25 @@ ly_add_target(
FILES_CMAKE
automatedtesting_files.cmake
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
+ automatedtesting_autogen_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
+ PUBLIC
+ AZ::AzNetworking
+ Gem::Multiplayer
PRIVATE
AZ::AzCore
Gem::Atom_AtomBridge.Static
+ Gem::Multiplayer.Static
+ AUTOGEN_RULES
+ *.AutoComponent.xml,AutoComponent_Header.jinja,$path/$fileprefix.AutoComponent.h
+ *.AutoComponent.xml,AutoComponent_Source.jinja,$path/$fileprefix.AutoComponent.cpp
+ *.AutoComponent.xml,AutoComponentTypes_Header.jinja,$path/AutoComponentTypes.h
+ *.AutoComponent.xml,AutoComponentTypes_Source.jinja,$path/AutoComponentTypes.cpp
)
# if enabled, AutomatedTesting is used by all kinds of applications
diff --git a/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml b/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml
new file mode 100644
index 0000000000..12c222add6
--- /dev/null
+++ b/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Gem/Code/Source/AutomatedTestingModule.cpp b/AutomatedTesting/Gem/Code/Source/AutomatedTestingModule.cpp
index 7ed37e89ca..b5be8410ef 100644
--- a/AutomatedTesting/Gem/Code/Source/AutomatedTestingModule.cpp
+++ b/AutomatedTesting/Gem/Code/Source/AutomatedTestingModule.cpp
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
@@ -27,6 +28,8 @@ namespace AutomatedTesting
m_descriptors.insert(m_descriptors.end(), {
AutomatedTestingSystemComponent::CreateDescriptor(),
});
+
+ CreateComponentDescriptors(m_descriptors); //< Register multiplayer components
}
/**
diff --git a/AutomatedTesting/Gem/Code/Source/AutomatedTestingSystemComponent.cpp b/AutomatedTesting/Gem/Code/Source/AutomatedTestingSystemComponent.cpp
index 3b373b1f65..06a0775d70 100644
--- a/AutomatedTesting/Gem/Code/Source/AutomatedTestingSystemComponent.cpp
+++ b/AutomatedTesting/Gem/Code/Source/AutomatedTestingSystemComponent.cpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
@@ -45,7 +46,7 @@ namespace AutomatedTesting
void AutomatedTestingSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
- AZ_UNUSED(required);
+ required.push_back(AZ_CRC_CE("MultiplayerService"));
}
void AutomatedTestingSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
@@ -60,6 +61,7 @@ namespace AutomatedTesting
void AutomatedTestingSystemComponent::Activate()
{
AutomatedTestingRequestBus::Handler::BusConnect();
+ RegisterMultiplayerComponents(); //< Register AutomatedTesting's multiplayer components to assign NetComponentIds
}
void AutomatedTestingSystemComponent::Deactivate()
diff --git a/AutomatedTesting/Gem/Code/automatedtesting_autogen_files.cmake b/AutomatedTesting/Gem/Code/automatedtesting_autogen_files.cmake
new file mode 100644
index 0000000000..b3c6fcaa0b
--- /dev/null
+++ b/AutomatedTesting/Gem/Code/automatedtesting_autogen_files.cmake
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+ ${LY_ROOT_FOLDER}/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Common.jinja
+ ${LY_ROOT_FOLDER}/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja
+ ${LY_ROOT_FOLDER}/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja
+ ${LY_ROOT_FOLDER}/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja
+ ${LY_ROOT_FOLDER}/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja
+)
diff --git a/AutomatedTesting/Gem/Code/automatedtesting_files.cmake b/AutomatedTesting/Gem/Code/automatedtesting_files.cmake
index 6bf2bf72d9..eb619104a4 100644
--- a/AutomatedTesting/Gem/Code/automatedtesting_files.cmake
+++ b/AutomatedTesting/Gem/Code/automatedtesting_files.cmake
@@ -11,4 +11,5 @@ set(FILES
Source/AutomatedTestingModule.cpp
Source/AutomatedTestingSystemComponent.cpp
Source/AutomatedTestingSystemComponent.h
+ Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml
)
diff --git a/AutomatedTesting/Gem/Code/enabled_gems.cmake b/AutomatedTesting/Gem/Code/enabled_gems.cmake
index f653a54505..e6a4d6ca37 100644
--- a/AutomatedTesting/Gem/Code/enabled_gems.cmake
+++ b/AutomatedTesting/Gem/Code/enabled_gems.cmake
@@ -55,4 +55,5 @@ set(ENABLED_GEMS
PrefabBuilder
AudioSystem
Profiler
+ Multiplayer
)
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt
index ff3cd5c465..87a66c880e 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt
+++ b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt
@@ -20,19 +20,6 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED)
COMPONENT
Atom
)
- ly_add_pytest(
- NAME AutomatedTesting::Atom_TestSuite_Main_Optimized
- TEST_SUITE main
- PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_Optimized.py
- TEST_SERIAL
- TIMEOUT 600
- RUNTIME_DEPENDENCIES
- AssetProcessor
- AutomatedTesting.Assets
- Editor
- COMPONENT
- Atom
- )
ly_add_pytest(
NAME AutomatedTesting::Atom_TestSuite_Sandbox
TEST_SUITE sandbox
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py
index 6cc48984ab..52e7ec993e 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py
@@ -4,252 +4,95 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
-import logging
-import os
-
import pytest
-import ly_test_tools.environment.file_system as file_system
-import editor_python_test_tools.hydra_test_utils as hydra
-from Atom.atom_utils.atom_constants import LIGHT_TYPES
-
-logger = logging.getLogger(__name__)
-TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests")
+from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
-@pytest.mark.parametrize("level", ["auto_test"])
-class TestAtomEditorComponentsMain(object):
- """Holds tests for Atom components."""
+class TestAutomation(EditorTestSuite):
- @pytest.mark.test_case_id("C32078118") # Decal
- @pytest.mark.test_case_id("C32078119") # DepthOfField
- @pytest.mark.test_case_id("C32078120") # Directional Light
- @pytest.mark.test_case_id("C32078121") # Exposure Control
- @pytest.mark.test_case_id("C32078115") # Global Skylight (IBL)
- @pytest.mark.test_case_id("C32078125") # Physical Sky
- @pytest.mark.test_case_id("C32078127") # PostFX Layer
- @pytest.mark.test_case_id("C32078131") # PostFX Radius Weight Modifier
- @pytest.mark.test_case_id("C32078117") # Light
- @pytest.mark.test_case_id("C36525660") # Display Mapper
- def test_AtomEditorComponents_AddedToEntity(self, request, editor, level, workspace, project, launcher_platform):
- """
- Please review the hydra script run by this test for more specific test info.
- Tests the Atom components & verifies all "expected_lines" appear in Editor.log
- """
- cfg_args = [level]
+ @pytest.mark.test_case_id("C36525657")
+ class AtomEditorComponents_BloomAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_BloomAdded as test_module
- expected_lines = [
- # Decal Component
- "Decal Entity successfully created",
- "Decal_test: Component added to the entity: True",
- "Decal_test: Component removed after UNDO: True",
- "Decal_test: Component added after REDO: True",
- "Decal_test: Entered game mode: True",
- "Decal_test: Exit game mode: True",
- "Decal Controller|Configuration|Material: SUCCESS",
- "Decal_test: Entity is hidden: True",
- "Decal_test: Entity is shown: True",
- "Decal_test: Entity deleted: True",
- "Decal_test: UNDO entity deletion works: True",
- "Decal_test: REDO entity deletion works: True",
- # DepthOfField Component
- "DepthOfField Entity successfully created",
- "DepthOfField_test: Component added to the entity: True",
- "DepthOfField_test: Component removed after UNDO: True",
- "DepthOfField_test: Component added after REDO: True",
- "DepthOfField_test: Entered game mode: True",
- "DepthOfField_test: Exit game mode: True",
- "DepthOfField_test: Entity disabled initially: True",
- "DepthOfField_test: Entity enabled after adding required components: True",
- "DepthOfField Controller|Configuration|Camera Entity: SUCCESS",
- "DepthOfField_test: Entity is hidden: True",
- "DepthOfField_test: Entity is shown: True",
- "DepthOfField_test: Entity deleted: True",
- "DepthOfField_test: UNDO entity deletion works: True",
- "DepthOfField_test: REDO entity deletion works: True",
- # Directional Light Component
- "Directional Light Entity successfully created",
- "Directional Light_test: Component added to the entity: True",
- "Directional Light_test: Component removed after UNDO: True",
- "Directional Light_test: Component added after REDO: True",
- "Directional Light_test: Entered game mode: True",
- "Directional Light_test: Exit game mode: True",
- "Directional Light_test: Entity is hidden: True",
- "Directional Light_test: Entity is shown: True",
- "Directional Light_test: Entity deleted: True",
- "Directional Light_test: UNDO entity deletion works: True",
- "Directional Light_test: REDO entity deletion works: True",
- # Exposure Control Component
- "Exposure Control Entity successfully created",
- "Exposure Control_test: Component added to the entity: True",
- "Exposure Control_test: Component removed after UNDO: True",
- "Exposure Control_test: Component added after REDO: True",
- "Exposure Control_test: Entered game mode: True",
- "Exposure Control_test: Exit game mode: True",
- "Exposure Control_test: Entity disabled initially: True",
- "Exposure Control_test: Entity enabled after adding required components: True",
- "Exposure Control_test: Entity is hidden: True",
- "Exposure Control_test: Entity is shown: True",
- "Exposure Control_test: Entity deleted: True",
- "Exposure Control_test: UNDO entity deletion works: True",
- "Exposure Control_test: REDO entity deletion works: True",
- # Global Skylight (IBL) Component
- "Global Skylight (IBL) Entity successfully created",
- "Global Skylight (IBL)_test: Component added to the entity: True",
- "Global Skylight (IBL)_test: Component removed after UNDO: True",
- "Global Skylight (IBL)_test: Component added after REDO: True",
- "Global Skylight (IBL)_test: Entered game mode: True",
- "Global Skylight (IBL)_test: Exit game mode: True",
- "Global Skylight (IBL) Controller|Configuration|Diffuse Image: SUCCESS",
- "Global Skylight (IBL) Controller|Configuration|Specular Image: SUCCESS",
- "Global Skylight (IBL)_test: Entity is hidden: True",
- "Global Skylight (IBL)_test: Entity is shown: True",
- "Global Skylight (IBL)_test: Entity deleted: True",
- "Global Skylight (IBL)_test: UNDO entity deletion works: True",
- "Global Skylight (IBL)_test: REDO entity deletion works: True",
- # Physical Sky Component
- "Physical Sky Entity successfully created",
- "Physical Sky component was added to entity",
- "Entity has a Physical Sky component",
- "Physical Sky_test: Component added to the entity: True",
- "Physical Sky_test: Component removed after UNDO: True",
- "Physical Sky_test: Component added after REDO: True",
- "Physical Sky_test: Entered game mode: True",
- "Physical Sky_test: Exit game mode: True",
- "Physical Sky_test: Entity is hidden: True",
- "Physical Sky_test: Entity is shown: True",
- "Physical Sky_test: Entity deleted: True",
- "Physical Sky_test: UNDO entity deletion works: True",
- "Physical Sky_test: REDO entity deletion works: True",
- # PostFX Layer Component
- "PostFX Layer Entity successfully created",
- "PostFX Layer_test: Component added to the entity: True",
- "PostFX Layer_test: Component removed after UNDO: True",
- "PostFX Layer_test: Component added after REDO: True",
- "PostFX Layer_test: Entered game mode: True",
- "PostFX Layer_test: Exit game mode: True",
- "PostFX Layer_test: Entity is hidden: True",
- "PostFX Layer_test: Entity is shown: True",
- "PostFX Layer_test: Entity deleted: True",
- "PostFX Layer_test: UNDO entity deletion works: True",
- "PostFX Layer_test: REDO entity deletion works: True",
- # PostFX Radius Weight Modifier Component
- "PostFX Radius Weight Modifier Entity successfully created",
- "PostFX Radius Weight Modifier_test: Component added to the entity: True",
- "PostFX Radius Weight Modifier_test: Component removed after UNDO: True",
- "PostFX Radius Weight Modifier_test: Component added after REDO: True",
- "PostFX Radius Weight Modifier_test: Entered game mode: True",
- "PostFX Radius Weight Modifier_test: Exit game mode: True",
- "PostFX Radius Weight Modifier_test: Entity is hidden: True",
- "PostFX Radius Weight Modifier_test: Entity is shown: True",
- "PostFX Radius Weight Modifier_test: Entity deleted: True",
- "PostFX Radius Weight Modifier_test: UNDO entity deletion works: True",
- "PostFX Radius Weight Modifier_test: REDO entity deletion works: True",
- # Light Component
- "Light Entity successfully created",
- "Light_test: Component added to the entity: True",
- "Light_test: Component removed after UNDO: True",
- "Light_test: Component added after REDO: True",
- "Light_test: Entered game mode: True",
- "Light_test: Exit game mode: True",
- "Light_test: Entity is hidden: True",
- "Light_test: Entity is shown: True",
- "Light_test: Entity deleted: True",
- "Light_test: UNDO entity deletion works: True",
- "Light_test: REDO entity deletion works: True",
- # Display Mapper Component
- "Display Mapper Entity successfully created",
- "Display Mapper_test: Component added to the entity: True",
- "Display Mapper_test: Component removed after UNDO: True",
- "Display Mapper_test: Component added after REDO: True",
- "Display Mapper_test: Entered game mode: True",
- "Display Mapper_test: Exit game mode: True",
- "Display Mapper_test: Entity is hidden: True",
- "Display Mapper_test: Entity is shown: True",
- "Display Mapper_test: Entity deleted: True",
- "Display Mapper_test: UNDO entity deletion works: True",
- "Display Mapper_test: REDO entity deletion works: True",
- ]
+ @pytest.mark.test_case_id("C32078118")
+ class AtomEditorComponents_DecalAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_DecalAdded as test_module
- unexpected_lines = [
- "Trace::Assert",
- "Trace::Error",
- "Traceback (most recent call last):",
- ]
+ @pytest.mark.test_case_id("C36525658")
+ class AtomEditorComponents_DeferredFogAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_DeferredFogAdded as test_module
- hydra.launch_and_validate_results(
- request,
- TEST_DIRECTORY,
- editor,
- "hydra_AtomEditorComponents_AddedToEntity.py",
- timeout=120,
- expected_lines=expected_lines,
- unexpected_lines=unexpected_lines,
- halt_on_unexpected=True,
- null_renderer=True,
- cfg_args=cfg_args,
- )
+ @pytest.mark.test_case_id("C32078119")
+ class AtomEditorComponents_DepthOfFieldAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module
- @pytest.mark.test_case_id("C34525095")
- def test_AtomEditorComponents_LightComponent(
- self, request, editor, workspace, project, launcher_platform, level):
- """
- Please review the hydra script run by this test for more specific test info.
- Tests that the Light component has the expected property options available to it.
- """
- cfg_args = [level]
+ @pytest.mark.test_case_id("C32078120")
+ class AtomEditorComponents_DirectionalLightAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_DirectionalLightAdded as test_module
- expected_lines = [
- "light_entity Entity successfully created",
- "Entity has a Light component",
- "light_entity_test: Component added to the entity: True",
- f"light_entity_test: Property value is {LIGHT_TYPES['sphere']} which matches {LIGHT_TYPES['sphere']}",
- "Controller|Configuration|Shadows|Enable shadow set to True",
- "light_entity Controller|Configuration|Shadows|Shadowmap size: SUCCESS",
- "Controller|Configuration|Shadows|Shadow filter method set to 1", # PCF
- "Controller|Configuration|Shadows|Filtering sample count set to 4",
- "Controller|Configuration|Shadows|Filtering sample count set to 64",
- "Controller|Configuration|Shadows|Shadow filter method set to 2", # ESM
- "Controller|Configuration|Shadows|ESM exponent set to 50.0",
- "Controller|Configuration|Shadows|ESM exponent set to 5000.0",
- "Controller|Configuration|Shadows|Shadow filter method set to 3", # ESM+PCF
- f"light_entity_test: Property value is {LIGHT_TYPES['spot_disk']} which matches {LIGHT_TYPES['spot_disk']}",
- f"light_entity_test: Property value is {LIGHT_TYPES['capsule']} which matches {LIGHT_TYPES['capsule']}",
- f"light_entity_test: Property value is {LIGHT_TYPES['quad']} which matches {LIGHT_TYPES['quad']}",
- "light_entity Controller|Configuration|Fast approximation: SUCCESS",
- "light_entity Controller|Configuration|Both directions: SUCCESS",
- f"light_entity_test: Property value is {LIGHT_TYPES['polygon']} which matches {LIGHT_TYPES['polygon']}",
- f"light_entity_test: Property value is {LIGHT_TYPES['simple_point']} "
- f"which matches {LIGHT_TYPES['simple_point']}",
- "Controller|Configuration|Attenuation radius|Mode set to 0",
- "Controller|Configuration|Attenuation radius|Radius set to 100.0",
- f"light_entity_test: Property value is {LIGHT_TYPES['simple_spot']} "
- f"which matches {LIGHT_TYPES['simple_spot']}",
- "Controller|Configuration|Shutters|Outer angle set to 45.0",
- "Controller|Configuration|Shutters|Outer angle set to 90.0",
- "light_entity_test: Component added to the entity: True",
- "Light component test (non-GPU) completed.",
- ]
+ @pytest.mark.test_case_id("C36525660")
+ class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module
- unexpected_lines = [
- "Trace::Assert",
- "Trace::Error",
- "Traceback (most recent call last):",
- ]
+ @pytest.mark.test_case_id("C32078121")
+ class AtomEditorComponents_ExposureControlAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_ExposureControlAdded as test_module
- hydra.launch_and_validate_results(
- request,
- TEST_DIRECTORY,
- editor,
- "hydra_AtomEditorComponents_LightComponent.py",
- timeout=120,
- expected_lines=expected_lines,
- unexpected_lines=unexpected_lines,
- halt_on_unexpected=True,
- null_renderer=True,
- cfg_args=cfg_args,
- )
+ @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("C36525671")
+ class AtomEditorComponents_HDRColorGradingAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_HDRColorGradingAdded 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("C36525663")
+ class AtomEditorComponents_OcclusionCullingPlaneAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_OcclusionCullingPlaneAdded 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("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
+
+ class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest):
+ from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py
index 23fb249761..f0e92c7e3e 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py
@@ -13,10 +13,10 @@ import zipfile
import pytest
import ly_test_tools.environment.file_system as file_system
-from ly_test_tools.image.screenshot_compare_qssim import qssim as compare_screenshots
from ly_test_tools.benchmark.data_aggregator import BenchmarkDataAggregator
import editor_python_test_tools.hydra_test_utils as hydra
+from .atom_utils.atom_component_helper import compare_screenshot_similarity, ImageComparisonTestFailure
logger = logging.getLogger(__name__)
DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots'
@@ -69,7 +69,7 @@ def create_screenshots_archive(screenshot_path):
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ["windows_editor"])
-@pytest.mark.parametrize("level", ["auto_test"])
+@pytest.mark.parametrize("level", ["Base"])
class TestAllComponentsIndepthTests(object):
@pytest.mark.parametrize("screenshot_name", ["AtomBasicLevelSetup.ppm"])
@@ -91,12 +91,7 @@ class TestAllComponentsIndepthTests(object):
"Viewport is set to the expected size: True",
"Exited game mode"
]
- unexpected_lines = [
- "Trace::Assert",
- "Trace::Error",
- "Traceback (most recent call last):",
- "Screenshot failed"
- ]
+ unexpected_lines = ["Traceback (most recent call last):"]
hydra.launch_and_validate_results(
request,
@@ -111,10 +106,12 @@ class TestAllComponentsIndepthTests(object):
null_renderer=False,
)
- for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images):
- compare_screenshots(test_screenshot, golden_screenshot)
-
- create_screenshots_archive(screenshot_directory)
+ similarity_threshold = 0.99
+ for test_screenshot, golden_image in zip(test_screenshots, golden_images):
+ screenshot_comparison_result = compare_screenshot_similarity(
+ test_screenshot, golden_image, similarity_threshold, True, screenshot_directory)
+ if screenshot_comparison_result != "Screenshots match":
+ raise Exception(f"Screenshot test failed: {screenshot_comparison_result}")
@pytest.mark.test_case_id("C34525095")
def test_LightComponent_ScreenshotMatchesGoldenImage(
@@ -149,12 +146,7 @@ class TestAllComponentsIndepthTests(object):
golden_images.append(golden_image_path)
expected_lines = ["spot_light Controller|Configuration|Shadows|Shadowmap size: SUCCESS"]
- unexpected_lines = [
- "Trace::Assert",
- "Trace::Error",
- "Traceback (most recent call last):",
- "Screenshot failed",
- ]
+ unexpected_lines = ["Traceback (most recent call last):"]
hydra.launch_and_validate_results(
request,
TEST_DIRECTORY,
@@ -168,10 +160,12 @@ class TestAllComponentsIndepthTests(object):
null_renderer=False,
)
- for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images):
- compare_screenshots(test_screenshot, golden_screenshot)
-
- create_screenshots_archive(screenshot_directory)
+ similarity_threshold = 0.99
+ for test_screenshot, golden_image in zip(test_screenshots, golden_images):
+ screenshot_comparison_result = compare_screenshot_similarity(
+ test_screenshot, golden_image, similarity_threshold, True, screenshot_directory)
+ if screenshot_comparison_result != "Screenshots match":
+ raise ImageComparisonTestFailure(f"Screenshot test failed: {screenshot_comparison_result}")
@pytest.mark.parametrize('rhi', ['dx12', 'vulkan'])
@@ -219,7 +213,6 @@ class TestPerformanceBenchmarkSuite(object):
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ['windows_generic'])
-@pytest.mark.system
class TestMaterialEditor(object):
@pytest.mark.parametrize("cfg_args,expected_lines", [
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py
deleted file mode 100644
index c29a391be4..0000000000
--- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py
+++ /dev/null
@@ -1,79 +0,0 @@
-"""
-Copyright (c) Contributors to the Open 3D Engine Project.
-For complete copyright and license terms please see the LICENSE at the root of this distribution.
-
-SPDX-License-Identifier: Apache-2.0 OR MIT
-"""
-import pytest
-
-from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
-
-
-@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.")
-@pytest.mark.parametrize("project", ["AutomatedTesting"])
-@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
-class TestAutomation(EditorTestSuite):
-
- @pytest.mark.test_case_id("C32078118")
- class AtomEditorComponents_DecalAdded(EditorSharedTest):
- from Atom.tests import hydra_AtomEditorComponents_DecalAdded as test_module
-
- @pytest.mark.test_case_id("C32078119")
- class AtomEditorComponents_DepthOfFieldAdded(EditorSharedTest):
- from Atom.tests import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module
-
- @pytest.mark.test_case_id("C32078120")
- class AtomEditorComponents_DirectionalLightAdded(EditorSharedTest):
- from Atom.tests import hydra_AtomEditorComponents_DirectionalLightAdded as test_module
-
- @pytest.mark.test_case_id("C32078121")
- class AtomEditorComponents_ExposureControlAdded(EditorSharedTest):
- from Atom.tests import hydra_AtomEditorComponents_ExposureControlAdded as test_module
-
- @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("C32078125")
- class AtomEditorComponents_PhysicalSkyAdded(EditorSharedTest):
- from Atom.tests import hydra_AtomEditorComponents_PhysicalSkyAdded 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("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/TestSuite_Sandbox.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py
index ad45e51080..9ceb3c951f 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py
@@ -9,63 +9,84 @@ import os
import pytest
+import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
+from Atom.atom_utils.atom_constants import LIGHT_TYPES
+
logger = logging.getLogger(__name__)
TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests")
+
class TestAtomEditorComponentsSandbox(object):
# It requires at least one test
def test_Dummy(self, request, editor, level, workspace, project, launcher_platform):
pass
- @pytest.mark.parametrize("project", ["AutomatedTesting"])
- @pytest.mark.parametrize("launcher_platform", ['windows_editor'])
- @pytest.mark.parametrize("level", ["auto_test"])
- class TestAtomEditorComponentsMain(object):
- """Holds tests for Atom components."""
- @pytest.mark.test_case_id("C32078128")
- def test_AtomEditorComponents_ReflectionProbeAddedToEntity(
- self, request, editor, level, workspace, project, launcher_platform):
- """
- Please review the hydra script run by this test for more specific test info.
- Tests the following Atom components and verifies all "expected_lines" appear in Editor.log:
- 1. Reflection Probe
- """
- cfg_args = [level]
+@pytest.mark.parametrize("project", ["AutomatedTesting"])
+@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
+@pytest.mark.parametrize("level", ["auto_test"])
+class TestAtomEditorComponentsMain(object):
+ """Holds tests for Atom components."""
- expected_lines = [
- # Reflection Probe Component
- "Reflection Probe Entity successfully created",
- "Reflection Probe_test: Component added to the entity: True",
- "Reflection Probe_test: Component removed after UNDO: True",
- "Reflection Probe_test: Component added after REDO: True",
- "Reflection Probe_test: Entered game mode: True",
- "Reflection Probe_test: Exit game mode: True",
- "Reflection Probe_test: Entity disabled initially: True",
- "Reflection Probe_test: Entity enabled after adding required components: True",
- "Reflection Probe_test: Cubemap is generated: True",
- "Reflection Probe_test: Entity is hidden: True",
- "Reflection Probe_test: Entity is shown: True",
- "Reflection Probe_test: Entity deleted: True",
- "Reflection Probe_test: UNDO entity deletion works: True",
- "Reflection Probe_test: REDO entity deletion works: True",
- ]
+ @pytest.mark.test_case_id("C34525095")
+ def test_AtomEditorComponents_LightComponent(
+ self, request, editor, workspace, project, launcher_platform, level):
+ """
+ Please review the hydra script run by this test for more specific test info.
+ Tests that the Light component has the expected property options available to it.
+ """
+ cfg_args = [level]
+
+ expected_lines = [
+ "light_entity Entity successfully created",
+ "Entity has a Light component",
+ "light_entity_test: Component added to the entity: True",
+ f"light_entity_test: Property value is {LIGHT_TYPES['sphere']} which matches {LIGHT_TYPES['sphere']}",
+ "Controller|Configuration|Shadows|Enable shadow set to True",
+ "light_entity Controller|Configuration|Shadows|Shadowmap size: SUCCESS",
+ "Controller|Configuration|Shadows|Shadow filter method set to 1", # PCF
+ "Controller|Configuration|Shadows|Filtering sample count set to 4",
+ "Controller|Configuration|Shadows|Filtering sample count set to 64",
+ "Controller|Configuration|Shadows|Shadow filter method set to 2", # ESM
+ "Controller|Configuration|Shadows|ESM exponent set to 50.0",
+ "Controller|Configuration|Shadows|ESM exponent set to 5000.0",
+ "Controller|Configuration|Shadows|Shadow filter method set to 3", # ESM+PCF
+ f"light_entity_test: Property value is {LIGHT_TYPES['spot_disk']} which matches {LIGHT_TYPES['spot_disk']}",
+ f"light_entity_test: Property value is {LIGHT_TYPES['capsule']} which matches {LIGHT_TYPES['capsule']}",
+ f"light_entity_test: Property value is {LIGHT_TYPES['quad']} which matches {LIGHT_TYPES['quad']}",
+ "light_entity Controller|Configuration|Fast approximation: SUCCESS",
+ "light_entity Controller|Configuration|Both directions: SUCCESS",
+ f"light_entity_test: Property value is {LIGHT_TYPES['polygon']} which matches {LIGHT_TYPES['polygon']}",
+ f"light_entity_test: Property value is {LIGHT_TYPES['simple_point']} "
+ f"which matches {LIGHT_TYPES['simple_point']}",
+ "Controller|Configuration|Attenuation radius|Mode set to 0",
+ "Controller|Configuration|Attenuation radius|Radius set to 100.0",
+ f"light_entity_test: Property value is {LIGHT_TYPES['simple_spot']} "
+ f"which matches {LIGHT_TYPES['simple_spot']}",
+ "Controller|Configuration|Shutters|Outer angle set to 45.0",
+ "Controller|Configuration|Shutters|Outer angle set to 90.0",
+ "light_entity_test: Component added to the entity: True",
+ "Light component test (non-GPU) completed.",
+ ]
+
+ unexpected_lines = ["Traceback (most recent call last):"]
+
+ hydra.launch_and_validate_results(
+ request,
+ TEST_DIRECTORY,
+ editor,
+ "hydra_AtomEditorComponents_LightComponent.py",
+ timeout=120,
+ expected_lines=expected_lines,
+ unexpected_lines=unexpected_lines,
+ halt_on_unexpected=True,
+ null_renderer=True,
+ cfg_args=cfg_args,
+ )
- hydra.launch_and_validate_results(
- request,
- TEST_DIRECTORY,
- editor,
- "hydra_AtomEditorComponents_AddedToEntity.py",
- timeout=120,
- expected_lines=expected_lines,
- unexpected_lines=[],
- halt_on_unexpected=True,
- null_renderer=True,
- cfg_args=cfg_args,
- )
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ['windows_generic'])
@@ -119,8 +140,6 @@ class TestMaterialEditorBasicTests(object):
"Save All worked as expected: True",
]
unexpected_lines = [
- # "Trace::Assert",
- # "Trace::Error",
"Traceback (most recent call last):"
]
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py
index 11832f8846..50cc15f7e8 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py
@@ -1,5 +1,6 @@
"""
-Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
+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
@@ -8,12 +9,19 @@ import datetime
import os
import zipfile
+from ly_test_tools.image.screenshot_compare_qssim import qssim as compare_screenshots
+
+
+class ImageComparisonTestFailure(Exception):
+ """Custom test failure for failed image comparisons."""
+ pass
+
def create_screenshots_archive(screenshot_path):
"""
Creates a new zip file archive at archive_path containing all files listed within archive_path.
:param screenshot_path: location containing the files to archive, the zip archive file will also be saved here.
- :return: None, but creates a new zip file archive inside path containing all of the files inside archive_path.
+ :return: path to the created .zip file archive.
"""
files_to_archive = []
@@ -27,14 +35,16 @@ def create_screenshots_archive(screenshot_path):
# Setup variables for naming the zip archive file.
timestamp = datetime.datetime.now().timestamp()
formatted_timestamp = datetime.datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d_%H-%M-%S")
- screenshots_file = os.path.join(screenshot_path, f'screenshots_{formatted_timestamp}.zip')
+ screenshots_zip_file = os.path.join(screenshot_path, f'screenshots_{formatted_timestamp}.zip')
# Write all of the valid .png and .ppm files to the archive file.
- with zipfile.ZipFile(screenshots_file, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as zip_archive:
+ with zipfile.ZipFile(screenshots_zip_file, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as zip_archive:
for file_path in files_to_archive:
file_name = os.path.basename(file_path)
zip_archive.write(file_path, file_name)
+ return screenshots_zip_file
+
def golden_images_directory():
"""
@@ -53,6 +63,36 @@ def golden_images_directory():
return golden_images_dir
+def compare_screenshot_similarity(
+ test_screenshot, golden_image, similarity_threshold, create_zip_archive=False, screenshot_directory=""):
+ """
+ Compares the similarity between a test screenshot and a golden image.
+ It returns a "Screenshots match" string if the comparison mean value is higher than the similarity threshold.
+ Otherwise, it returns an error string.
+ :param test_screenshot: path to the test screenshot to compare.
+ :param golden_image: path to the golden image to compare.
+ :param similarity_threshold: value for the comparison mean value to be asserted against.
+ :param create_zip_archive: toggle to create a zip archive containing the screenshots if the assert check fails.
+ :param screenshot_directory: directory containing screenshots to create zip archive from.
+ :return: Error string if compared mean value < similarity threshold or screenshot_directory is missing for .zip,
+ otherwise it returns a "Screenshots match" string.
+ """
+ result = "Screenshots match"
+ if create_zip_archive and not screenshot_directory:
+ result = 'You must specify a screenshot_directory in order to create a zip archive.\n'
+
+ mean_similarity = compare_screenshots(test_screenshot, golden_image)
+ if not mean_similarity > similarity_threshold:
+ if create_zip_archive:
+ create_screenshots_archive(screenshot_directory)
+ result = (
+ f"When comparing the test_screenshot: '{test_screenshot}' "
+ f"to golden_image: '{golden_image}' the mean similarity of '{mean_similarity}' "
+ f"was lower than the similarity threshold of '{similarity_threshold}'. ")
+
+ return result
+
+
def create_basic_atom_level(level_name):
"""
Creates a new level inside the Editor matching level_name & adds the following:
@@ -76,29 +116,11 @@ def create_basic_atom_level(level_name):
helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper")
- # Create a new level.
- new_level_name = level_name
- heightmap_resolution = 512
- heightmap_meters_per_pixel = 1
- terrain_texture_resolution = 412
- use_terrain = False
-
- # Return codes are ECreateLevelResult defined in CryEdit.h
- return_code = general.create_level_no_prompt(
- new_level_name, heightmap_resolution, heightmap_meters_per_pixel, terrain_texture_resolution, use_terrain)
- if return_code == 1:
- general.log(f"{new_level_name} level already exists")
- elif return_code == 2:
- general.log("Failed to create directory")
- elif return_code == 3:
- general.log("Directory length is too long")
- elif return_code != 0:
- general.log("Unknown error, failed to create level")
- else:
- general.log(f"{new_level_name} level created successfully")
-
- # Enable idle and update viewport.
+ # Wait for Editor idle loop before executing Python hydra scripts.
general.idle_enable(True)
+
+ # Basic setup for opened level.
+ helper.open_level(level_name="Base")
general.idle_wait(1.0)
general.update_viewport()
general.idle_wait(0.5) # half a second is more than enough for updating the viewport.
@@ -165,24 +187,25 @@ def create_basic_atom_level(level_name):
components=["Material"],
parent_id=default_level.id)
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", ground_plane.id, 32.0)
- ground_plane_material_asset_path = os.path.join(
- "Materials", "Presets", "PBR", "metal_chrome.azmaterial")
- ground_plane_material_asset_value = asset.AssetCatalogRequestBus(
- bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False)
- ground_plane.get_set_test(0, "Default Material|Material Asset", ground_plane_material_asset_value)
- # Work around to add the correct Atom Mesh component
+ # Work around to add the correct Atom Mesh component and asset.
mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId
ground_plane.components.append(
editor.EditorComponentAPIBus(
bus.Broadcast, "AddComponentsOfType", ground_plane.id, [mesh_type_id]
).GetValue()[0]
)
- ground_plane_mesh_asset_path = os.path.join("Models", "plane.azmodel")
+ ground_plane_mesh_asset_path = os.path.join("TestData", "Objects", "plane.azmodel")
ground_plane_mesh_asset_value = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", ground_plane_mesh_asset_path, math.Uuid(), False)
ground_plane.get_set_test(1, "Controller|Configuration|Mesh Asset", ground_plane_mesh_asset_value)
+ # Add Atom Material component and asset.
+ ground_plane_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_chrome.azmaterial")
+ ground_plane_material_asset_value = asset.AssetCatalogRequestBus(
+ bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False)
+ ground_plane.get_set_test(0, "Default Material|Material Asset", ground_plane_material_asset_value)
+
# Create directional_light entity and set the properties
directional_light = hydra.Entity("directional_light")
directional_light.create_entity(
@@ -199,12 +222,8 @@ def create_basic_atom_level(level_name):
entity_position=math.Vector3(0.0, 0.0, 1.0),
components=["Material"],
parent_id=default_level.id)
- sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial")
- sphere_material_asset_value = asset.AssetCatalogRequestBus(
- bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False)
- sphere_entity.get_set_test(0, "Default Material|Material Asset", sphere_material_asset_value)
- # Work around to add the correct Atom Mesh component
+ # Work around to add the correct Atom Mesh component and asset.
sphere_entity.components.append(
editor.EditorComponentAPIBus(
bus.Broadcast, "AddComponentsOfType", sphere_entity.id, [mesh_type_id]
@@ -215,6 +234,12 @@ def create_basic_atom_level(level_name):
bus.Broadcast, "GetAssetIdByPath", sphere_mesh_asset_path, math.Uuid(), False)
sphere_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", sphere_mesh_asset_value)
+ # Add Atom Material component and asset.
+ sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial")
+ sphere_material_asset_value = asset.AssetCatalogRequestBus(
+ bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False)
+ sphere_entity.get_set_test(0, "Default Material|Material Asset", sphere_material_asset_value)
+
# Create camera component and set the properties
camera_entity = hydra.Entity("camera")
camera_entity.create_entity(
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py
index 344a0dbd29..70156b9375 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py
@@ -42,12 +42,14 @@ class AtomComponentProperties:
Bloom component properties. Requires PostFX Layer component.
- 'requires' a list of component names as strings required by this component.
Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n
+ - 'Enable Bloom' Toggle active state of the component True/False
:param property: From the last element of the property tree path. Default 'name' for component name string.
:return: Full property path OR component name if no property specified.
"""
properties = {
'name': 'Bloom',
'requires': [AtomComponentProperties.postfx_layer()],
+ 'Enable Bloom': 'Controller|Configuration|Enable Bloom',
}
return properties[property]
@@ -83,12 +85,14 @@ class AtomComponentProperties:
Deferred Fog component properties. Requires PostFX Layer component.
- 'requires' a list of component names as strings required by this component.
Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n
+ - 'Enable Deferred Fog' Toggle active state of the component True/False
:param property: From the last element of the property tree path. Default 'name' for component name string.
:return: Full property path OR component name if no property specified.
"""
properties = {
'name': 'Deferred Fog',
'requires': [AtomComponentProperties.postfx_layer()],
+ 'Enable Deferred Fog': 'Controller|Configuration|Enable Deferred Fog',
}
return properties[property]
@@ -212,12 +216,14 @@ class AtomComponentProperties:
HDR Color Grading component properties. Requires PostFX Layer component.
- 'requires' a list of component names as strings required by this component.
Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n
+ - 'Enable HDR color grading' Toggle active state of the component True/False
:param property: From the last element of the property tree path. Default 'name' for component name string.
:return: Full property path OR component name if no property specified.
"""
properties = {
'name': 'HDR Color Grading',
'requires': [AtomComponentProperties.postfx_layer()],
+ 'Enable HDR color grading': 'Controller|Configuration|Enable HDR color grading',
}
return properties[property]
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py
deleted file mode 100644
index bbc8463152..0000000000
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py
+++ /dev/null
@@ -1,238 +0,0 @@
-"""
-Copyright (c) Contributors to the Open 3D Engine Project.
-For complete copyright and license terms please see the LICENSE at the root of this distribution.
-
-SPDX-License-Identifier: Apache-2.0 OR MIT
-"""
-
-import os
-import sys
-
-import azlmbr.math as math
-import azlmbr.bus as bus
-import azlmbr.paths
-import azlmbr.asset as asset
-import azlmbr.entity as entity
-import azlmbr.legacy.general as general
-import azlmbr.editor as editor
-import azlmbr.render as render
-
-sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
-
-import editor_python_test_tools.hydra_editor_utils as hydra
-from editor_python_test_tools.utils import TestHelper
-
-
-def run():
- """
- Summary:
- The below common tests are done for each of the components.
- 1) Addition of component to the entity
- 2) UNDO/REDO of addition of component
- 3) Enter/Exit game mode
- 4) Hide/Show entity containing component
- 5) Deletion of component
- 6) UNDO/REDO of deletion of component
- Some additional tests for specific components include
- 1) Assigning value to some properties of each component
- 2) Verifying if the component is activated only when the required components are added
-
- Expected Result:
- 1) Component can be added to an entity.
- 2) The addition of component can be undone and redone.
- 3) Game mode can be entered/exited without issue.
- 4) Entity with component can be hidden/shown.
- 5) Component can be deleted.
- 6) The deletion of component can be undone and redone.
- 7) Component is activated only when the required components are added
- 8) Values can be assigned to the properties of the component
-
- :return: None
- """
-
- def create_entity_undo_redo_component_addition(component_name):
- new_entity = hydra.Entity(f"{component_name}")
- new_entity.create_entity(math.Vector3(512.0, 512.0, 34.0), [component_name])
- general.log(f"{component_name}_test: Component added to the entity: "
- f"{hydra.has_components(new_entity.id, [component_name])}")
-
- # undo component addition
- general.undo()
- TestHelper.wait_for_condition(lambda: not hydra.has_components(new_entity.id, [component_name]), 2.0)
- general.log(f"{component_name}_test: Component removed after UNDO: "
- f"{not hydra.has_components(new_entity.id, [component_name])}")
-
- # redo component addition
- general.redo()
- TestHelper.wait_for_condition(lambda: hydra.has_components(new_entity.id, [component_name]), 2.0)
- general.log(f"{component_name}_test: Component added after REDO: "
- f"{hydra.has_components(new_entity.id, [component_name])}")
-
- return new_entity
-
- def verify_enter_exit_game_mode(component_name):
- general.enter_game_mode()
- TestHelper.wait_for_condition(lambda: general.is_in_game_mode(), 2.0)
- general.log(f"{component_name}_test: Entered game mode: {general.is_in_game_mode()}")
- general.exit_game_mode()
- TestHelper.wait_for_condition(lambda: not general.is_in_game_mode(), 2.0)
- general.log(f"{component_name}_test: Exit game mode: {not general.is_in_game_mode()}")
-
- def verify_hide_unhide_entity(component_name, entity_obj):
-
- def is_entity_hidden(entity_id):
- return editor.EditorEntityInfoRequestBus(bus.Event, "IsHidden", entity_id)
-
- editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", entity_obj.id, False)
- general.idle_wait_frames(1)
- general.log(f"{component_name}_test: Entity is hidden: {is_entity_hidden(entity_obj.id)}")
- editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", entity_obj.id, True)
- general.idle_wait_frames(1)
- general.log(f"{component_name}_test: Entity is shown: {not is_entity_hidden(entity_obj.id)}")
-
- def verify_deletion_undo_redo(component_name, entity_obj):
- editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntityById", entity_obj.id)
- TestHelper.wait_for_condition(lambda: not hydra.find_entity_by_name(entity_obj.name), 2.0)
- general.log(f"{component_name}_test: Entity deleted: {not hydra.find_entity_by_name(entity_obj.name)}")
-
- general.undo()
- TestHelper.wait_for_condition(lambda: hydra.find_entity_by_name(entity_obj.name) is not None, 2.0)
- general.log(f"{component_name}_test: UNDO entity deletion works: "
- f"{hydra.find_entity_by_name(entity_obj.name) is not None}")
-
- general.redo()
- TestHelper.wait_for_condition(lambda: not hydra.find_entity_by_name(entity_obj.name), 2.0)
- general.log(f"{component_name}_test: REDO entity deletion works: "
- f"{not hydra.find_entity_by_name(entity_obj.name)}")
-
- def verify_required_component_addition(entity_obj, components_to_add, component_name):
-
- def is_component_enabled(entity_componentid_pair):
- return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", entity_componentid_pair)
-
- general.log(
- f"{component_name}_test: Entity disabled initially: "
- f"{not is_component_enabled(entity_obj.components[0])}")
- for component in components_to_add:
- entity_obj.add_component(component)
- TestHelper.wait_for_condition(lambda: is_component_enabled(entity_obj.components[0]), 2.0)
- general.log(
- f"{component_name}_test: Entity enabled after adding "
- f"required components: {is_component_enabled(entity_obj.components[0])}"
- )
-
- def verify_set_property(entity_obj, path, value):
- entity_obj.get_set_test(0, path, value)
-
- # Verify cubemap generation
- def verify_cubemap_generation(component_name, entity_obj):
- # Initially Check if the component has Reflection Probe component
- if not hydra.has_components(entity_obj.id, ["Reflection Probe"]):
- raise ValueError(f"Given entity {entity_obj.name} has no Reflection Probe component")
- render.EditorReflectionProbeBus(azlmbr.bus.Event, "BakeReflectionProbe", entity_obj.id)
-
- def get_value():
- hydra.get_component_property_value(entity_obj.components[0], "Cubemap|Baked Cubemap Path")
-
- TestHelper.wait_for_condition(lambda: get_value() != "", 20.0)
- general.log(f"{component_name}_test: Cubemap is generated: {get_value() != ''}")
-
- # Wait for Editor idle loop before executing Python hydra scripts.
- TestHelper.init_idle()
-
- # Delete all existing entities initially
- search_filter = azlmbr.entity.SearchFilter()
- all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter)
- editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities)
-
- class ComponentTests:
- """Test launcher for each component."""
- def __init__(self, component_name, *additional_tests):
- self.component_name = component_name
- self.additional_tests = additional_tests
- self.run_component_tests()
-
- def run_component_tests(self):
- # Run common and additional tests
- entity_obj = create_entity_undo_redo_component_addition(self.component_name)
-
- # Enter/Exit game mode test
- verify_enter_exit_game_mode(self.component_name)
-
- # Any additional tests are executed here
- for test in self.additional_tests:
- test(entity_obj)
-
- # Hide/Unhide entity test
- verify_hide_unhide_entity(self.component_name, entity_obj)
-
- # Deletion/Undo/Redo test
- verify_deletion_undo_redo(self.component_name, entity_obj)
-
- # DepthOfField Component
- camera_entity = hydra.Entity("camera_entity")
- camera_entity.create_entity(math.Vector3(512.0, 512.0, 34.0), ["Camera"])
- depth_of_field = "DepthOfField"
- ComponentTests(
- depth_of_field,
- lambda entity_obj: verify_required_component_addition(entity_obj, ["PostFX Layer"], depth_of_field),
- lambda entity_obj: verify_set_property(
- entity_obj, "Controller|Configuration|Camera Entity", camera_entity.id))
-
- # Decal Component
- material_asset_path = os.path.join("AutomatedTesting", "Materials", "basic_grey.material")
- material_asset = asset.AssetCatalogRequestBus(
- bus.Broadcast, "GetAssetIdByPath", material_asset_path, math.Uuid(), False)
- ComponentTests(
- "Decal", lambda entity_obj: verify_set_property(
- entity_obj, "Controller|Configuration|Material", material_asset))
-
- # Directional Light Component
- ComponentTests(
- "Directional Light",
- lambda entity_obj: verify_set_property(
- entity_obj, "Controller|Configuration|Shadow|Camera", camera_entity.id))
-
- # Exposure Control Component
- ComponentTests(
- "Exposure Control", lambda entity_obj: verify_required_component_addition(
- entity_obj, ["PostFX Layer"], "Exposure Control"))
-
- # Global Skylight (IBL) Component
- diffuse_image_path = os.path.join("LightingPresets", "greenwich_park_02_4k_iblskyboxcm.exr.streamingimage")
- diffuse_image_asset = asset.AssetCatalogRequestBus(
- bus.Broadcast, "GetAssetIdByPath", diffuse_image_path, math.Uuid(), False)
- specular_image_path = os.path.join("LightingPresets", "greenwich_park_02_4k_iblskyboxcm.exr.streamingimage")
- specular_image_asset = asset.AssetCatalogRequestBus(
- bus.Broadcast, "GetAssetIdByPath", specular_image_path, math.Uuid(), False)
- ComponentTests(
- "Global Skylight (IBL)",
- lambda entity_obj: verify_set_property(
- entity_obj, "Controller|Configuration|Diffuse Image", diffuse_image_asset),
- lambda entity_obj: verify_set_property(
- entity_obj, "Controller|Configuration|Specular Image", specular_image_asset))
-
- # Physical Sky Component
- ComponentTests("Physical Sky")
-
- # PostFX Layer Component
- ComponentTests("PostFX Layer")
-
- # PostFX Radius Weight Modifier Component
- ComponentTests("PostFX Radius Weight Modifier")
-
- # Light Component
- ComponentTests("Light")
-
- # Display Mapper Component
- ComponentTests("Display Mapper")
-
- # Reflection Probe Component
- reflection_probe = "Reflection Probe"
- ComponentTests(
- reflection_probe,
- lambda entity_obj: verify_required_component_addition(entity_obj, ["Box Shape"], reflection_probe),
- lambda entity_obj: verify_cubemap_generation(reflection_probe, entity_obj),)
-
-if __name__ == "__main__":
- run()
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_BloomAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_BloomAdded.py
new file mode 100644
index 0000000000..56b22eb20d
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_BloomAdded.py
@@ -0,0 +1,189 @@
+"""
+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")
+ bloom_creation = (
+ "Bloom Entity successfully created",
+ "Bloom Entity failed to be created")
+ bloom_component = (
+ "Entity has a Bloom component",
+ "Entity failed to find Bloom component")
+ bloom_disabled = (
+ "Bloom component disabled",
+ "Bloom component was not disabled")
+ postfx_layer_component = (
+ "Entity has a PostFX Layer component",
+ "Entity did not have an PostFX Layer component")
+ bloom_enabled = (
+ "Bloom component enabled",
+ "Bloom component was not enabled")
+ enable_bloom_parameter_enabled = (
+ "Enable Bloom parameter enabled",
+ "Enable Bloom parameter 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_Bloom_AddedToEntity():
+ """
+ Summary:
+ Tests the Bloom component can be added to an entity and has the expected functionality.
+
+ Test setup:
+ - Wait for Editor idle loop.
+ - Open the "Base" level.
+
+ Expected Behavior:
+ The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
+ Creation and deletion undo/redo should also work.
+
+ Test Steps:
+ 1) Create an Bloom entity with no components.
+ 2) Add Bloom component to Bloom entity.
+ 3) UNDO the entity creation and component addition.
+ 4) REDO the entity creation and component addition.
+ 5) Verify Bloom component not enabled.
+ 6) Add PostFX Layer component since it is required by the Bloom component.
+ 7) Verify Bloom component is enabled.
+ 8) Enable the "Enable Bloom" parameter.
+ 9) Enter/Exit game mode.
+ 10) Test IsHidden.
+ 11) Test IsVisible.
+ 12) Delete Bloom entity.
+ 13) UNDO deletion.
+ 14) REDO deletion.
+ 15) Look for errors.
+
+ :return: None
+ """
+
+ 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 an Bloom entity with no components.
+ bloom_entity = EditorEntity.create_editor_entity(AtomComponentProperties.bloom())
+ Report.critical_result(Tests.bloom_creation, bloom_entity.exists())
+
+ # 2. Add Bloom component to Bloom entity.
+ bloom_component = bloom_entity.add_component(AtomComponentProperties.bloom())
+ Report.critical_result(Tests.bloom_component, bloom_entity.has_component(AtomComponentProperties.bloom()))
+
+ # 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 bloom_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, bloom_entity.exists())
+
+ # 5. Verify Bloom component not enabled.
+ Report.result(Tests.bloom_disabled, not bloom_component.is_enabled())
+
+ # 6. Add PostFX Layer component since it is required by the Bloom component.
+ bloom_entity.add_component(AtomComponentProperties.postfx_layer())
+ Report.result(
+ Tests.postfx_layer_component,
+ bloom_entity.has_component(AtomComponentProperties.postfx_layer()))
+
+ # 7. Verify Bloom component is enabled.
+ Report.result(Tests.bloom_enabled, bloom_component.is_enabled())
+
+ # 8. Enable the "Enable Bloom" parameter.
+ bloom_component.set_component_property_value(AtomComponentProperties.bloom('Enable Bloom'), True)
+ Report.result(
+ Tests.enable_bloom_parameter_enabled,
+ bloom_component.get_component_property_value(AtomComponentProperties.bloom('Enable Bloom')) is True)
+
+ # 9. 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)
+
+ # 10. Test IsHidden.
+ bloom_entity.set_visibility_state(False)
+ Report.result(Tests.is_hidden, bloom_entity.is_hidden() is True)
+
+ # 11. Test IsVisible.
+ bloom_entity.set_visibility_state(True)
+ general.idle_wait_frames(1)
+ Report.result(Tests.is_visible, bloom_entity.is_visible() is True)
+
+ # 12. Delete Bloom entity.
+ bloom_entity.delete()
+ Report.result(Tests.entity_deleted, not bloom_entity.exists())
+
+ # 13. UNDO deletion.
+ general.undo()
+ Report.result(Tests.deletion_undo, bloom_entity.exists())
+
+ # 14. REDO deletion.
+ general.redo()
+ Report.result(Tests.deletion_redo, not bloom_entity.exists())
+
+ # 15. 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__":
+ from editor_python_test_tools.utils import Report
+ Report.start_test(AtomEditorComponents_Bloom_AddedToEntity)
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_DeferredFogAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py
new file mode 100644
index 0000000000..71163ece94
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py
@@ -0,0 +1,193 @@
+"""
+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")
+ deferred_fog_creation = (
+ "Deferred Fog Entity successfully created",
+ "Deferred Fog Entity failed to be created")
+ deferred_fog_component = (
+ "Entity has a Deferred Fog component",
+ "Entity failed to find Deferred Fog component")
+ deferred_fog_disabled = (
+ "Deferred Fog component disabled",
+ "Deferred Fog component was not disabled")
+ postfx_layer_component = (
+ "Entity has a PostFX Layer component",
+ "Entity did not have an PostFX Layer component")
+ deferred_fog_enabled = (
+ "Deferred Fog component enabled",
+ "Deferred Fog component was not enabled")
+ enable_deferred_fog_parameter_enabled = (
+ "Enable Deferred Fog parameter enabled",
+ "Enable Deferred Fog parameter 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_DeferredFog_AddedToEntity():
+ """
+ Summary:
+ Tests the Deferred Fog component can be added to an entity and has the expected functionality.
+
+ Test setup:
+ - Wait for Editor idle loop.
+ - Open the "Base" level.
+
+ Expected Behavior:
+ The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
+ Creation and deletion undo/redo should also work.
+
+ Test Steps:
+ 1) Create an Deferred Fog entity with no components.
+ 2) Add Deferred Fog component to Deferred Fog entity.
+ 3) UNDO the entity creation and component addition.
+ 4) REDO the entity creation and component addition.
+ 5) Verify Deferred Fog component not enabled.
+ 6) Add PostFX Layer component since it is required by the Deferred Fog component.
+ 7) Verify Deferred Fog component is enabled.
+ 8) Enable the "Enable Deferred Fog" parameter.
+ 9) Enter/Exit game mode.
+ 10) Test IsHidden.
+ 11) Test IsVisible.
+ 12) Delete Deferred Fog entity.
+ 13) UNDO deletion.
+ 14) REDO deletion.
+ 15) Look for errors.
+
+ :return: None
+ """
+
+ 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 an Deferred Fog entity with no components.
+ deferred_fog_entity = EditorEntity.create_editor_entity(AtomComponentProperties.deferred_fog())
+ Report.critical_result(Tests.deferred_fog_creation, deferred_fog_entity.exists())
+
+ # 2. Add Deferred Fog component to Deferred Fog entity.
+ deferred_fog_component = deferred_fog_entity.add_component(
+ AtomComponentProperties.deferred_fog())
+ Report.critical_result(
+ Tests.deferred_fog_component,
+ deferred_fog_entity.has_component(AtomComponentProperties.deferred_fog()))
+
+ # 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 deferred_fog_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, deferred_fog_entity.exists())
+
+ # 5. Verify Deferred Fog component not enabled.
+ Report.result(Tests.deferred_fog_disabled, not deferred_fog_component.is_enabled())
+
+ # 6. Add PostFX Layer component since it is required by the Deferred Fog component.
+ deferred_fog_entity.add_component(AtomComponentProperties.postfx_layer())
+ Report.result(
+ Tests.postfx_layer_component,
+ deferred_fog_entity.has_component(AtomComponentProperties.postfx_layer()))
+
+ # 7. Verify Deferred Fog component is enabled.
+ Report.result(Tests.deferred_fog_enabled, deferred_fog_component.is_enabled())
+
+ # 8. Enable the "Enable Deferred Fog" parameter.
+ deferred_fog_component.set_component_property_value(
+ AtomComponentProperties.deferred_fog('Enable Deferred Fog'), True)
+ Report.result(Tests.enable_deferred_fog_parameter_enabled,
+ deferred_fog_component.get_component_property_value(
+ AtomComponentProperties.deferred_fog('Enable Deferred Fog')) is True)
+
+ # 9. 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)
+
+ # 10. Test IsHidden.
+ deferred_fog_entity.set_visibility_state(False)
+ Report.result(Tests.is_hidden, deferred_fog_entity.is_hidden() is True)
+
+ # 11. Test IsVisible.
+ deferred_fog_entity.set_visibility_state(True)
+ general.idle_wait_frames(1)
+ Report.result(Tests.is_visible, deferred_fog_entity.is_visible() is True)
+
+ # 12. Delete Deferred Fog entity.
+ deferred_fog_entity.delete()
+ Report.result(Tests.entity_deleted, not deferred_fog_entity.exists())
+
+ # 13. UNDO deletion.
+ general.undo()
+ Report.result(Tests.deletion_undo, deferred_fog_entity.exists())
+
+ # 14. REDO deletion.
+ general.redo()
+ Report.result(Tests.deletion_redo, not deferred_fog_entity.exists())
+
+ # 15. 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__":
+ from editor_python_test_tools.utils import Report
+ Report.start_test(AtomEditorComponents_DeferredFog_AddedToEntity)
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_HDRColorGradingAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRColorGradingAdded.py
new file mode 100644
index 0000000000..4972079fcd
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRColorGradingAdded.py
@@ -0,0 +1,192 @@
+"""
+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")
+ hdr_color_grading_creation = (
+ "HDR Color Grading Entity successfully created",
+ "HDR Color Grading Entity failed to be created")
+ hdr_color_grading_component = (
+ "Entity has an HDR Color Grading component",
+ "Entity failed to find HDR Color Grading component")
+ hdr_color_grading_disabled = (
+ "HDR Color Grading component disabled",
+ "HDR Color Grading component was not disabled")
+ postfx_layer_component = (
+ "Entity has a PostFX Layer component",
+ "Entity did not have an PostFX Layer component")
+ hdr_color_grading_enabled = (
+ "HDR Color Grading component enabled",
+ "HDR Color Grading component was not enabled")
+ enable_hdr_color_grading_parameter_enabled = (
+ "Enable HDR Color Grading parameter enabled",
+ "Enable HDR Color Grading parameter 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_HDRColorGrading_AddedToEntity():
+ """
+ Summary:
+ Tests the HDR Color Grading component can be added to an entity and has the expected functionality.
+
+ Test setup:
+ - Wait for Editor idle loop.
+ - Open the "Base" level.
+
+ Expected Behavior:
+ The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
+ Creation and deletion undo/redo should also work.
+
+ Test Steps:
+ 1) Create an HDR Color Grading entity with no components.
+ 2) Add HDR Color Grading component to HDR Color Grading entity.
+ 3) UNDO the entity creation and component addition.
+ 4) REDO the entity creation and component addition.
+ 5) Verify HDR Color Grading component not enabled.
+ 6) Add PostFX Layer component since it is required by the HDR Color Grading component.
+ 7) Verify HDR Color Grading component is enabled.
+ 8) Enable the "Enable HDR Color Grading" parameter.
+ 9) Enter/Exit game mode.
+ 10) Test IsHidden.
+ 11) Test IsVisible.
+ 12) Delete HDR Color Grading entity.
+ 13) UNDO deletion.
+ 14) REDO deletion.
+ 15) Look for errors.
+
+ :return: None
+ """
+
+ 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 an HDR Color Grading entity with no components.
+ hdr_color_grading_entity = EditorEntity.create_editor_entity(AtomComponentProperties.hdr_color_grading())
+ Report.critical_result(Tests.hdr_color_grading_creation, hdr_color_grading_entity.exists())
+
+ # 2. Add HDR Color Grading component to HDR Color Grading entity.
+ hdr_color_grading_component = hdr_color_grading_entity.add_component(
+ AtomComponentProperties.hdr_color_grading())
+ Report.critical_result(
+ Tests.hdr_color_grading_component,
+ hdr_color_grading_entity.has_component(AtomComponentProperties.hdr_color_grading()))
+
+ # 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 hdr_color_grading_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, hdr_color_grading_entity.exists())
+
+ # 5. Verify HDR Color Grading component not enabled.
+ Report.result(Tests.hdr_color_grading_disabled, not hdr_color_grading_component.is_enabled())
+
+ # 6. Add PostFX Layer component since it is required by the HDR Color Grading component.
+ hdr_color_grading_entity.add_component(AtomComponentProperties.postfx_layer())
+ Report.result(
+ Tests.postfx_layer_component,
+ hdr_color_grading_entity.has_component(AtomComponentProperties.postfx_layer()))
+
+ # 7. Verify HDR Color Grading component is enabled.
+ Report.result(Tests.hdr_color_grading_enabled, hdr_color_grading_component.is_enabled())
+
+ # 8. Enable the "Enable HDR Color Grading" parameter.
+ hdr_color_grading_component.set_component_property_value(
+ AtomComponentProperties.hdr_color_grading('Enable HDR color grading'), True)
+ Report.result(Tests.enable_hdr_color_grading_parameter_enabled,
+ hdr_color_grading_component.get_component_property_value(
+ AtomComponentProperties.hdr_color_grading('Enable HDR color grading')) is True)
+
+ # 9. 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)
+
+ # 10. Test IsHidden.
+ hdr_color_grading_entity.set_visibility_state(False)
+ Report.result(Tests.is_hidden, hdr_color_grading_entity.is_hidden() is True)
+
+ # 11. Test IsVisible.
+ hdr_color_grading_entity.set_visibility_state(True)
+ general.idle_wait_frames(1)
+ Report.result(Tests.is_visible, hdr_color_grading_entity.is_visible() is True)
+
+ # 12. Delete HDR Color Grading entity.
+ hdr_color_grading_entity.delete()
+ Report.result(Tests.entity_deleted, not hdr_color_grading_entity.exists())
+
+ # 13. UNDO deletion.
+ general.undo()
+ Report.result(Tests.deletion_undo, hdr_color_grading_entity.exists())
+
+ # 14. REDO deletion.
+ general.redo()
+ Report.result(Tests.deletion_redo, not hdr_color_grading_entity.exists())
+
+ # 15. 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__":
+ from editor_python_test_tools.utils import Report
+ Report.start_test(AtomEditorComponents_HDRColorGrading_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_OcclusionCullingPlaneAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_OcclusionCullingPlaneAdded.py
new file mode 100644
index 0000000000..4226ae3dfe
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_OcclusionCullingPlaneAdded.py
@@ -0,0 +1,159 @@
+"""
+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")
+ occlusion_culling_plane_entity_creation = (
+ "Occlusion Culling Plane Entity successfully created",
+ "Occlusion Culling Plane Entity failed to be created")
+ occlusion_culling_plane_component_added = (
+ "Entity has a Occlusion Culling Plane component",
+ "Entity failed to find Occlusion Culling Plane 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_OcclusionCullingPlane_AddedToEntity():
+ """
+ Summary:
+ Tests the occlusion culling plane 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 Occlusion Culling Plane entity with no components.
+ 2) Add a Occlusion Culling Plane component to Occlusion Culling Plane 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 Occlusion Culling Plane entity.
+ 9) UNDO deletion.
+ 10) REDO deletion.
+ 11) Look for errors.
+
+ :return: None
+ """
+
+ 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 occlusion culling plane entity with no components.
+ occlusion_culling_plane_entity = EditorEntity.create_editor_entity(
+ AtomComponentProperties.occlusion_culling_plane())
+ Report.critical_result(Tests.occlusion_culling_plane_entity_creation,
+ occlusion_culling_plane_entity.exists())
+
+ # 2. Add a occlusion culling plane component to occlusion culling plane entity.
+ occlusion_culling_plane_component = occlusion_culling_plane_entity.add_component(
+ AtomComponentProperties.occlusion_culling_plane())
+ Report.critical_result(
+ Tests.occlusion_culling_plane_component_added,
+ occlusion_culling_plane_entity.has_component(AtomComponentProperties.occlusion_culling_plane()))
+
+ # 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 occlusion_culling_plane_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, occlusion_culling_plane_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.
+ occlusion_culling_plane_entity.set_visibility_state(False)
+ Report.result(Tests.is_hidden, occlusion_culling_plane_entity.is_hidden() is True)
+
+ # 7. Test IsVisible.
+ occlusion_culling_plane_entity.set_visibility_state(True)
+ general.idle_wait_frames(1)
+ Report.result(Tests.is_visible, occlusion_culling_plane_entity.is_visible() is True)
+
+ # 8. Delete occlusion_culling_plane entity.
+ occlusion_culling_plane_entity.delete()
+ Report.result(Tests.entity_deleted, not occlusion_culling_plane_entity.exists())
+
+ # 9. UNDO deletion.
+ general.undo()
+ Report.result(Tests.deletion_undo, occlusion_culling_plane_entity.exists())
+
+ # 10. REDO deletion.
+ general.redo()
+ Report.result(Tests.deletion_redo, not occlusion_culling_plane_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_OcclusionCullingPlane_AddedToEntity)
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..efef4c0a43 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py
@@ -70,12 +70,11 @@ def AtomEditorComponents_postfx_layer_AddedToEntity():
: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.
@@ -85,13 +84,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/Atom/tests/hydra_GPUTest_BasicLevelSetup.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py
index 9515712583..645447e6de 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py
@@ -6,18 +6,6 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
-import sys
-
-import azlmbr.asset as asset
-import azlmbr.bus as bus
-import azlmbr.camera
-import azlmbr.entity as entity
-import azlmbr.legacy.general as general
-import azlmbr.math as math
-import azlmbr.paths
-import azlmbr.editor as editor
-
-sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
@@ -45,9 +33,18 @@ def run():
11. Adds a "camera" entity to "default_level" & adds a Camera component with 80 degree FOV and Transform values:
Translate - x:5.5m, y:-12.0m, z:9.0m
Rotate - x:-27.0, y:-12.0, z:25.0
- 12. Finally enters game mode, takes a screenshot, exits game mode, & saves the level.
+ 12. Finally enters game mode, takes a screenshot, & exits game mode.
:return: None
"""
+ import azlmbr.asset as asset
+ import azlmbr.bus as bus
+ import azlmbr.camera as camera
+ import azlmbr.entity as entity
+ import azlmbr.legacy.general as general
+ import azlmbr.math as math
+ import azlmbr.paths
+ import azlmbr.editor as editor
+
def initial_viewport_setup(screen_width, screen_height):
general.set_viewport_size(screen_width, screen_height)
general.update_viewport()
@@ -84,33 +81,11 @@ def run():
general.run_console("r_displayInfo=0")
general.idle_wait(1.0)
- return True
-
# Wait for Editor idle loop before executing Python hydra scripts.
general.idle_enable(True)
- # Open the auto_test level.
- new_level_name = "auto_test" # Specified in class TestAllComponentsIndepthTests()
- heightmap_resolution = 512
- heightmap_meters_per_pixel = 1
- terrain_texture_resolution = 412
- use_terrain = False
-
- # Return codes are ECreateLevelResult defined in CryEdit.h
- return_code = general.create_level_no_prompt(
- new_level_name, heightmap_resolution, heightmap_meters_per_pixel, terrain_texture_resolution, use_terrain)
- if return_code == 1:
- general.log(f"{new_level_name} level already exists")
- elif return_code == 2:
- general.log("Failed to create directory")
- elif return_code == 3:
- general.log("Directory length is too long")
- elif return_code != 0:
- general.log("Unknown error, failed to create level")
- else:
- general.log(f"{new_level_name} level created successfully")
-
- # Basic setup for newly created level.
+ # Basic setup for opened level.
+ helper.open_level(level_name="Base")
after_level_load()
initial_viewport_setup(SCREEN_WIDTH, SCREEN_HEIGHT)
@@ -147,22 +122,25 @@ def run():
parent_id=default_level.id
)
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", ground_plane.id, 32.0)
- ground_plane_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_chrome.azmaterial")
- ground_plane_material_asset = asset.AssetCatalogRequestBus(
- bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False)
- ground_plane.get_set_test(0, "Default Material|Material Asset", ground_plane_material_asset)
- # Work around to add the correct Atom Mesh component
+
+ # Work around to add the correct Atom Mesh component and asset.
mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId
ground_plane.components.append(
editor.EditorComponentAPIBus(
bus.Broadcast, "AddComponentsOfType", ground_plane.id, [mesh_type_id]
).GetValue()[0]
)
- ground_plane_mesh_asset_path = os.path.join("Objects", "plane.azmodel")
+ ground_plane_mesh_asset_path = os.path.join("TestData", "Objects", "plane.azmodel")
ground_plane_mesh_asset = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", ground_plane_mesh_asset_path, math.Uuid(), False)
hydra.get_set_test(ground_plane, 1, "Controller|Configuration|Mesh Asset", ground_plane_mesh_asset)
+ # Add Atom Material component and asset.
+ ground_plane_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_chrome.azmaterial")
+ ground_plane_material_asset = asset.AssetCatalogRequestBus(
+ bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False)
+ ground_plane.get_set_test(0, "Default Material|Material Asset", ground_plane_material_asset)
+
# Create directional_light entity and set the properties
directional_light = hydra.Entity("directional_light")
directional_light.create_entity(
@@ -180,11 +158,8 @@ def run():
components=["Material"],
parent_id=default_level.id
)
- sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial")
- sphere_material_asset = asset.AssetCatalogRequestBus(
- bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False)
- sphere.get_set_test(0, "Default Material|Material Asset", sphere_material_asset)
- # Work around to add the correct Atom Mesh component
+
+ # Work around to add the correct Atom Mesh component and asset.
sphere.components.append(
editor.EditorComponentAPIBus(
bus.Broadcast, "AddComponentsOfType", sphere.id, [mesh_type_id]
@@ -195,6 +170,12 @@ def run():
bus.Broadcast, "GetAssetIdByPath", sphere_mesh_asset_path, math.Uuid(), False)
hydra.get_set_test(sphere, 1, "Controller|Configuration|Mesh Asset", sphere_mesh_asset)
+ # Add Atom Material component and asset.
+ sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial")
+ sphere_material_asset = asset.AssetCatalogRequestBus(
+ bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False)
+ sphere.get_set_test(0, "Default Material|Material Asset", sphere_material_asset)
+
# Create camera component and set the properties
camera_entity = hydra.Entity("camera")
position = math.Vector3(5.5, -12.0, 9.0)
@@ -204,10 +185,9 @@ def run():
)
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", camera_entity.id, rotation)
camera_entity.get_set_test(0, "Controller|Configuration|Field of view", 60.0)
- azlmbr.camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id)
+ camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id)
- # Save level, enter game mode, take screenshot, & exit game mode.
- general.save_level()
+ # Enter game mode, take screenshot, & exit game mode.
general.idle_wait(0.5)
general.enter_game_mode()
general.idle_wait(1.0)
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py
index 1c3e6226c1..f69ceb2120 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py
@@ -22,7 +22,7 @@ from editor_python_test_tools.editor_test_helper import EditorTestHelper
helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper")
-LEVEL_NAME = "auto_test"
+LEVEL_NAME = "Base"
LIGHT_COMPONENT = "Light"
LIGHT_TYPE_PROPERTY = 'Controller|Configuration|Light type'
DEGREE_RADIAN_FACTOR = 0.0174533
diff --git a/AutomatedTesting/Gem/PythonTests/Blast/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Blast/CMakeLists.txt
index cb83fe5344..aea2562e0b 100644
--- a/AutomatedTesting/Gem/PythonTests/Blast/CMakeLists.txt
+++ b/AutomatedTesting/Gem/PythonTests/Blast/CMakeLists.txt
@@ -6,7 +6,11 @@
#
#
-if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
+ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
+
+include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) # for PAL_TRAIT_BLAST Traits
+
+if(PAL_TRAIT_BLAST_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::BlastTests_Main
TEST_SUITE main
diff --git a/AutomatedTesting/Gem/PythonTests/Blast/Platform/Android/PAL_android.cmake b/AutomatedTesting/Gem/PythonTests/Blast/Platform/Android/PAL_android.cmake
new file mode 100644
index 0000000000..f91b18e9f1
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Blast/Platform/Android/PAL_android.cmake
@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_BLAST_TESTS_SUPPORTED FALSE)
diff --git a/AutomatedTesting/Gem/PythonTests/Blast/Platform/Linux/PAL_linux.cmake b/AutomatedTesting/Gem/PythonTests/Blast/Platform/Linux/PAL_linux.cmake
new file mode 100644
index 0000000000..f91b18e9f1
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Blast/Platform/Linux/PAL_linux.cmake
@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_BLAST_TESTS_SUPPORTED FALSE)
diff --git a/AutomatedTesting/Gem/PythonTests/Blast/Platform/Mac/PAL_mac.cmake b/AutomatedTesting/Gem/PythonTests/Blast/Platform/Mac/PAL_mac.cmake
new file mode 100644
index 0000000000..f91b18e9f1
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Blast/Platform/Mac/PAL_mac.cmake
@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_BLAST_TESTS_SUPPORTED FALSE)
diff --git a/AutomatedTesting/Gem/PythonTests/Blast/Platform/Windows/PAL_windows.cmake b/AutomatedTesting/Gem/PythonTests/Blast/Platform/Windows/PAL_windows.cmake
new file mode 100644
index 0000000000..28767237de
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Blast/Platform/Windows/PAL_windows.cmake
@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_BLAST_TESTS_SUPPORTED TRUE)
diff --git a/AutomatedTesting/Gem/PythonTests/Blast/Platform/iOS/PAL_ios.cmake b/AutomatedTesting/Gem/PythonTests/Blast/Platform/iOS/PAL_ios.cmake
new file mode 100644
index 0000000000..f91b18e9f1
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Blast/Platform/iOS/PAL_ios.cmake
@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_BLAST_TESTS_SUPPORTED FALSE)
diff --git a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt
index fd3222ba83..bec49185bd 100644
--- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt
+++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt
@@ -59,5 +59,8 @@ add_subdirectory(smoke)
## AWS ##
add_subdirectory(AWS)
+## Multiplayer ##
+add_subdirectory(Multiplayer)
+
## Integration tests for editor testing framework ##
add_subdirectory(editor_test_testing)
diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py
index 844f8de903..9e857ed8bc 100644
--- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py
+++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py
@@ -107,6 +107,19 @@ class EditorComponent:
return type_ids
+
+def convert_to_azvector3(xyz) -> azlmbr.math.Vector3:
+ """
+ Converts a vector3-like element into a azlmbr.math.Vector3
+ """
+ if isinstance(xyz, Tuple) or isinstance(xyz, List):
+ assert len(xyz) == 3, ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
+ return math.Vector3(float(xyz[0]), float(xyz[1]), float(xyz[2]))
+ elif isinstance(xyz, type(math.Vector3())):
+ return xyz
+ else:
+ raise ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
+
class EditorEntity:
"""
Entity class is used to create and interact with Editor Entities.
@@ -183,15 +196,6 @@ class EditorEntity:
:return: EditorEntity class object
"""
- def convert_to_azvector3(xyz) -> math.Vector3:
- if isinstance(xyz, Tuple) or isinstance(xyz, List):
- assert len(xyz) == 3, ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
- return math.Vector3(*xyz)
- elif isinstance(xyz, type(math.Vector3())):
- return xyz
- else:
- raise ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
-
if parent_id is None:
parent_id = azlmbr.entity.EntityId()
@@ -206,7 +210,7 @@ class EditorEntity:
return entity
# Methods
- def set_name(self, entity_name: str):
+ def set_name(self, entity_name: str) -> None:
"""
Given entity_name, sets name to Entity
:param: entity_name: Name of the entity to set
@@ -324,7 +328,7 @@ class EditorEntity:
self.start_status = status
return status
- def set_start_status(self, desired_start_status: str):
+ def set_start_status(self, desired_start_status: str) -> None:
"""
Set an entity as active/inactive at beginning of runtime or it is editor-only,
given its entity id and the start status then return set success
@@ -382,18 +386,75 @@ class EditorEntity:
"""
return editor.EditorEntityInfoRequestBus(bus.Event, "IsVisible", self.id)
+ # World Transform Functions
+ def get_world_translation(self) -> azlmbr.math.Vector3:
+ """
+ Gets the world translation of the entity
+ """
+ return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
+
+ def set_world_translation(self, new_translation) -> None:
+ """
+ Sets the new world translation of the current entity
+ """
+ new_translation = convert_to_azvector3(new_translation)
+ azlmbr.components.TransformBus(azlmbr.bus.Event, "SetWorldTranslation", self.id, new_translation)
+
+ def get_world_rotation(self) -> azlmbr.math.Quaternion:
+ """
+ Gets the world rotation of the entity
+ """
+ return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", self.id)
+
+ def set_world_rotation(self, new_rotation):
+ """
+ Sets the new world rotation of the current entity
+ """
+ new_rotation = convert_to_azvector3(new_rotation)
+ azlmbr.components.TransformBus(azlmbr.bus.Event, "SetWorldRotation", self.id, new_rotation)
+
+ # Local Transform Functions
+ def get_local_uniform_scale(self) -> float:
+ """
+ Gets the local uniform scale of the entity
+ """
+ return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetLocalUniformScale", self.id)
+
def set_local_uniform_scale(self, scale_float) -> None:
"""
- Sets the "SetLocalUniformScale" value on the entity.
+ Sets the local uniform scale value(relative to the parent) on the entity.
:param scale_float: value for "SetLocalUniformScale" to set to.
:return: None
"""
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", self.id, scale_float)
- def set_local_rotation(self, vector3_rotation) -> None:
+ def get_local_rotation(self) -> azlmbr.math.Quaternion:
"""
- Sets the "SetLocalRotation" value on the entity.
+ Gets the local rotation of the entity
+ """
+ return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetLocalRotation", self.id)
+
+ def set_local_rotation(self, new_rotation) -> None:
+ """
+ Sets the set the local rotation(relative to the parent) of the current entity.
:param vector3_rotation: The math.Vector3 value to use for rotation on the entity (uses radians).
:return: None
"""
- azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", self.id, vector3_rotation)
+ new_rotation = convert_to_azvector3(new_rotation)
+ azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", self.id, new_rotation)
+
+ def get_local_translation(self) -> azlmbr.math.Vector3:
+ """
+ Gets the local translation of the current entity.
+ :return: The math.Vector3 value of the local translation.
+ """
+ return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetLocalTranslation", self.id)
+
+ def set_local_translation(self, new_translation) -> None:
+ """
+ Sets the local translation(relative to the parent) of the current entity.
+ :param vector3_translation: The math.Vector3 value to use for translation on the entity.
+ :return: None
+ """
+ new_translation = convert_to_azvector3(new_translation)
+ azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalTranslation", self.id, new_translation)
diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py
index 10a6ab1ef4..76c2a42c0a 100644
--- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py
+++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py
@@ -138,6 +138,14 @@ class PrefabInstance:
self.container_entity = reparented_container_entity
current_instance_prefab.instances.add(self)
+ def get_direct_child_entities(self):
+ """
+ Returns the entities only contained in the current prefab instance.
+ This function does not return entities contained in other child instances
+ """
+ return self.container_entity.get_children()
+
+
# This is a helper class which contains some of the useful information about a prefab template.
class Prefab:
diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py
index 1b094b1cfa..481d73274f 100644
--- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py
+++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py
@@ -14,7 +14,10 @@ from typing import Callable, Tuple
import azlmbr
import azlmbr.legacy.general as general
+import azlmbr.multiplayer as multiplayer
import azlmbr.debug
+import ly_test_tools.environment.waiter as waiter
+import ly_test_tools.environment.process_utils as process_utils
class FailFast(Exception):
@@ -66,6 +69,56 @@ class TestHelper:
TestHelper.wait_for_condition(lambda : general.is_in_game_mode(), 1.0)
Report.critical_result(msgtuple_success_fail, general.is_in_game_mode())
+ @staticmethod
+ def multiplayer_enter_game_mode(msgtuple_success_fail : Tuple[str, str], sv_default_player_spawn_asset : str):
+ # type: (tuple) -> None
+ """
+ :param msgtuple_success_fail: The tuple with the expected/unexpected messages for entering game mode.
+ :param sv_default_player_spawn_asset: The path to the network player prefab that will be automatically spawned upon entering gamemode. The engine default is "prefabs/player.network.spawnable"
+
+ :return: None
+ """
+
+ # looks for an expected line in a list of tracers lines
+ # lines: the tracer list of lines to search. options are section_tracer.warnings, section_tracer.errors, section_tracer.asserts, section_tracer.prints
+ # return: true if the line is found, otherwise false
+ def find_expected_line(expected_line, lines):
+ found_lines = [printInfo.message.strip() for printInfo in lines]
+ return expected_line in found_lines
+
+ def wait_for_critical_expected_line(expected_line, lines, time_out):
+ TestHelper.wait_for_condition(lambda : find_expected_line(expected_line, lines), time_out)
+ Report.critical_result(("Found expected line: " + expected_line, "Failed to find expected line: " + expected_line), find_expected_line(expected_line, lines))
+
+ def wait_for_critical_unexpected_line(unexpected_line, lines, time_out):
+ TestHelper.wait_for_condition(lambda : find_expected_line(unexpected_line, lines), time_out)
+ Report.critical_result(("Unexpected line not found: " + unexpected_line, "Unexpected line found: " + unexpected_line), not find_expected_line(unexpected_line, lines))
+
+
+ Report.info("Entering game mode")
+ if sv_default_player_spawn_asset :
+ general.set_cvar("sv_defaultPlayerSpawnAsset", sv_default_player_spawn_asset)
+
+ with Tracer() as section_tracer:
+ # enter game-mode.
+ # game-mode in multiplayer will also launch ServerLauncher.exe and connect to the editor
+ multiplayer.PythonEditorFuncs_enter_game_mode()
+
+ # make sure the server launcher binary exists
+ wait_for_critical_unexpected_line("LaunchEditorServer failed! The ServerLauncher binary is missing!", section_tracer.errors, 0.5)
+
+ # make sure the server launcher is running
+ waiter.wait_for(lambda: process_utils.process_exists("AutomatedTesting.ServerLauncher", ignore_extensions=True), timeout=5.0, exc=AssertionError("AutomatedTesting.ServerLauncher has NOT launched!"), interval=1.0)
+
+ # make sure the editor connects to the editor-server and sends the level data packet
+ wait_for_critical_expected_line("Editor is sending the editor-server the level data packet.", section_tracer.prints, 5.0)
+
+ # make sure the editor finally connects to the editor-server network simulation
+ wait_for_critical_expected_line("Editor-server ready. Editor has successfully connected to the editor-server's network simulation.", section_tracer.prints, 5.0)
+
+ TestHelper.wait_for_condition(lambda : multiplayer.PythonEditorFuncs_is_in_game_mode(), 5.0)
+ Report.critical_result(msgtuple_success_fail, multiplayer.PythonEditorFuncs_is_in_game_mode())
+
@staticmethod
def exit_game_mode(msgtuple_success_fail : Tuple[str, str]):
# type: (tuple) -> None
diff --git a/AutomatedTesting/Gem/PythonTests/Multiplayer/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Multiplayer/CMakeLists.txt
new file mode 100644
index 0000000000..367de4da9a
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Multiplayer/CMakeLists.txt
@@ -0,0 +1,23 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
+ ly_add_pytest(
+ NAME AutomatedTesting::MultiplayerTests_Sandbox
+ TEST_SUITE sandbox
+ TEST_SERIAL
+ PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Sandbox.py
+ RUNTIME_DEPENDENCIES
+ Legacy::Editor
+ AZ::AssetProcessor
+ AutomatedTesting.Assets
+ AutomatedTesting.ServerLauncher
+ COMPONENT
+ Multiplayer
+ )
+endif()
diff --git a/AutomatedTesting/Gem/PythonTests/Multiplayer/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Multiplayer/TestSuite_Main.py
new file mode 100644
index 0000000000..df1eb62943
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Multiplayer/TestSuite_Main.py
@@ -0,0 +1,29 @@
+"""
+Copyright (c) Contributors to the Open 3D Engine Project.
+For complete copyright and license terms please see the LICENSE at the root of this distribution.
+
+SPDX-License-Identifier: Apache-2.0 OR MIT
+
+"""
+
+# This suite consists of all test cases that are under development and have not been verified yet.
+# Once they are verified, please move them to TestSuite_Active.py
+
+import pytest
+import os
+import sys
+
+
+sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
+
+from base import TestAutomationBase
+
+@pytest.mark.parametrize("project", ["AutomatedTesting"])
+@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
+class TestAutomation(TestAutomationBase):
+ def _run_prefab_test(self, request, workspace, editor, test_module, batch_mode=True, autotest_mode=True):
+ self._run_test(request, workspace, editor, test_module,
+ extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"],
+ batch_mode=batch_mode,
+ autotest_mode=autotest_mode)
+
diff --git a/AutomatedTesting/Gem/PythonTests/Multiplayer/TestSuite_Sandbox.py b/AutomatedTesting/Gem/PythonTests/Multiplayer/TestSuite_Sandbox.py
new file mode 100644
index 0000000000..52ac19b26e
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Multiplayer/TestSuite_Sandbox.py
@@ -0,0 +1,35 @@
+"""
+Copyright (c) Contributors to the Open 3D Engine Project.
+For complete copyright and license terms please see the LICENSE at the root of this distribution.
+
+SPDX-License-Identifier: Apache-2.0 OR MIT
+
+"""
+
+# This suite consists of all test cases that are under development and have not been verified yet.
+# Once they are verified, please move them to TestSuite_Active.py
+
+import pytest
+import os
+import sys
+
+
+sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
+
+from base import TestAutomationBase
+
+@pytest.mark.SUITE_sandbox
+@pytest.mark.parametrize("project", ["AutomatedTesting"])
+@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
+class TestAutomation(TestAutomationBase):
+ def _run_prefab_test(self, request, workspace, editor, test_module, batch_mode=True, autotest_mode=True):
+ self._run_test(request, workspace, editor, test_module,
+ extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"],
+ batch_mode=batch_mode,
+ autotest_mode=autotest_mode)
+
+ ## Seems to be flaky, need to investigate
+ def test_Multiplayer_AutoComponent_NetworkInput(self, request, workspace, editor, launcher_platform):
+ from .tests import Multiplayer_AutoComponent_NetworkInput as test_module
+ self._run_prefab_test(request, workspace, editor, test_module)
+
diff --git a/AutomatedTesting/Gem/PythonTests/Multiplayer/__init__.py b/AutomatedTesting/Gem/PythonTests/Multiplayer/__init__.py
new file mode 100644
index 0000000000..f5193b300e
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Multiplayer/__init__.py
@@ -0,0 +1,6 @@
+"""
+Copyright (c) Contributors to the Open 3D Engine Project.
+For complete copyright and license terms please see the LICENSE at the root of this distribution.
+
+SPDX-License-Identifier: Apache-2.0 OR MIT
+"""
diff --git a/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_NetworkInput.py b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_NetworkInput.py
new file mode 100644
index 0000000000..7b56213313
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_NetworkInput.py
@@ -0,0 +1,115 @@
+"""
+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
+"""
+
+
+# Test Case Title : Check that network input can be created, received by the authority, and processed
+
+
+# fmt: off
+class Tests():
+ enter_game_mode = ("Entered game mode", "Failed to enter game mode")
+ exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
+ find_network_player = ("Found network player", "Couldn't find network player")
+ found_lines = ("Expected log lines were found", "Expected log lines were not found")
+ found_unexpected_lines = ("Unexpected log lines were not found", "Unexpected log lines were found")
+# fmt: on
+
+
+def Multiplayer_AutoComponent_NetworkInput():
+ r"""
+ Summary:
+ Runs a test to make sure that network input can be sent from the autonomous player, received by the authority, and processed
+
+ Level Description:
+ - Dynamic
+ 1. Although the level is empty, when the server and editor connect the server will spawn and replicate the player network prefab.
+ a. The player network prefab has a NetworkTestPlayerComponent.AutoComponent and a script canvas attached which will listen for the CreateInput and ProcessInput events.
+ Print logs occur upon triggering the CreateInput and ProcessInput events along with their values; we are testing to make sure the expected events are values are recieved.
+ - Static
+ 1. This is an empty level. All the logic occurs on the Player.network.spawnable (see the above Dynamic description)
+
+
+ Expected Outcome:
+ We should see editor logs stating that network input has been created and processed.
+ However, if the script receives unexpected values for the Process event we will see print logs for bad data as well.
+
+ :return:
+ """
+ import azlmbr.legacy.general as general
+ from editor_python_test_tools.utils import Report
+ from editor_python_test_tools.utils import Tracer
+
+ from editor_python_test_tools.utils import TestHelper as helper
+ from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole
+
+
+ def find_expected_line(expected_line):
+ found_lines = [printInfo.message.strip() for printInfo in section_tracer.prints]
+ return expected_line in found_lines
+
+ def find_unexpected_line(expected_line):
+ return not find_expected_line(expected_line)
+
+ unexpected_lines = [
+ 'AutoComponent_NetworkInput received bad fwdback!',
+ 'AutoComponent_NetworkInput received bad leftright!',
+
+ ]
+ expected_lines = [
+ 'AutoComponent_NetworkInput ProcessInput called!',
+ 'AutoComponent_NetworkInput CreateInput called!',
+ ]
+
+ expected_lines_server = [
+ '(Script) - AutoComponent_NetworkInput ProcessInput called!',
+ ]
+
+ level_name = "AutoComponent_NetworkInput"
+ player_prefab_name = "Player"
+ player_prefab_path = f"levels/multiplayer/{level_name}/{player_prefab_name}.network.spawnable"
+
+ helper.init_idle()
+
+
+ # 1) Open Level
+ helper.open_level("Multiplayer", level_name)
+
+ with Tracer() as section_tracer:
+ # 2) Enter game mode
+ helper.multiplayer_enter_game_mode(Tests.enter_game_mode, player_prefab_path.lower())
+
+ # 3) Make sure the network player was spawned
+ player_id = general.find_game_entity(player_prefab_name)
+ Report.critical_result(Tests.find_network_player, player_id.IsValid())
+
+ # 4) Check the editor logs for expected and unexpected log output
+ EXPECTEDLINE_WAIT_TIME_SECONDS = 1.0
+ for expected_line in expected_lines :
+ helper.wait_for_condition(lambda: find_expected_line(expected_line), EXPECTEDLINE_WAIT_TIME_SECONDS)
+ Report.result(Tests.found_lines, find_expected_line(expected_line))
+
+ general.idle_wait_frames(1)
+ for unexpected_line in unexpected_lines :
+ Report.result(Tests.found_unexpected_lines, find_unexpected_line(unexpected_line))
+
+ # 5) Check the ServerLauncher logs for expected log output
+ # Since the editor has started a server launcher, the RemoteConsole with the default port=4600 will automatically be able to read the server logs
+ server_console = RemoteConsole()
+ server_console.start()
+ for line in expected_lines_server:
+ assert server_console.expect_log_line(line, EXPECTEDLINE_WAIT_TIME_SECONDS), f"Expected line not found: {line}"
+ server_console.stop()
+
+
+ # Exit game mode
+ helper.exit_game_mode(Tests.exit_game_mode)
+
+
+
+if __name__ == "__main__":
+ from editor_python_test_tools.utils import Report
+ Report.start_test(Multiplayer_AutoComponent_NetworkInput)
diff --git a/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Main_Optimized.py
index 63d3b58249..3d668a2085 100644
--- a/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Main_Optimized.py
+++ b/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Main_Optimized.py
@@ -53,6 +53,27 @@ class EditorSingleTest_WithFileOverrides(EditorSingleTest):
for f in original_file_list:
fm._restore_file(f, file_list[f])
+@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.")
+@pytest.mark.SUITE_main
+@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
+@pytest.mark.parametrize("project", ["AutomatedTesting"])
+class TestAutomationWithPrefabSystemEnabled(EditorTestSuite):
+
+ global_extra_cmdline_args = ['-BatchMode', '-autotest_mode',
+ 'extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"]']
+
+ @staticmethod
+ def get_number_parallel_editors():
+ return 16
+
+ class C4982801_PhysXColliderShape_CanBeSelected(EditorSharedTest):
+ from .tests.collider import Collider_BoxShapeEditing as test_module
+
+ class C4982800_PhysXColliderShape_CanBeSelected(EditorSharedTest):
+ from .tests.collider import Collider_SphereShapeEditing as test_module
+
+ class C4982802_PhysXColliderShape_CanBeSelected(EditorSharedTest):
+ from .tests.collider import Collider_CapsuleShapeEditing as test_module
@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.")
@pytest.mark.SUITE_main
@@ -286,15 +307,6 @@ class TestAutomation(EditorTestSuite):
class C19723164_ShapeCollider_WontCrashEditor(EditorSharedTest):
from .tests.shape_collider import ShapeCollider_LargeNumberOfShapeCollidersWontCrashEditor as test_module
- class C4982800_PhysXColliderShape_CanBeSelected(EditorSharedTest):
- from .tests.collider import Collider_SphereShapeEditting as test_module
-
- class C4982801_PhysXColliderShape_CanBeSelected(EditorSharedTest):
- from .tests.collider import Collider_BoxShapeEditting as test_module
-
- class C4982802_PhysXColliderShape_CanBeSelected(EditorSharedTest):
- from .tests.collider import Collider_CapsuleShapeEditting as test_module
-
class C12905528_ForceRegion_WithNonTriggerCollider(EditorSharedTest):
from .tests.force_region import ForceRegion_WithNonTriggerColliderWarning as test_module
# Fixme: expected_lines = ["[Warning] (PhysX Force Region) - Please ensure collider component marked as trigger exists in entity"]
diff --git a/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Periodic.py
index e5dd9adbb9..55e51dd2f8 100755
--- a/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Periodic.py
+++ b/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Periodic.py
@@ -401,19 +401,22 @@ class TestAutomation(TestAutomationBase):
self._run_test(request, workspace, editor, test_module)
@revert_physics_config
- def test_Collider_SphereShapeEditting(self, request, workspace, editor, launcher_platform):
- from .tests.collider import Collider_SphereShapeEditting as test_module
- self._run_test(request, workspace, editor, test_module)
+ def test_Collider_SphereShapeEditing(self, request, workspace, editor, launcher_platform):
+ from .tests.collider import Collider_SphereShapeEditing as test_module
+ self._run_test(request, workspace, editor, test_module,
+ extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"])
@revert_physics_config
- def test_Collider_BoxShapeEditting(self, request, workspace, editor, launcher_platform):
- from .tests.collider import Collider_BoxShapeEditting as test_module
- self._run_test(request, workspace, editor, test_module)
+ def test_Collider_BoxShapeEditing(self, request, workspace, editor, launcher_platform):
+ from .tests.collider import Collider_BoxShapeEditing as test_module
+ self._run_test(request, workspace, editor, test_module,
+ extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"])
@revert_physics_config
- def test_Collider_CapsuleShapeEditting(self, request, workspace, editor, launcher_platform):
- from .tests.collider import Collider_CapsuleShapeEditting as test_module
- self._run_test(request, workspace, editor, test_module)
+ def test_Collider_CapsuleShapeEditing(self, request, workspace, editor, launcher_platform):
+ from .tests.collider import Collider_CapsuleShapeEditing as test_module
+ self._run_test(request, workspace, editor, test_module,
+ extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"])
def test_ForceRegion_WithNonTriggerColliderWarning(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_WithNonTriggerColliderWarning as test_module
diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_BoxShapeEditting.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_BoxShapeEditing.py
similarity index 97%
rename from AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_BoxShapeEditting.py
rename to AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_BoxShapeEditing.py
index 68ff0b4edc..a6730c8559 100644
--- a/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_BoxShapeEditting.py
+++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_BoxShapeEditing.py
@@ -19,7 +19,7 @@ class Tests():
# fmt: on
-def Collider_BoxShapeEditting():
+def Collider_BoxShapeEditing():
"""
Summary:
Adding PhysX Collider and Shape components to test entity, then attempting to modify the shape's dimensions
@@ -73,7 +73,7 @@ def Collider_BoxShapeEditting():
helper.init_idle()
# 1) Load the empty level
- helper.open_level("Physics", "Base")
+ helper.open_level("", "Base")
# 2) Create the test entity
test_entity = Entity.create_editor_entity("Test Entity")
@@ -102,4 +102,4 @@ def Collider_BoxShapeEditting():
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
- Report.start_test(Collider_BoxShapeEditting)
+ Report.start_test(Collider_BoxShapeEditing)
diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_CapsuleShapeEditting.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_CapsuleShapeEditing.py
similarity index 97%
rename from AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_CapsuleShapeEditting.py
rename to AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_CapsuleShapeEditing.py
index 7df12c68f0..12435cc54a 100644
--- a/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_CapsuleShapeEditting.py
+++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_CapsuleShapeEditing.py
@@ -19,7 +19,7 @@ class Tests():
# fmt: on
-def Collider_CapsuleShapeEditting():
+def Collider_CapsuleShapeEditing():
"""
Summary:
Adding PhysX Collider and Shape components to test entity, then attempting to modify the shape's dimensions
@@ -74,7 +74,7 @@ def Collider_CapsuleShapeEditting():
helper.init_idle()
# 1) Load the empty level
- helper.open_level("Physics", "Base")
+ helper.open_level("", "Base")
# 2) Create the test entity
test_entity = Entity.create_editor_entity("Test Entity")
@@ -102,4 +102,4 @@ def Collider_CapsuleShapeEditting():
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
- Report.start_test(Collider_CapsuleShapeEditting)
+ Report.start_test(Collider_CapsuleShapeEditing)
diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_SphereShapeEditting.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_SphereShapeEditing.py
similarity index 96%
rename from AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_SphereShapeEditting.py
rename to AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_SphereShapeEditing.py
index bffd041d92..ef91235411 100644
--- a/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_SphereShapeEditting.py
+++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_SphereShapeEditing.py
@@ -19,7 +19,7 @@ class Tests():
# fmt: on
-def Collider_SphereShapeEditting():
+def Collider_SphereShapeEditing():
"""
Summary:
Adding PhysX Collider and Shape components to test entity, then attempting to modify the shape's dimensions
@@ -57,7 +57,7 @@ def Collider_SphereShapeEditting():
helper.init_idle()
# 1) Load the empty level
- helper.open_level("Physics", "Base")
+ helper.open_level("", "Base")
# 2) Create the test entity
test_entity = Entity.create_editor_entity("Test Entity")
@@ -90,4 +90,4 @@ def Collider_SphereShapeEditting():
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
- Report.start_test(Collider_SphereShapeEditting)
+ Report.start_test(Collider_SphereShapeEditing)
diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py
index 5337f0669c..479915752f 100644
--- a/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py
+++ b/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py
@@ -55,3 +55,11 @@ class TestAutomation(TestAutomationBase):
def test_PrefabBasicWorkflow_CreateAndDuplicatePrefab(self, request, workspace, editor, launcher_platform):
from .tests import PrefabBasicWorkflow_CreateAndDuplicatePrefab as test_module
self._run_prefab_test(request, workspace, editor, test_module)
+
+ def test_PrefabComplexWorflow_CreatePrefabOfChildEntity(self, request, workspace, editor, launcher_platform):
+ from .tests import PrefabComplexWorflow_CreatePrefabOfChildEntity as test_module
+ self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False)
+
+ def test_PrefabComplexWorflow_CreatePrefabInsidePrefab(self, request, workspace, editor, launcher_platform):
+ from .tests import PrefabComplexWorflow_CreatePrefabInsidePrefab as test_module
+ self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False)
diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabInsidePrefab.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabInsidePrefab.py
new file mode 100644
index 0000000000..e14fc96449
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabInsidePrefab.py
@@ -0,0 +1,57 @@
+"""
+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
+"""
+
+def PrefabComplexWorflow_CreatePrefabInsidePrefab():
+ """
+ Test description:
+ - Creates an entity with a physx collider
+ - Creates a prefab "Outer_prefab" and an instance based of that entity
+ - Creates a prefab "Inner_prefab" inside "Outer_prefab" based the entity contained inside of it
+ Checks that the entity is correctly handlded by the prefab system checking the name and that it contains the physx collider
+ """
+
+ from editor_python_test_tools.editor_entity_utils import EditorEntity
+ from editor_python_test_tools.prefab_utils import Prefab
+
+ import PrefabTestUtils as prefab_test_utils
+
+ prefab_test_utils.open_base_tests_level()
+
+ # Creates a new Entity at the root level
+ # Asserts if creation didn't succeed
+ entity = EditorEntity.create_editor_entity_at((100.0, 100.0, 100.0), name = "TestEntity")
+ assert entity.id.IsValid(), "Couldn't create entity"
+ entity.add_component("PhysX Collider")
+ assert entity.has_component("PhysX Collider"), "Attempted to add a PhysX Collider but no physx collider collider was found afterwards"
+
+ # Create a prefab based on that entity
+ outer_prefab, outer_instance = Prefab.create_prefab([entity], "Outer_prefab")
+ # The test should be now inside the outer prefab instance.
+ entity = outer_instance.get_direct_child_entities()[0]
+ # We track if that is the same entity by checking the name and if it still contains the component that we created before
+ assert entity.get_name() == "TestEntity", f"Entity name inside outer_prefab doesn't match the original name, original:'TestEntity' current:'{entity.get_name()}'"
+ assert entity.has_component("PhysX Collider"), "Entity name inside outer_prefab doesn't have the collider component it should"
+
+ # Now, create another prefab, based on the entity that is inside outer_prefab
+ inner_prefab, inner_instance = Prefab.create_prefab([entity], "Inner_prefab")
+ # The test entity should now be inside the inner prefab instance
+ entity = inner_instance.get_direct_child_entities()[0]
+ # We track if that is the same entity by checking the name and if it still contains the component that we created before
+ assert entity.get_name() == "TestEntity", f"Entity name inside inner_prefab doesn't match the original name, original:'TestEntity' current:'{entity.get_name()}'"
+ assert entity.has_component("PhysX Collider"), "Entity name inside inner_prefab doesn't have the collider component it should"
+
+ # Verify hierarchy of entities:
+ # Outer_prefab
+ # |- Inner_prefab
+ # | |- TestEntity
+ assert entity.get_parent_id() == inner_instance.container_entity.id
+ assert inner_instance.container_entity.get_parent_id() == outer_instance.container_entity.id
+
+
+if __name__ == "__main__":
+ from editor_python_test_tools.utils import Report
+ Report.start_test(PrefabComplexWorflow_CreatePrefabInsidePrefab)
diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabOfChildEntity.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabOfChildEntity.py
new file mode 100644
index 0000000000..dec44d52be
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabOfChildEntity.py
@@ -0,0 +1,52 @@
+"""
+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
+"""
+
+def PrefabComplexWorflow_CreatePrefabOfChildEntity():
+ """
+ Test description:
+ - Creates two entities, parent and child. Child entity has Parent entity as its parent.
+ - Creates a prefab of the child entity.
+ Test is successful if the new instanced prefab of the child has the parent entity id
+ """
+
+ CAR_PREFAB_FILE_NAME = 'car_prefab'
+
+ from editor_python_test_tools.editor_entity_utils import EditorEntity
+ from editor_python_test_tools.prefab_utils import Prefab
+
+ import PrefabTestUtils as prefab_test_utils
+
+ prefab_test_utils.open_base_tests_level()
+
+ # Creates a new Entity at the root level
+ # Asserts if creation didn't succeed
+ parent_entity = EditorEntity.create_editor_entity_at((100.0, 100.0, 100.0))
+ assert parent_entity.id.IsValid(), "Couldn't create parent entity"
+
+ child_entity = EditorEntity.create_editor_entity(parent_id=parent_entity.id)
+ assert child_entity.id.IsValid(), "Couldn't create child entity"
+ assert child_entity.get_world_translation().IsClose(parent_entity.get_world_translation()), f"Child entity position{child_entity.get_world_translation().ToString()}" \
+ f" is not located at the same position as the parent{parent_entity.get_world_translation().ToString()}"
+
+ # Asserts if prefab creation doesn't succeed
+ child_prefab, child_instance = Prefab.create_prefab([child_entity], CAR_PREFAB_FILE_NAME)
+ child_entity_on_child_instance = child_instance.get_direct_child_entities()[0]
+ assert child_instance.container_entity.get_parent_id().IsValid(), "Newly instanced entity has no parent"
+ assert child_instance.container_entity.get_parent_id() == parent_entity.id, "Newly instanced entity parent does not match the expected parent"
+ assert child_instance.container_entity.get_world_translation().IsClose(parent_entity.get_world_translation()), "Newly instanced entity position is not located at the same position as the parent"
+ # Move the parent position, it should update the child position
+ parent_entity.set_world_translation((200.0, 200.0, 200.0))
+ child_instance_translation = child_instance.container_entity.get_world_translation()
+ assert child_instance_translation.IsClose(azlmbr.math.Vector3(200.0, 200.0, 200.0)), f"Instance position position{child_instance_translation.ToString()} didn't get updated" \
+ f" to the same position as the parent{parent_entity.get_world_translation().ToString()}"
+ child_translation = child_entity_on_child_instance.get_world_translation()
+ assert child_translation.IsClose(azlmbr.math.Vector3(200.0, 200.0, 200.0)), f"Entity position{child_translation.ToString()} of the instance didn't get updated" \
+ f" to the same position as the parent{parent_entity.get_world_translation().ToString()}"
+
+if __name__ == "__main__":
+ from editor_python_test_tools.utils import Report
+ Report.start_test(PrefabComplexWorflow_CreatePrefabOfChildEntity)
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/WhiteBox/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/WhiteBox/CMakeLists.txt
index 10cf949c25..733e8edf29 100644
--- a/AutomatedTesting/Gem/PythonTests/WhiteBox/CMakeLists.txt
+++ b/AutomatedTesting/Gem/PythonTests/WhiteBox/CMakeLists.txt
@@ -6,7 +6,11 @@
#
#
-if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
+ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
+
+include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) # for PAL_TRAIT_WHITEBOX Traits
+
+if(PAL_TRAIT_WHITEBOX_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::WhiteBoxTests
TEST_SUITE main
diff --git a/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/Android/PAL_android.cmake b/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/Android/PAL_android.cmake
new file mode 100644
index 0000000000..07aa0bb13d
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/Android/PAL_android.cmake
@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_WHITEBOX_TESTS_SUPPORTED FALSE)
diff --git a/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/Linux/PAL_linux.cmake b/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/Linux/PAL_linux.cmake
new file mode 100644
index 0000000000..07aa0bb13d
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/Linux/PAL_linux.cmake
@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_WHITEBOX_TESTS_SUPPORTED FALSE)
diff --git a/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/Mac/PAL_mac.cmake b/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/Mac/PAL_mac.cmake
new file mode 100644
index 0000000000..07aa0bb13d
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/Mac/PAL_mac.cmake
@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_WHITEBOX_TESTS_SUPPORTED FALSE)
diff --git a/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/Windows/PAL_windows.cmake b/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/Windows/PAL_windows.cmake
new file mode 100644
index 0000000000..a83d56788a
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/Windows/PAL_windows.cmake
@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_WHITEBOX_TESTS_SUPPORTED TRUE)
diff --git a/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/iOS/PAL_ios.cmake b/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/iOS/PAL_ios.cmake
new file mode 100644
index 0000000000..07aa0bb13d
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/WhiteBox/Platform/iOS/PAL_ios.cmake
@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_WHITEBOX_TESTS_SUPPORTED FALSE)
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..303a012cda 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
@@ -47,82 +47,6 @@ class TestsAssetProcessorBatch_DependenycyTests(object):
"""
AssetProcessorBatch Dependency tests
"""
-
- @pytest.mark.test_case_id("C16877166")
- @pytest.mark.BAT
- @pytest.mark.assetpipeline
- # fmt:off
- def test_WindowsMacPlatforms_RunAPBatch_NotMissingDependency(self, ap_setup_fixture, asset_processor,
- workspace):
- # fmt:on
- """
- Engine Schema
- This test case has a conditional scenario depending on the existence of surfacetypes.xml in a project.
- Some projects have this file and others do not. Run the conditional scenario depending on the existence
- of the file in the project
- libs/materialeffects/surfacetypes.xml is listed as an entry engine_dependencies.xml
- libs/materialeffects/surfacetypes.xml is not listed as a missing dependency
- in the 'assetprocessorbatch' console output
-
- Test Steps:
- 1. Assets are pre-processed
- 2. Verify that engine_dependencies.xml exists
- 3. Verify engine_dependencies.xml has surfacetypes.xml present
- 4. Run Missing Dependency scanner against the engine_dependenciese.xml
- 5. Verify that Surfacetypes.xml is NOT in the missing depdencies output
- 6. Add the schema file which allows our xml parser to understand dependencies for our engine_dependencies file
- 7. Process assets
- 8. Run Missing Dependency scanner against the engine_dependenciese.xml
- 9. Verify that surfacetypes.xml is in the missing dependencies out
- """
-
- env = ap_setup_fixture
- BATCH_LOG_PATH = env["ap_batch_log_file"]
- asset_processor.create_temp_asset_root()
- 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"))
-
- # Precondition: Assets are all processed
- asset_processor.batch_process()
-
- DEPENDENCIES_PATH = os.path.join(asset_processor.temp_project_cache(), "engine_dependencies.xml")
- assert os.path.exists(DEPENDENCIES_PATH), "The engine_dependencies.xml does not exist."
- surfacetypes_in_dependencies = False
- surfacetypes_missing_logline = False
-
- # Read engine_dependencies.xml to see if surfacetypes.xml is present
- with open(DEPENDENCIES_PATH, "r") as dependencies_file:
- for line in dependencies_file.readlines():
- if "surfacetypes.xml" in line:
- surfacetypes_in_dependencies = True
- logger.info("Surfacetypes.xml was listed in the engine_dependencies.xml file.")
- break
-
- if not surfacetypes_in_dependencies:
- logger.info("Surfacetypes.xml was not listed in the engine_dependencies.xml file.")
-
- _, output = asset_processor.batch_process(capture_output=True,
- extra_params="--dsp=%engine_dependencies.xml")
- log = APOutputParser(output)
- for _ in log.get_lines(run=-1, contains=["surfacetypes.xml", "Missing"]):
- surfacetypes_missing_logline = True
-
- assert surfacetypes_missing_logline, "Surfacetypes.xml not seen in the batch log as missing."
-
- # Add the schema file which allows our xml parser to understand dependencies for our engine_dependencies file
- asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "Schema", "enginedependency.xmlschema"))
- asset_processor.batch_process()
-
- _, output = asset_processor.batch_process(capture_output=True,
- extra_params="--dsp=%engine_dependencies.xml")
- log = APOutputParser(output)
- surfacetypes_missing_logline = False
- for _ in log.get_lines(run=-1, contains=["surfacetypes.xml", "Missing"]):
- surfacetypes_missing_logline = True
-
- assert not surfacetypes_missing_logline, "Surfacetypes.xml not seen in the batch log as missing."
-
schemas = [
("C16877167", ".ent"),
("C16877168", "Environment.xml"),
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests.py
index f3483d9c1f..2d67bca8fe 100755
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests.py
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_tests.py
@@ -329,7 +329,7 @@ class TestsAssetProcessorBatch_AllPlatforms(object):
# or an expected behavior has changed. Processing bootstrap.cfg sometimes but not other times should not
# cause a failure in this test.
num_processed_assets = asset_processor_utils.get_num_processed_assets(output)
- assert num_processed_assets >= 8, f'Wrong number of successfully processed assets found in output: '\
+ assert num_processed_assets >= 6, f'Wrong number of successfully processed assets found in output: '\
'{num_processed_assets}'
missing_assets, _ = asset_processor.compare_assets_with_cache()
@@ -585,20 +585,18 @@ class TestsAssetProcessorBatch_AllPlatforms(object):
3. Verify that logs exist for both AP Batch & AP GUI
"""
asset_processor.create_temp_asset_root()
+ asset_processor.create_temp_log_root()
LOG_PATH = {
"batch_log": workspace.paths.ap_batch_log(),
- "gui_log": workspace.paths.ap_gui_log(),
- "job_logs": workspace.paths.ap_job_logs(),
+ "gui_log": workspace.paths.ap_gui_log()
}
class LogTimes:
batch_log_start_time = 0
gui_log_start_time = 0
- job_logs_start_time = 0
batch_log_final_time = 0
gui_log_final_time = 0
- job_logs_final_time = 0
@staticmethod
def Report():
@@ -607,12 +605,10 @@ class TestsAssetProcessorBatch_AllPlatforms(object):
Original Times:
Batch: {LogTimes.batch_log_start_time}
GUI: {LogTimes.gui_log_start_time}
- JobLogs:{LogTimes.job_logs_start_time}
Post-Run Times:
Batch: {LogTimes.batch_log_final_time}
GUI: {LogTimes.gui_log_final_time}
- JobLogs:{LogTimes.job_logs_final_time}
"""
)
@@ -629,23 +625,19 @@ class TestsAssetProcessorBatch_AllPlatforms(object):
LogTimes.Report()
def check_existence(name, path):
- assert os.path.exists(path), f"{name} could not be located after running the AP."
+ assert os.path.exists(path), f"{name} could not be located {path} after running the AP."
# Check if log files previously exist and grab their modification times
update_times("_start_time")
# Run the Batch process
- assert asset_processor.batch_process(), "Batch process failed to successfully terminate"
+ assert asset_processor.batch_process(create_temp_log=False), "Batch process failed to successfully terminate"
- asset_processor.gui_process(quitonidle=True)
+ asset_processor.gui_process(quitonidle=True, create_temp_log=False)
# Check that the Logs directory exists (C1564055)
check_existence("Logs Directory", workspace.paths.ap_log_dir())
- # Check that the logs and JobLogs directory have updated modified times (C1564056)
- for key in LOG_PATH.keys():
- check_existence(key, LOG_PATH[key])
-
update_times("_final_time")
for key in LOG_PATH.keys():
@@ -708,6 +700,7 @@ class TestsAssetProcessorBatch_AllPlatforms(object):
@pytest.mark.BAT
@pytest.mark.assetpipeline
+ @pytest.mark.skip(reason="need to change assets from .slice files to an asset type that can have nested dependencies")
def test_validateNestedPreloadDependency_Found(self, asset_processor, ap_setup_fixture, workspace):
"""
Tests processing of a nested circular dependency and verifies that Asset Processor will return an error
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1568831/cgf_to_delete.cgf b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1568831/cgf_to_delete.cgf
deleted file mode 100644
index 47571e39a9..0000000000
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1568831/cgf_to_delete.cgf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7932fada1523fad4eb6ad5c13111bb4c00d6b0584ec8641060bf25f306b17e69
-size 84632
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1568831/fbx_to_delete.fbx b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1568831/fbx_to_delete.fbx
deleted file mode 100644
index 165bc01a54..0000000000
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1568831/fbx_to_delete.fbx
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:bf94b548eccb78432db65077124cadf20de975919b181d712fde081f59edd353
-size 38848
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1568831/file_to_delete.prefab b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1568831/file_to_delete.prefab
new file mode 100644
index 0000000000..9e26dfeeb6
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1568831/file_to_delete.prefab
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1568831/file_to_delete.txt b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1568831/file_to_delete.txt
new file mode 100644
index 0000000000..3d789f8b83
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1568831/file_to_delete.txt
@@ -0,0 +1 @@
+to be deleted
\ No newline at end of file
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1571774/test_mesh_robot.cgf b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1571774/test_mesh_robot.cgf
deleted file mode 100644
index fcb3513ed0..0000000000
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1571774/test_mesh_robot.cgf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1675471085483e0bd252a5bdd4db1b365c66f16ac32a6e6dd70bb1c23df83fa5
-size 24160
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1591338/test_mesh_robot.cgf b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1591338/test_mesh_robot.cgf
deleted file mode 100644
index fcb3513ed0..0000000000
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1591338/test_mesh_robot.cgf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1675471085483e0bd252a5bdd4db1b365c66f16ac32a6e6dd70bb1c23df83fa5
-size 24160
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1591338/test_mesh_robot.prefab b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1591338/test_mesh_robot.prefab
new file mode 100644
index 0000000000..9e26dfeeb6
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/C1591338/test_mesh_robot.prefab
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_AddSameAssetsDifferentNames_ShouldProcess/Energy_Background_One.png b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_AddSameAssetsDifferentNames_ShouldProcess/Energy_Background_One.png
deleted file mode 100644
index e012e887e7..0000000000
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_AddSameAssetsDifferentNames_ShouldProcess/Energy_Background_One.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:21df6ab62f2572daa6d0710ad6819a728ee751a62170903a6773563d614aa51f
-size 15191
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_AddSameAssetsDifferentNames_ShouldProcess/Energy_Background_Two.png b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_AddSameAssetsDifferentNames_ShouldProcess/Energy_Background_Two.png
deleted file mode 100644
index e012e887e7..0000000000
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_AddSameAssetsDifferentNames_ShouldProcess/Energy_Background_Two.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:21df6ab62f2572daa6d0710ad6819a728ee751a62170903a6773563d614aa51f
-size 15191
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessAndDeleteCache_APBatchShouldReprocess/Init.bnk b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessAndDeleteCache_APBatchShouldReprocess/Init.bnk
deleted file mode 100644
index f525a402af..0000000000
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessAndDeleteCache_APBatchShouldReprocess/Init.bnk
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f0b4750147acbcc6229a1043ed47ee48e7703a5241f27fc72976e95741144369
-size 2372
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessAndDeleteCache_APBatchShouldReprocess/entity_icon_example_2.png b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessAndDeleteCache_APBatchShouldReprocess/entity_icon_example_2.png
deleted file mode 100644
index 47e2d35331..0000000000
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessAndDeleteCache_APBatchShouldReprocess/entity_icon_example_2.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a036c3763b96079dde8505ad6995f8b717a777c78d7a434ac8de6f1ccb5d8dd1
-size 4327
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessByBothApAndBatch_Md5ShouldMatch/SoundWave_1.png b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessByBothApAndBatch_Md5ShouldMatch/SoundWave_1.png
deleted file mode 100644
index b3b8fba4a1..0000000000
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessByBothApAndBatch_Md5ShouldMatch/SoundWave_1.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cf55a4372d82d70d8cdcc95468367cd4fad7649d3fb99c4d85049977b506885c
-size 13429
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessByBothApAndBatch_Md5ShouldMatch/extra_file.prefab b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessByBothApAndBatch_Md5ShouldMatch/extra_file.prefab
new file mode 100644
index 0000000000..9e26dfeeb6
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessByBothApAndBatch_Md5ShouldMatch/extra_file.prefab
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessByBothApAndBatch_Md5ShouldMatch/test_cube.fbx b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessByBothApAndBatch_Md5ShouldMatch/test_cube.fbx
deleted file mode 100644
index 57fa7b327c..0000000000
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/assets/test_ProcessByBothApAndBatch_Md5ShouldMatch/test_cube.fbx
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1821d000b583821fd36ec73f91073d51ae90762225a34a81a74771c36236b5e1
-size 12963
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/SceneDebug/shaderball.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/SceneDebug/shaderball.dbgsg
new file mode 100644
index 0000000000..7d070fcea0
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/SceneDebug/shaderball.dbgsg
@@ -0,0 +1,3111 @@
+ProductName: shaderball.dbgsg
+debugSceneGraphVersion: 1
+shaderball
+Node Name: RootNode
+Node Path: RootNode
+Node Type: RootBoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, 1.000000, 0.000000>
+ BasisZ: < 0.000000, 0.000000, 1.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: ShaderBall_1m
+Node Path: RootNode.ShaderBall_1m
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: transform
+Node Path: RootNode.ShaderBall_1m.transform
+Node Type: TransformData
+ Matrix:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: InnerPortion
+Node Path: RootNode.ShaderBall_1m.InnerPortion
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: MaterialBase
+Node Path: RootNode.ShaderBall_1m.MaterialBase
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: InlayRings
+Node Path: RootNode.ShaderBall_1m.InlayRings
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: MainSphere
+Node Path: RootNode.ShaderBall_1m.MainSphere
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: HubCap_1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1
+Node Type: MeshData
+ Positions: Count 20476. Hash: 5411361036988924549
+ Normals: Count 20476. Hash: 5915154682063029054
+ FaceList: Count 8676. Hash: 9142863582186575896
+ FaceMaterialIds: Count 8676. Hash: 723360536895379791
+
+Node Name: HubCap_2
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: HubCap_1_optimized
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized
+Node Type: MeshData
+ Positions: Count 4724. Hash: 448283466401665158
+ Normals: Count 4724. Hash: 345267294337234954
+ FaceList: Count 8676. Hash: 11155608373229651496
+ FaceMaterialIds: Count 8676. Hash: 723360536895379791
+
+Node Name: InnerSphere_1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1
+Node Type: MeshData
+ Positions: Count 19800. Hash: 17342106809405761922
+ Normals: Count 19800. Hash: 602384960091561079
+ FaceList: Count 9800. Hash: 15975352410309879244
+ FaceMaterialIds: Count 9800. Hash: 2219364576630417284
+
+Node Name: InnerSphere_2
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: InnerSphere_1_optimized
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized
+Node Type: MeshData
+ Positions: Count 5846. Hash: 18229431498904963984
+ Normals: Count 5846. Hash: 12980164457801827192
+ FaceList: Count 9800. Hash: 2914108932212582430
+ FaceMaterialIds: Count 9800. Hash: 2219364576630417284
+
+Node Name: InnerCone_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1
+Node Type: MeshData
+ Positions: Count 192. Hash: 3022774266117638288
+ Normals: Count 192. Hash: 12863565187316537150
+ FaceList: Count 64. Hash: 1255131899577053537
+ FaceMaterialIds: Count 64. Hash: 6312841653578246165
+
+Node Name: InnerCone_2
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: InnerCone_1_optimized
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized
+Node Type: MeshData
+ Positions: Count 65. Hash: 832325255726840940
+ Normals: Count 65. Hash: 14121159448916141450
+ FaceList: Count 64. Hash: 9660474080562375138
+ FaceMaterialIds: Count 64. Hash: 6312841653578246165
+
+Node Name: InnerPost_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1
+Node Type: MeshData
+ Positions: Count 4864. Hash: 7530018531436061848
+ Normals: Count 4864. Hash: 534420091635424803
+ FaceList: Count 2432. Hash: 4330575616942580147
+ FaceMaterialIds: Count 2432. Hash: 12393250111858627709
+
+Node Name: InnerPost_2
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: InnerPost_1_optimized
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized
+Node Type: MeshData
+ Positions: Count 1300. Hash: 15277132209442728123
+ Normals: Count 1300. Hash: 1247055552073340932
+ FaceList: Count 2432. Hash: 16808521883586752319
+ FaceMaterialIds: Count 2432. Hash: 12393250111858627709
+
+Node Name: InnerBaseCuff_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1
+Node Type: MeshData
+ Positions: Count 4096. Hash: 1814004361979755447
+ Normals: Count 4096. Hash: 1760582409511017750
+ FaceList: Count 2048. Hash: 6642755656211284824
+ FaceMaterialIds: Count 2048. Hash: 2795877824940392899
+
+Node Name: InnerBaseCuff_2
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: InnerBaseCuff_1_optimized
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized
+Node Type: MeshData
+ Positions: Count 1122. Hash: 15009434720129565864
+ Normals: Count 1122. Hash: 7245598986723209052
+ FaceList: Count 2048. Hash: 2887888015166354330
+ FaceMaterialIds: Count 2048. Hash: 2795877824940392899
+
+Node Name: Inset_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1
+Node Type: MeshData
+ Positions: Count 1536. Hash: 6247721054948161820
+ Normals: Count 1536. Hash: 16362051041722971623
+ FaceList: Count 768. Hash: 3142075668387852387
+ FaceMaterialIds: Count 768. Hash: 2979441869813298271
+
+Node Name: Inset_2
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: Inset_1_optimized
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized
+Node Type: MeshData
+ Positions: Count 448. Hash: 9520190263881589378
+ Normals: Count 448. Hash: 8658432920439039136
+ FaceList: Count 768. Hash: 8621713142583851338
+ FaceMaterialIds: Count 768. Hash: 2979441869813298271
+
+Node Name: BottomCap_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1
+Node Type: MeshData
+ Positions: Count 960. Hash: 13502046855286963834
+ Normals: Count 960. Hash: 7165134632415312413
+ FaceList: Count 448. Hash: 11458684524699877690
+ FaceMaterialIds: Count 448. Hash: 15444662993354423801
+
+Node Name: BottomCap_2
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: BottomCap_1_optimized
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized
+Node Type: MeshData
+ Positions: Count 257. Hash: 6437758550313952689
+ Normals: Count 257. Hash: 9382966961287919705
+ FaceList: Count 448. Hash: 16093592375171717669
+ FaceMaterialIds: Count 448. Hash: 15444662993354423801
+
+Node Name: InnerCushion_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1
+Node Type: MeshData
+ Positions: Count 54208. Hash: 17548679297809443982
+ Normals: Count 54208. Hash: 18279651495381460361
+ FaceList: Count 27104. Hash: 12714006452420198903
+ FaceMaterialIds: Count 27104. Hash: 12674758055018775830
+
+Node Name: InnerCushion_2
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: InnerCushion_1_optimized
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized
+Node Type: MeshData
+ Positions: Count 13860. Hash: 15965614532178328362
+ Normals: Count 13860. Hash: 7297376140184766040
+ FaceList: Count 27104. Hash: 2523751700832022798
+ FaceMaterialIds: Count 27104. Hash: 12674758055018775830
+
+Node Name: OuterBaseCuff_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1
+Node Type: MeshData
+ Positions: Count 20048. Hash: 3861768905934340307
+ Normals: Count 20048. Hash: 12292053423401502106
+ FaceList: Count 10024. Hash: 4821138438569111218
+ FaceMaterialIds: Count 10024. Hash: 9849297365743356453
+
+Node Name: OuterBaseCuff_2
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: OuterBaseCuff_1_optimized
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized
+Node Type: MeshData
+ Positions: Count 5206. Hash: 5201932106801476439
+ Normals: Count 5206. Hash: 15696187265416627056
+ FaceList: Count 10024. Hash: 10907381836011223214
+ FaceMaterialIds: Count 10024. Hash: 9849297365743356453
+
+Node Name: RingLeft_1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1
+Node Type: MeshData
+ Positions: Count 2560. Hash: 10944366330558725569
+ Normals: Count 2560. Hash: 11471590896496428199
+ FaceList: Count 1280. Hash: 2548580276766813978
+ FaceMaterialIds: Count 1280. Hash: 6371267399123018661
+
+Node Name: RingLeft_2
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: RingLeft_1_optimized
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized
+Node Type: MeshData
+ Positions: Count 720. Hash: 14374869925777029719
+ Normals: Count 720. Hash: 2252820527750115179
+ FaceList: Count 1280. Hash: 17583744445000895264
+ FaceMaterialIds: Count 1280. Hash: 6371267399123018661
+
+Node Name: RingRight_1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1
+Node Type: MeshData
+ Positions: Count 2560. Hash: 17639843025153488175
+ Normals: Count 2560. Hash: 8564843488923790338
+ FaceList: Count 1280. Hash: 2548580276766813978
+ FaceMaterialIds: Count 1280. Hash: 6371267399123018661
+
+Node Name: RingRight_2
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: RingRight_1_optimized
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized
+Node Type: MeshData
+ Positions: Count 720. Hash: 17033597990859129189
+ Normals: Count 720. Hash: 14263134585377771060
+ FaceList: Count 1280. Hash: 13824345071081010014
+ FaceMaterialIds: Count 1280. Hash: 6371267399123018661
+
+Node Name: RightHub_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1
+Node Type: MeshData
+ Positions: Count 6304. Hash: 11021385291123971383
+ Normals: Count 6304. Hash: 10267084099841111837
+ FaceList: Count 3152. Hash: 17728162485525521181
+ FaceMaterialIds: Count 3152. Hash: 17405713692885844041
+
+Node Name: RightHub_2
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: RightHub_1_optimized
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized
+Node Type: MeshData
+ Positions: Count 1617. Hash: 9820946434895369083
+ Normals: Count 1617. Hash: 16951123277321747448
+ FaceList: Count 3152. Hash: 15437295155178226041
+ FaceMaterialIds: Count 3152. Hash: 17405713692885844041
+
+Node Name: LeftHub_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1
+Node Type: MeshData
+ Positions: Count 6304. Hash: 7564731352768958355
+ Normals: Count 6304. Hash: 8133714814490222392
+ FaceList: Count 3152. Hash: 17728162485525521181
+ FaceMaterialIds: Count 3152. Hash: 17405713692885844041
+
+Node Name: LeftHub_2
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: LeftHub_1_optimized
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized
+Node Type: MeshData
+ Positions: Count 1617. Hash: 8811470420139443913
+ Normals: Count 1617. Hash: 11224729257132823435
+ FaceList: Count 3152. Hash: 16696390019846851737
+ FaceMaterialIds: Count 3152. Hash: 17405713692885844041
+
+Node Name: Inside_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1
+Node Type: MeshData
+ Positions: Count 3520. Hash: 13476777876937219698
+ Normals: Count 3520. Hash: 10561277746451021236
+ FaceList: Count 1760. Hash: 1345243954764462275
+ FaceMaterialIds: Count 1760. Hash: 3100204266221257056
+
+Node Name: Inside_2
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: Inside_1_optimized
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized
+Node Type: MeshData
+ Positions: Count 960. Hash: 1074011803485396393
+ Normals: Count 960. Hash: 16318911598614464642
+ FaceList: Count 1760. Hash: 3382287220404120115
+ FaceMaterialIds: Count 1760. Hash: 3100204266221257056
+
+Node Name: MainOuterSphere_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1
+Node Type: MeshData
+ Positions: Count 18912. Hash: 15956257973657753552
+ Normals: Count 18912. Hash: 9328348641512406334
+ FaceList: Count 9456. Hash: 9836933646038198686
+ FaceMaterialIds: Count 9456. Hash: 13982281543095132650
+
+Node Name: MainOuterSphere_2
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_2
+Node Type: BoneData
+ WorldTransform:
+ BasisX: < 1.000000, 0.000000, 0.000000>
+ BasisY: < 0.000000, -0.000000, 1.000000>
+ BasisZ: < 0.000000, -1.000000, -0.000000>
+ Transl: < 0.000000, 0.000000, 0.000000>
+
+Node Name: MainOuterSphere_1_optimized
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized
+Node Type: MeshData
+ Positions: Count 4891. Hash: 9579089515723090907
+ Normals: Count 4891. Hash: 10089610259011330329
+ FaceList: Count 9456. Hash: 13541126452398082145
+ FaceMaterialIds: Count 9456. Hash: 13982281543095132650
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 20476. Hash: 10688945422788452939
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 20476. Hash: 1387390223232454000
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 20476. Hash: 4424021631256816544
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 20476. Hash: 9768152532901400557
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 20476. Hash: 4890305528292926235
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 20476. Hash: 309820643999247955
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 20476. Hash: 10688945422788452939
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 20476. Hash: 1387390223232454000
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 4724. Hash: 10265340188340999982
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 4724. Hash: 6393506322209434620
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 4724. Hash: 7072046838448900487
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 4724. Hash: 14469479861311642848
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 4724. Hash: 14586570136206675867
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 4724. Hash: 1080930468361041453
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 19800. Hash: 9998270082112342253
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 19800. Hash: 715698668253946311
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 19800. Hash: 9689144294054390217
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 19800. Hash: 13129471596255615133
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 19800. Hash: 12915864712175384367
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 19800. Hash: 704744783983559605
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 19800. Hash: 9998270082112342253
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 19800. Hash: 715698668253946311
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 5846. Hash: 2128276120164603588
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 5846. Hash: 954788723450394678
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 5846. Hash: 14309642535096835998
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 5846. Hash: 2996000735336843208
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 5846. Hash: 6423899825309547347
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 5846. Hash: 6861847030641362531
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 192. Hash: 10645867109602892598
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 192. Hash: 7257961874201179082
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 192. Hash: 12720324392877726426
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 192. Hash: 7937958557505694755
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 192. Hash: 15065829696130008213
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 192. Hash: 14378088137727336097
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 192. Hash: 10645867109602892598
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 192. Hash: 7257961874201179082
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 65. Hash: 3145658351065228323
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 65. Hash: 13102825703658386866
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 65. Hash: 14651886668877289638
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 65. Hash: 7706988068999921308
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 65. Hash: 1183045102730537867
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 65. Hash: 15200278891890596008
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 4864. Hash: 5283498994389857134
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 4864. Hash: 4759806696539235318
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 4864. Hash: 1916980755154570809
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 4864. Hash: 3641075817419129841
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 4864. Hash: 1597884606887295389
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 4864. Hash: 12470368568863176335
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 4864. Hash: 5283498994389857134
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 4864. Hash: 4759806696539235318
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 1300. Hash: 519927422840758162
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 1300. Hash: 16690819534142395524
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 1300. Hash: 13697492049473980098
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 1300. Hash: 16069718749273082363
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 1300. Hash: 9503213552298852479
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 1300. Hash: 14920629477034919393
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 4096. Hash: 12344372623177285558
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 4096. Hash: 3415525735687273566
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 4096. Hash: 1937219152553164558
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 4096. Hash: 16718347243549159919
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 4096. Hash: 7775661729866538946
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 4096. Hash: 16465645522600859391
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 4096. Hash: 12344372623177285558
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 4096. Hash: 3415525735687273566
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 1122. Hash: 10794215007683911939
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 1122. Hash: 4030215540982392192
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 1122. Hash: 14289595630739500233
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 1122. Hash: 13010448485976282215
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 1122. Hash: 8318401526835048407
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 1122. Hash: 100095329364523248
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 1536. Hash: 7123339701675171032
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 1536. Hash: 15827204344457762670
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 1536. Hash: 15732245908985477324
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 1536. Hash: 865397990859725823
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 1536. Hash: 2552281640891423471
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 1536. Hash: 17779645716491562667
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 1536. Hash: 7123339701675171032
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 1536. Hash: 15827204344457762670
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 448. Hash: 11533511688039530581
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 448. Hash: 6463578982826635244
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 448. Hash: 10590255987412720146
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 448. Hash: 17744412650281038495
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 448. Hash: 8278337182397882868
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 448. Hash: 8818080862566813062
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 960. Hash: 12059380739436291361
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 960. Hash: 17894062399627363441
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 960. Hash: 3025207993250150716
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 960. Hash: 909667023812047269
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 960. Hash: 9807712812840041710
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 960. Hash: 15826838159231044203
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 960. Hash: 12059380739436291361
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 960. Hash: 17894062399627363441
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 257. Hash: 8823641736072761245
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 257. Hash: 17988941723121644388
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 257. Hash: 8180735114915510878
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 257. Hash: 6608315278879931556
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 257. Hash: 15606348252975307518
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 257. Hash: 9909736394462106525
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 54208. Hash: 3444203649101035485
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 54208. Hash: 937532470362399061
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 54208. Hash: 196567085853229565
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 54208. Hash: 1716600532837684655
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 54208. Hash: 2905235470236164097
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 54208. Hash: 2074363216216487237
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 54208. Hash: 3444203649101035485
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 54208. Hash: 937532470362399061
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 13860. Hash: 263381874971540959
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 13860. Hash: 15191673020616208011
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 13860. Hash: 7105458185902008309
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 13860. Hash: 16259705089531364237
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 13860. Hash: 16022317673583270139
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 13860. Hash: 7251784463761381809
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 20048. Hash: 10464397683020008867
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 20048. Hash: 11067273583889078226
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 20048. Hash: 15901168792190323178
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 20048. Hash: 552570814640404138
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 20048. Hash: 17726428588184726937
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 20048. Hash: 7508591493894188819
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 20048. Hash: 10464397683020008867
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 20048. Hash: 11067273583889078226
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 5206. Hash: 1074257132915638034
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 5206. Hash: 2243653809093009497
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 5206. Hash: 11559777087289074275
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 5206. Hash: 15450183130901763011
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 5206. Hash: 15359221084106995089
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 5206. Hash: 8652443944286435468
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 2560. Hash: 14931201357194905697
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 2560. Hash: 5600314145323623005
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 2560. Hash: 11738661055172304644
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 2560. Hash: 320620113692599118
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 2560. Hash: 8466587534284734762
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 2560. Hash: 3318206549561696188
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 2560. Hash: 14931201357194905697
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 2560. Hash: 5600314145323623005
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 720. Hash: 75483450873662317
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 720. Hash: 3793407172213641704
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 720. Hash: 2508676912793167321
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 720. Hash: 2013136453053212946
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 720. Hash: 9302779689053257196
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 720. Hash: 16922723248982534245
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 2560. Hash: 9568588494434360329
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 2560. Hash: 5573555818259644549
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 2560. Hash: 12082144485035076372
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 2560. Hash: 5616878210949329467
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 2560. Hash: 4057154333260499171
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 2560. Hash: 1620460765416970456
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 2560. Hash: 9568588494434360329
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 2560. Hash: 5573555818259644549
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 720. Hash: 3542867785718437760
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 720. Hash: 5575788853295372734
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 720. Hash: 8244955966647049157
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 720. Hash: 17763430282605007327
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 720. Hash: 5442657821959315522
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 720. Hash: 12477592739739533067
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 6304. Hash: 4609122246850169975
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 6304. Hash: 2696528076485355457
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 6304. Hash: 6640465288865380105
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 6304. Hash: 133343330720363387
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 6304. Hash: 790774688340154169
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 6304. Hash: 3046294637431015510
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 6304. Hash: 4609122246850169975
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 6304. Hash: 2696528076485355457
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 1617. Hash: 16761061667647714654
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 1617. Hash: 14954885971875692232
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 1617. Hash: 37061345418264916
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 1617. Hash: 7368059604555723833
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 1617. Hash: 6244533771017459078
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 1617. Hash: 6416015160247779547
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 6304. Hash: 2524875548439506384
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 6304. Hash: 89810986084680009
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 6304. Hash: 1044733215619569246
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 6304. Hash: 15252409165383740719
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 6304. Hash: 3184716392697283856
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 6304. Hash: 4969210758291089995
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 6304. Hash: 2524875548439506384
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 6304. Hash: 89810986084680009
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 1617. Hash: 8467946356718878053
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 1617. Hash: 2373603727160338558
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 1617. Hash: 1521191693628786862
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 1617. Hash: 7175852234718691900
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 1617. Hash: 12283058591680528758
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 1617. Hash: 14516337485055158228
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 3520. Hash: 7734180808251274182
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 3520. Hash: 13560118186140352568
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 3520. Hash: 5538036360908204376
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 3520. Hash: 470358662493460341
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 3520. Hash: 13801426056203770982
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 3520. Hash: 8463107387658894201
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 3520. Hash: 7734180808251274182
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 3520. Hash: 13560118186140352568
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 960. Hash: 2691029117997309291
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 960. Hash: 10093724573967674240
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 960. Hash: 1221219959437752888
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 960. Hash: 1294720383009806722
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 960. Hash: 10294793677923893113
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 960. Hash: 6108415656799664788
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 18912. Hash: 4120783891454032649
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 18912. Hash: 9011003754405408275
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 18912. Hash: 12406712159692783345
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 18912. Hash: 7868083933985729169
+ GenerationMethod: 1
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 18912. Hash: 1990603474898794477
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 18912. Hash: 4812378464029296668
+ GenerationMethod: 1
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_2.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 18912. Hash: 4120783891454032649
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_2.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 18912. Hash: 9011003754405408275
+ UVCustomName: Unwrapped
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_2.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
+Node Name: Tiled
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.Tiled
+Node Type: MeshVertexUVData
+ UVs: Count 4891. Hash: 15498889522919365505
+ UVCustomName: Tiled
+
+Node Name: Unwrapped
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.Unwrapped
+Node Type: MeshVertexUVData
+ UVs: Count 4891. Hash: 15832520573612498718
+ UVCustomName: Unwrapped
+
+Node Name: TangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.TangentSet_0
+Node Type: MeshVertexTangentData
+ Tangents: Count 4891. Hash: 6697133368486369688
+ GenerationMethod: 1
+ SetIndex: 0
+
+Node Name: TangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.TangentSet_1
+Node Type: MeshVertexTangentData
+ Tangents: Count 4891. Hash: 18420832496569008358
+ GenerationMethod: 1
+ SetIndex: 1
+
+Node Name: BitangentSet_0
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.BitangentSet_0
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 4891. Hash: 2267250201584370787
+ GenerationMethod: 1
+
+Node Name: BitangentSet_1
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.BitangentSet_1
+Node Type: MeshVertexBitangentData
+ Bitangents: Count 4891. Hash: 7889928104085840247
+ GenerationMethod: 1
+
+Node Name: blinn1
+Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.blinn1
+Node Type: MaterialData
+ MaterialName: blinn1
+ UniqueId: 2076548245838624187
+ IsNoDraw: false
+ DiffuseColor: < 0.800000, 0.800000, 0.800000>
+ SpecularColor: < 0.500000, 0.500000, 0.500000>
+ EmissiveColor: < 0.000000, 0.000000, 0.000000>
+ Opacity: 1.000000
+ Shininess: 6.311791
+ UseColorMap: Not set
+ BaseColor: Not set
+ UseMetallicMap: Not set
+ MetallicFactor: Not set
+ UseRoughnessMap: Not set
+ RoughnessFactor: Not set
+ UseEmissiveMap: Not set
+ EmissiveIntensity: Not set
+ UseAOMap: Not set
+ DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+ SpecularTexture:
+ BumpTexture:
+ NormalTexture:
+ MetallicTexture:
+ RoughnessTexture:
+ AmbientOcclusionTexture:
+ EmissiveTexture:
+ BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png
+
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/ShaderBall/_dev_shaderball_00_basecolor.png b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/_dev_shaderball_00_basecolor.png
new file mode 100644
index 0000000000..415ca3e521
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/_dev_shaderball_00_basecolor.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:93a7e033d9fb0fcac221647322bde03716643d789390f79078c4fcc37ecfd005
+size 68327
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.fbx b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.fbx
new file mode 100644
index 0000000000..caf6dcbe8f
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.fbx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e63a55a35c749a16a03e10a1f53a48bd426c61db80151de080235b14cf6b70d
+size 2479344
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 71f200074c..c28dfa64f2 100755
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py
@@ -21,8 +21,8 @@ from ly_test_tools.o3de.asset_processor import ASSET_PROCESSOR_PLATFORM_MAP
from ..ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
from ..ap_fixtures.ap_setup_fixture import ap_setup_fixture as ap_setup_fixture
from ..ap_fixtures.ap_config_backup_fixture import ap_config_backup_fixture as ap_config_backup_fixture
-from ..ap_fixtures.ap_config_default_platform_fixture import ap_config_default_platform_fixture as ap_config_default_platform_fixture
-
+from ..ap_fixtures.ap_config_default_platform_fixture \
+ import ap_config_default_platform_fixture as ap_config_default_platform_fixture
# Import LyShared
import ly_test_tools.o3de.pipeline_utils as utils
@@ -33,6 +33,7 @@ logger = logging.getLogger(__name__)
# Helper: variables we will use for parameter values in the test:
targetProjects = ["AutomatedTesting"]
+
@pytest.fixture
@pytest.mark.SUITE_sandbox
def local_resources(request, workspace, ap_setup_fixture):
@@ -54,25 +55,30 @@ class BlackboxAssetTest:
blackbox_fbx_tests = [
pytest.param(
BlackboxAssetTest(
- test_name= "OneMeshOneMaterial_RunAP_SuccessWithMatchingProducts",
- asset_folder= "OneMeshOneMaterial",
+ test_name="OneMeshOneMaterial_RunAP_SuccessWithMatchingProducts",
+ asset_folder="OneMeshOneMaterial",
scene_debug_file="onemeshonematerial.dbgsg",
- assets = [
+ assets=[
asset_db_utils.DBSourceAsset(
- source_file_name = "OneMeshOneMaterial.fbx",
- uuid = b"8a9164adb84859be893e18aa819438e1",
- jobs = [
+ source_file_name="OneMeshOneMaterial.fbx",
+ uuid=b"8a9164adb84859be893e18aa819438e1",
+ jobs=[
asset_db_utils.DBJob(
- job_key= "Scene compilation",
+ job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=1,
- products = [
+ products=[
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'
+ )
]
),
]
@@ -86,25 +92,30 @@ blackbox_fbx_tests = [
BlackboxAssetTest(
# Verifies that the soft naming convention feature with level of detail meshes works.
# https://docs.aws.amazon.com/lumberyard/latest/userguide/char-fbx-importer-soft-naming.html
- test_name= "SoftNamingLOD_RunAP_SuccessWithMatchingProducts",
- asset_folder= "SoftNamingLOD",
+ test_name="SoftNamingLOD_RunAP_SuccessWithMatchingProducts",
+ asset_folder="SoftNamingLOD",
scene_debug_file="lodtest.dbgsg",
- assets = [
+ assets=[
asset_db_utils.DBSourceAsset(
- source_file_name = "lodtest.fbx",
- uuid = b"44c8627fe2c25aae91fe3ff9547be3b9",
- jobs = [
+ source_file_name="lodtest.fbx",
+ uuid=b"44c8627fe2c25aae91fe3ff9547be3b9",
+ jobs=[
asset_db_utils.DBJob(
- job_key= "Scene compilation",
+ job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=22,
- products = [
+ products=[
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'
+ )
]
),
]
@@ -118,31 +129,36 @@ blackbox_fbx_tests = [
BlackboxAssetTest(
# Verifies that the soft naming convention feature with physics proxies works.
# https://docs.aws.amazon.com/lumberyard/latest/userguide/char-fbx-importer-soft-naming.html
- test_name= "SoftNamingPhysics_RunAP_SuccessWithMatchingProducts",
- asset_folder= "SoftNamingPhysics",
+ test_name="SoftNamingPhysics_RunAP_SuccessWithMatchingProducts",
+ asset_folder="SoftNamingPhysics",
scene_debug_file="physicstest.dbgsg",
- assets = [
+ assets=[
asset_db_utils.DBSourceAsset(
- source_file_name = "physicstest.fbx",
- uuid = b"df957b7918cf5b029806c73f630fa1c8",
- jobs = [
+ source_file_name="physicstest.fbx",
+ uuid=b"df957b7918cf5b029806c73f630fa1c8",
+ jobs=[
asset_db_utils.DBJob(
- job_key= "Scene compilation",
+ job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=14,
- products = [
+ products=[
asset_db_utils.DBProduct(
product_name='softnamingphysics/physicstest.dbgsg',
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"
- ),
+ )
]
),
]
@@ -152,25 +168,29 @@ blackbox_fbx_tests = [
),
pytest.param(
BlackboxAssetTest(
- test_name= "MultipleMeshOneMaterial_RunAP_SuccessWithMatchingProducts",
- asset_folder= "TwoMeshOneMaterial",
+ test_name="MultipleMeshOneMaterial_RunAP_SuccessWithMatchingProducts",
+ asset_folder="TwoMeshOneMaterial",
scene_debug_file="multiple_mesh_one_material.dbgsg",
- assets = [
+ assets=[
asset_db_utils.DBSourceAsset(
- source_file_name = "multiple_mesh_one_material.fbx",
- uuid = b"597618fd497659a1b197a015fe47aa95",
- jobs = [
+ source_file_name="multiple_mesh_one_material.fbx",
+ uuid=b"597618fd497659a1b197a015fe47aa95",
+ jobs=[
asset_db_utils.DBJob(
- job_key= "Scene compilation",
+ job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=2,
- products = [
+ products=[
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')
]
),
]
@@ -183,26 +203,31 @@ blackbox_fbx_tests = [
pytest.param(
BlackboxAssetTest(
# Verifies whether multiple meshes can share linked materials
- test_name= "MultipleMeshLinkedMaterials_RunAP_SuccessWithMatchingProducts",
- asset_folder= "TwoMeshLinkedMaterials",
- scene_debug_file= "multiple_mesh_linked_materials.dbgsg",
- assets = [
+ test_name="MultipleMeshLinkedMaterials_RunAP_SuccessWithMatchingProducts",
+ asset_folder="TwoMeshLinkedMaterials",
+ scene_debug_file="multiple_mesh_linked_materials.dbgsg",
+ assets=[
asset_db_utils.DBSourceAsset(
- source_file_name = "multiple_mesh_linked_materials.fbx",
- uuid = b"25d8301c2eef5dc7bded310db8ea608d",
- jobs = [
+ source_file_name="multiple_mesh_linked_materials.fbx",
+ uuid=b"25d8301c2eef5dc7bded310db8ea608d",
+ jobs=[
asset_db_utils.DBJob(
- job_key= "Scene compilation",
- platform= "pc",
+ job_key="Scene compilation",
+ platform="pc",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=2,
- products= [
+ products=[
asset_db_utils.DBProduct(
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'
)
]
),
@@ -216,26 +241,31 @@ blackbox_fbx_tests = [
pytest.param(
BlackboxAssetTest(
# Verifies a mesh with multiple materials
- test_name= "SingleMeshMultipleMaterials_RunAP_SuccessWithMatchingProducts",
- asset_folder= "OneMeshMultipleMaterials",
+ test_name="SingleMeshMultipleMaterials_RunAP_SuccessWithMatchingProducts",
+ asset_folder="OneMeshMultipleMaterials",
scene_debug_file="single_mesh_multiple_materials.dbgsg",
- assets = [
+ assets=[
asset_db_utils.DBSourceAsset(
- source_file_name = "single_mesh_multiple_materials.fbx",
- uuid = b"f08fd585dfa35881b4bf86637da5e858",
- jobs = [
+ source_file_name="single_mesh_multiple_materials.fbx",
+ uuid=b"f08fd585dfa35881b4bf86637da5e858",
+ jobs=[
asset_db_utils.DBJob(
- job_key= "Scene compilation",
- platform= "pc",
+ job_key="Scene compilation",
+ platform="pc",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=1,
- products = [
+ products=[
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'
+ )
]
),
]
@@ -265,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'
+ )
]
),
]
@@ -277,25 +312,30 @@ blackbox_fbx_tests = [
),
pytest.param(
BlackboxAssetTest(
- test_name= "MotionTest_RunAP_SuccessWithMatchingProducts",
- asset_folder= "Motion",
+ test_name="MotionTest_RunAP_SuccessWithMatchingProducts",
+ asset_folder="Motion",
scene_debug_file="Jack_Idle_Aim_ZUp.dbgsg",
- assets = [
+ assets=[
asset_db_utils.DBSourceAsset(
- source_file_name = "Jack_Idle_Aim_ZUp.fbx",
- uuid = b"eda904ae0e145f8b973d57fc5809918b",
- jobs = [
+ source_file_name="Jack_Idle_Aim_ZUp.fbx",
+ uuid=b"eda904ae0e145f8b973d57fc5809918b",
+ jobs=[
asset_db_utils.DBJob(
- job_key= "Scene compilation",
+ job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=0,
- products = [
+ products=[
asset_db_utils.DBProduct(
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,
@@ -307,33 +347,69 @@ blackbox_fbx_tests = [
]
),
),
+ pytest.param(
+ BlackboxAssetTest(
+ test_name="ShaderBall_RunAP_SuccessWithMatchingProducts",
+ asset_folder="ShaderBall",
+ scene_debug_file="shaderball.dbgsg",
+ assets=[
+ asset_db_utils.DBSourceAsset(
+ source_file_name="shaderball.fbx",
+ uuid=b"48181ba8038e5193997540fc8dffb06d",
+ jobs=[
+ asset_db_utils.DBJob(
+ job_key="Scene compilation",
+ builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
+ status=4,
+ error_count=0,
+ warning_count=30,
+ products=[
+ asset_db_utils.DBProduct(
+ 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'),
+ ]
+ ),
+ ]
+ )
+ ]
+ ),
+ ),
]
-
blackbox_fbx_special_tests = [
pytest.param(
BlackboxAssetTest(
- test_name= "MultipleMeshMultipleMaterial_MultipleAssetInfo_RunAP_SuccessWithMatchingProducts",
- asset_folder= "TwoMeshTwoMaterial",
- override_asset_folder = "OverrideAssetInfoForTwoMeshTwoMaterial",
+ test_name="MultipleMeshMultipleMaterial_MultipleAssetInfo_RunAP_SuccessWithMatchingProducts",
+ asset_folder="TwoMeshTwoMaterial",
+ override_asset_folder="OverrideAssetInfoForTwoMeshTwoMaterial",
scene_debug_file="multiple_mesh_multiple_material.dbgsg",
override_scene_debug_file="multiple_mesh_multiple_material_override.dbgsg",
- assets = [
+ assets=[
asset_db_utils.DBSourceAsset(
- source_file_name = "multiple_mesh_multiple_material.fbx",
- uuid = b"b5915fb874af5c8a866ccabbddb57595",
- jobs = [
+ source_file_name="multiple_mesh_multiple_material.fbx",
+ uuid=b"b5915fb874af5c8a866ccabbddb57595",
+ jobs=[
asset_db_utils.DBJob(
job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=2,
- products = [
+ products=[
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'
+ )
]
),
]
@@ -341,20 +417,25 @@ blackbox_fbx_special_tests = [
],
override_assets=[
asset_db_utils.DBSourceAsset(
- source_file_name = "multiple_mesh_multiple_material.fbx",
- uuid = b"b5915fb874af5c8a866ccabbddb57595",
- jobs = [
+ source_file_name="multiple_mesh_multiple_material.fbx",
+ uuid=b"b5915fb874af5c8a866ccabbddb57595",
+ jobs=[
asset_db_utils.DBJob(
- job_key= "Scene compilation",
+ job_key="Scene compilation",
builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3",
status=4,
error_count=0,
warning_count=2,
- products = [
+ products=[
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'
+ )
]
),
]
@@ -378,29 +459,26 @@ class TestsFBX_AllPlatforms(object):
@pytest.mark.BAT
@pytest.mark.SUITE_sandbox
@pytest.mark.parametrize("blackbox_param", blackbox_fbx_tests)
- def test_FBXBlackboxTest_SourceFiles_Processed_ResultInExpectedProducts(self, workspace,
- ap_setup_fixture, asset_processor, project,
- blackbox_param):
+ def test_FBXBlackboxTest_SourceFiles_Processed_ResultInExpectedProducts(self, workspace, ap_setup_fixture,
+ asset_processor, project, blackbox_param):
"""
- Please see run_fbx_test(...) for details
+ Please see run_fbx_test(...) for details
Test Steps:
1. Determine if blackbox is set to none
2. Run FBX Test
+
"""
if blackbox_param == None:
return
- self.run_fbx_test(workspace, ap_setup_fixture,
- asset_processor, project, blackbox_param)
+ self.run_fbx_test(workspace, ap_setup_fixture, asset_processor, project, blackbox_param)
@pytest.mark.BAT
@pytest.mark.SUITE_sandbox
@pytest.mark.parametrize("blackbox_param", blackbox_fbx_special_tests)
- def test_FBXBlackboxTest_AssetInfoModified_AssetReprocessed_ResultInExpectedProducts(self,
- workspace, ap_setup_fixture,
- asset_processor, project,
- blackbox_param):
+ def test_FBXBlackboxTest_AssetInfoModified_AssetReprocessed_ResultInExpectedProducts(
+ self, workspace, ap_setup_fixture, asset_processor, project, blackbox_param):
"""
Please see run_fbx_test(...) for details
@@ -429,8 +507,21 @@ 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):
+ project, blackbox_params: BlackboxAssetTest, overrideAsset=False):
"""
These tests work by having the test case ingest the test data and determine the run pattern.
Tests will process scene settings files and will additionally do a verification against a provided debug file
@@ -469,32 +560,27 @@ class TestsFBX_AllPlatforms(object):
expected_product_list.append(expected_product.product_name)
missing_assets, _ = utils.compare_assets_with_cache(expected_product_list,
- asset_processor.project_test_cache_folder())
+ asset_processor.project_test_cache_folder())
- assert not missing_assets, f'The following assets were expected to be in, but not found in cache: {str(missing_assets)}'
+ assert not missing_assets, \
+ f'The following assets were expected to be in, but not found in cache: {str(missing_assets)}'
# Load the asset database.
db_path = os.path.join(asset_processor.temp_asset_root(), "Cache",
"assetdb.sqlite")
cache_root = os.path.dirname(os.path.join(asset_processor.temp_asset_root(), "Cache",
- ASSET_PROCESSOR_PLATFORM_MAP[workspace.asset_processor_platform]))
+ ASSET_PROCESSOR_PLATFORM_MAP[workspace.asset_processor_platform]))
if blackbox_params.scene_debug_file:
- scene_debug_file = blackbox_params.override_scene_debug_file if overrideAsset\
+ 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/editor_test_testing/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py
index 15ba6690a1..50b89ab138 100644
--- a/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py
+++ b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py
@@ -15,6 +15,7 @@ import sys
import importlib
import re
+import ly_test_tools
from ly_test_tools import LAUNCHERS
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
@@ -25,8 +26,15 @@ import ly_test_tools.environment.process_utils as process_utils
import argparse, sys
-@pytest.mark.SUITE_main
-@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
+def get_editor_launcher_platform():
+ if ly_test_tools.WINDOWS:
+ return "windows_editor"
+ elif ly_test_tools.LINUX:
+ return "linux_editor"
+ else:
+ return None
+
+@pytest.mark.parametrize("launcher_platform", [get_editor_launcher_platform()])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestEditorTest:
@@ -69,7 +77,7 @@ class TestEditorTest:
from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorTestSuite
@pytest.mark.SUITE_main
- @pytest.mark.parametrize("launcher_platform", ['windows_editor'])
+ @pytest.mark.parametrize("launcher_platform", [{get_editor_launcher_platform()}])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
class test_single(EditorSingleTest):
@@ -123,7 +131,7 @@ class TestEditorTest:
from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorTestSuite
@pytest.mark.SUITE_main
- @pytest.mark.parametrize("launcher_platform", ['windows_editor'])
+ @pytest.mark.parametrize("launcher_platform", [{get_editor_launcher_platform()}])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
{module_class_code}
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/AutomatedTesting/Levels/Base/Base.prefab b/AutomatedTesting/Levels/Base/Base.prefab
new file mode 100644
index 0000000000..98495663b7
--- /dev/null
+++ b/AutomatedTesting/Levels/Base/Base.prefab
@@ -0,0 +1,53 @@
+{
+ "ContainerEntity": {
+ "Id": "ContainerEntity",
+ "Name": "Base",
+ "Components": {
+ "Component_[10182366347512475253]": {
+ "$type": "EditorPrefabComponent",
+ "Id": 10182366347512475253
+ },
+ "Component_[12917798267488243668]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 12917798267488243668
+ },
+ "Component_[3261249813163778338]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 3261249813163778338
+ },
+ "Component_[3837204912784440039]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 3837204912784440039
+ },
+ "Component_[4272963378099646759]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 4272963378099646759,
+ "Parent Entity": ""
+ },
+ "Component_[4848458548047175816]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 4848458548047175816
+ },
+ "Component_[5787060997243919943]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 5787060997243919943
+ },
+ "Component_[7804170251266531779]": {
+ "$type": "EditorLockComponent",
+ "Id": 7804170251266531779
+ },
+ "Component_[7874177159288365422]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 7874177159288365422
+ },
+ "Component_[8018146290632383969]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 8018146290632383969
+ },
+ "Component_[8452360690590857075]": {
+ "$type": "SelectionComponent",
+ "Id": 8452360690590857075
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_NetworkInput/AutoComponent_NetworkInput.prefab b/AutomatedTesting/Levels/Multiplayer/AutoComponent_NetworkInput/AutoComponent_NetworkInput.prefab
new file mode 100644
index 0000000000..78c88144fd
--- /dev/null
+++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_NetworkInput/AutoComponent_NetworkInput.prefab
@@ -0,0 +1,525 @@
+{
+ "ContainerEntity": {
+ "Id": "Entity_[1146574390643]",
+ "Name": "Level",
+ "Components": {
+ "Component_[10641544592923449938]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 10641544592923449938
+ },
+ "Component_[12039882709170782873]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 12039882709170782873
+ },
+ "Component_[12265484671603697631]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 12265484671603697631
+ },
+ "Component_[14126657869720434043]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 14126657869720434043
+ },
+ "Component_[15230859088967841193]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 15230859088967841193,
+ "Parent Entity": ""
+ },
+ "Component_[16239496886950819870]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 16239496886950819870
+ },
+ "Component_[5688118765544765547]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 5688118765544765547
+ },
+ "Component_[6545738857812235305]": {
+ "$type": "SelectionComponent",
+ "Id": 6545738857812235305
+ },
+ "Component_[7247035804068349658]": {
+ "$type": "EditorPrefabComponent",
+ "Id": 7247035804068349658
+ },
+ "Component_[9307224322037797205]": {
+ "$type": "EditorLockComponent",
+ "Id": 9307224322037797205
+ },
+ "Component_[9562516168917670048]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 9562516168917670048
+ }
+ }
+ },
+ "Entities": {
+ "Entity_[1155164325235]": {
+ "Id": "Entity_[1155164325235]",
+ "Name": "Sun",
+ "Components": {
+ "Component_[10440557478882592717]": {
+ "$type": "SelectionComponent",
+ "Id": 10440557478882592717
+ },
+ "Component_[13620450453324765907]": {
+ "$type": "EditorLockComponent",
+ "Id": 13620450453324765907
+ },
+ "Component_[2134313378593666258]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 2134313378593666258
+ },
+ "Component_[234010807770404186]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 234010807770404186
+ },
+ "Component_[2970359110423865725]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 2970359110423865725
+ },
+ "Component_[3722854130373041803]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 3722854130373041803
+ },
+ "Component_[5992533738676323195]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 5992533738676323195
+ },
+ "Component_[7378860763541895402]": {
+ "$type": "AZ::Render::EditorDirectionalLightComponent",
+ "Id": 7378860763541895402,
+ "Controller": {
+ "Configuration": {
+ "Intensity": 1.0,
+ "CameraEntityId": "",
+ "ShadowFilterMethod": 1
+ }
+ }
+ },
+ "Component_[7892834440890947578]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 7892834440890947578,
+ "Parent Entity": "Entity_[1176639161715]",
+ "Transform Data": {
+ "Translate": [
+ 0.0,
+ 0.0,
+ 13.487043380737305
+ ],
+ "Rotate": [
+ -76.13099670410156,
+ -0.847000002861023,
+ -15.8100004196167
+ ]
+ }
+ },
+ "Component_[8599729549570828259]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 8599729549570828259
+ },
+ "Component_[952797371922080273]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 952797371922080273
+ }
+ }
+ },
+ "Entity_[1159459292531]": {
+ "Id": "Entity_[1159459292531]",
+ "Name": "Ground",
+ "Components": {
+ "Component_[11701138785793981042]": {
+ "$type": "SelectionComponent",
+ "Id": 11701138785793981042
+ },
+ "Component_[12260880513256986252]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 12260880513256986252
+ },
+ "Component_[13711420870643673468]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 13711420870643673468
+ },
+ "Component_[138002849734991713]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 138002849734991713
+ },
+ "Component_[16578565737331764849]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 16578565737331764849,
+ "Parent Entity": "Entity_[1176639161715]"
+ },
+ "Component_[16919232076966545697]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 16919232076966545697
+ },
+ "Component_[5182430712893438093]": {
+ "$type": "EditorMaterialComponent",
+ "Id": 5182430712893438093,
+ "materialSlots": [
+ {
+ "id": {
+ "materialSlotStableId": 803645540
+ }
+ },
+ {
+ "id": {
+ "materialSlotStableId": 803645540
+ }
+ }
+ ],
+ "materialSlotsByLod": [
+ [
+ {
+ "id": {
+ "lodIndex": 0,
+ "materialSlotStableId": 803645540
+ }
+ }
+ ],
+ [
+ {
+ "id": {
+ "lodIndex": 0,
+ "materialSlotStableId": 803645540
+ }
+ }
+ ]
+ ]
+ },
+ "Component_[5675108321710651991]": {
+ "$type": "AZ::Render::EditorMeshComponent",
+ "Id": 5675108321710651991,
+ "Controller": {
+ "Configuration": {
+ "ModelAsset": {
+ "assetId": {
+ "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
+ "subId": 277889906
+ },
+ "assetHint": "objects/groudplane/groundplane_512x512m.azmodel"
+ }
+ }
+ }
+ },
+ "Component_[5681893399601237518]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 5681893399601237518
+ },
+ "Component_[592692962543397545]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 592692962543397545
+ },
+ "Component_[7090012899106946164]": {
+ "$type": "EditorLockComponent",
+ "Id": 7090012899106946164
+ },
+ "Component_[9410832619875640998]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 9410832619875640998
+ }
+ }
+ },
+ "Entity_[1163754259827]": {
+ "Id": "Entity_[1163754259827]",
+ "Name": "Camera",
+ "Components": {
+ "Component_[11895140916889160460]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 11895140916889160460
+ },
+ "Component_[16880285896855930892]": {
+ "$type": "{CA11DA46-29FF-4083-B5F6-E02C3A8C3A3D} EditorCameraComponent",
+ "Id": 16880285896855930892,
+ "Controller": {
+ "Configuration": {
+ "Field of View": 55.0,
+ "EditorEntityId": 12554887233631987164
+ }
+ }
+ },
+ "Component_[17187464423780271193]": {
+ "$type": "EditorLockComponent",
+ "Id": 17187464423780271193
+ },
+ "Component_[17495696818315413311]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 17495696818315413311
+ },
+ "Component_[18086214374043522055]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 18086214374043522055,
+ "Parent Entity": "Entity_[1176639161715]",
+ "Transform Data": {
+ "Translate": [
+ -2.3000001907348633,
+ -3.9368600845336914,
+ 1.0
+ ],
+ "Rotate": [
+ -2.050307512283325,
+ 1.9552897214889526,
+ -43.623355865478516
+ ]
+ }
+ },
+ "Component_[18387556550380114975]": {
+ "$type": "SelectionComponent",
+ "Id": 18387556550380114975
+ },
+ "Component_[2654521436129313160]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 2654521436129313160
+ },
+ "Component_[5265045084611556958]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 5265045084611556958
+ },
+ "Component_[7169798125182238623]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 7169798125182238623
+ },
+ "Component_[7255796294953281766]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 7255796294953281766,
+ "m_template": {
+ "$type": "FlyCameraInputComponent"
+ }
+ },
+ "Component_[8866210352157164042]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 8866210352157164042
+ },
+ "Component_[9129253381063760879]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 9129253381063760879
+ }
+ }
+ },
+ "Entity_[1168049227123]": {
+ "Id": "Entity_[1168049227123]",
+ "Name": "Grid",
+ "Components": {
+ "Component_[11443347433215807130]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 11443347433215807130
+ },
+ "Component_[11779275529534764488]": {
+ "$type": "SelectionComponent",
+ "Id": 11779275529534764488
+ },
+ "Component_[14249419413039427459]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 14249419413039427459
+ },
+ "Component_[15448581635946161318]": {
+ "$type": "AZ::Render::EditorGridComponent",
+ "Id": 15448581635946161318,
+ "Controller": {
+ "Configuration": {
+ "primarySpacing": 4.0,
+ "primaryColor": [
+ 0.501960813999176,
+ 0.501960813999176,
+ 0.501960813999176
+ ],
+ "secondarySpacing": 0.5,
+ "secondaryColor": [
+ 0.250980406999588,
+ 0.250980406999588,
+ 0.250980406999588
+ ]
+ }
+ }
+ },
+ "Component_[1843303322527297409]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 1843303322527297409
+ },
+ "Component_[380249072065273654]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 380249072065273654,
+ "Parent Entity": "Entity_[1176639161715]"
+ },
+ "Component_[7476660583684339787]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 7476660583684339787
+ },
+ "Component_[7557626501215118375]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 7557626501215118375
+ },
+ "Component_[7984048488947365511]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 7984048488947365511
+ },
+ "Component_[8118181039276487398]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 8118181039276487398
+ },
+ "Component_[9189909764215270515]": {
+ "$type": "EditorLockComponent",
+ "Id": 9189909764215270515
+ }
+ }
+ },
+ "Entity_[1176639161715]": {
+ "Id": "Entity_[1176639161715]",
+ "Name": "Atom Default Environment",
+ "Components": {
+ "Component_[10757302973393310045]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 10757302973393310045,
+ "Parent Entity": "Entity_[1146574390643]"
+ },
+ "Component_[14505817420424255464]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 14505817420424255464,
+ "ComponentOrderEntryArray": [
+ {
+ "ComponentId": 10757302973393310045
+ }
+ ]
+ },
+ "Component_[14988041764659020032]": {
+ "$type": "EditorLockComponent",
+ "Id": 14988041764659020032
+ },
+ "Component_[15808690248755038124]": {
+ "$type": "SelectionComponent",
+ "Id": 15808690248755038124
+ },
+ "Component_[15900837685796817138]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 15900837685796817138
+ },
+ "Component_[3298767348226484884]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 3298767348226484884
+ },
+ "Component_[4076975109609220594]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 4076975109609220594
+ },
+ "Component_[5679760548946028854]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 5679760548946028854
+ },
+ "Component_[5855590796136709437]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 5855590796136709437,
+ "ChildEntityOrderEntryArray": [
+ {
+ "EntityId": "Entity_[1155164325235]"
+ },
+ {
+ "EntityId": "Entity_[1180934129011]",
+ "SortIndex": 1
+ },
+ {
+ "EntityId": "",
+ "SortIndex": 2
+ },
+ {
+ "EntityId": "Entity_[1168049227123]",
+ "SortIndex": 3
+ },
+ {
+ "EntityId": "Entity_[1163754259827]",
+ "SortIndex": 4
+ },
+ {
+ "EntityId": "Entity_[1159459292531]",
+ "SortIndex": 5
+ }
+ ]
+ },
+ "Component_[9277695270015777859]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 9277695270015777859
+ }
+ }
+ },
+ "Entity_[1180934129011]": {
+ "Id": "Entity_[1180934129011]",
+ "Name": "Global Sky",
+ "Components": {
+ "Component_[11231930600558681245]": {
+ "$type": "AZ::Render::EditorHDRiSkyboxComponent",
+ "Id": 11231930600558681245,
+ "Controller": {
+ "Configuration": {
+ "CubemapAsset": {
+ "assetId": {
+ "guid": "{215E47FD-D181-5832-B1AB-91673ABF6399}",
+ "subId": 1000
+ },
+ "assetHint": "lightingpresets/highcontrast/goegap_4k_skyboxcm.exr.streamingimage"
+ }
+ }
+ }
+ },
+ "Component_[11980494120202836095]": {
+ "$type": "SelectionComponent",
+ "Id": 11980494120202836095
+ },
+ "Component_[1428633914413949476]": {
+ "$type": "EditorLockComponent",
+ "Id": 1428633914413949476
+ },
+ "Component_[14936200426671614999]": {
+ "$type": "AZ::Render::EditorImageBasedLightComponent",
+ "Id": 14936200426671614999,
+ "Controller": {
+ "Configuration": {
+ "diffuseImageAsset": {
+ "assetId": {
+ "guid": "{3FD09945-D0F2-55C8-B9AF-B2FD421FE3BE}",
+ "subId": 3000
+ },
+ "assetHint": "lightingpresets/highcontrast/goegap_4k_iblglobalcm_ibldiffuse.exr.streamingimage"
+ },
+ "specularImageAsset": {
+ "assetId": {
+ "guid": "{3FD09945-D0F2-55C8-B9AF-B2FD421FE3BE}",
+ "subId": 2000
+ },
+ "assetHint": "lightingpresets/highcontrast/goegap_4k_iblglobalcm_iblspecular.exr.streamingimage"
+ }
+ }
+ }
+ },
+ "Component_[14994774102579326069]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 14994774102579326069
+ },
+ "Component_[15417479889044493340]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 15417479889044493340
+ },
+ "Component_[15826613364991382688]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 15826613364991382688
+ },
+ "Component_[1665003113283562343]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 1665003113283562343
+ },
+ "Component_[3704934735944502280]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 3704934735944502280
+ },
+ "Component_[5698542331457326479]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 5698542331457326479
+ },
+ "Component_[6644513399057217122]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 6644513399057217122,
+ "Parent Entity": "Entity_[1176639161715]"
+ },
+ "Component_[931091830724002070]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 931091830724002070
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_NetworkInput/AutoComponent_NetworkInput.scriptcanvas b/AutomatedTesting/Levels/Multiplayer/AutoComponent_NetworkInput/AutoComponent_NetworkInput.scriptcanvas
new file mode 100644
index 0000000000..2f8a434108
--- /dev/null
+++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_NetworkInput/AutoComponent_NetworkInput.scriptcanvas
@@ -0,0 +1,1827 @@
+{
+ "Type": "JsonSerialization",
+ "Version": 1,
+ "ClassName": "ScriptCanvasData",
+ "ClassData": {
+ "m_scriptCanvas": {
+ "Id": {
+ "id": 20239954977260
+ },
+ "Name": "AutoComponent_NetworkInput",
+ "Components": {
+ "Component_[14059414856740480991]": {
+ "$type": "EditorGraphVariableManagerComponent",
+ "Id": 14059414856740480991,
+ "m_variableData": {
+ "m_nameVariableMap": [
+ {
+ "Key": {
+ "m_id": "{720D213F-AA7A-48B0-ABBB-A3756C528CF9}"
+ },
+ "Value": {
+ "Datum": {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.25
+ },
+ "VariableId": {
+ "m_id": "{720D213F-AA7A-48B0-ABBB-A3756C528CF9}"
+ },
+ "VariableName": "leftright"
+ }
+ },
+ {
+ "Key": {
+ "m_id": "{B2E338E9-2ACB-42FC-B0BE-EDA14A8A86AD}"
+ },
+ "Value": {
+ "Datum": {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 1.0
+ },
+ "VariableId": {
+ "m_id": "{B2E338E9-2ACB-42FC-B0BE-EDA14A8A86AD}"
+ },
+ "VariableName": "fwdback"
+ }
+ },
+ {
+ "Key": {
+ "m_id": "{D843B13F-B3C7-4619-8092-7164EB1D7888}"
+ },
+ "Value": {
+ "Datum": {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 20.0
+ },
+ "VariableId": {
+ "m_id": "{D843B13F-B3C7-4619-8092-7164EB1D7888}"
+ },
+ "VariableName": "VELOCITY"
+ }
+ }
+ ]
+ }
+ },
+ "Component_[642525765775040231]": {
+ "$type": "{4D755CA9-AB92-462C-B24F-0B3376F19967} Graph",
+ "Id": 642525765775040231,
+ "m_graphData": {
+ "m_nodes": [
+ {
+ "Id": {
+ "id": 20265724781036
+ },
+ "Name": "SC-Node(NotEqualTo)",
+ "Components": {
+ "Component_[12036973200504716538]": {
+ "$type": "NotEqualTo",
+ "Id": 12036973200504716538,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{6D0EF497-7C52-432B-9301-7BAB26F4F5A5}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Result",
+ "DisplayDataType": {
+ "m_type": 0
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{AE7E453C-15DE-47D2-954A-05C3895B7AC6}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Signal to perform the evaluation when desired.",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{AC364E17-A9A1-42DB-A29D-7B2D666E4287}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "True",
+ "toolTip": "Signaled if the result of the operation is true.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{0754AC37-61A6-42A7-B4EC-D35D18D27960}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "False",
+ "toolTip": "Signaled if the result of the operation is false.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{57EBC15B-452E-49B3-8BD3-4FBBB07F1F14}"
+ },
+ "DynamicTypeOverride": 3,
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Value A",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DynamicGroup": {
+ "Value": 3545012108
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{21864AEE-7954-4F68-82C6-573C175724EF}"
+ },
+ "DynamicTypeOverride": 3,
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Value B",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DynamicGroup": {
+ "Value": 3545012108
+ },
+ "DataType": 1,
+ "IsReference": true,
+ "VariableReference": {
+ "m_id": "{720D213F-AA7A-48B0-ABBB-A3756C528CF9}"
+ }
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0,
+ "label": "Value A"
+ },
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0,
+ "label": "Value B"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20257134846444
+ },
+ "Name": "SC-Node(NotEqualTo)",
+ "Components": {
+ "Component_[12036973200504716538]": {
+ "$type": "NotEqualTo",
+ "Id": 12036973200504716538,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{6D0EF497-7C52-432B-9301-7BAB26F4F5A5}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Result",
+ "DisplayDataType": {
+ "m_type": 0
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{AE7E453C-15DE-47D2-954A-05C3895B7AC6}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Signal to perform the evaluation when desired.",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{AC364E17-A9A1-42DB-A29D-7B2D666E4287}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "True",
+ "toolTip": "Signaled if the result of the operation is true.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{0754AC37-61A6-42A7-B4EC-D35D18D27960}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "False",
+ "toolTip": "Signaled if the result of the operation is false.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{57EBC15B-452E-49B3-8BD3-4FBBB07F1F14}"
+ },
+ "DynamicTypeOverride": 3,
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Value A",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DynamicGroup": {
+ "Value": 3545012108
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{21864AEE-7954-4F68-82C6-573C175724EF}"
+ },
+ "DynamicTypeOverride": 3,
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Value B",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DynamicGroup": {
+ "Value": 3545012108
+ },
+ "DataType": 1,
+ "IsReference": true,
+ "VariableReference": {
+ "m_id": "{B2E338E9-2ACB-42FC-B0BE-EDA14A8A86AD}"
+ }
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0,
+ "label": "Value A"
+ },
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0,
+ "label": "Value B"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20278609682924
+ },
+ "Name": "SC-Node(Print)",
+ "Components": {
+ "Component_[12996217042650310160]": {
+ "$type": "Print",
+ "Id": 12996217042650310160,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{D994D58A-DBF1-4929-B779-F0D2CBAD2F0D}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Input signal",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{233C15BC-0186-4A7F-B272-6B2C020DE57A}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "m_format": "AutoComponent_NetworkInput ProcessInput called!",
+ "m_unresolvedString": [
+ "AutoComponent_NetworkInput ProcessInput called!"
+ ]
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20244249944556
+ },
+ "Name": "SC-Node(CreateFromValues)",
+ "Components": {
+ "Component_[16858161274259468878]": {
+ "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method",
+ "Id": 16858161274259468878,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{7CF3B6C0-FE59-4E16-ABC6-E7CC8AFA8EF3}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "fwdBack",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1,
+ "IsReference": true,
+ "VariableReference": {
+ "m_id": "{B2E338E9-2ACB-42FC-B0BE-EDA14A8A86AD}"
+ }
+ },
+ {
+ "id": {
+ "m_id": "{02473A8A-A0F0-4E73-8999-0B16D17E8E2A}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "leftRight",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1,
+ "IsReference": true,
+ "VariableReference": {
+ "m_id": "{720D213F-AA7A-48B0-ABBB-A3756C528CF9}"
+ }
+ },
+ {
+ "id": {
+ "m_id": "{514CDDAA-290F-4758-B28F-4003E719E635}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{E3A94716-C880-4D6D-ADD6-34D019161ED1}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{90E52F81-54E0-4C63-9881-B661FB5D87D1}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Result: NetworkTestPlayerComponentNetworkInput",
+ "DisplayDataType": {
+ "m_type": 4,
+ "m_azType": "{12A1776B-61F6-4E5F-356A-AD718A62051F}"
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0,
+ "label": "fwdBack"
+ },
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0,
+ "label": "leftRight"
+ }
+ ],
+ "methodType": 2,
+ "methodName": "CreateFromValues",
+ "className": "NetworkTestPlayerComponentNetworkInput",
+ "resultSlotIDs": [
+ {}
+ ],
+ "inputSlots": [
+ {
+ "m_id": "{7CF3B6C0-FE59-4E16-ABC6-E7CC8AFA8EF3}"
+ },
+ {
+ "m_id": "{02473A8A-A0F0-4E73-8999-0B16D17E8E2A}"
+ }
+ ],
+ "prettyClassName": "NetworkTestPlayerComponentNetworkInput"
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20252839879148
+ },
+ "Name": "EBusEventHandler",
+ "Components": {
+ "Component_[1734984895901771768]": {
+ "$type": "EBusEventHandler",
+ "Id": 1734984895901771768,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{41899533-66B3-423F-8BE2-A624A1FB6FCB}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Connect",
+ "toolTip": "Connect this event handler to the specified entity.",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{65C09819-1B00-4BA8-B6E0-8DD6D8B44A36}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Disconnect",
+ "toolTip": "Disconnect this event handler.",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{B7437693-34D0-4BDE-8516-2CADB9F509BF}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "OnConnected",
+ "toolTip": "Signaled when a connection has taken place.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{BCE36ED4-0899-4403-826A-FE89BE033A0A}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "OnDisconnected",
+ "toolTip": "Signaled when this event handler is disconnected.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{796993F4-685D-4A85-A0CA-032E3466FB57}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "OnFailure",
+ "toolTip": "Signaled when it is not possible to connect this handler.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{680E9668-87B4-4FF2-AA24-EB8F11352A49}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Source",
+ "toolTip": "ID used to connect on a specific Event address (Type: EntityId)",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{ADF9B366-8324-4C1E-B601-C059DA70FDDE}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Result: NetworkTestPlayerComponentNetworkInput",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{3301102C-AD42-48A0-8764-D2D4D31E0C75}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Number",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{B831AC60-7641-4B74-9829-26A3576B4766}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "ExecutionSlot:CreateInput",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ },
+ "IsLatent": true
+ },
+ {
+ "id": {
+ "m_id": "{8F69FA2E-28D8-4DF1-A4B5-AEF3985095C5}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "NetworkTestPlayerComponentNetworkInput",
+ "DisplayDataType": {
+ "m_type": 4,
+ "m_azType": "{12A1776B-61F6-4E5F-356A-AD718A62051F}"
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{BE3E3F84-E91C-4F6C-B148-E13BF01B5FBC}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Number",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{4C8F2908-12B0-4C35-8468-31D3D4DF36AA}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "ExecutionSlot:ProcessInput",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ },
+ "IsLatent": true
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 1
+ },
+ "isNullPointer": false,
+ "$type": "EntityId",
+ "value": {
+ "id": 2901262558
+ },
+ "label": "Source"
+ },
+ {
+ "scriptCanvasType": {
+ "m_type": 4,
+ "m_azType": "{12A1776B-61F6-4E5F-356A-AD718A62051F}"
+ },
+ "isNullPointer": false,
+ "$type": "NetworkTestPlayerComponentNetworkInput",
+ "label": "Result: NetworkTestPlayerComponentNetworkInput"
+ }
+ ],
+ "m_eventMap": [
+ {
+ "Key": {
+ "Value": 78438309
+ },
+ "Value": {
+ "m_eventName": "CreateInput",
+ "m_eventId": {
+ "Value": 78438309
+ },
+ "m_eventSlotId": {
+ "m_id": "{B831AC60-7641-4B74-9829-26A3576B4766}"
+ },
+ "m_resultSlotId": {
+ "m_id": "{ADF9B366-8324-4C1E-B601-C059DA70FDDE}"
+ },
+ "m_parameterSlotIds": [
+ {
+ "m_id": "{3301102C-AD42-48A0-8764-D2D4D31E0C75}"
+ }
+ ],
+ "m_numExpectedArguments": 1
+ }
+ },
+ {
+ "Key": {
+ "Value": 1793364217
+ },
+ "Value": {
+ "m_eventName": "ProcessInput",
+ "m_eventId": {
+ "Value": 1793364217
+ },
+ "m_eventSlotId": {
+ "m_id": "{4C8F2908-12B0-4C35-8468-31D3D4DF36AA}"
+ },
+ "m_parameterSlotIds": [
+ {
+ "m_id": "{8F69FA2E-28D8-4DF1-A4B5-AEF3985095C5}"
+ },
+ {
+ "m_id": "{BE3E3F84-E91C-4F6C-B148-E13BF01B5FBC}"
+ }
+ ],
+ "m_numExpectedArguments": 2
+ }
+ }
+ ],
+ "m_ebusName": "NetworkTestPlayerComponentBusHandler",
+ "m_busId": {
+ "Value": 3690077280
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20248544911852
+ },
+ "Name": "SC-Node(ExtractProperty)",
+ "Components": {
+ "Component_[2395597035843331661]": {
+ "$type": "ExtractProperty",
+ "Id": 2395597035843331661,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{C80C50EE-F216-4F44-B107-6B35354AFD52}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "When signaled assigns property values using the supplied source input",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{C69C098D-D667-4DC7-85E5-AFD119727D94}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "toolTip": "Signaled after all property haves have been pushed to the output slots",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{D387C800-352B-4B01-8765-4F4B40DF45CB}"
+ },
+ "DynamicTypeOverride": 1,
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Source",
+ "toolTip": "The value on which to extract properties from.",
+ "DisplayDataType": {
+ "m_type": 4,
+ "m_azType": "{12A1776B-61F6-4E5F-356A-AD718A62051F}"
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{0C03D491-DE25-46C2-BF09-14769FA49FDB}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "FwdBack: Number",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{4C13F9EF-60BF-4AD1-8FA9-66F46455411C}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "LeftRight: Number",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 4,
+ "m_azType": "{12A1776B-61F6-4E5F-356A-AD718A62051F}"
+ },
+ "isNullPointer": false,
+ "$type": "NetworkTestPlayerComponentNetworkInput",
+ "label": "Source"
+ }
+ ],
+ "m_dataType": {
+ "m_type": 4,
+ "m_azType": "{12A1776B-61F6-4E5F-356A-AD718A62051F}"
+ },
+ "m_propertyAccounts": [
+ {
+ "m_propertySlotId": {
+ "m_id": "{0C03D491-DE25-46C2-BF09-14769FA49FDB}"
+ },
+ "m_propertyType": {
+ "m_type": 3
+ },
+ "m_propertyName": "FwdBack"
+ },
+ {
+ "m_propertySlotId": {
+ "m_id": "{4C13F9EF-60BF-4AD1-8FA9-66F46455411C}"
+ },
+ "m_propertyType": {
+ "m_type": 3
+ },
+ "m_propertyName": "LeftRight"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20270019748332
+ },
+ "Name": "SC-Node(Print)",
+ "Components": {
+ "Component_[7568030783460446634]": {
+ "$type": "Print",
+ "Id": 7568030783460446634,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{733AA75D-022C-45E9-9D0F-3EF9A1633ADC}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Input signal",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{2E77B0FC-DE56-4D78-9A59-152B7A0666F3}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "m_format": "AutoComponent_NetworkInput received bad fwdback!",
+ "m_unresolvedString": [
+ "AutoComponent_NetworkInput received bad fwdback!"
+ ]
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20274314715628
+ },
+ "Name": "SC-Node(Print)",
+ "Components": {
+ "Component_[7568030783460446634]": {
+ "$type": "Print",
+ "Id": 7568030783460446634,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{733AA75D-022C-45E9-9D0F-3EF9A1633ADC}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Input signal",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{2E77B0FC-DE56-4D78-9A59-152B7A0666F3}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "m_format": "AutoComponent_NetworkInput received bad leftright!",
+ "m_unresolvedString": [
+ "AutoComponent_NetworkInput received bad leftright!"
+ ]
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20261429813740
+ },
+ "Name": "SC-Node(Print)",
+ "Components": {
+ "Component_[8131385522131771125]": {
+ "$type": "Print",
+ "Id": 8131385522131771125,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{2B6DB3BC-AA87-4280-B4C3-42C1EE17CBA3}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Input signal",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{68F1C47B-3127-4CBA-AC9B-5B9B736C70AD}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "m_format": "AutoComponent_NetworkInput CreateInput called!",
+ "m_unresolvedString": [
+ "AutoComponent_NetworkInput CreateInput called!"
+ ]
+ }
+ }
+ }
+ ],
+ "m_connections": [
+ {
+ "Id": {
+ "id": 20282904650220
+ },
+ "Name": "srcEndpoint=(NetworkTestPlayerComponentBusHandler Handler: ExecutionSlot:CreateInput), destEndpoint=(Print: In)",
+ "Components": {
+ "Component_[3586317167340048684]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 3586317167340048684,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 20252839879148
+ },
+ "slotId": {
+ "m_id": "{B831AC60-7641-4B74-9829-26A3576B4766}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 20261429813740
+ },
+ "slotId": {
+ "m_id": "{2B6DB3BC-AA87-4280-B4C3-42C1EE17CBA3}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20287199617516
+ },
+ "Name": "srcEndpoint=(NetworkTestPlayerComponentBusHandler Handler: ExecutionSlot:CreateInput), destEndpoint=(CreateFromValues: In)",
+ "Components": {
+ "Component_[15956251897822268937]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 15956251897822268937,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 20252839879148
+ },
+ "slotId": {
+ "m_id": "{B831AC60-7641-4B74-9829-26A3576B4766}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 20244249944556
+ },
+ "slotId": {
+ "m_id": "{514CDDAA-290F-4758-B28F-4003E719E635}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20291494584812
+ },
+ "Name": "srcEndpoint=(CreateFromValues: Result: NetworkTestPlayerComponentNetworkInput), destEndpoint=(NetworkTestPlayerComponentBusHandler Handler: Result: NetworkTestPlayerComponentNetworkInput)",
+ "Components": {
+ "Component_[3864080489501353126]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 3864080489501353126,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 20244249944556
+ },
+ "slotId": {
+ "m_id": "{90E52F81-54E0-4C63-9881-B661FB5D87D1}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 20252839879148
+ },
+ "slotId": {
+ "m_id": "{ADF9B366-8324-4C1E-B601-C059DA70FDDE}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20295789552108
+ },
+ "Name": "srcEndpoint=(NetworkTestPlayerComponentBusHandler Handler: ExecutionSlot:ProcessInput), destEndpoint=(Print: In)",
+ "Components": {
+ "Component_[8628095809445337119]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 8628095809445337119,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 20252839879148
+ },
+ "slotId": {
+ "m_id": "{4C8F2908-12B0-4C35-8468-31D3D4DF36AA}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 20278609682924
+ },
+ "slotId": {
+ "m_id": "{D994D58A-DBF1-4929-B779-F0D2CBAD2F0D}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20300084519404
+ },
+ "Name": "srcEndpoint=(NetworkTestPlayerComponentBusHandler Handler: ExecutionSlot:ProcessInput), destEndpoint=(Extract Properties: In)",
+ "Components": {
+ "Component_[10621112306443381493]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 10621112306443381493,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 20252839879148
+ },
+ "slotId": {
+ "m_id": "{4C8F2908-12B0-4C35-8468-31D3D4DF36AA}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 20248544911852
+ },
+ "slotId": {
+ "m_id": "{C80C50EE-F216-4F44-B107-6B35354AFD52}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20304379486700
+ },
+ "Name": "srcEndpoint=(NetworkTestPlayerComponentBusHandler Handler: NetworkTestPlayerComponentNetworkInput), destEndpoint=(Extract Properties: Source)",
+ "Components": {
+ "Component_[14013500888143163469]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 14013500888143163469,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 20252839879148
+ },
+ "slotId": {
+ "m_id": "{8F69FA2E-28D8-4DF1-A4B5-AEF3985095C5}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 20248544911852
+ },
+ "slotId": {
+ "m_id": "{D387C800-352B-4B01-8765-4F4B40DF45CB}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20308674453996
+ },
+ "Name": "srcEndpoint=(Extract Properties: Out), destEndpoint=(Not Equal To (!=): In)",
+ "Components": {
+ "Component_[14597948098713219792]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 14597948098713219792,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 20248544911852
+ },
+ "slotId": {
+ "m_id": "{C69C098D-D667-4DC7-85E5-AFD119727D94}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 20257134846444
+ },
+ "slotId": {
+ "m_id": "{AE7E453C-15DE-47D2-954A-05C3895B7AC6}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20312969421292
+ },
+ "Name": "srcEndpoint=(Extract Properties: FwdBack: Number), destEndpoint=(Not Equal To (!=): Value A)",
+ "Components": {
+ "Component_[14915522756837814768]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 14915522756837814768,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 20248544911852
+ },
+ "slotId": {
+ "m_id": "{0C03D491-DE25-46C2-BF09-14769FA49FDB}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 20257134846444
+ },
+ "slotId": {
+ "m_id": "{57EBC15B-452E-49B3-8BD3-4FBBB07F1F14}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20317264388588
+ },
+ "Name": "srcEndpoint=(Extract Properties: Out), destEndpoint=(Not Equal To (!=): In)",
+ "Components": {
+ "Component_[6510282773353837676]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 6510282773353837676,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 20248544911852
+ },
+ "slotId": {
+ "m_id": "{C69C098D-D667-4DC7-85E5-AFD119727D94}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 20265724781036
+ },
+ "slotId": {
+ "m_id": "{AE7E453C-15DE-47D2-954A-05C3895B7AC6}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20321559355884
+ },
+ "Name": "srcEndpoint=(Extract Properties: LeftRight: Number), destEndpoint=(Not Equal To (!=): Value A)",
+ "Components": {
+ "Component_[16150645152204311425]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 16150645152204311425,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 20248544911852
+ },
+ "slotId": {
+ "m_id": "{4C13F9EF-60BF-4AD1-8FA9-66F46455411C}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 20265724781036
+ },
+ "slotId": {
+ "m_id": "{57EBC15B-452E-49B3-8BD3-4FBBB07F1F14}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20325854323180
+ },
+ "Name": "srcEndpoint=(Not Equal To (!=): True), destEndpoint=(Print: In)",
+ "Components": {
+ "Component_[3322355580364572639]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 3322355580364572639,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 20257134846444
+ },
+ "slotId": {
+ "m_id": "{AC364E17-A9A1-42DB-A29D-7B2D666E4287}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 20270019748332
+ },
+ "slotId": {
+ "m_id": "{733AA75D-022C-45E9-9D0F-3EF9A1633ADC}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 20330149290476
+ },
+ "Name": "srcEndpoint=(Not Equal To (!=): True), destEndpoint=(Print: In)",
+ "Components": {
+ "Component_[1975626970668030308]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 1975626970668030308,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 20265724781036
+ },
+ "slotId": {
+ "m_id": "{AC364E17-A9A1-42DB-A29D-7B2D666E4287}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 20274314715628
+ },
+ "slotId": {
+ "m_id": "{733AA75D-022C-45E9-9D0F-3EF9A1633ADC}"
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "m_assetType": "{3E2AC8CD-713F-453E-967F-29517F331784}",
+ "versionData": {
+ "_grammarVersion": 1,
+ "_runtimeVersion": 1,
+ "_fileVersion": 1
+ },
+ "m_variableCounter": 2,
+ "GraphCanvasData": [
+ {
+ "Key": {
+ "id": 20239954977260
+ },
+ "Value": {
+ "ComponentData": {
+ "{5F84B500-8C45-40D1-8EFC-A5306B241444}": {
+ "$type": "SceneComponentSaveData",
+ "ViewParams": {
+ "Scale": 1.0097068678919363,
+ "AnchorX": 1086.4539794921875,
+ "AnchorY": 198.07728576660156
+ }
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 20244249944556
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "MethodNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 740.0,
+ 100.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData",
+ "SubStyle": ".method"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{7A7C96CB-4B5A-48DD-A0AD-0094A113549B}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 20248544911852
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "DefaultNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 740.0,
+ 320.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{ED8F0B6C-0811-4FB7-AEE8-B71DB87FABF0}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 20252839879148
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 160.0,
+ 100.0
+ ]
+ },
+ "{9E81C95F-89C0-4476-8E82-63CCC4E52E04}": {
+ "$type": "EBusHandlerNodeDescriptorSaveData",
+ "EventIds": [
+ {
+ "Value": 78438309
+ },
+ {
+ "Value": 1793364217
+ }
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{BB97BA3E-F2AB-4078-9BB3-2A0F31DC771C}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 20257134846444
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "MathNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 1040.0,
+ 320.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{71AB4748-41F4-4FEA-874C-F54037236F31}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 20261429813740
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "StringNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 740.0,
+ -100.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{26E363EE-F35A-4096-88EF-DF907A809894}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 20265724781036
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "MathNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 1040.0,
+ 520.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{F64E211C-8DDD-4223-9235-8DB802A017CB}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 20270019748332
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "StringNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 1480.0,
+ 320.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{9161B9FA-8493-479D-BE35-10D92644D5C0}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 20274314715628
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "StringNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 1480.0,
+ 520.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{1AF129A6-751C-4FC0-805E-65AC2D4B6D3D}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 20278609682924
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "StringNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 740.0,
+ 740.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{F7407B9B-C302-4B50-BFB0-D1208DF1D5AC}"
+ }
+ }
+ }
+ }
+ ],
+ "StatisticsHelper": {
+ "InstanceCounter": [
+ {
+ "Key": 5842116704436214676,
+ "Value": 1
+ },
+ {
+ "Key": 5842116706017748280,
+ "Value": 1
+ },
+ {
+ "Key": 7441100700879769985,
+ "Value": 2
+ },
+ {
+ "Key": 10684225535275896474,
+ "Value": 4
+ },
+ {
+ "Key": 10715014621082578046,
+ "Value": 1
+ },
+ {
+ "Key": 14285852892804039565,
+ "Value": 1
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_NetworkInput/Player.prefab b/AutomatedTesting/Levels/Multiplayer/AutoComponent_NetworkInput/Player.prefab
new file mode 100644
index 0000000000..72fce2d291
--- /dev/null
+++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_NetworkInput/Player.prefab
@@ -0,0 +1,196 @@
+{
+ "ContainerEntity": {
+ "Id": "ContainerEntity",
+ "Name": "Player",
+ "Components": {
+ "Component_[10591405285626521927]": {
+ "$type": "EditorLockComponent",
+ "Id": 10591405285626521927
+ },
+ "Component_[10962884071806037909]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 10962884071806037909,
+ "Parent Entity": ""
+ },
+ "Component_[14883697413991420474]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 14883697413991420474
+ },
+ "Component_[1497622121956209837]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 1497622121956209837
+ },
+ "Component_[16429314387772079347]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 16429314387772079347
+ },
+ "Component_[16665294301093657382]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 16665294301093657382
+ },
+ "Component_[1706666252612720326]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 1706666252612720326
+ },
+ "Component_[4216896820422195198]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 4216896820422195198
+ },
+ "Component_[4540089401187370610]": {
+ "$type": "EditorPrefabComponent",
+ "Id": 4540089401187370610
+ },
+ "Component_[6378576046601184103]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 6378576046601184103
+ },
+ "Component_[7745420981568587180]": {
+ "$type": "SelectionComponent",
+ "Id": 7745420981568587180
+ }
+ }
+ },
+ "Entities": {
+ "Entity_[1028733630164]": {
+ "Id": "Entity_[1028733630164]",
+ "Name": "Player",
+ "Components": {
+ "Component_[12294726333564087591]": {
+ "$type": "SelectionComponent",
+ "Id": 12294726333564087591
+ },
+ "Component_[13587084088242540786]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 13587084088242540786,
+ "ComponentOrderEntryArray": [
+ {
+ "ComponentId": 6819443882832501114
+ },
+ {
+ "ComponentId": 5577505593558922067,
+ "SortIndex": 1
+ },
+ {
+ "ComponentId": 2069554278758260821,
+ "SortIndex": 2
+ },
+ {
+ "ComponentId": 16508969730014660362,
+ "SortIndex": 3
+ },
+ {
+ "ComponentId": 8125406152674415588,
+ "SortIndex": 4
+ },
+ {
+ "ComponentId": 4337571454344109612,
+ "SortIndex": 5
+ },
+ {
+ "ComponentId": 16457408099527309065,
+ "SortIndex": 6
+ }
+ ]
+ },
+ "Component_[14335168881008289852]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 14335168881008289852
+ },
+ "Component_[16308902899170829847]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 16308902899170829847
+ },
+ "Component_[16457408099527309065]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 16457408099527309065,
+ "m_template": {
+ "$type": "Multiplayer::NetworkTransformComponent"
+ }
+ },
+ "Component_[16508969730014660362]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 16508969730014660362,
+ "m_template": {
+ "$type": "AutomatedTesting::NetworkTestPlayerComponent"
+ }
+ },
+ "Component_[16541569566865026527]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 16541569566865026527
+ },
+ "Component_[2002761223483048905]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 2002761223483048905
+ },
+ "Component_[2069554278758260821]": {
+ "$type": "EditorScriptCanvasComponent",
+ "Id": 2069554278758260821,
+ "m_name": "AutoComponent_NetworkInput",
+ "m_assetHolder": {
+ "m_asset": {
+ "assetId": {
+ "guid": "{D079F53D-CCAA-5C98-8E0C-B485B7821747}"
+ },
+ "assetHint": "levels/multiplayer/autocomponent_networkinput/autocomponent_networkinput.scriptcanvas"
+ }
+ },
+ "runtimeDataIsValid": true,
+ "runtimeDataOverrides": {
+ "source": {
+ "assetId": {
+ "guid": "{D079F53D-CCAA-5C98-8E0C-B485B7821747}"
+ },
+ "assetHint": "levels/multiplayer/autocomponent_networkinput/autocomponent_networkinput.scriptcanvas"
+ }
+ }
+ },
+ "Component_[4337571454344109612]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 4337571454344109612,
+ "m_template": {
+ "$type": "NetBindComponent"
+ }
+ },
+ "Component_[477591477979440744]": {
+ "$type": "EditorLockComponent",
+ "Id": 477591477979440744
+ },
+ "Component_[5577505593558922067]": {
+ "$type": "AZ::Render::EditorMeshComponent",
+ "Id": 5577505593558922067,
+ "Controller": {
+ "Configuration": {
+ "ModelAsset": {
+ "assetId": {
+ "guid": "{6DE0E9A8-A1C7-5D0F-9407-4E627C1F223C}",
+ "subId": 284780167
+ },
+ "assetHint": "models/sphere.azmodel"
+ }
+ }
+ }
+ },
+ "Component_[5828214869455694702]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 5828214869455694702
+ },
+ "Component_[6819443882832501114]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 6819443882832501114,
+ "Parent Entity": "ContainerEntity"
+ },
+ "Component_[8125406152674415588]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 8125406152674415588,
+ "m_template": {
+ "$type": "Multiplayer::LocalPredictionPlayerInputComponent"
+ }
+ },
+ "Component_[8838623765985560328]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 8838623765985560328
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_NetworkInput/tags.txt b/AutomatedTesting/Levels/Multiplayer/AutoComponent_NetworkInput/tags.txt
new file mode 100644
index 0000000000..0d6c1880e7
--- /dev/null
+++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_NetworkInput/tags.txt
@@ -0,0 +1,12 @@
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
diff --git a/AutomatedTesting/Materials/MinimalBlue.material b/AutomatedTesting/Materials/MinimalBlue.material
new file mode 100644
index 0000000000..7310a90250
--- /dev/null
+++ b/AutomatedTesting/Materials/MinimalBlue.material
@@ -0,0 +1,17 @@
+{
+ "description": "",
+ "parentMaterial": "",
+ "materialType": "TestData/Materials/Types/MinimalPBR.materialtype",
+ "materialTypeVersion": 3,
+ "properties": {
+ "settings": {
+ "color": [
+ 0.08522164076566696,
+ 0.11898985505104065,
+ 1.0,
+ 1.0
+ ],
+ "roughness": 0.33000001311302185
+ }
+ }
+}
\ No newline at end of file
diff --git a/AutomatedTesting/Passes/MainPipeline.pass b/AutomatedTesting/Passes/MainPipeline.pass
index aa9f3757c4..c34c556983 100644
--- a/AutomatedTesting/Passes/MainPipeline.pass
+++ b/AutomatedTesting/Passes/MainPipeline.pass
@@ -205,6 +205,99 @@
}
]
},
+
+ {
+ // NOTE: HairParentPass does not write into Depth MSAA from Opaque Pass. If new passes downstream
+ // of HairParentPass will need to use Depth MSAA, HairParentPass will need to be updated to use Depth MSAA
+ // instead of regular Depth as DepthStencil. Specifically, HairResolvePPLL.pass and the associated
+ // .azsl file will need to be updated.
+ "Name": "HairParentPass",
+ // Note: The following two lines represent the choice of rendering pipeline for the hair.
+ // You can either choose to use PPLL or ShortCut and accordingly change the flag
+ // 'm_usePPLLRenderTechnique' in the class 'HairFeatureProcessor.cpp'
+// "TemplateName": "HairParentPassTemplate",
+ "TemplateName": "HairParentShortCutPassTemplate",
+ "Enabled": true,
+ "Connections": [
+ // Critical to keep DepthLinear as input - used to set the size of the Head PPLL image buffer.
+ // If DepthLinear is not available - connect to another viewport (non MSAA) image.
+ {
+ "LocalSlot": "DepthLinearInput",
+ "AttachmentRef": {
+ "Pass": "DepthPrePass",
+ "Attachment": "DepthLinear"
+ }
+ },
+ {
+ "LocalSlot": "Depth",
+ "AttachmentRef": {
+ "Pass": "DepthPrePass",
+ "Attachment": "Depth"
+ }
+ },
+ {
+ "LocalSlot": "RenderTargetInputOutput",
+ "AttachmentRef": {
+ "Pass": "OpaquePass",
+ "Attachment": "Output"
+ }
+ },
+ {
+ "LocalSlot": "RenderTargetInputOnly",
+ "AttachmentRef": {
+ "Pass": "OpaquePass",
+ "Attachment": "Output"
+ }
+ },
+
+ // Shadows resources
+ {
+ "LocalSlot": "DirectionalShadowmap",
+ "AttachmentRef": {
+ "Pass": "ShadowPass",
+ "Attachment": "DirectionalShadowmap"
+ }
+ },
+ {
+ "LocalSlot": "DirectionalESM",
+ "AttachmentRef": {
+ "Pass": "ShadowPass",
+ "Attachment": "DirectionalESM"
+ }
+ },
+ {
+ "LocalSlot": "ProjectedShadowmap",
+ "AttachmentRef": {
+ "Pass": "ShadowPass",
+ "Attachment": "ProjectedShadowmap"
+ }
+ },
+ {
+ "LocalSlot": "ProjectedESM",
+ "AttachmentRef": {
+ "Pass": "ShadowPass",
+ "Attachment": "ProjectedESM"
+ }
+ },
+
+ // Lighting Resources
+ {
+ "LocalSlot": "TileLightData",
+ "AttachmentRef": {
+ "Pass": "LightCullingPass",
+ "Attachment": "TileLightData"
+ }
+ },
+ {
+ "LocalSlot": "LightListRemapped",
+ "AttachmentRef": {
+ "Pass": "LightCullingPass",
+ "Attachment": "LightListRemapped"
+ }
+ }
+ ]
+ },
+
{
"Name": "TransparentPass",
"TemplateName": "TransparentParentTemplate",
@@ -254,22 +347,22 @@
{
"LocalSlot": "InputLinearDepth",
"AttachmentRef": {
- "Pass": "DepthPrePass",
+ "Pass": "HairParentPass",
"Attachment": "DepthLinear"
}
},
{
"LocalSlot": "DepthStencil",
"AttachmentRef": {
- "Pass": "DepthPrePass",
+ "Pass": "HairParentPass",
"Attachment": "Depth"
}
},
{
"LocalSlot": "InputOutput",
"AttachmentRef": {
- "Pass": "OpaquePass",
- "Attachment": "Output"
+ "Pass": "HairParentPass",
+ "Attachment": "RenderTargetInputOutput"
}
}
]
@@ -282,22 +375,22 @@
{
"LocalSlot": "InputLinearDepth",
"AttachmentRef": {
- "Pass": "DepthPrePass",
+ "Pass": "HairParentPass",
"Attachment": "DepthLinear"
}
},
{
"LocalSlot": "InputDepthStencil",
"AttachmentRef": {
- "Pass": "DepthPrePass",
+ "Pass": "HairParentPass",
"Attachment": "Depth"
}
},
{
"LocalSlot": "RenderTargetInputOutput",
"AttachmentRef": {
- "Pass": "TransparentPass",
- "Attachment": "InputOutput"
+ "Pass": "HairParentPass",
+ "Attachment": "RenderTargetInputOutput"
}
}
],
@@ -337,7 +430,7 @@
{
"LocalSlot": "Depth",
"AttachmentRef": {
- "Pass": "DepthPrePass",
+ "Pass": "HairParentPass",
"Attachment": "Depth"
}
},
@@ -372,7 +465,7 @@
{
"LocalSlot": "DepthInputOutput",
"AttachmentRef": {
- "Pass": "DepthPrePass",
+ "Pass": "HairParentPass",
"Attachment": "Depth"
}
}
@@ -431,7 +524,7 @@
{
"LocalSlot": "DepthInputOutput",
"AttachmentRef": {
- "Pass": "DepthPrePass",
+ "Pass": "HairParentPass",
"Attachment": "Depth"
}
}
@@ -451,7 +544,7 @@
{
"LocalSlot": "DepthInputOutput",
"AttachmentRef": {
- "Pass": "DepthPrePass",
+ "Pass": "HairParentPass",
"Attachment": "Depth"
}
}
diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp
index 9fe4cfd0d2..bcaaa02b67 100644
--- a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp
+++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp
@@ -96,10 +96,6 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent)
connect(
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
&AzAssetBrowserWindow::SetTableViewVisibleAfterFilter);
-
- connect(
- m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
- &AzAssetBrowserWindow::UpdateTableModelAfterFilter);
connect(
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this,
&AzAssetBrowserWindow::SelectionChangedSlot);
@@ -251,24 +247,6 @@ void AzAssetBrowserWindow::SetExpandedAssetBrowserMode()
m_assetBrowserDisplayState = AzAssetBrowser::AssetBrowserDisplayState::ExpandedMode;
- disconnect(
- m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
- &AzAssetBrowserWindow::UpdateTableModelAfterFilter);
- disconnect(
- m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
- &AzAssetBrowserWindow::SetTableViewVisibleAfterFilter);
-
- disconnect(
- m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this,
- &AzAssetBrowserWindow::SelectionChangedSlot);
- disconnect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem);
- disconnect(
- m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget,
- &AzAssetBrowser::SearchWidget::ClearStringFilter);
- disconnect(
- m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget,
- &AzAssetBrowser::SearchWidget::ClearTypeFilter);
-
if (m_ui->m_assetBrowserTableViewWidget->isVisible())
{
m_ui->m_assetBrowserTableViewWidget->setVisible(false);
@@ -281,37 +259,9 @@ void AzAssetBrowserWindow::SetDefaultAssetBrowserMode()
namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
m_assetBrowserDisplayState = AzAssetBrowser::AssetBrowserDisplayState::DefaultMode;
-
- connect(
- m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
- &AzAssetBrowserWindow::SetTableViewVisibleAfterFilter);
-
- connect(
- m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
- &AzAssetBrowserWindow::UpdateTableModelAfterFilter);
- connect(
- m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this,
- &AzAssetBrowserWindow::SelectionChangedSlot);
- connect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem);
- connect(
- m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget,
- &AzAssetBrowser::SearchWidget::ClearStringFilter);
- connect(
- m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget,
- &AzAssetBrowser::SearchWidget::ClearTypeFilter);
-
- //If the filter is not empty we want to switch views and Update the model
- UpdateTableModelAfterFilter();
SetTableViewVisibleAfterFilter();
}
-void AzAssetBrowserWindow::UpdateTableModelAfterFilter()
-{
- if (!m_ui->m_searchWidget->GetFilterString().isEmpty())
- {
- m_tableModel->UpdateTableModelMaps();
- }
-}
void AzAssetBrowserWindow::SetTableViewVisibleAfterFilter()
{
@@ -389,8 +339,8 @@ void AzAssetBrowserWindow::SelectionChangedSlot(const QItemSelection& /*selected
UpdatePreview();
}
-// while its tempting to use Activated here, we dont actually want it to count as activation
-// just becuase on some OS clicking once is activation.
+// while its tempting to use Activated here, we don't actually want it to count as activation
+// just because on some OS clicking once is activation.
void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& element)
{
namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h
index 753f000300..34103316ab 100644
--- a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h
+++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h
@@ -68,7 +68,6 @@ protected slots:
void CreateSwitchViewMenu();
void SetExpandedAssetBrowserMode();
void SetDefaultAssetBrowserMode();
- void UpdateTableModelAfterFilter();
void SetTableViewVisibleAfterFilter();
private:
diff --git a/Code/Editor/CVarMenu.h b/Code/Editor/CVarMenu.h
index efcc8e8caf..5195bd99d7 100644
--- a/Code/Editor/CVarMenu.h
+++ b/Code/Editor/CVarMenu.h
@@ -16,9 +16,12 @@
#include
#include
+struct ICVar;
+
class CVarMenu
: public QMenu
{
+ Q_OBJECT
public:
// CVar that can be toggled on and off
struct CVarToggle
diff --git a/Code/Editor/ConfigGroup.cpp b/Code/Editor/ConfigGroup.cpp
index 74fe6f7b5c..42236e43dd 100644
--- a/Code/Editor/ConfigGroup.cpp
+++ b/Code/Editor/ConfigGroup.cpp
@@ -19,10 +19,9 @@ namespace Config
CConfigGroup::~CConfigGroup()
{
- for (TConfigVariables::const_iterator it = m_vars.begin();
- it != m_vars.end(); ++it)
+ for (IConfigVar* var : m_vars)
{
- delete (*it);
+ delete var;
}
}
@@ -31,17 +30,15 @@ namespace Config
m_vars.push_back(var);
}
- uint32 CConfigGroup::GetVarCount()
+ AZ::u32 CConfigGroup::GetVarCount()
{
- return static_cast(m_vars.size());
+ return aznumeric_cast(m_vars.size());
}
IConfigVar* CConfigGroup::GetVar(const char* szName)
{
- for (TConfigVariables::const_iterator it = m_vars.begin();
- it != m_vars.end(); ++it)
+ for (IConfigVar* var : m_vars)
{
- IConfigVar* var = (*it);
if (0 == _stricmp(szName, var->GetName().c_str()))
{
return var;
@@ -53,20 +50,19 @@ namespace Config
const IConfigVar* CConfigGroup::GetVar(const char* szName) const
{
- for (TConfigVariables::const_iterator it = m_vars.begin();
- it != m_vars.end(); ++it)
+ for (const IConfigVar* var : m_vars)
{
- IConfigVar* var = (*it);
if (0 == _stricmp(szName, var->GetName().c_str()))
{
return var;
}
+
}
return nullptr;
}
- IConfigVar* CConfigGroup::GetVar(uint index)
+ IConfigVar* CConfigGroup::GetVar(AZ::u32 index)
{
if (index < m_vars.size())
{
@@ -76,7 +72,7 @@ namespace Config
return nullptr;
}
- const IConfigVar* CConfigGroup::GetVar(uint index) const
+ const IConfigVar* CConfigGroup::GetVar(AZ::u32 index) const
{
if (index < m_vars.size())
{
@@ -89,114 +85,110 @@ namespace Config
void CConfigGroup::SaveToXML(XmlNodeRef node)
{
// save only values that don't have default values
- for (TConfigVariables::const_iterator it = m_vars.begin();
- it != m_vars.end(); ++it)
+ for (const IConfigVar* var : m_vars)
{
- IConfigVar* var = (*it);
- if (!var->IsFlagSet(IConfigVar::eFlag_DoNotSave))
+ if (var->IsFlagSet(IConfigVar::eFlag_DoNotSave) || var->IsDefault())
{
- if (!var->IsDefault())
- {
- const char* szName = var->GetName().c_str();
+ continue;
+ }
- switch (var->GetType())
- {
- case IConfigVar::eType_BOOL:
- {
- bool currentValue = false;
- var->Get(¤tValue);
- node->setAttr(szName, currentValue);
- break;
- }
+ const char* szName = var->GetName().c_str();
- case IConfigVar::eType_INT:
- {
- int currentValue = 0;
- var->Get(¤tValue);
- node->setAttr(szName, currentValue);
- break;
- }
+ switch (var->GetType())
+ {
+ case IConfigVar::eType_BOOL:
+ {
+ bool currentValue = false;
+ var->Get(¤tValue);
+ node->setAttr(szName, currentValue);
+ break;
+ }
- case IConfigVar::eType_FLOAT:
- {
- float currentValue = 0;
- var->Get(¤tValue);
- node->setAttr(szName, currentValue);
- break;
- }
+ case IConfigVar::eType_INT:
+ {
+ int currentValue = 0;
+ var->Get(¤tValue);
+ node->setAttr(szName, currentValue);
+ break;
+ }
- case IConfigVar::eType_STRING:
- {
- AZStd::string currentValue;
- var->Get(¤tValue);
- node->setAttr(szName, currentValue.c_str());
- break;
- }
- }
- }
+ case IConfigVar::eType_FLOAT:
+ {
+ float currentValue = 0;
+ var->Get(¤tValue);
+ node->setAttr(szName, currentValue);
+ break;
+ }
+
+ case IConfigVar::eType_STRING:
+ {
+ AZStd::string currentValue;
+ var->Get(¤tValue);
+ node->setAttr(szName, currentValue.c_str());
+ break;
+ }
}
}
}
void CConfigGroup::LoadFromXML(XmlNodeRef node)
{
- // save only values that don't have default values
- for (TConfigVariables::const_iterator it = m_vars.begin();
- it != m_vars.end(); ++it)
+ // load values that are save-able
+ for (IConfigVar* var : m_vars)
{
- IConfigVar* var = (*it);
- if (!var->IsFlagSet(IConfigVar::eFlag_DoNotSave))
+ if (var->IsFlagSet(IConfigVar::eFlag_DoNotSave))
{
- const char* szName = var->GetName().c_str();
+ continue;
+ }
+ const char* szName = var->GetName().c_str();
- switch (var->GetType())
+ switch (var->GetType())
+ {
+ case IConfigVar::eType_BOOL:
+ {
+ bool currentValue = false;
+ var->GetDefault(¤tValue);
+ if (node->getAttr(szName, currentValue))
{
- case IConfigVar::eType_BOOL:
- {
- bool currentValue = false;
- var->GetDefault(¤tValue);
- if (node->getAttr(szName, currentValue))
- {
- var->Set(¤tValue);
- }
- break;
+ var->Set(¤tValue);
}
+ break;
+ }
- case IConfigVar::eType_INT:
+ case IConfigVar::eType_INT:
+ {
+ int currentValue = 0;
+ var->GetDefault(¤tValue);
+ if (node->getAttr(szName, currentValue))
{
- int currentValue = 0;
- var->GetDefault(¤tValue);
- if (node->getAttr(szName, currentValue))
- {
- var->Set(¤tValue);
- }
- break;
+ var->Set(¤tValue);
}
+ break;
+ }
- case IConfigVar::eType_FLOAT:
+ case IConfigVar::eType_FLOAT:
+ {
+ float currentValue = 0;
+ var->GetDefault(¤tValue);
+ if (node->getAttr(szName, currentValue))
{
- float currentValue = 0;
- var->GetDefault(¤tValue);
- if (node->getAttr(szName, currentValue))
- {
- var->Set(¤tValue);
- }
- break;
+ var->Set(¤tValue);
}
+ break;
+ }
- case IConfigVar::eType_STRING:
+ case IConfigVar::eType_STRING:
+ {
+ AZStd::string currentValue;
+ var->GetDefault(¤tValue);
+ QString readValue(currentValue.c_str());
+ if (node->getAttr(szName, readValue))
{
- AZStd::string currentValue;
- var->GetDefault(¤tValue);
- QString readValue(currentValue.c_str());
- if (node->getAttr(szName, readValue))
- {
- currentValue = readValue.toUtf8().data();
- var->Set(¤tValue);
- }
- break;
- }
+ currentValue = readValue.toUtf8().data();
+ var->Set(¤tValue);
}
+ break;
+ }
}
}
}
diff --git a/Code/Editor/ConfigGroup.h b/Code/Editor/ConfigGroup.h
index 769a29ba8a..004725e32c 100644
--- a/Code/Editor/ConfigGroup.h
+++ b/Code/Editor/ConfigGroup.h
@@ -8,8 +8,12 @@
#pragma once
-#ifndef CRYINCLUDE_EDITOR_CONFIGGROUP_H
-#define CRYINCLUDE_EDITOR_CONFIGGROUP_H
+#include
+#include
+#include
+
+struct ICVar;
+class XmlNodeRef;
namespace Config
{
@@ -32,7 +36,7 @@ namespace Config
eFlag_DoNotSave = 1 << 2,
};
- IConfigVar(const char* szName, const char* szDescription, EType varType, uint8 flags)
+ IConfigVar(const char* szName, const char* szDescription, EType varType, AZ::u8 flags)
: m_name(szName)
, m_description(szDescription)
, m_type(varType)
@@ -42,22 +46,22 @@ namespace Config
virtual ~IConfigVar() = default;
- ILINE EType GetType() const
+ AZ_FORCE_INLINE EType GetType() const
{
return m_type;
}
- ILINE const AZStd::string& GetName() const
+ AZ_FORCE_INLINE const AZStd::string& GetName() const
{
return m_name;
}
- ILINE const AZStd::string& GetDescription() const
+ AZ_FORCE_INLINE const AZStd::string& GetDescription() const
{
return m_description;
}
- ILINE bool IsFlagSet(EFlags flag) const
+ AZ_FORCE_INLINE bool IsFlagSet(EFlags flag) const
{
return 0 != (m_flags & flag);
}
@@ -68,73 +72,28 @@ namespace Config
virtual void GetDefault(void* outPtr) const = 0;
virtual void Reset() = 0;
- static EType TranslateType(const bool&) { return eType_BOOL; }
- static EType TranslateType(const int&) { return eType_INT; }
- static EType TranslateType(const float&) { return eType_FLOAT; }
- static EType TranslateType(const AZStd::string&) { return eType_STRING; }
+ static constexpr EType TranslateType(const bool&) { return eType_BOOL; }
+ static constexpr EType TranslateType(const int&) { return eType_INT; }
+ static constexpr EType TranslateType(const float&) { return eType_FLOAT; }
+ static constexpr EType TranslateType(const AZStd::string&) { return eType_STRING; }
protected:
EType m_type;
- uint8 m_flags;
+ AZ::u8 m_flags;
AZStd::string m_name;
AZStd::string m_description;
void* m_ptr;
ICVar* m_pCVar;
};
- // Typed wrapper for config variable
- template
- class TConfigVar
- : public IConfigVar
- {
- private:
- T m_default;
-
- public:
- TConfigVar(const char* szName, const char* szDescription, uint8 flags, T& ptr, const T& defaultValue)
- : IConfigVar(szName, szDescription, IConfigVar::TranslateType(ptr), flags)
- , m_default(defaultValue)
- {
- m_ptr = &ptr;
-
- // reset to default value on initializations
- ptr = defaultValue;
- }
-
- virtual void Get(void* outPtr) const
- {
- *reinterpret_cast(outPtr) = *reinterpret_cast(m_ptr);
- }
-
- virtual void Set(const void* ptr)
- {
- *reinterpret_cast(m_ptr) = *reinterpret_cast(ptr);
- }
-
- virtual void Reset()
- {
- *reinterpret_cast(m_ptr) = m_default;
- }
-
- virtual void GetDefault(void* outPtr) const
- {
- *reinterpret_cast(outPtr) = m_default;
- }
-
- virtual bool IsDefault() const
- {
- return *reinterpret_cast(m_ptr) == m_default;
- }
- };
-
// Group of configuration variables with optional mapping to CVars
class CConfigGroup
{
private:
- typedef std::vector TConfigVariables;
+ using TConfigVariables = AZStd::vector ;
TConfigVariables m_vars;
- typedef std::vector TConsoleVariables;
+ using TConsoleVariables = AZStd::vector;
TConsoleVariables m_consoleVars;
public:
@@ -142,20 +101,13 @@ namespace Config
virtual ~CConfigGroup();
void AddVar(IConfigVar* var);
- uint32 GetVarCount();
+ AZ::u32 GetVarCount();
IConfigVar* GetVar(const char* szName);
- IConfigVar* GetVar(uint index);
+ IConfigVar* GetVar(AZ::u32 index);
const IConfigVar* GetVar(const char* szName) const;
- const IConfigVar* GetVar(uint index) const;
+ const IConfigVar* GetVar(AZ::u32 index) const;
void SaveToXML(XmlNodeRef node);
void LoadFromXML(XmlNodeRef node);
-
- template
- void AddVar(const char* szName, const char* szDescription, T& var, const T& defaultValue, uint8 flags = 0)
- {
- AddVar(new TConfigVar(szName, szDescription, flags, var, defaultValue));
- }
};
};
-#endif // CRYINCLUDE_EDITOR_CONFIGGROUP_H
diff --git a/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.h b/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.h
index 5ec24b679d..849e44cedd 100644
--- a/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.h
+++ b/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.h
@@ -6,8 +6,6 @@
*
*/
-#ifndef CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H
-#define CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
@@ -53,6 +51,7 @@ private:
class UserPopupWidgetHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarUser, UserPropertyEditor>
{
+ Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(UserPopupWidgetHandler, AZ::SystemAllocator, 0);
bool IsDefaultHandler() const override { return false; }
@@ -67,6 +66,7 @@ public:
class FloatCurveHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarSpline, CSplineCtrl>
{
+ Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(FloatCurveHandler, AZ::SystemAllocator, 0);
bool IsDefaultHandler() const override { return false; }
@@ -80,5 +80,3 @@ public:
void OnSplineChange(CSplineCtrl*);
};
-
-#endif // CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H
diff --git a/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.cpp b/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.cpp
index c5ccc599d6..d26e978ae8 100644
--- a/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.cpp
+++ b/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.cpp
@@ -58,17 +58,9 @@ private:
void OnClicked() override
{
QString tempValue("");
- QString ext("");
- if (m_path.isEmpty() == false)
+ if (!m_path.isEmpty() && !Path::GetExt(m_path).isEmpty())
{
- if (Path::GetExt(m_path) == "")
- {
- tempValue = "";
- }
- else
- {
- tempValue = m_path;
- }
+ tempValue = m_path;
}
AssetSelectionModel selection;
diff --git a/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.h b/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.h
index fa30eba034..087ee9f1db 100644
--- a/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.h
+++ b/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.h
@@ -99,6 +99,7 @@ class FileResourceSelectorWidgetHandler
: QObject
, public AzToolsFramework::PropertyHandler < CReflectedVarResource, FileResourceSelectorWidget >
{
+ Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(FileResourceSelectorWidgetHandler, AZ::SystemAllocator, 0);
diff --git a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp
index b3f1b35461..2320938f08 100644
--- a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp
+++ b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp
@@ -446,41 +446,54 @@ void ReflectedVarUserAdapter::SetVariable(IVariable *pVariable)
m_reflectedVar.reset(new CReflectedVarUser( pVariable->GetHumanName().toUtf8().data()));
}
-void ReflectedVarUserAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
+void ReflectedVarUserAdapter::SyncReflectedVarToIVar(IVariable* pVariable)
{
QString value;
pVariable->Get(value);
m_reflectedVar->m_value = value.toUtf8().data();
- //extract the list of custom items from the IVariable user data
- IVariable::IGetCustomItems* pGetCustomItems = static_cast (pVariable->GetUserData().value());
- if (pGetCustomItems != nullptr)
- {
- std::vector items;
- QString dlgTitle;
- // call the user supplied callback to fill-in items and get dialog title
- bool bShowIt = pGetCustomItems->GetItems(pVariable, items, dlgTitle);
- if (bShowIt) // if func didn't veto, show the dialog
- {
- m_reflectedVar->m_enableEdit = true;
- m_reflectedVar->m_useTree = pGetCustomItems->UseTree();
- m_reflectedVar->m_treeSeparator = pGetCustomItems->GetTreeSeparator();
- m_reflectedVar->m_dialogTitle = dlgTitle.toUtf8().data();
- m_reflectedVar->m_itemNames.resize(items.size());
- m_reflectedVar->m_itemDescriptions.resize(items.size());
-
- QByteArray ba;
- int i = -1;
- std::generate(m_reflectedVar->m_itemNames.begin(), m_reflectedVar->m_itemNames.end(), [&items, &i, &ba]() { ++i; ba = items[i].name.toUtf8(); return ba.data(); });
- i = -1;
- std::generate(m_reflectedVar->m_itemDescriptions.begin(), m_reflectedVar->m_itemDescriptions.end(), [&items, &i, &ba]() { ++i; ba = items[i].desc.toUtf8(); return ba.data(); });
-
- }
- }
- else
+ // extract the list of custom items from the IVariable user data
+ IVariable::IGetCustomItems* pGetCustomItems = static_cast(pVariable->GetUserData().value());
+ if (pGetCustomItems == nullptr)
{
m_reflectedVar->m_enableEdit = false;
+ return;
}
+
+ std::vector items;
+ QString dlgTitle;
+ // call the user supplied callback to fill-in items and get dialog title
+ bool bShowIt = pGetCustomItems->GetItems(pVariable, items, dlgTitle);
+ if (!bShowIt) // if func vetoed it, don't show the dialog
+ {
+ return;
+ }
+ m_reflectedVar->m_enableEdit = true;
+ m_reflectedVar->m_useTree = pGetCustomItems->UseTree();
+ m_reflectedVar->m_treeSeparator = pGetCustomItems->GetTreeSeparator();
+ m_reflectedVar->m_dialogTitle = dlgTitle.toUtf8().data();
+ m_reflectedVar->m_itemNames.resize(items.size());
+ m_reflectedVar->m_itemDescriptions.resize(items.size());
+
+ QByteArray ba;
+ int i = -1;
+ AZStd::generate(
+ m_reflectedVar->m_itemNames.begin(), m_reflectedVar->m_itemNames.end(),
+ [&items, &i, &ba]()
+ {
+ ++i;
+ ba = items[i].name.toUtf8();
+ return ba.data();
+ });
+ i = -1;
+ AZStd::generate(
+ m_reflectedVar->m_itemDescriptions.begin(), m_reflectedVar->m_itemDescriptions.end(),
+ [&items, &i, &ba]()
+ {
+ ++i;
+ ba = items[i].desc.toUtf8();
+ return ba.data();
+ });
}
void ReflectedVarUserAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
diff --git a/Code/Editor/Controls/TimelineCtrl.cpp b/Code/Editor/Controls/TimelineCtrl.cpp
index aa30c326ac..3890b46b2b 100644
--- a/Code/Editor/Controls/TimelineCtrl.cpp
+++ b/Code/Editor/Controls/TimelineCtrl.cpp
@@ -126,7 +126,6 @@ void TimelineWidget::DrawTicks(QPainter* painter)
const QPen pOldPen = painter->pen();
const QPen ltgray(QColor(110, 110, 110));
- const QPen black(palette().color(QPalette::Normal, QPalette::Text));
const QPen redpen(QColor(255, 0, 255));
// Draw time ticks every tick step seconds.
@@ -598,7 +597,6 @@ void TimelineWidget::DrawSecondTicks(QPainter* painter)
{
const QPen ltgray(QColor(110, 110, 110));
const QPen black(palette().color(QPalette::Normal, QPalette::Text));
- const QPen redpen(QColor(255, 0, 255));
for (int gx = m_grid.firstGridLine.x(); gx < m_grid.firstGridLine.x() + m_grid.numGridLines.x() + 1; gx++)
{
diff --git a/Code/Editor/Core/LevelEditorMenuHandler.cpp b/Code/Editor/Core/LevelEditorMenuHandler.cpp
index fdde1ac305..70aff10f87 100644
--- a/Code/Editor/Core/LevelEditorMenuHandler.cpp
+++ b/Code/Editor/Core/LevelEditorMenuHandler.cpp
@@ -832,7 +832,7 @@ QAction* LevelEditorMenuHandler::CreateViewPaneAction(const QtViewPane* view)
if (view->m_options.showOnToolsToolbar)
{
- action->setIcon(QIcon(view->m_options.toolbarIcon));
+ action->setIcon(QIcon(view->m_options.toolbarIcon.c_str()));
}
m_actionManager->AddAction(view->m_id, action);
diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp
index e4138da932..b47febd5ed 100644
--- a/Code/Editor/CryEdit.cpp
+++ b/Code/Editor/CryEdit.cpp
@@ -45,6 +45,7 @@ AZ_POP_DISABLE_WARNING
// AzCore
#include
+#include
#include
#include
#include
@@ -547,7 +548,6 @@ public:
{ "BatchMode", m_bConsoleMode },
{ "NullRenderer", m_bNullRenderer },
{ "devmode", m_bDeveloperMode },
- { "VTUNE", dummy },
{ "runpython", m_bRunPythonScript },
{ "runpythontest", m_bRunPythonTestScript },
{ "version", m_bShowVersionInfo },
@@ -1686,6 +1686,11 @@ bool CCryEditApp::InitInstance()
return false;
}
+ if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get())
+ {
+ AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "LegacySystemInterfaceCreated", R"({})");
+ }
+
// Process some queued events come from system init
// Such as asset catalog loaded notification.
// There are some systems need to load configurations from assets for post initialization but before loading level
@@ -4137,7 +4142,15 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[])
AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware);
Editor::EditorQtApplication* app = Editor::EditorQtApplication::newInstance(argc, argv);
- if (app->arguments().contains("-autotest_mode"))
+ QStringList qArgs = app->arguments();
+ const bool is_automated_test = AZStd::any_of(qArgs.begin(), qArgs.end(),
+ [](const QString& elem)
+ {
+ return elem.endsWith("autotest_mode") || elem.endsWith("runpythontest");
+ }
+ );
+
+ if (is_automated_test)
{
// Nullroute all stdout to null for automated tests, this way we make sure
// that the test result output is not polluted with unrelated output data.
diff --git a/Code/Editor/CryEdit.h b/Code/Editor/CryEdit.h
index f68cfdc33d..53ee8f1905 100644
--- a/Code/Editor/CryEdit.h
+++ b/Code/Editor/CryEdit.h
@@ -431,6 +431,7 @@ public:
class CCrySingleDocTemplate
: public QObject
{
+ Q_OBJECT
private:
explicit CCrySingleDocTemplate(const QMetaObject* pDocClass)
: QObject()
diff --git a/Code/Editor/CryEditDoc.cpp b/Code/Editor/CryEditDoc.cpp
index 4944c3bff5..212606c737 100644
--- a/Code/Editor/CryEditDoc.cpp
+++ b/Code/Editor/CryEditDoc.cpp
@@ -60,15 +60,6 @@
#include
#include // for LmbrCentral::EditorLightComponentRequestBus
-//#define PROFILE_LOADING_WITH_VTUNE
-
-// profilers api.
-//#include "pure.h"
-#ifdef PROFILE_LOADING_WITH_VTUNE
-#include "C:\Program Files\Intel\Vtune\Analyzer\Include\VTuneApi.h"
-#pragma comment(lib,"C:\\Program Files\\Intel\\Vtune\\Analyzer\\Lib\\VTuneApi.lib")
-#endif
-
static const char* kAutoBackupFolder = "_autobackup";
static const char* kHoldFolder = "$tmp_hold"; // conform to the ignored file types $tmp[0-9]*_ regex
static const char* kSaveBackupFolder = "_savebackup";
@@ -408,9 +399,6 @@ void CCryEditDoc::Load(TDocMultiArchive& arrXmlAr, const QString& szFilename)
int t0 = GetTickCount();
-#ifdef PROFILE_LOADING_WITH_VTUNE
- VTResume();
-#endif
// Load level-specific audio data.
AZStd::string levelFileName{ fileName.toUtf8().constData() };
AZStd::to_lower(levelFileName.begin(), levelFileName.end());
@@ -484,10 +472,6 @@ void CCryEditDoc::Load(TDocMultiArchive& arrXmlAr, const QString& szFilename)
CSurfaceTypeValidator().Validate();
-#ifdef PROFILE_LOADING_WITH_VTUNE
- VTPause();
-#endif
-
LogLoadTime(GetTickCount() - t0);
// Loaded with success, remove event from log file
GetIEditor()->GetSettingsManager()->UnregisterEvent(loadEvent);
diff --git a/Code/Editor/CustomResolutionDlg.h b/Code/Editor/CustomResolutionDlg.h
index 5dd9acaae8..e1b8035c65 100644
--- a/Code/Editor/CustomResolutionDlg.h
+++ b/Code/Editor/CustomResolutionDlg.h
@@ -12,8 +12,6 @@
// Notice : Refer to ViewportTitleDlg.cpp for a use case.
-#ifndef CRYINCLUDE_EDITOR_CUSTOMRESOLUTIONDLG_H
-#define CRYINCLUDE_EDITOR_CUSTOMRESOLUTIONDLG_H
#pragma once
#if !defined(Q_MOC_RUN)
@@ -28,6 +26,7 @@ namespace Ui
class CCustomResolutionDlg
: public QDialog
{
+ Q_OBJECT
public:
CCustomResolutionDlg(int w, int h, QWidget* pParent = nullptr);
~CCustomResolutionDlg();
@@ -42,5 +41,3 @@ protected:
QScopedPointer m_ui;
};
-
-#endif // CRYINCLUDE_EDITOR_CUSTOMRESOLUTIONDLG_H
diff --git a/Code/Editor/CustomizeKeyboardDialog.cpp b/Code/Editor/CustomizeKeyboardDialog.cpp
index ce09f8d878..280d6c5323 100644
--- a/Code/Editor/CustomizeKeyboardDialog.cpp
+++ b/Code/Editor/CustomizeKeyboardDialog.cpp
@@ -211,9 +211,9 @@ public:
void Reset(QAction& action)
{
- emit beginResetModel();
+ beginResetModel();
m_action = &action;
- emit endResetModel();
+ endResetModel();
}
private:
@@ -266,7 +266,7 @@ QStringList CustomizeKeyboardDialog::BuildModels(QWidget* parent)
categories.append(category);
QMenu* menu = menuAction->menu();
- m_menuActions[category] = GetAllActionsForMenu(menu, QStringLiteral(""));
+ m_menuActions[category] = GetAllActionsForMenu(menu, QString());
}
return categories;
diff --git a/Code/Editor/ErrorDialog.cpp b/Code/Editor/ErrorDialog.cpp
index 0a45b2bf89..e43df75947 100644
--- a/Code/Editor/ErrorDialog.cpp
+++ b/Code/Editor/ErrorDialog.cpp
@@ -25,9 +25,9 @@ namespace SandboxEditor
connect(m_ui->okButton, &QPushButton::clicked, this, &ErrorDialog::OnOK);
connect(
m_ui->messages,
- SIGNAL(itemSelectionChanged()),
+ &QTreeWidget::itemSelectionChanged,
this,
- SLOT(MessageSelectionChanged()));
+ &ErrorDialog::MessageSelectionChanged);
}
ErrorDialog::~ErrorDialog()
diff --git a/Code/Editor/KeyboardCustomizationSettings.cpp b/Code/Editor/KeyboardCustomizationSettings.cpp
index c4f9133f66..81d9375850 100644
--- a/Code/Editor/KeyboardCustomizationSettings.cpp
+++ b/Code/Editor/KeyboardCustomizationSettings.cpp
@@ -240,7 +240,7 @@ QJsonObject KeyboardCustomizationSettings::ExportGroup()
void KeyboardCustomizationSettings::ImportFromFile(QWidget* parent)
{
- QString fileName = QFileDialog::getOpenFileName(parent, QObject::tr("Export Keyboard Shortcuts"), QStringLiteral(""), QObject::tr("Keyboard Settings (*.keys)"));
+ QString fileName = QFileDialog::getOpenFileName(parent, QObject::tr("Export Keyboard Shortcuts"), QString(), QObject::tr("Keyboard Settings (*.keys)"));
if (fileName.isEmpty())
{
return;
diff --git a/Code/Editor/Lib/Tests/test_ViewportManipulatorController.cpp b/Code/Editor/Lib/Tests/test_ViewportManipulatorController.cpp
index 8c7023634e..63e59ea940 100644
--- a/Code/Editor/Lib/Tests/test_ViewportManipulatorController.cpp
+++ b/Code/Editor/Lib/Tests/test_ViewportManipulatorController.cpp
@@ -85,6 +85,7 @@ namespace UnitTest
m_rootWidget = AZStd::make_unique();
m_rootWidget->setFixedSize(QSize(100, 100));
+ QApplication::setActiveWindow(m_rootWidget.get());
m_controllerList = AZStd::make_shared();
m_controllerList->RegisterViewportContext(TestViewportId);
@@ -100,6 +101,8 @@ namespace UnitTest
m_controllerList.reset();
m_rootWidget.reset();
+ QApplication::setActiveWindow(nullptr);
+
AllocatorsTestFixture::TearDown();
}
@@ -110,7 +113,7 @@ namespace UnitTest
const AzFramework::ViewportId ViewportManipulatorControllerFixture::TestViewportId = AzFramework::ViewportId(0);
- TEST_F(ViewportManipulatorControllerFixture, An_event_is_not_propagated_to_the_viewport_when_a_manipulator_handles_it_first)
+ TEST_F(ViewportManipulatorControllerFixture, AnEventIsNotPropagatedToTheViewportWhenAManipulatorHandlesItFirst)
{
// forward input events to our controller list
QObject::connect(
@@ -151,4 +154,77 @@ namespace UnitTest
editorInteractionViewportFake.Disconnect();
}
+
+ TEST_F(ViewportManipulatorControllerFixture, ChangingFocusDoesNotClearInput)
+ {
+ bool endedEvent = false;
+ // detect input events and ensure that the Alt key press does not end before the end of the test
+ QObject::connect(
+ m_inputChannelMapper.get(), &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, m_rootWidget.get(),
+ [&endedEvent](const AzFramework::InputChannel* inputChannel, [[maybe_unused]] QEvent* event)
+ {
+ if (inputChannel->GetInputChannelId() == AzFramework::InputDeviceKeyboard::Key::ModifierAltL &&
+ inputChannel->IsStateEnded())
+ {
+ endedEvent = true;
+ }
+ });
+
+ // given
+ auto* secondaryWidget = new QWidget(m_rootWidget.get());
+
+ m_rootWidget->show();
+ secondaryWidget->show();
+
+ m_rootWidget->setFocus();
+
+ // simulate a key press when root widget has focus
+ QTest::keyPress(m_rootWidget.get(), Qt::Key_Alt, Qt::KeyboardModifier::AltModifier);
+
+ // when
+ // change focus to secondary widget
+ secondaryWidget->setFocus();
+
+ // then
+ // the alt key was not released (cleared)
+ EXPECT_FALSE(endedEvent);
+ }
+
+ // note: Application State Change includes events such as switching to another application or minimizing
+ // the current application
+ TEST_F(ViewportManipulatorControllerFixture, ApplicationStateChangeDoesClearInput)
+ {
+ bool endedEvent = false;
+ // detect input events and ensure that the Alt key press does not end before the end of the test
+ QObject::connect(
+ m_inputChannelMapper.get(), &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, m_rootWidget.get(),
+ [&endedEvent](const AzFramework::InputChannel* inputChannel, [[maybe_unused]] QEvent* event)
+ {
+ if (inputChannel->GetInputChannelId() == AzFramework::InputDeviceKeyboard::Key::AlphanumericW &&
+ inputChannel->IsStateEnded())
+ {
+ endedEvent = true;
+ }
+ });
+
+ // given
+ auto* secondaryWidget = new QWidget(m_rootWidget.get());
+
+ m_rootWidget->show();
+ secondaryWidget->show();
+
+ m_rootWidget->setFocus();
+
+ // simulate a key press when root widget has focus
+ QTest::keyPress(m_rootWidget.get(), Qt::Key_W);
+
+ // when
+ // simulate changing the window state
+ QApplicationStateChangeEvent applicationStateChangeEvent(Qt::ApplicationState::ApplicationInactive);
+ QCoreApplication::sendEvent(m_rootWidget.get(), &applicationStateChangeEvent);
+
+ // then
+ // the key was released (cleared)
+ EXPECT_TRUE(endedEvent);
+ }
} // namespace UnitTest
diff --git a/Code/Editor/MainStatusBar.cpp b/Code/Editor/MainStatusBar.cpp
index f955035bd8..acc7f664df 100644
--- a/Code/Editor/MainStatusBar.cpp
+++ b/Code/Editor/MainStatusBar.cpp
@@ -419,7 +419,7 @@ void MemoryStatusItem::updateStatus()
GeneralStatusItem::GeneralStatusItem(QString name, MainStatusBar* parent)
: StatusBarItem(name, parent)
{
- connect(parent, SIGNAL(messageChanged(QString)), this, SLOT(update()));
+ connect(parent, &MainStatusBar::messageChanged, this, [this](const QString&) { update(); });
}
QString GeneralStatusItem::CurrentText() const
diff --git a/Code/Editor/NewLevelDialog.cpp b/Code/Editor/NewLevelDialog.cpp
index 29445cef2b..c773acdb6f 100644
--- a/Code/Editor/NewLevelDialog.cpp
+++ b/Code/Editor/NewLevelDialog.cpp
@@ -100,9 +100,10 @@ CNewLevelDialog::CNewLevelDialog(QWidget* pParent /*=nullptr*/)
m_level = "";
// First of all, keyboard focus is related to widget tab order, and the default tab order is based on the order in which
// widgets are constructed. Therefore, creating more widgets changes the keyboard focus. That is why setFocus() is called last.
- // Secondly, using singleShot() allows setFocus() slot of the QLineEdit instance to be invoked right after the event system
- // is ready to do so. Therefore, it is better to use singleShot() than directly call setFocus().
- QTimer::singleShot(0, ui->LEVEL, SLOT(OnStartup()));
+ // in OnStartup()
+ // Secondly, using singleShot() allows OnStartup() slot of the QLineEdit instance to be invoked right after the event system
+ // is ready to do so. Therefore, it is better to use singleShot() than directly call OnStartup().
+ QTimer::singleShot(0, this, &CNewLevelDialog::OnStartup);
ReloadLevelFolder();
}
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..82d9f9ede2 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
@@ -38,6 +38,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -642,6 +643,9 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
AzToolsFramework::EntityIdList selected;
GetSelectedOrHighlightedEntities(selected);
+ bool prefabSystemEnabled = false;
+ AzFramework::ApplicationRequests::Bus::BroadcastResult(prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
+
QAction* action = nullptr;
// when nothing is selected, entity is created at root level
@@ -658,18 +662,20 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
// when a single entity is selected, entity is created as its child
else if (selected.size() == 1)
{
- action = menu->addAction(QObject::tr("Create entity"));
- QObject::connect(
- action, &QAction::triggered, action,
- [selected]
- {
- EBUS_EVENT(AzToolsFramework::EditorRequests::Bus, CreateNewEntityAsChild, selected.front());
- });
+ auto containerEntityInterface = AZ::Interface::Get();
+ if (!prefabSystemEnabled || (containerEntityInterface && containerEntityInterface->IsContainerOpen(selected.front())))
+ {
+ action = menu->addAction(QObject::tr("Create entity"));
+ QObject::connect(
+ action, &QAction::triggered, action,
+ [selected]
+ {
+ AzToolsFramework::EditorRequestBus::Broadcast(&AzToolsFramework::EditorRequestBus::Handler::CreateNewEntityAsChild, selected.front());
+ }
+ );
+ }
}
- bool prefabSystemEnabled = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
-
if (!prefabSystemEnabled)
{
menu->addSeparator();
@@ -1788,6 +1794,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/AssetCatalogModel.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.cpp
index d0091f968e..df8d6db4a8 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.cpp
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.cpp
@@ -197,48 +197,46 @@ AssetCatalogModel::~AssetCatalogModel()
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
}
-AZ::Data::AssetType AssetCatalogModel::GetAssetType(QString filename) const
+AZ::Data::AssetType AssetCatalogModel::GetAssetType(const QString &filename) const
{
- AZ::Data::AssetType returnType = AZ::Uuid::CreateNull();
// Compare file extensions with the map created from the asset database.
int dotIndex = filename.lastIndexOf('.');
- if (dotIndex >= 0)
+ if (dotIndex < 0)
{
- QString extension = filename.mid(dotIndex);
- for (auto pair : m_extensionToAssetType)
- {
- QString qExtensions = pair.first.c_str();
- if (qExtensions.indexOf(extension) >= 0)
- {
- if (pair.second.size() > 1)
- {
- // There are multiple types with this extension. Check each handler to see if they can handle this data type.
- AZStd::string azFilename = filename.toStdString().c_str();
- EBUS_EVENT(AzFramework::ApplicationRequests::Bus, MakePathAssetRootRelative, azFilename);
- AZ::Data::AssetId assetId;
- EBUS_EVENT_RESULT(assetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, azFilename.c_str(), AZ::Data::s_invalidAssetType, false);
+ return AZ::Uuid::CreateNull();
+ }
- for (AZ::Uuid type : pair.second)
- {
- const AZ::Data::AssetHandler* handler = AZ::Data::AssetManager::Instance().GetHandler(type);
- if (handler && handler->CanHandleAsset(assetId))
- {
- returnType = type;
- break;
- }
- }
- }
- else
- {
- returnType = pair.second[0];
- break;
- }
+ QStringRef extension = filename.midRef(dotIndex);
+ for (const auto& pair : m_extensionToAssetType)
+ {
+ QString qExtensions = pair.first.c_str();
+ if (qExtensions.indexOf(extension) < 0 || pair.second.empty())
+ {
+ continue;
+ }
+ if (pair.second.size() == 1)
+ {
+ return pair.second[0];
+ }
+
+ // There are multiple types with this extension. Search for a handler that can handle this data type.
+ AZStd::string azFilename = filename.toStdString().c_str();
+ EBUS_EVENT(AzFramework::ApplicationRequests::Bus, MakePathAssetRootRelative, azFilename);
+ AZ::Data::AssetId assetId;
+ EBUS_EVENT_RESULT(assetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, azFilename.c_str(), AZ::Data::s_invalidAssetType, false);
+
+ for (const AZ::Uuid& type : pair.second)
+ {
+ const AZ::Data::AssetHandler* handler = AZ::Data::AssetManager::Instance().GetHandler(type);
+ if (handler && handler->CanHandleAsset(assetId))
+ {
+ return type;
}
}
}
- return returnType;
+ return AZ::Uuid::CreateNull();
}
QStandardItem* AssetCatalogModel::GetPath(QString& path, bool createIfNeeded, QStandardItem* parent)
@@ -419,7 +417,7 @@ AssetCatalogEntry* AssetCatalogModel::AddAsset(QString assetPath, AZ::Data::Asse
// icons' memory being reclaimed and crashing the Editor.
QSize size = fileIcon.actualSize(QSize(16, 16));
QIcon deepCopy = fileIcon.pixmap(size).copy(0, 0, size.width(), size.height());
-
+
if (!fileIcon.isNull())
{
m_assetTypeToIcon[assetType] = deepCopy;
diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.h b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.h
index c9143bb259..1dffa81d67 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.h
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.h
@@ -110,7 +110,7 @@ protected:
void SetFilterRegExp(const AZStd::string& filterType, const QRegExp& regExp);
void ClearFilterRegExp(const AZStd::string& filterType = AZStd::string());
- AZ::Data::AssetType GetAssetType(QString filename) const;
+ AZ::Data::AssetType GetAssetType(const QString &filename) const;
QStandardItem* GetPath(QString& path, bool createIfNeeded, QStandardItem* parent = nullptr);
void ApplyFilter(QStandardItem* parent);
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/Plugins/ComponentEntityEditorPlugin/UI/Outliner/EntityOutliner.qss b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/EntityOutliner.qss
index de35f91678..a7d3949527 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/EntityOutliner.qss
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/EntityOutliner.qss
@@ -11,7 +11,6 @@ OutlinerWidget #m_display_options
{
qproperty-icon: url(:/Menu/menu.svg);
qproperty-iconSize: 16px 16px;
- qproperty-flat: true;
}
OutlinerWidget QWidget[PulseHighlight="true"]
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..8e15871bc0
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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, bool autoRegisterEvent)
+ {
+ using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+ using Type = AZ::SettingsRegistryInterface::Type;
+ using NotifyEventHandler = AZ::SettingsRegistryInterface::NotifyEventHandler;
+
+ // Some systems may attempt to register a handler before the settings registry has been loaded
+ // If so, this flag lets them automatically register an event if it hasn't yet been registered.
+ // RegisterEvent calls validate event.
+ if ((!autoRegisterEvent && !ValidateEvent(settingsRegistry, eventName)) ||
+ (autoRegisterEvent && !RegisterEvent(settingsRegistry, eventName)))
+ {
+ AZ_Warning(
+ "ComponentApplicationLifecycle", false,
+ R"(Cannot register event %.*s. Name 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..6f07c0da3f
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace AZ::ComponentApplicationLifecycle
+{
+ //! Root Key where lifecycle events should be registered under
+ inline constexpr AZStd::string_view ApplicationLifecycleEventRegistrationKey = "/O3DE/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
+ //! @param autoRegisterEvent automatically register this event if it hasn't been registered yet. This is useful
+ //! when registering a handler before the settings registry has been loaded.
+ //! @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, bool autoRegisterEvent = false);
+}
diff --git a/Code/Framework/AzCore/AzCore/Component/TransformBus.h b/Code/Framework/AzCore/AzCore/Component/TransformBus.h
index 1af77da51b..f8e30147da 100644
--- a/Code/Framework/AzCore/AzCore/Component/TransformBus.h
+++ b/Code/Framework/AzCore/AzCore/Component/TransformBus.h
@@ -168,6 +168,11 @@ namespace AZ
//! Rotation modifiers
//! @{
+ //! Set the world rotation matrix using the composition of rotations around
+ //! the principle axes in the order of z-axis first and y-axis and then x-axis.
+ //! @param eulerRadianAngles A Vector3 denoting radian angles of the rotations around each principle axis.
+ virtual void SetWorldRotation([[maybe_unused]] const AZ::Vector3& eulerAnglesRadian) {}
+
//! Sets the entity's rotation in the world in quaternion notation.
//! The origin of the axes is the entity's position in world space.
//! @param quaternion A quaternion that represents the rotation to use for the entity.
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/Debug/Profiler.cpp b/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp
index 0bb92b923d..0cb77b2d6e 100644
--- a/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp
+++ b/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp
@@ -7,4 +7,63 @@
*/
#include
+#include
+#include
+#include
+#include
+namespace AZ::Debug
+{
+ AZStd::string GenerateOutputFile(const char* nameHint)
+ {
+ AZ::IO::FixedMaxPathString captureOutput = GetProfilerCaptureLocation();
+ return AZStd::string::format("%s/capture_%s_%lld.json", captureOutput.c_str(), nameHint, AZStd::GetTimeNowSecond());
+ }
+
+ void ProfilerCaptureFrame([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
+ {
+ if (auto profilerSystem = ProfilerSystemInterface::Get(); profilerSystem)
+ {
+ AZStd::string captureFile = GenerateOutputFile("single");
+ AZLOG_INFO("Setting capture file to %s", captureFile.c_str());
+ profilerSystem->CaptureFrame(captureFile);
+ }
+ }
+ AZ_CONSOLEFREEFUNC(ProfilerCaptureFrame, AZ::ConsoleFunctorFlags::DontReplicate, "Capture a single frame of profiling data");
+
+ void ProfilerStartCapture([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
+ {
+ if (auto profilerSystem = ProfilerSystemInterface::Get(); profilerSystem)
+ {
+ AZStd::string captureFile = GenerateOutputFile("multi");
+ AZLOG_INFO("Setting capture file to %s", captureFile.c_str());
+ profilerSystem->StartCapture(AZStd::move(captureFile));
+ }
+ }
+ AZ_CONSOLEFREEFUNC(ProfilerStartCapture, AZ::ConsoleFunctorFlags::DontReplicate, "Start a multi-frame capture of profiling data");
+
+ void ProfilerEndCapture([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
+ {
+ if (auto profilerSystem = ProfilerSystemInterface::Get(); profilerSystem)
+ {
+ profilerSystem->EndCapture();
+ }
+ }
+ AZ_CONSOLEFREEFUNC(ProfilerEndCapture, AZ::ConsoleFunctorFlags::DontReplicate, "End and dump an in-progress continuous capture");
+
+ AZ::IO::FixedMaxPathString GetProfilerCaptureLocation()
+ {
+ AZ::IO::FixedMaxPathString captureOutput;
+ if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry)
+ {
+ settingsRegistry->Get(captureOutput, RegistryKey_ProfilerCaptureLocation);
+ }
+
+ if (captureOutput.empty())
+ {
+ captureOutput = ProfilerCaptureLocationFallback;
+ }
+
+ return captureOutput;
+ }
+} // namespace AZ::Debug
diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h b/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h
index 1537be3b81..322bfb60c2 100644
--- a/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h
+++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h
@@ -9,11 +9,20 @@
#pragma once
#include
+#include
+#include
+#include
namespace AZ
{
namespace Debug
{
+ //! settings registry entry for specifying where to output profiler captures
+ static constexpr const char* RegistryKey_ProfilerCaptureLocation = "/O3DE/AzCore/Debug/Profiler/CaptureLocation";
+
+ //! fallback value in the event the settings registry isn't ready or doesn't contain the key
+ static constexpr const char* ProfilerCaptureLocationFallback = "@user@/Profiler";
+
/**
* ProfilerNotifications provides a profiler event interface that can be used to update listeners on profiler status
*/
@@ -23,32 +32,38 @@ namespace AZ
public:
virtual ~ProfilerNotifications() = default;
- virtual void OnProfileSystemInitialized() = 0;
+ //! Notify when the current profiler capture is finished
+ //! @param result Set to true if it's finished successfully
+ //! @param info The output file path or error information which depends on the return.
+ virtual void OnCaptureFinished(bool result, const AZStd::string& info) = 0;
};
using ProfilerNotificationBus = AZ::EBus;
- enum class ProfileFrameAdvanceType
- {
- Game,
- Render,
- Default = Game
- };
-
/**
* ProfilerRequests provides an interface for making profiling system requests
*/
class ProfilerRequests
- : public AZ::EBusTraits
{
public:
- // Allow multiple threads to concurrently make requests
- using MutexType = AZStd::mutex;
-
+ AZ_RTTI(ProfilerRequests, "{90AEC117-14C1-4BAE-9704-F916E49EF13F}");
virtual ~ProfilerRequests() = default;
- virtual bool IsActive() = 0;
- virtual void FrameAdvance(ProfileFrameAdvanceType type) = 0;
+ //! Getter/setter for the profiler active state
+ virtual bool IsActive() const = 0;
+ virtual void SetActive(bool active) = 0;
+
+ //! Capture a single frame of profiling data
+ virtual bool CaptureFrame(const AZStd::string& outputFilePath) = 0;
+
+ //! Starting/ending a multi-frame capture of profiling data
+ virtual bool StartCapture(AZStd::string outputFilePath) = 0;
+ virtual bool EndCapture() = 0;
};
- using ProfilerRequestBus = AZ::EBus;
- }
-}
+
+ using ProfilerSystemInterface = AZ::Interface;
+
+ //! helper function for getting the profiler capture location from the settings registry that
+ //! includes fallback handing in the event the registry value can't be determined
+ AZ::IO::FixedMaxPathString GetProfilerCaptureLocation();
+ } // namespace Debug
+} // namespace AZ
diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp
new file mode 100644
index 0000000000..42151a6996
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+
+namespace AZ::Debug
+{
+ static constexpr const char* ProfilerScriptCategory = "Profiler";
+ static constexpr const char* ProfilerScriptModule = "debug";
+ static constexpr AZ::Script::Attributes::ScopeFlags ProfilerScriptScope = AZ::Script::Attributes::ScopeFlags::Automation;
+
+ class ProfilerNotificationBusHandler final
+ : public ProfilerNotificationBus::Handler
+ , public AZ::BehaviorEBusHandler
+ {
+ public:
+ AZ_EBUS_BEHAVIOR_BINDER(ProfilerNotificationBusHandler, "{44161459-B816-4876-95A4-BA16DEC767D6}", AZ::SystemAllocator,
+ OnCaptureFinished
+ );
+
+ void OnCaptureFinished(bool result, const AZStd::string& info) override
+ {
+ Call(FN_OnCaptureFinished, result, info);
+ }
+
+ static void Reflect(AZ::ReflectContext* context)
+ {
+ if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context))
+ {
+ behaviorContext->EBus("ProfilerNotificationBus")
+ ->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory)
+ ->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule)
+ ->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope)
+ ->Handler();
+ }
+ }
+ };
+
+ class ProfilerSystemScriptProxy
+ : public BehaviorInterfaceProxy
+ {
+ public:
+ AZ_RTTI(ProfilerSystemScriptProxy, "{D671FB70-8B09-4C3A-96CD-06A339F3138E}", BehaviorInterfaceProxy);
+
+ AZ_BEHAVIOR_INTERFACE(ProfilerSystemScriptProxy, ProfilerRequests);
+ };
+
+ void ProfilerReflect(AZ::ReflectContext* context)
+ {
+ if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context))
+ {
+ behaviorContext->ConstantProperty("g_ProfilerSystem", ProfilerSystemScriptProxy::GetProxy)
+ ->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory)
+ ->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule)
+ ->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope);
+
+ behaviorContext->Class("ProfilerSystemInterface")
+ ->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory)
+ ->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule)
+ ->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope)
+
+ ->Method("IsValid", &ProfilerSystemScriptProxy::IsValid)
+
+ ->Method("GetCaptureLocation",
+ [](ProfilerSystemScriptProxy*) -> AZStd::string
+ {
+ AZ::IO::FixedMaxPathString captureOutput = GetProfilerCaptureLocation();
+ return AZStd::string(captureOutput.c_str(), captureOutput.length());
+ })
+
+ ->Method("IsActive", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::IsActive>())
+ ->Method("SetActive", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::SetActive>())
+
+ ->Method("CaptureFrame", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::CaptureFrame>())
+
+ ->Method("StartCapture", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::StartCapture>())
+ ->Method("EndCapture", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::EndCapture>());
+ }
+
+ ProfilerNotificationBusHandler::Reflect(context);
+ }
+} // namespace AZ::Debug
diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.h b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.h
new file mode 100644
index 0000000000..d6857defe5
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+namespace AZ
+{
+ class ReflectContext;
+
+ namespace Debug
+ {
+ //! Reflects the profiler bus script bindings
+ void ProfilerReflect(AZ::ReflectContext* context);
+ } // namespace Debug
+} // namespace AZ
diff --git a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp
index 357149d096..b9e4003500 100644
--- a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp
+++ b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp
@@ -27,26 +27,21 @@
#include
#include
-namespace AZ
+namespace AZ::Debug
{
- namespace Debug
+ struct StackFrame;
+
+ namespace Platform
{
- struct StackFrame;
-
- namespace Platform
- {
#if defined(AZ_ENABLE_DEBUG_TOOLS)
- bool AttachDebugger();
- bool IsDebuggerPresent();
- void HandleExceptions(bool isEnabled);
- void DebugBreak();
+ bool AttachDebugger();
+ bool IsDebuggerPresent();
+ void HandleExceptions(bool isEnabled);
+ void DebugBreak();
#endif
- void Terminate(int exitCode);
- }
+ void Terminate(int exitCode);
}
- using namespace AZ::Debug;
-
namespace DebugInternal
{
// other threads can trigger fatals and errors, but the same thread should not, to avoid stack overflow.
@@ -60,7 +55,7 @@ namespace AZ
// Globals
const int g_maxMessageLength = 4096;
static const char* g_dbgSystemWnd = "System";
- Trace Debug::g_tracer;
+ Trace g_tracer;
void* g_exceptionInfo = nullptr;
// Environment var needed to track ignored asserts across systems and disable native UI under certain conditions
@@ -616,4 +611,4 @@ namespace AZ
val.Set(level);
}
}
-} // namspace AZ
+} // namspace AZ::Debug
diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp
index 9728866fb6..9a465021c2 100644
--- a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp
+++ b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp
@@ -156,7 +156,10 @@ namespace AZ
void StreamerComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
bool isEnabled = false;
- AZ::Debug::ProfilerRequestBus::BroadcastResult(isEnabled, &AZ::Debug::ProfilerRequests::IsActive);
+ if (auto profilerSystem = AZ::Debug::ProfilerSystemInterface::Get(); profilerSystem)
+ {
+ isEnabled = profilerSystem->IsActive();
+ }
if (isEnabled)
{
diff --git a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathCommon_sse.inl b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathCommon_sse.inl
index e228a19d68..8355f9bfe0 100644
--- a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathCommon_sse.inl
+++ b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathCommon_sse.inl
@@ -383,36 +383,36 @@ namespace AZ
AZ_MATH_INLINE bool CmpAllEq(__m128 arg1, __m128 arg2, int32_t mask)
{
- const __m128i compare = CastToInt(CmpNeq(arg1, arg2));
- return (_mm_movemask_epi8(compare) & mask) == 0;
+ const __m128 compare = CmpEq(arg1, arg2);
+ return (_mm_movemask_ps(compare) & mask) == mask;
}
AZ_MATH_INLINE bool CmpAllLt(__m128 arg1, __m128 arg2, int32_t mask)
{
- const __m128i compare = CastToInt(CmpGtEq(arg1, arg2));
- return (_mm_movemask_epi8(compare) & mask) == 0;
+ const __m128 compare = CmpLt(arg1, arg2);
+ return (_mm_movemask_ps(compare) & mask) == mask;
}
AZ_MATH_INLINE bool CmpAllLtEq(__m128 arg1, __m128 arg2, int32_t mask)
{
- const __m128i compare = CastToInt(CmpGt(arg1, arg2));
- return (_mm_movemask_epi8(compare) & mask) == 0;
+ const __m128 compare = CmpLtEq(arg1, arg2);
+ return (_mm_movemask_ps(compare) & mask) == mask;
}
AZ_MATH_INLINE bool CmpAllGt(__m128 arg1, __m128 arg2, int32_t mask)
{
- const __m128i compare = CastToInt(CmpLtEq(arg1, arg2));
- return (_mm_movemask_epi8(compare) & mask) == 0;
+ const __m128 compare = CmpGt(arg1, arg2);
+ return (_mm_movemask_ps(compare) & mask) == mask;
}
AZ_MATH_INLINE bool CmpAllGtEq(__m128 arg1, __m128 arg2, int32_t mask)
{
- const __m128i compare = CastToInt(CmpLt(arg1, arg2));
- return (_mm_movemask_epi8(compare) & mask) == 0;
+ const __m128 compare = CmpGtEq(arg1, arg2);
+ return (_mm_movemask_ps(compare) & mask) == mask;
}
diff --git a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec1_sse.inl b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec1_sse.inl
index ecdcf40743..bb332c03a2 100644
--- a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec1_sse.inl
+++ b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec1_sse.inl
@@ -331,31 +331,32 @@ namespace AZ
AZ_MATH_INLINE bool Vec1::CmpAllEq(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllEq(arg1, arg2, 0x000F);
+ // Only check the first bit for Vector1
+ return Sse::CmpAllEq(arg1, arg2, 0b0001);
}
AZ_MATH_INLINE bool Vec1::CmpAllLt(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllLt(arg1, arg2, 0x000F);
+ return Sse::CmpAllLt(arg1, arg2, 0b0001);
}
AZ_MATH_INLINE bool Vec1::CmpAllLtEq(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllLtEq(arg1, arg2, 0x000F);
+ return Sse::CmpAllLtEq(arg1, arg2, 0b0001);
}
AZ_MATH_INLINE bool Vec1::CmpAllGt(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllGt(arg1, arg2, 0x000F);
+ return Sse::CmpAllGt(arg1, arg2, 0b0001);
}
AZ_MATH_INLINE bool Vec1::CmpAllGtEq(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllGtEq(arg1, arg2, 0x000F);
+ return Sse::CmpAllGtEq(arg1, arg2, 0b0001);
}
@@ -397,7 +398,7 @@ namespace AZ
AZ_MATH_INLINE bool Vec1::CmpAllEq(Int32ArgType arg1, Int32ArgType arg2)
{
- return Sse::CmpAllEq(arg1, arg2, 0x000F);
+ return Sse::CmpAllEq(arg1, arg2, 0b0001);
}
diff --git a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec2_sse.inl b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec2_sse.inl
index c890aa5eb7..90fb97e694 100644
--- a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec2_sse.inl
+++ b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec2_sse.inl
@@ -383,31 +383,32 @@ namespace AZ
AZ_MATH_INLINE bool Vec2::CmpAllEq(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllEq(arg1, arg2, 0x00FF);
+ // Only check the first two bits for Vector2
+ return Sse::CmpAllEq(arg1, arg2, 0b0011);
}
AZ_MATH_INLINE bool Vec2::CmpAllLt(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllLt(arg1, arg2, 0x00FF);
+ return Sse::CmpAllLt(arg1, arg2, 0b0011);
}
AZ_MATH_INLINE bool Vec2::CmpAllLtEq(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllLtEq(arg1, arg2, 0x00FF);
+ return Sse::CmpAllLtEq(arg1, arg2, 0b0011);
}
AZ_MATH_INLINE bool Vec2::CmpAllGt(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllGt(arg1, arg2, 0x00FF);
+ return Sse::CmpAllGt(arg1, arg2, 0b0011);
}
AZ_MATH_INLINE bool Vec2::CmpAllGtEq(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllGtEq(arg1, arg2, 0x00FF);
+ return Sse::CmpAllGtEq(arg1, arg2, 0b0011);
}
diff --git a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec3_sse.inl b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec3_sse.inl
index a8ff962837..31e8d19d65 100644
--- a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec3_sse.inl
+++ b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec3_sse.inl
@@ -419,31 +419,32 @@ namespace AZ
AZ_MATH_INLINE bool Vec3::CmpAllEq(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllEq(arg1, arg2, 0x0FFF);
+ // Only check the first three bits for Vector3
+ return Sse::CmpAllEq(arg1, arg2, 0b0111);
}
AZ_MATH_INLINE bool Vec3::CmpAllLt(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllLt(arg1, arg2, 0x0FFF);
+ return Sse::CmpAllLt(arg1, arg2, 0b0111);
}
AZ_MATH_INLINE bool Vec3::CmpAllLtEq(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllLtEq(arg1, arg2, 0x0FFF);
+ return Sse::CmpAllLtEq(arg1, arg2, 0b0111);
}
AZ_MATH_INLINE bool Vec3::CmpAllGt(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllGt(arg1, arg2, 0x0FFF);
+ return Sse::CmpAllGt(arg1, arg2, 0b0111);
}
AZ_MATH_INLINE bool Vec3::CmpAllGtEq(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllGtEq(arg1, arg2, 0x0FFF);
+ return Sse::CmpAllGtEq(arg1, arg2, 0b0111);
}
@@ -485,7 +486,7 @@ namespace AZ
AZ_MATH_INLINE bool Vec3::CmpAllEq(Int32ArgType arg1, Int32ArgType arg2)
{
- return Sse::CmpAllEq(arg1, arg2, 0x0FFF);
+ return Sse::CmpAllEq(arg1, arg2, 0b0111);
}
diff --git a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec4_sse.inl b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec4_sse.inl
index 1cf0a35d7f..f3a4feb524 100644
--- a/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec4_sse.inl
+++ b/Code/Framework/AzCore/AzCore/Math/Internal/SimdMathVec4_sse.inl
@@ -455,31 +455,32 @@ namespace AZ
AZ_MATH_INLINE bool Vec4::CmpAllEq(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllEq(arg1, arg2, 0xFFFF);
+ // Check the first four bits for Vector4
+ return Sse::CmpAllEq(arg1, arg2, 0b1111);
}
AZ_MATH_INLINE bool Vec4::CmpAllLt(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllLt(arg1, arg2, 0xFFFF);
+ return Sse::CmpAllLt(arg1, arg2, 0b1111);
}
AZ_MATH_INLINE bool Vec4::CmpAllLtEq(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllLtEq(arg1, arg2, 0xFFFF);
+ return Sse::CmpAllLtEq(arg1, arg2, 0b1111);
}
AZ_MATH_INLINE bool Vec4::CmpAllGt(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllGt(arg1, arg2, 0xFFFF);
+ return Sse::CmpAllGt(arg1, arg2, 0b1111);
}
AZ_MATH_INLINE bool Vec4::CmpAllGtEq(FloatArgType arg1, FloatArgType arg2)
{
- return Sse::CmpAllGtEq(arg1, arg2, 0xFFFF);
+ return Sse::CmpAllGtEq(arg1, arg2, 0b1111);
}
@@ -521,7 +522,7 @@ namespace AZ
AZ_MATH_INLINE bool Vec4::CmpAllEq(Int32ArgType arg1, Int32ArgType arg2)
{
- return Sse::CmpAllEq(arg1, arg2, 0xFFFF);
+ return Sse::CmpAllEq(arg1, arg2, 0b1111);
}
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/RTTI/BehaviorInterfaceProxy.h b/Code/Framework/AzCore/AzCore/RTTI/BehaviorInterfaceProxy.h
new file mode 100644
index 0000000000..0e7a4ac356
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/RTTI/BehaviorInterfaceProxy.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+namespace AZ
+{
+ /**
+ * Utility class for reflecting an AZ::Interface through the BehaviorContext
+ *
+ * Example:
+ *
+ * class MyInterface
+ * {
+ * public:
+ * AZ_RTTI(MyInterface, "{BADDF000D-CDCD-CDCD-CDCD-BAAAADF0000D}");
+ * virtual ~MyInterface() = default;
+ *
+ * virtual AZStd::string Foo() = 0;
+ * virtual void Bar(float x, float y) = 0;
+ * };
+ *
+ * class MySystemProxy
+ * : public BehaviorInterfaceProxy
+ * {
+ * public:
+ * AZ_RTTI(MySystemProxy, "{CDCDCDCD-BAAD-BADD-F00D-CDCDCDCDCDCD}", BehaviorInterfaceProxy);
+ * AZ_BEHAVIOR_INTERFACE(MySystemProxy, MyInterface);
+ * };
+ *
+ * void Reflect(AZ::ReflectContext* context)
+ * {
+ * if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context))
+ * {
+ * behaviorContext->ConstantProperty("g_MySystem", MySystemProxy::GetProxy)
+ * ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
+ * ->Attribute(AZ::Script::Attributes::Module, "MyModule");
+ *
+ * behaviorContext->Class("MySystemInterface")
+ * ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
+ * ->Attribute(AZ::Script::Attributes::Module, "MyModule")
+ *
+ * ->Method("Foo", MySystemProxy::WrapMethod<&MyInterface::Foo>())
+ * ->Method("Bar", MySystemProxy::WrapMethod<&MyInterface::Bar>());
+ * }
+ * }
+ */
+ template
+ class BehaviorInterfaceProxy
+ {
+ public:
+ AZ_CLASS_ALLOCATOR(BehaviorInterfaceProxy, AZ::SystemAllocator, 0);
+ AZ_RTTI(BehaviorInterfaceProxy, "{E7CC8D27-4499-454E-A7DF-3F72FBECD30D}");
+
+ BehaviorInterfaceProxy() = default;
+ virtual ~BehaviorInterfaceProxy() = default;
+
+ //! Stores the instance which will use the provided shared_ptr deleter when the reference count hits zero
+ BehaviorInterfaceProxy(AZStd::shared_ptr sharedInstance)
+ : m_instance(AZStd::move(sharedInstance))
+ {
+ }
+
+ //! Stores the instance which will perform a no-op deleter when the reference count hits zero
+ BehaviorInterfaceProxy(T* rawIntance)
+ : m_instance(rawIntance, [](T*) {})
+ {
+ }
+
+ //! Returns if the m_instance shared pointer is non-nullptr
+ bool IsValid() const { return m_instance; }
+
+ protected:
+ //! Internal access for use in the derived GetProxy function
+ static T* GetInstance()
+ {
+ T* interfacePtr = AZ::Interface::Get();
+ AZ_Warning("BehaviorInterfaceProxy", interfacePtr,
+ "There is currently no global %s registered with an AZ Interface",
+ AzTypeInfo::Name()
+ );
+ // Don't delete the global instance, it is not owned by the behavior context
+ return interfacePtr;
+ }
+
+ template
+ struct MethodWrapper
+ {
+ template
+ static auto WrapMethod()
+ {
+ using ReturnType = AZStd::function_traits_get_result_t>;
+ return [](Proxy* proxy, Args... params) -> ReturnType
+ {
+ if (proxy && proxy->IsValid())
+ {
+ return AZStd::invoke(Method, proxy->m_instance, AZStd::forward(params)...);
+ }
+ return ReturnType();
+ };
+ }
+ };
+
+ AZStd::shared_ptr m_instance;
+ };
+
+ #define AZ_BEHAVIOR_INTERFACE(ProxyType, InterfaceType) \
+ static ProxyType GetProxy() { return GetInstance(); } \
+ template \
+ static auto WrapMethod() { \
+ using FuncTraits = AZStd::function_traits>; \
+ return FuncTraits::template expand_args::template WrapMethod(); \
+ } \
+ ProxyType() = default; \
+ ProxyType(AZStd::shared_ptr sharedInstance) : BehaviorInterfaceProxy(sharedInstance) {} \
+ ProxyType(InterfaceType* rawIntance) : BehaviorInterfaceProxy(rawIntance) {}
+} // namespace AZ
diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp
index a83232c8cb..3b2aebdee6 100644
--- a/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp
+++ b/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -87,6 +88,8 @@ void ScriptSystemComponent::Activate()
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::AddExtension, "lua");
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::AddExtension, "luac");
+ AZ::Data::AssetCatalogRequestBus::Broadcast(
+ &AZ::Data::AssetCatalogRequests::EnableCatalogForAsset, AZ::AzTypeInfo::Uuid());
if (Data::AssetManager::Instance().IsReady())
{
@@ -925,6 +928,7 @@ void ScriptSystemComponent::Reflect(ReflectContext* reflection)
// reflect default entity
MathReflect(behaviorContext);
ScriptDebug::Reflect(behaviorContext);
+ Debug::ProfilerReflect(behaviorContext);
Debug::TraceReflect(behaviorContext);
behaviorContext->Class("Platform")
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/Task/TaskExecutor.cpp b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp
index 4097348798..08cfb11d34 100644
--- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp
+++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp
@@ -363,7 +363,11 @@ namespace AZ
{
++m_graphsRemaining;
- event->m_executor = this; // Used to validate event is not waited for inside a job
+ if (event)
+ {
+ event->IncWaitCount();
+ event->m_executor = this; // Used to validate event is not waited for inside a job
+ }
// Submit all tasks that have no inbound edges
for (Internal::Task& task : graph.Tasks())
diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp b/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp
index f57b06890a..eeb46d6887 100644
--- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp
+++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp
@@ -20,6 +20,46 @@ namespace AZ
m_semaphore.acquire();
}
+ void TaskGraphEvent::IncWaitCount()
+ {
+ // guess zero to optimize for single task graph using an event, if multiple are using it then this will take 2+ comp_exch calls
+ int expectedValue = 0;
+ while(!m_waitCount.compare_exchange_weak(expectedValue, expectedValue + 1))
+ {
+ // value will be negative once event is ready to signal or has been signaled. Shouldn't happen.
+ AZ_Assert(expectedValue >= 0, "Called TaskGraphEvent::IncWaitCount on a signalled event");
+ if (expectedValue < 0) // event already signaled, skip
+ {
+ return;
+ }
+ };
+ }
+
+ void TaskGraphEvent::Signal()
+ {
+ // guess one to optimize for single task graph using an event, if multiple are using it then this will take 2+ comp_exch calls
+ int expectedValue = 1;
+ while(!m_waitCount.compare_exchange_weak(expectedValue, expectedValue - 1))
+ {
+ // It's an error for Signal to be called if no one is waiting, or the event has already been signaled
+ AZ_Assert(expectedValue > 0, "Called TaskGraphEvent::Signal when event is either signaled or unused");
+ if (expectedValue < 0) // return if already signaled
+ {
+ return;
+ }
+ };
+
+ if (expectedValue == 1) // This call to Signal decremented the value to 0.
+ {
+ expectedValue = 0;
+ // validate no one incremented the wait count and mark signalling state
+ if (m_waitCount.compare_exchange_strong(expectedValue, -1))
+ {
+ m_semaphore.release();
+ }
+ }
+ }
+
void TaskToken::PrecedesInternal(TaskToken& comesAfter)
{
AZ_Assert(!m_parent.m_submitted, "Cannot mutate a TaskGraph that was previously submitted.");
diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.h b/Code/Framework/AzCore/AzCore/Task/TaskGraph.h
index 9553013a4b..fa6f5dbe94 100644
--- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.h
+++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.h
@@ -61,14 +61,14 @@ namespace AZ
uint32_t m_index;
};
- // A TaskGraphEvent may be used to block until a task graph has finished executing. Usage
+ // A TaskGraphEvent may be used to block until one or more task graphs has finished executing. Usage
// is NOT recommended for the majority of tasks (prefer to simply containing expanding/contracting
// the graph without synchronization over the course of the frame). However, the event
// is useful for the edges of the computation graph.
//
// You are responsible for ensuring the event object lifetime exceeds the task graph lifetime.
//
- // After the TaskGraphEvent is signaled, you are allowed to reuse the same TaskGraphEvent
+ // After the TaskGraphEvent is signaled, you are NOT allowed to reuse the same TaskGraphEvent
// for a future submission.
class TaskGraphEvent
{
@@ -81,10 +81,12 @@ namespace AZ
friend class TaskGraph;
friend class TaskExecutor;
+ void IncWaitCount();
void Signal();
AZStd::binary_semaphore m_semaphore;
- TaskExecutor* m_executor = nullptr;
+ AZStd::atomic_int m_waitCount = 0;
+ TaskExecutor* m_executor = nullptr;
};
// The TaskGraph encapsulates a set of tasks and their interdependencies. After adding
diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl b/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl
index 7b2f0cefdc..9a5289eb82 100644
--- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl
+++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl
@@ -33,11 +33,6 @@ namespace AZ
return m_semaphore.try_acquire_for(AZStd::chrono::milliseconds{ 0 });
}
- inline void TaskGraphEvent::Signal()
- {
- m_semaphore.release();
- }
-
template
TaskToken TaskGraph::AddTask(TaskDescriptor const& desc, Lambda&& lambda)
{
diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake
index 41229429f2..a5cc3fdcd4 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
@@ -106,6 +108,8 @@ set(FILES
Debug/Profiler.inl
Debug/Profiler.h
Debug/ProfilerBus.h
+ Debug/ProfilerReflection.cpp
+ Debug/ProfilerReflection.h
Debug/StackTracer.h
Debug/EventTrace.h
Debug/EventTrace.cpp
@@ -121,6 +125,8 @@ set(FILES
Debug/TraceMessagesDrillerBus.h
Debug/TraceReflection.cpp
Debug/TraceReflection.h
+ DOM/DomVisitor.cpp
+ DOM/DomVisitor.h
Driller/DefaultStringPool.h
Driller/Driller.cpp
Driller/Driller.h
@@ -452,6 +458,7 @@ set(FILES
RTTI/BehaviorContext.h
RTTI/BehaviorContextUtilities.h
RTTI/BehaviorContextUtilities.cpp
+ RTTI/BehaviorInterfaceProxy.h
RTTI/BehaviorObjectSignals.h
RTTI/TypeSafeIntegral.h
Script/ScriptAsset.cpp
diff --git a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Debug/Trace_WinAPI.cpp b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Debug/Trace_WinAPI.cpp
index 28d3459ba3..4535387902 100644
--- a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Debug/Trace_WinAPI.cpp
+++ b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Debug/Trace_WinAPI.cpp
@@ -17,7 +17,7 @@
#include
-namespace AZ
+namespace AZ::Debug
{
#if defined(AZ_ENABLE_DEBUG_TOOLS)
LONG WINAPI ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo);
@@ -26,94 +26,91 @@ namespace AZ
constexpr int g_maxMessageLength = 4096;
- namespace Debug
+ namespace Platform
{
- namespace Platform
- {
#if defined(AZ_ENABLE_DEBUG_TOOLS)
- bool IsDebuggerPresent()
+ bool IsDebuggerPresent()
+ {
+ return ::IsDebuggerPresent() ? true : false;
+ }
+
+ void HandleExceptions(bool isEnabled)
+ {
+ if (isEnabled)
{
- return ::IsDebuggerPresent() ? true : false;
+ g_previousExceptionHandler = ::SetUnhandledExceptionFilter(&ExceptionHandler);
}
-
- void HandleExceptions(bool isEnabled)
+ else
{
- if (isEnabled)
- {
- g_previousExceptionHandler = ::SetUnhandledExceptionFilter(&ExceptionHandler);
- }
- else
- {
- ::SetUnhandledExceptionFilter(g_previousExceptionHandler);
- g_previousExceptionHandler = NULL;
- }
- }
-
- bool AttachDebugger()
- {
- if (IsDebuggerPresent())
- {
- return true;
- }
-
- // Launch vsjitdebugger.exe, this app is always present in System32 folder
- // with an installation of any version of visual studio.
- // It will open a debugging dialog asking the user what debugger to use
-
- STARTUPINFOW startupInfo = {0};
- startupInfo.cb = sizeof(startupInfo);
- PROCESS_INFORMATION processInfo = {0};
-
- wchar_t cmdline[MAX_PATH];
- swprintf_s(cmdline, L"vsjitdebugger.exe -p %li", ::GetCurrentProcessId());
- bool success = ::CreateProcessW(
- NULL, // No module name (use command line)
- cmdline, // Command line
- NULL, // Process handle not inheritable
- NULL, // Thread handle not inheritable
- FALSE, // No handle inheritance
- 0, // No creation flags
- NULL, // Use parent's environment block
- NULL, // Use parent's starting directory
- &startupInfo, // Pointer to STARTUPINFO structure
- &processInfo); // Pointer to PROCESS_INFORMATION structure
-
- if (success)
- {
- ::WaitForSingleObject(processInfo.hProcess, INFINITE);
- ::CloseHandle(processInfo.hProcess);
- ::CloseHandle(processInfo.hThread);
- return true;
- }
- return false;
- }
-
- void DebugBreak()
- {
- __debugbreak();
- }
-#endif // AZ_ENABLE_DEBUG_TOOLS
-
- void Terminate(int exitCode)
- {
- TerminateProcess(GetCurrentProcess(), exitCode);
- }
-
- void OutputToDebugger([[maybe_unused]] const char* window, const char* message)
- {
- AZStd::fixed_wstring tmpW;
- if(window)
- {
- AZStd::to_wstring(tmpW, window);
- tmpW += L": ";
- OutputDebugStringW(tmpW.c_str());
- tmpW.clear();
- }
- AZStd::to_wstring(tmpW, message);
- OutputDebugStringW(tmpW.c_str());
+ ::SetUnhandledExceptionFilter(g_previousExceptionHandler);
+ g_previousExceptionHandler = NULL;
}
}
- }
+
+ bool AttachDebugger()
+ {
+ if (IsDebuggerPresent())
+ {
+ return true;
+ }
+
+ // Launch vsjitdebugger.exe, this app is always present in System32 folder
+ // with an installation of any version of visual studio.
+ // It will open a debugging dialog asking the user what debugger to use
+
+ STARTUPINFOW startupInfo = {0};
+ startupInfo.cb = sizeof(startupInfo);
+ PROCESS_INFORMATION processInfo = {0};
+
+ wchar_t cmdline[MAX_PATH];
+ swprintf_s(cmdline, L"vsjitdebugger.exe -p %li", ::GetCurrentProcessId());
+ bool success = ::CreateProcessW(
+ NULL, // No module name (use command line)
+ cmdline, // Command line
+ NULL, // Process handle not inheritable
+ NULL, // Thread handle not inheritable
+ FALSE, // No handle inheritance
+ 0, // No creation flags
+ NULL, // Use parent's environment block
+ NULL, // Use parent's starting directory
+ &startupInfo, // Pointer to STARTUPINFO structure
+ &processInfo); // Pointer to PROCESS_INFORMATION structure
+
+ if (success)
+ {
+ ::WaitForSingleObject(processInfo.hProcess, INFINITE);
+ ::CloseHandle(processInfo.hProcess);
+ ::CloseHandle(processInfo.hThread);
+ return true;
+ }
+ return false;
+ }
+
+ void DebugBreak()
+ {
+ __debugbreak();
+ }
+#endif // AZ_ENABLE_DEBUG_TOOLS
+
+ void Terminate(int exitCode)
+ {
+ TerminateProcess(GetCurrentProcess(), exitCode);
+ }
+
+ void OutputToDebugger([[maybe_unused]] const char* window, const char* message)
+ {
+ AZStd::fixed_wstring tmpW;
+ if(window)
+ {
+ AZStd::to_wstring(tmpW, window);
+ tmpW += L": ";
+ OutputDebugStringW(tmpW.c_str());
+ tmpW.clear();
+ }
+ AZStd::to_wstring(tmpW, message);
+ OutputDebugStringW(tmpW.c_str());
+ }
+ } // namespace Platform
#if defined(AZ_ENABLE_DEBUG_TOOLS)
@@ -187,6 +184,8 @@ namespace AZ
azsnprintf(message, g_maxMessageLength, "Exception : 0x%lX - '%s' [%p]\n", ExceptionInfo->ExceptionRecord->ExceptionCode, GetExeptionName(ExceptionInfo->ExceptionRecord->ExceptionCode), ExceptionInfo->ExceptionRecord->ExceptionAddress);
Debug::Trace::Instance().Output(nullptr, message);
+ Debug::Trace::Instance().PrintCallstack(nullptr, 0, ExceptionInfo->ContextRecord);
+
EBUS_EVENT(Debug::TraceMessageDrillerBus, OnException, message);
bool result = false;
@@ -198,7 +197,7 @@ namespace AZ
// if someone ever returns TRUE we assume that they somehow handled this exception and continue.
return EXCEPTION_CONTINUE_EXECUTION;
}
- Debug::Trace::Instance().PrintCallstack(nullptr, 0, ExceptionInfo->ContextRecord);
+
Debug::Trace::Instance().Output(nullptr, "==================================================================\n");
// allowing continue of execution is not valid here. This handler gets called for serious exceptions.
@@ -211,4 +210,4 @@ namespace AZ
}
#endif
-}
+} // namspace AZ::Debug
diff --git a/Code/Framework/AzCore/Tests/TaskTests.cpp b/Code/Framework/AzCore/Tests/TaskTests.cpp
index 9e839f60ee..4f53772d51 100644
--- a/Code/Framework/AzCore/Tests/TaskTests.cpp
+++ b/Code/Framework/AzCore/Tests/TaskTests.cpp
@@ -610,15 +610,16 @@ namespace UnitTest
g.Follows(e, f);
g.Precedes(d);
- TaskGraphEvent ev;
- graph.SubmitOnExecutor(*m_executor, &ev);
- ev.Wait();
+ TaskGraphEvent ev1;
+ graph.SubmitOnExecutor(*m_executor, &ev1);
+ ev1.Wait();
EXPECT_EQ(3 | 0b100000, x);
x = 0;
- graph.SubmitOnExecutor(*m_executor, &ev);
- ev.Wait();
+ TaskGraphEvent ev2;
+ graph.SubmitOnExecutor(*m_executor, &ev2);
+ ev2.Wait();
EXPECT_EQ(3 | 0b100000, x);
}
diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
index f8d0ea8bb1..323e834413 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,24 @@ 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 +235,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/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
index cd30b753b5..c1c5775958 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
@@ -12,6 +12,7 @@
#include
#include
+#include
#include
#include
#include
@@ -363,6 +364,23 @@ namespace AZ::IO
, m_mainThreadId{ AZStd::this_thread::get_id() }
{
CompressionBus::Handler::BusConnect();
+
+ // If the settings registry is not available at this point,
+ // then something catastrophic has happened in the application startup.
+ // That should have been caught and messaged out earlier in startup.
+ if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
+ {
+ // Automatically register the event if it's not registered, because
+ // this system is initialized before the settings registry has loaded the event list.
+ AZ::ComponentApplicationLifecycle::RegisterHandler(
+ *settingsRegistry, m_componentApplicationLifecycleHandler,
+ [this](AZStd::string_view /*path*/, AZ::SettingsRegistryInterface::Type /*type*/)
+ {
+ OnSystemEntityActivated();
+ },
+ "SystemComponentsActivated",
+ /*autoRegisterEvent*/ true);
+ }
}
//////////////////////////////////////////////////////////////////////////
@@ -1175,13 +1193,20 @@ namespace AZ::IO
}
}
- auto bundleManifest = GetBundleManifest(desc.pZip);
AZStd::shared_ptr bundleCatalog;
+ auto bundleManifest = GetBundleManifest(desc.pZip);
if (bundleManifest)
{
bundleCatalog = GetBundleCatalog(desc.pZip, bundleManifest->GetCatalogName());
}
+ // If this archive is loaded before the serialize context is available, then the manifest and catalog will need to be loaded later.
+ if (!bundleManifest || !bundleCatalog)
+ {
+ m_archivesWithCatalogsToLoad.push_back(
+ ArchivesWithCatalogsToLoad(szFullPath, szBindRoot, flags, nextBundle, desc.m_strFileName));
+ }
+
bool usePrefabSystemForLevels = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
@@ -1219,12 +1244,17 @@ namespace AZ::IO
m_levelOpenEvent.Signal(levelDirs);
}
- AZ::IO::ArchiveNotificationBus::Broadcast([](AZ::IO::ArchiveNotifications* archiveNotifications, const char* bundleName,
- AZStd::shared_ptr bundleManifest, const AZ::IO::FixedMaxPath& nextBundle, AZStd::shared_ptr bundleCatalog)
+ if (bundleManifest && bundleCatalog)
{
- archiveNotifications->BundleOpened(bundleName, bundleManifest, nextBundle.c_str(), bundleCatalog);
- }, desc.m_strFileName.c_str(), bundleManifest, nextBundle, bundleCatalog);
-
+ AZ::IO::ArchiveNotificationBus::Broadcast(
+ [](AZ::IO::ArchiveNotifications* archiveNotifications, const char* bundleName,
+ AZStd::shared_ptr bundleManifest, const AZ::IO::FixedMaxPath& nextBundle,
+ AZStd::shared_ptr bundleCatalog)
+ {
+ archiveNotifications->BundleOpened(bundleName, bundleManifest, nextBundle.c_str(), bundleCatalog);
+ },
+ desc.m_strFileName.c_str(), bundleManifest, nextBundle, bundleCatalog);
+ }
return true;
}
@@ -2138,7 +2168,7 @@ namespace AZ::IO
}
currentDirPattern = currentDir + AZ_FILESYSTEM_SEPARATOR_WILDCARD;
- currentFilePattern = currentDir + AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING + "levels.pak";
+ currentFilePattern = currentDir + AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING + "level.pak";
ZipDir::FileEntry* fileEntry = findFile.FindExact(currentFilePattern.c_str());
if (fileEntry)
@@ -2175,4 +2205,36 @@ namespace AZ::IO
return catalogInfo;
}
+
+ void Archive::OnSystemEntityActivated()
+ {
+ for (const auto& archiveInfo : m_archivesWithCatalogsToLoad)
+ {
+ AZStd::intrusive_ptr archive =
+ OpenArchive(archiveInfo.m_fullPath, archiveInfo.m_bindRoot, archiveInfo.m_flags, nullptr);
+ if (!archive)
+ {
+ continue;
+ }
+
+ ZipDir::CachePtr pZip = static_cast(archive.get())->GetCache();
+
+ AZStd::shared_ptr bundleCatalog;
+ auto bundleManifest = GetBundleManifest(pZip);
+ if (bundleManifest)
+ {
+ bundleCatalog = GetBundleCatalog(pZip, bundleManifest->GetCatalogName());
+ }
+
+ AZ::IO::ArchiveNotificationBus::Broadcast(
+ [](AZ::IO::ArchiveNotifications* archiveNotifications, const char* bundleName,
+ AZStd::shared_ptr bundleManifest, const AZ::IO::FixedMaxPath& nextBundle,
+ AZStd::shared_ptr bundleCatalog)
+ {
+ archiveNotifications->BundleOpened(bundleName, bundleManifest, nextBundle.c_str(), bundleCatalog);
+ },
+ archiveInfo.m_strFileName.c_str(), bundleManifest, archiveInfo.m_nextBundle, bundleCatalog);
+ }
+ m_archivesWithCatalogsToLoad.clear();
+ }
}
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h
index f08d90a66e..279702b433 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h
+++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h
@@ -19,6 +19,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -271,6 +272,11 @@ namespace AZ::IO
ZipDir::CachePtr* pZip = {}) const;
private:
+ // Archives can't be fully mounted until the system entity has been activated,
+ // because mounting them requires the BundlingSystemComponent and the serialization system
+ // to both be available.
+ void OnSystemEntityActivated();
+
bool OpenPackCommon(AZStd::string_view szBindRoot, AZStd::string_view pName, AZStd::intrusive_ptr pData = nullptr, bool addLevels = true);
bool OpenPacksCommon(AZStd::string_view szDir, AZStd::string_view pWildcardIn, AZStd::vector* pFullPaths = nullptr, bool addLevels = true);
@@ -313,6 +319,8 @@ namespace AZ::IO
mutable AZStd::shared_mutex m_csZips;
ZipArray m_arrZips;
+ AZ::SettingsRegistryInterface::NotifyEventHandler m_componentApplicationLifecycleHandler;
+
//////////////////////////////////////////////////////////////////////////
// Opened files collector.
//////////////////////////////////////////////////////////////////////////
@@ -339,5 +347,34 @@ namespace AZ::IO
// [LYN-2376] Remove once legacy slice support is removed
LevelPackOpenEvent m_levelOpenEvent;
LevelPackCloseEvent m_levelCloseEvent;
+
+ // If pak files are loaded before the serialization and bundling system
+ // are ready to go, their asset catalogs can't be loaded.
+ // In this case, cache information about those archives,
+ // and attempt to load the catalogs later, when the required systems are enabled.
+ struct ArchivesWithCatalogsToLoad
+ {
+ ArchivesWithCatalogsToLoad(
+ AZStd::string_view fullPath,
+ AZStd::string_view bindRoot,
+ int flags,
+ AZ::IO::PathView nextBundle,
+ AZ::IO::Path strFileName)
+ : m_fullPath(fullPath)
+ , m_bindRoot(bindRoot)
+ , m_flags(flags)
+ , m_nextBundle(nextBundle)
+ , m_strFileName(strFileName)
+ {
+ }
+
+ AZ::IO::Path m_strFileName;
+ AZStd::string m_fullPath;
+ AZStd::string m_bindRoot;
+ AZ::IO::PathView m_nextBundle;
+ int m_flags;
+ };
+
+ AZStd::vector m_archivesWithCatalogsToLoad;
};
}
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/Components/TransformComponent.cpp b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp
index 68c4b00243..bc109b73c2 100644
--- a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp
@@ -323,6 +323,13 @@ namespace AzFramework
return localZ;
}
+ void TransformComponent::SetWorldRotation(const AZ::Vector3& eulerAnglesRadian)
+ {
+ AZ::Transform newWorldTransform = m_worldTM;
+ newWorldTransform.SetRotation(AZ::Quaternion::CreateFromEulerAnglesRadians(eulerAnglesRadian));
+ SetWorldTM(newWorldTransform);
+ }
+
void TransformComponent::SetWorldRotationQuaternion(const AZ::Quaternion& quaternion)
{
AZ::Transform newWorldTransform = m_worldTM;
diff --git a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h
index ac282b4d49..2375f28ed1 100644
--- a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h
+++ b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h
@@ -108,6 +108,7 @@ namespace AzFramework
float GetLocalZ() override;
// Rotation modifiers
+ void SetWorldRotation(const AZ::Vector3& eulerAnglesRadian) override;
void SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) override;
AZ::Vector3 GetWorldRotation() override;
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/AzFramework/Input/Buses/Requests/InputDeviceRequestBus.h b/Code/Framework/AzFramework/AzFramework/Input/Buses/Requests/InputDeviceRequestBus.h
index 147ff7f0b8..74e2459ab6 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Buses/Requests/InputDeviceRequestBus.h
+++ b/Code/Framework/AzFramework/AzFramework/Input/Buses/Requests/InputDeviceRequestBus.h
@@ -229,17 +229,13 @@ namespace AzFramework
//! Alias for the EBus implementation of this interface
using Bus = AZ::EBus>;
- ////////////////////////////////////////////////////////////////////////////////////////////
- //! Alias for the function type used to create the custom implementations
- using CreateFunctionType = typename InputDeviceType::Implementation*(*)(InputDeviceType&);
-
////////////////////////////////////////////////////////////////////////////////////////////
//! Set a custom implementation for this input device type, either for a specific instance
//! by addressing the call to an InputDeviceId, or for all existing instances by broadcast.
//! Passing InputDeviceType::Implementation::Create as the argument will create the default
//! device implementation, while passing nullptr will delete any existing implementation.
- //! \param[in] createFunction Pointer to the function that will create the implementation.
- virtual void SetCustomImplementation(CreateFunctionType createFunction) = 0;
+ //! \param[in] implementationFactory Pointer to the function that creates the implementation.
+ virtual void SetCustomImplementation(typename InputDeviceType::ImplementationFactory implementationFactory) = 0;
};
////////////////////////////////////////////////////////////////////////////////////////////////
@@ -267,18 +263,14 @@ namespace AzFramework
AZ_DISABLE_COPY_MOVE(InputDeviceImplementationRequestHandler);
protected:
- ////////////////////////////////////////////////////////////////////////////////////////////
- //! Alias for the function type used to create the custom implementations
- using CreateFunctionType = typename InputDeviceType::Implementation*(*)(InputDeviceType&);
-
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref InputDeviceImplementationRequest::SetCustomImplementation
- AZ_INLINE void SetCustomImplementation(CreateFunctionType createFunction) override
+ AZ_INLINE void SetCustomImplementation(typename InputDeviceType::ImplementationFactory implementationFactory) override
{
AZStd::unique_ptr newImplementation;
- if (createFunction)
+ if (implementationFactory)
{
- newImplementation.reset(createFunction(m_inputDevice));
+ newImplementation.reset(implementationFactory(m_inputDevice));
}
m_inputDevice.SetImplementation(AZStd::move(newImplementation));
}
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp
index 51aa008519..9759a95733 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp
@@ -94,7 +94,14 @@ namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceGamepad::InputDeviceGamepad(AZ::u32 index)
- : InputDevice(InputDeviceId(Name, index))
+ : InputDeviceGamepad(InputDeviceId(Name, index)) // Delegated constructor
+ {
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ InputDeviceGamepad::InputDeviceGamepad(const InputDeviceId& inputDeviceId,
+ ImplementationFactory implementationFactory)
+ : InputDevice(inputDeviceId)
, m_allChannelsById()
, m_buttonChannelsById()
, m_triggerChannelsById()
@@ -144,8 +151,8 @@ namespace AzFramework
m_thumbStickDirectionChannelsById[channelId] = channel;
}
- // Create the platform specific implementation
- m_pimpl.reset(Implementation::Create(*this));
+ // Create the platform specific or custom implementation
+ m_pimpl.reset(implementationFactory ? implementationFactory(*this) : nullptr);
// Connect to the haptic feedback request bus
InputHapticFeedbackRequestBus::Handler::BusConnect(GetInputDeviceId());
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h
index 286b4f0df3..7be498830b 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h
@@ -182,6 +182,14 @@ namespace AzFramework
// Reflection
static void Reflect(AZ::ReflectContext* context);
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Foward declare the internal Implementation class so it can be passed into the constructor
+ class Implementation;
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ //! Alias for the function type used to create a custom implementation for this input device
+ using ImplementationFactory = Implementation*(InputDeviceGamepad&);
+
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
explicit InputDeviceGamepad();
@@ -191,6 +199,13 @@ namespace AzFramework
//! \param[in] index Index of the game-pad device
explicit InputDeviceGamepad(AZ::u32 index);
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ //! Constructor
+ //! \param[in] inputDeviceId Id of the input device
+ //! \param[in] implementationFactory Optional override of the default Implementation::Create
+ explicit InputDeviceGamepad(const InputDeviceId& inputDeviceId,
+ ImplementationFactory implementationFactory = &Implementation::Create);
+
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputDeviceGamepad);
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp
index d1117ea895..2a76cc6e1b 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp
@@ -182,8 +182,9 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
- InputDeviceKeyboard::InputDeviceKeyboard(AzFramework::InputDeviceId id)
- : InputDevice(id)
+ InputDeviceKeyboard::InputDeviceKeyboard(const InputDeviceId& inputDeviceId,
+ ImplementationFactory implementationFactory)
+ : InputDevice(inputDeviceId)
, m_modifierKeyStates(AZStd::make_shared())
, m_allChannelsById()
, m_keyChannelsById()
@@ -203,8 +204,8 @@ namespace AzFramework
m_keyChannelsById[channelId] = channel;
}
- // Create the platform specific implementation
- m_pimpl.reset(Implementation::Create(*this));
+ // Create the platform specific or custom implementation
+ m_pimpl.reset(implementationFactory ? implementationFactory(*this) : nullptr);
// Connect to the text entry request bus
InputTextEntryRequestBus::Handler::BusConnect(GetInputDeviceId());
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h
index e3c21ec326..c0c2f5d92b 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h
@@ -370,9 +370,20 @@ namespace AzFramework
// Reflection
static void Reflect(AZ::ReflectContext* context);
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Foward declare the internal Implementation class so it can be passed into the constructor
+ class Implementation;
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ //! Alias for the function type used to create a custom implementation for this input device
+ using ImplementationFactory = Implementation*(InputDeviceKeyboard&);
+
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
- InputDeviceKeyboard(AzFramework::InputDeviceId id = Id);
+ //! \param[in] inputDeviceId Optional override of the default input device id
+ //! \param[in] implementationFactory Optional override of the default Implementation::Create
+ explicit InputDeviceKeyboard(const InputDeviceId& inputDeviceId = Id,
+ ImplementationFactory implementationFactory = &Implementation::Create);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp
index c144f63d2c..d4057bc5d3 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp
@@ -60,8 +60,9 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
- InputDeviceMotion::InputDeviceMotion()
- : InputDevice(Id)
+ InputDeviceMotion::InputDeviceMotion(const InputDeviceId& inputDeviceId,
+ ImplementationFactory implementationFactory)
+ : InputDevice(inputDeviceId)
, m_allChannelsById()
, m_accelerationChannelsById()
, m_rotationRateChannelsById()
@@ -107,8 +108,8 @@ namespace AzFramework
m_orientationChannelsById[channelId] = channel;
}
- // Create the platform specific implementation
- m_pimpl.reset(Implementation::Create(*this));
+ // Create the platform specific or custom implementation
+ m_pimpl.reset(implementationFactory ? implementationFactory(*this) : nullptr);
// Connect to the motion sensor request bus
InputMotionSensorRequestBus::Handler::BusConnect(GetInputDeviceId());
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h
index 872bd1cfa0..f0fc976963 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h
@@ -126,9 +126,20 @@ namespace AzFramework
// Reflection
static void Reflect(AZ::ReflectContext* context);
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Foward declare the internal Implementation class so it can be passed into the constructor
+ class Implementation;
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ //! Alias for the function type used to create a custom implementation for this input device
+ using ImplementationFactory = Implementation*(InputDeviceMotion&);
+
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
- InputDeviceMotion();
+ //! \param[in] inputDeviceId Optional override of the default input device id
+ //! \param[in] implementationFactory Optional override of the default Implementation::Create
+ explicit InputDeviceMotion(const InputDeviceId& inputDeviceId = Id,
+ ImplementationFactory implementationFactory = &Implementation::Create);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp
index e9af4cc4ce..39504d9352 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp
@@ -67,8 +67,9 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
- InputDeviceMouse::InputDeviceMouse(AzFramework::InputDeviceId id)
- : InputDevice(id)
+ InputDeviceMouse::InputDeviceMouse(const InputDeviceId& inputDeviceId,
+ ImplementationFactory implementationFactory)
+ : InputDevice(inputDeviceId)
, m_allChannelsById()
, m_buttonChannelsById()
, m_movementChannelsById()
@@ -97,8 +98,8 @@ namespace AzFramework
m_cursorPositionChannel = aznew InputChannelDeltaWithSharedPosition2D(SystemCursorPosition, *this, m_cursorPositionData2D);
m_allChannelsById[SystemCursorPosition] = m_cursorPositionChannel;
- // Create the platform specific implementation
- m_pimpl.reset(Implementation::Create(*this));
+ // Create the platform specific or custom implementation
+ m_pimpl.reset(implementationFactory ? implementationFactory(*this) : nullptr);
// Connect to the system cursor request bus
InputSystemCursorRequestBus::Handler::BusConnect(GetInputDeviceId());
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h
index 3b35e03f1b..3c519a3edb 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h
@@ -122,9 +122,20 @@ namespace AzFramework
// Reflection
static void Reflect(AZ::ReflectContext* context);
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Foward declare the internal Implementation class so it can be passed into the constructor
+ class Implementation;
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ //! Alias for the function type used to create a custom implementation for this input device
+ using ImplementationFactory = Implementation*(InputDeviceMouse&);
+
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
- explicit InputDeviceMouse(AzFramework::InputDeviceId id = Id);
+ //! \param[in] inputDeviceId Optional override of the default input device id
+ //! \param[in] implementationFactory Optional override of the default Implementation::Create
+ explicit InputDeviceMouse(const InputDeviceId& inputDeviceId = Id,
+ ImplementationFactory implementationFactory = &Implementation::Create);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp
index be850e9289..2695e3bb08 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp
@@ -59,8 +59,9 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
- InputDeviceTouch::InputDeviceTouch()
- : InputDevice(Id)
+ InputDeviceTouch::InputDeviceTouch(const InputDeviceId& inputDeviceId,
+ ImplementationFactory implementationFactory)
+ : InputDevice(inputDeviceId)
, m_allChannelsById()
, m_touchChannelsById()
, m_pimpl(nullptr)
@@ -75,8 +76,8 @@ namespace AzFramework
m_touchChannelsById[channelId] = channel;
}
- // Create the platform specific implementation
- m_pimpl.reset(Implementation::Create(*this));
+ // Create the platform specific or custom implementation
+ m_pimpl.reset(implementationFactory ? implementationFactory(*this) : nullptr);
}
////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h
index d21834c22a..12b8aded4b 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h
@@ -77,9 +77,20 @@ namespace AzFramework
// Reflection
static void Reflect(AZ::ReflectContext* context);
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Foward declare the internal Implementation class so it can be passed into the constructor
+ class Implementation;
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ //! Alias for the function type used to create a custom implementation for this input device
+ using ImplementationFactory = Implementation*(InputDeviceTouch&);
+
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
- InputDeviceTouch();
+ //! \param[in] inputDeviceId Optional override of the default input device id
+ //! \param[in] implementationFactory Optional override of the default Implementation::Create
+ explicit InputDeviceTouch(const InputDeviceId& inputDeviceId = Id,
+ ImplementationFactory implementationFactory = &Implementation::Create);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp
index f3024f11ab..bcbdeaa478 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp
@@ -51,8 +51,9 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
- InputDeviceVirtualKeyboard::InputDeviceVirtualKeyboard()
- : InputDevice(Id)
+ InputDeviceVirtualKeyboard::InputDeviceVirtualKeyboard(const InputDeviceId& inputDeviceId,
+ ImplementationFactory implementationFactory)
+ : InputDevice(inputDeviceId)
, m_allChannelsById()
, m_pimpl()
, m_implementationRequestHandler(*this)
@@ -65,8 +66,8 @@ namespace AzFramework
m_commandChannelsById[channelId] = channel;
}
- // Create the platform specific implementation
- m_pimpl.reset(Implementation::Create(*this));
+ // Create the platform specific or custom implementation
+ m_pimpl.reset(implementationFactory ? implementationFactory(*this) : nullptr);
// Connect to the text entry request bus
InputTextEntryRequestBus::Handler::BusConnect(GetInputDeviceId());
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h
index 6f62c3ec61..abe1312733 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h
@@ -69,9 +69,20 @@ namespace AzFramework
// Reflection
static void Reflect(AZ::ReflectContext* context);
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Foward declare the internal Implementation class so it can be passed into the constructor
+ class Implementation;
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ //! Alias for the function type used to create a custom implementation for this input device
+ using ImplementationFactory = Implementation*(InputDeviceVirtualKeyboard&);
+
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
- InputDeviceVirtualKeyboard();
+ //! \param[in] inputDeviceId Optional override of the default input device id
+ //! \param[in] implementationFactory Optional override of the default Implementation::Create
+ explicit InputDeviceVirtualKeyboard(const InputDeviceId& inputDeviceId = Id,
+ ImplementationFactory implementationFactory = &Implementation::Create);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h b/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h
index c657e0edc7..ccf9acdf8b 100644
--- a/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h
+++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h
@@ -24,17 +24,17 @@ namespace AzFramework
IMatchmakingRequests() = default;
virtual ~IMatchmakingRequests() = default;
- // Registers a player's acceptance or rejection of a proposed matchmaking.
- // @param acceptMatchRequest The request of AcceptMatch operation
+ //! Registers a player's acceptance or rejection of a proposed matchmaking.
+ //! @param acceptMatchRequest The request of AcceptMatch operation
virtual void AcceptMatch(const AcceptMatchRequest& acceptMatchRequest) = 0;
- // Create a game match for a group of players.
- // @param startMatchmakingRequest The request of StartMatchmaking operation
- // @return A unique identifier for a matchmaking ticket
+ //! Create a game match for a group of players.
+ //! @param startMatchmakingRequest The request of StartMatchmaking operation
+ //! @return A unique identifier for a matchmaking ticket
virtual AZStd::string StartMatchmaking(const StartMatchmakingRequest& startMatchmakingRequest) = 0;
- // Cancels a matchmaking ticket that is currently being processed.
- // @param stopMatchmakingRequest The request of StopMatchmaking operation
+ //! Cancels a matchmaking ticket that is currently being processed.
+ //! @param stopMatchmakingRequest The request of StopMatchmaking operation
virtual void StopMatchmaking(const StopMatchmakingRequest& stopMatchmakingRequest) = 0;
};
@@ -48,16 +48,16 @@ namespace AzFramework
IMatchmakingAsyncRequests() = default;
virtual ~IMatchmakingAsyncRequests() = default;
- // AcceptMatch Async
- // @param acceptMatchRequest The request of AcceptMatch operation
+ //! AcceptMatch Async
+ //! @param acceptMatchRequest The request of AcceptMatch operation
virtual void AcceptMatchAsync(const AcceptMatchRequest& acceptMatchRequest) = 0;
- // StartMatchmaking Async
- // @param startMatchmakingRequest The request of StartMatchmaking operation
+ //! StartMatchmaking Async
+ //! @param startMatchmakingRequest The request of StartMatchmaking operation
virtual void StartMatchmakingAsync(const StartMatchmakingRequest& startMatchmakingRequest) = 0;
- // StopMatchmaking Async
- // @param stopMatchmakingRequest The request of StopMatchmaking operation
+ //! StopMatchmaking Async
+ //! @param stopMatchmakingRequest The request of StopMatchmaking operation
virtual void StopMatchmakingAsync(const StopMatchmakingRequest& stopMatchmakingRequest) = 0;
};
@@ -76,14 +76,14 @@ namespace AzFramework
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
- // OnAcceptMatchAsyncComplete is fired once AcceptMatchAsync completes
+ //! OnAcceptMatchAsyncComplete is fired once AcceptMatchAsync completes
virtual void OnAcceptMatchAsyncComplete() = 0;
- // OnStartMatchmakingAsyncComplete is fired once StartMatchmakingAsync completes
- // @param matchmakingTicketId The unique identifier for the matchmaking ticket
+ //! OnStartMatchmakingAsyncComplete is fired once StartMatchmakingAsync completes
+ //! @param matchmakingTicketId The unique identifier for the matchmaking ticket
virtual void OnStartMatchmakingAsyncComplete(const AZStd::string& matchmakingTicketId) = 0;
- // OnStopMatchmakingAsyncComplete is fired once StopMatchmakingAsync completes
+ //! OnStopMatchmakingAsyncComplete is fired once StopMatchmakingAsync completes
virtual void OnStopMatchmakingAsyncComplete() = 0;
};
using MatchmakingAsyncRequestNotificationBus = AZ::EBus;
diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h
index aa19b94b4a..0ec52c7215 100644
--- a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h
+++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h
@@ -29,17 +29,17 @@ namespace AzFramework
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
- // OnMatchAcceptance is fired when match is found and pending on acceptance
- // Use this notification to accept found match
+ //! OnMatchAcceptance is fired when match is found and pending on acceptance
+ //! Use this notification to accept found match
virtual void OnMatchAcceptance() = 0;
- // OnMatchComplete is fired when match is complete
+ //! OnMatchComplete is fired when match is complete
virtual void OnMatchComplete() = 0;
- // OnMatchError is fired when match is processed with error
+ //! OnMatchError is fired when match is processed with error
virtual void OnMatchError() = 0;
- // OnMatchFailure is fired when match is failed to complete
+ //! OnMatchFailure is fired when match is failed to complete
virtual void OnMatchFailure() = 0;
};
using MatchmakingNotificationBus = AZ::EBus;
diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h
index 9169a83588..5f5dcc0643 100644
--- a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h
+++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h
@@ -29,11 +29,11 @@ namespace AzFramework
AcceptMatchRequest() = default;
virtual ~AcceptMatchRequest() = default;
- // Player response to accept or reject match
+ //! Player response to accept or reject match
bool m_acceptMatch;
- // A list of unique identifiers for players delivering the response
+ //! A list of unique identifiers for players delivering the response
AZStd::vector m_playerIds;
- // A unique identifier for a matchmaking ticket
+ //! A unique identifier for a matchmaking ticket
AZStd::string m_ticketId;
};
@@ -47,7 +47,7 @@ namespace AzFramework
StartMatchmakingRequest() = default;
virtual ~StartMatchmakingRequest() = default;
- // A unique identifier for a matchmaking ticket
+ //! A unique identifier for a matchmaking ticket
AZStd::string m_ticketId;
};
@@ -61,7 +61,7 @@ namespace AzFramework
StopMatchmakingRequest() = default;
virtual ~StopMatchmakingRequest() = default;
- // A unique identifier for a matchmaking ticket
+ //! A unique identifier for a matchmaking ticket
AZStd::string m_ticketId;
};
} // namespace AzFramework
diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp
index d2f74510e5..0a0e1bb8e3 100644
--- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp
@@ -10,6 +10,7 @@
#include
#include
+#include
namespace AzPhysics
{
@@ -28,6 +29,71 @@ namespace AzPhysics
->Field("ChildLocalPosition", &JointConfiguration::m_childLocalPosition)
->Field("StartSimulationEnabled", &JointConfiguration::m_startSimulationEnabled)
;
+
+ if (auto* editContext = serializeContext->GetEditContext())
+ {
+ editContext->Class("Joint Configuration", "Joint configuration.")
+ ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+ ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
+ ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_parentLocalRotation,
+ "Parent local rotation", "Parent joint frame relative to parent body.")
+ ->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetParentLocalRotationVisibility)
+ ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_parentLocalPosition,
+ "Parent local position", "Joint position relative to parent body.")
+ ->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetParentLocalPositionVisibility)
+ ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_childLocalRotation,
+ "Child local rotation", "Child joint frame relative to child body.")
+ ->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetChildLocalRotationVisibility)
+ ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_childLocalPosition,
+ "Child local position", "Joint position relative to child body.")
+ ->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetChildLocalPositionVisibility)
+ ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_startSimulationEnabled,
+ "Start simulation enabled", "When active, the joint will be enabled when the simulation begins.")
+ ->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetStartSimulationEnabledVisibility)
+ ;
+ }
}
}
-}
+
+ AZ::Crc32 JointConfiguration::GetPropertyVisibility(JointConfiguration::PropertyVisibility property) const
+ {
+ return (m_propertyVisibilityFlags & property) != 0 ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
+ }
+
+ void JointConfiguration::SetPropertyVisibility(JointConfiguration::PropertyVisibility property, bool isVisible)
+ {
+ if (isVisible)
+ {
+ m_propertyVisibilityFlags |= property;
+ }
+ else
+ {
+ m_propertyVisibilityFlags &= ~property;
+ }
+ }
+
+ AZ::Crc32 JointConfiguration::GetParentLocalRotationVisibility() const
+ {
+ return GetPropertyVisibility(JointConfiguration::PropertyVisibility::ParentLocalRotation);
+ }
+
+ AZ::Crc32 JointConfiguration::GetParentLocalPositionVisibility() const
+ {
+ return GetPropertyVisibility(JointConfiguration::PropertyVisibility::ParentLocalPosition);
+ }
+
+ AZ::Crc32 JointConfiguration::GetChildLocalRotationVisibility() const
+ {
+ return GetPropertyVisibility(JointConfiguration::PropertyVisibility::ChildLocalRotation);
+ }
+
+ AZ::Crc32 JointConfiguration::GetChildLocalPositionVisibility() const
+ {
+ return GetPropertyVisibility(JointConfiguration::PropertyVisibility::ChildLocalPosition);
+ }
+
+ AZ::Crc32 JointConfiguration::GetStartSimulationEnabledVisibility() const
+ {
+ return GetPropertyVisibility(JointConfiguration::PropertyVisibility::StartSimulationEnabled);
+ }
+} // namespace AzPhysics
diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.h
index ff9bfb5bea..2a246692d7 100644
--- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.h
+++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.h
@@ -31,6 +31,25 @@ namespace AzPhysics
JointConfiguration() = default;
virtual ~JointConfiguration() = default;
+ // Visibility helpers for use in the Editor when reflected.
+ enum PropertyVisibility : AZ::u8
+ {
+ ParentLocalRotation = 1 << 0, //!< Whether the parent local rotation is visible.
+ ParentLocalPosition = 1 << 1, //!< Whether the parent local position is visible.
+ ChildLocalRotation = 1 << 2, //!< Whether the child local rotation is visible.
+ ChildLocalPosition = 1 << 3, //!< Whether the child local position is visible.
+ StartSimulationEnabled = 1 << 4 //!< Whether the start simulation enabled setting is visible.
+ };
+
+ AZ::Crc32 GetPropertyVisibility(PropertyVisibility property) const;
+ void SetPropertyVisibility(PropertyVisibility property, bool isVisible);
+
+ AZ::Crc32 GetParentLocalRotationVisibility() const;
+ AZ::Crc32 GetParentLocalPositionVisibility() const;
+ AZ::Crc32 GetChildLocalRotationVisibility() const;
+ AZ::Crc32 GetChildLocalPositionVisibility() const;
+ AZ::Crc32 GetStartSimulationEnabledVisibility() const;
+
// Entity/object association.
void* m_customUserData = nullptr;
@@ -40,8 +59,11 @@ namespace AzPhysics
AZ::Quaternion m_childLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Child joint frame relative to child body.
AZ::Vector3 m_childLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to child body.
bool m_startSimulationEnabled = true;
-
+
// For debugging/tracking purposes only.
AZStd::string m_debugName;
+
+ // Default all visibility settings to invisible, since most joint configurations don't need to display these.
+ AZ::u8 m_propertyVisibilityFlags = 0;
};
}
diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h
index 065d6bb9d5..188ea7b994 100644
--- a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h
+++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h
@@ -18,16 +18,16 @@ namespace AzFramework
//! The properties for handling join session request.
struct SessionConnectionConfig
{
- // A unique identifier for registered player in session.
+ //! A unique identifier for registered player in session.
AZStd::string m_playerSessionId;
- // The DNS identifier assigned to the instance that is running the session.
+ //! The DNS identifier assigned to the instance that is running the session.
AZStd::string m_dnsName;
- // The IP address of the session.
+ //! The IP address of the session.
AZStd::string m_ipAddress;
- // The port number for the session.
+ //! The port number for the session.
uint16_t m_port = 0;
};
@@ -35,10 +35,10 @@ namespace AzFramework
//! The properties for handling player connect/disconnect
struct PlayerConnectionConfig
{
- // A unique identifier for player connection.
+ //! A unique identifier for player connection.
uint32_t m_playerConnectionId = 0;
- // A unique identifier for registered player in session.
+ //! A unique identifier for registered player in session.
AZStd::string m_playerSessionId;
};
@@ -51,12 +51,12 @@ namespace AzFramework
ISessionHandlingClientRequests() = default;
virtual ~ISessionHandlingClientRequests() = default;
- // Request the player join session
- // @param sessionConnectionConfig The required properties to handle the player join session process
- // @return The result of player join session process
+ //! Request the player join session
+ //! @param sessionConnectionConfig The required properties to handle the player join session process
+ //! @return The result of player join session process
virtual bool RequestPlayerJoinSession(const SessionConnectionConfig& sessionConnectionConfig) = 0;
- // Request the connected player leave session
+ //! Request the connected player leave session
virtual void RequestPlayerLeaveSession() = 0;
};
@@ -69,26 +69,26 @@ namespace AzFramework
ISessionHandlingProviderRequests() = default;
virtual ~ISessionHandlingProviderRequests() = default;
- // Handle the destroy session process
+ //! Handle the destroy session process
virtual void HandleDestroySession() = 0;
- // Validate the player join session process
- // @param playerConnectionConfig The required properties to validate the player join session process
- // @return The result of player join session validation
+ //! Validate the player join session process
+ //! @param playerConnectionConfig The required properties to validate the player join session process
+ //! @return The result of player join session validation
virtual bool ValidatePlayerJoinSession(const PlayerConnectionConfig& playerConnectionConfig) = 0;
- // Handle the player leave session process
- // @param playerConnectionConfig The required properties to handle the player leave session process
+ //! Handle the player leave session process
+ //! @param playerConnectionConfig The required properties to handle the player leave session process
virtual void HandlePlayerLeaveSession(const PlayerConnectionConfig& playerConnectionConfig) = 0;
- // Retrieves the file location of a pem-encoded TLS certificate for Client to Server communication
- // @return If successful, returns the file location of TLS certificate file; if not successful, returns
- // empty string.
+ //! Retrieves the file location of a pem-encoded TLS certificate for Client to Server communication
+ //! @return If successful, returns the file location of TLS certificate file; if not successful, returns
+ //! empty string.
virtual AZ::IO::Path GetExternalSessionCertificate() = 0;
- // Retrieves the file location of a pem-encoded TLS certificate for Server to Server communication
- // @return If successful, returns the file location of TLS certificate file; if not successful, returns
- // empty string.
+ //! Retrieves the file location of a pem-encoded TLS certificate for Server to Server communication
+ //! @return If successful, returns the file location of TLS certificate file; if not successful, returns
+ //! empty string.
virtual AZ::IO::Path GetInternalSessionCertificate() = 0;
};
} // namespace AzFramework
diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h
index bdc3fe1444..8b985a3187 100644
--- a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h
+++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h
@@ -25,22 +25,22 @@ namespace AzFramework
ISessionRequests() = default;
virtual ~ISessionRequests() = default;
- // Create a session for players to find and join.
- // @param createSessionRequest The request of CreateSession operation
- // @return The request id if session creation request succeeds; empty if it fails
+ //! Create a session for players to find and join.
+ //! @param createSessionRequest The request of CreateSession operation
+ //! @return The request id if session creation request succeeds; empty if it fails
virtual AZStd::string CreateSession(const CreateSessionRequest& createSessionRequest) = 0;
- // Retrieve all active sessions that match the given search criteria and sorted in specific order.
- // @param searchSessionsRequest The request of SearchSessions operation
- // @return The response of SearchSessions operation
+ //! Retrieve all active sessions that match the given search criteria and sorted in specific order.
+ //! @param searchSessionsRequest The request of SearchSessions operation
+ //! @return The response of SearchSessions operation
virtual SearchSessionsResponse SearchSessions(const SearchSessionsRequest& searchSessionsRequest) const = 0;
- // Reserve an open player slot in a session, and perform connection from client to server.
- // @param joinSessionRequest The request of JoinSession operation
- // @return True if joining session succeeds; False otherwise
+ //! Reserve an open player slot in a session, and perform connection from client to server.
+ //! @param joinSessionRequest The request of JoinSession operation
+ //! @return True if joining session succeeds; False otherwise
virtual bool JoinSession(const JoinSessionRequest& joinSessionRequest) = 0;
- // Disconnect player from session.
+ //! Disconnect player from session.
virtual void LeaveSession() = 0;
};
@@ -54,19 +54,19 @@ namespace AzFramework
ISessionAsyncRequests() = default;
virtual ~ISessionAsyncRequests() = default;
- // CreateSession Async
- // @param createSessionRequest The request of CreateSession operation
+ //! CreateSession Async
+ //! @param createSessionRequest The request of CreateSession operation
virtual void CreateSessionAsync(const CreateSessionRequest& createSessionRequest) = 0;
- // SearchSessions Async
- // @param searchSessionsRequest The request of SearchSessions operation
+ //! SearchSessions Async
+ //! @param searchSessionsRequest The request of SearchSessions operation
virtual void SearchSessionsAsync(const SearchSessionsRequest& searchSessionsRequest) const = 0;
- // JoinSession Async
- // @param joinSessionRequest The request of JoinSession operation
+ //! JoinSession Async
+ //! @param joinSessionRequest The request of JoinSession operation
virtual void JoinSessionAsync(const JoinSessionRequest& joinSessionRequest) = 0;
- // LeaveSession Async
+ //! LeaveSession Async
virtual void LeaveSessionAsync() = 0;
};
@@ -85,19 +85,19 @@ namespace AzFramework
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
- // OnCreateSessionAsyncComplete is fired once CreateSessionAsync completes
- // @param createSessionResponse The request id if session creation request succeeds; empty if it fails
+ //! OnCreateSessionAsyncComplete is fired once CreateSessionAsync completes
+ //! @param createSessionResponse The request id if session creation request succeeds; empty if it fails
virtual void OnCreateSessionAsyncComplete(const AZStd::string& createSessionReponse) = 0;
- // OnSearchSessionsAsyncComplete is fired once SearchSessionsAsync completes
- // @param searchSessionsResponse The response of SearchSessions call
+ //! OnSearchSessionsAsyncComplete is fired once SearchSessionsAsync completes
+ //! @param searchSessionsResponse The response of SearchSessions call
virtual void OnSearchSessionsAsyncComplete(const SearchSessionsResponse& searchSessionsResponse) = 0;
- // OnJoinSessionAsyncComplete is fired once JoinSessionAsync completes
- // @param joinSessionsResponse True if joining session succeeds; False otherwise
+ //! OnJoinSessionAsyncComplete is fired once JoinSessionAsync completes
+ //! @param joinSessionsResponse True if joining session succeeds; False otherwise
virtual void OnJoinSessionAsyncComplete(bool joinSessionsResponse) = 0;
- // OnLeaveSessionAsyncComplete is fired once LeaveSessionAsync completes
+ //! OnLeaveSessionAsyncComplete is fired once LeaveSessionAsync completes
virtual void OnLeaveSessionAsyncComplete() = 0;
};
using SessionAsyncRequestNotificationBus = AZ::EBus