diff --git a/Assets/Engine/exclude.filetag b/Assets/Engine/exclude.filetag
index 52534a87d8..3528454ec4 100644
--- a/Assets/Engine/exclude.filetag
+++ b/Assets/Engine/exclude.filetag
@@ -125,17 +125,6 @@
-
-
-
-
-
-
-
-
-
-
-
@@ -207,27 +196,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/AutomatedTesting/Config/shader_global_build_options.json b/AutomatedTesting/Config/shader_global_build_options.json
index 08e4d7f502..1aacb05575 100644
--- a/AutomatedTesting/Config/shader_global_build_options.json
+++ b/AutomatedTesting/Config/shader_global_build_options.json
@@ -3,9 +3,15 @@
"Version": 1,
"ClassName": "GlobalBuildOptions",
"ClassData": {
- "ShaderCompilerArguments" : {
- "DefaultMatrixOrder" : "Row",
- "AzslcAdditionalFreeArguments" : "--strip-unused-srgs"
+ "ShaderCompilerArguments": {
+ "DefaultMatrixOrder": "Row",
+ "AzslcAdditionalFreeArguments": "--strip-unused-srgs"
+ },
+ "PreprocessorOptions": {
+ "predefinedMacros": [ "AZSL=17" ],
+ "projectIncludePaths": [
+ "Gems/AtomTressFX/Assets/Shaders"
+ ]
}
}
}
\ No newline at end of file
diff --git a/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py b/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py
new file mode 100644
index 0000000000..d5a7acfe91
--- /dev/null
+++ b/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py
@@ -0,0 +1,162 @@
+#
+# 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, traceback, binascii, sys, json, pathlib
+import azlmbr.math
+import azlmbr.bus
+
+#
+# SceneAPI Processor
+#
+
+
+def log_exception_traceback():
+ exc_type, exc_value, exc_tb = sys.exc_info()
+ data = traceback.format_exception(exc_type, exc_value, exc_tb)
+ print(str(data))
+
+def get_mesh_node_names(sceneGraph):
+ import azlmbr.scene as sceneApi
+ import azlmbr.scene.graph
+ from scene_api import scene_data as sceneData
+
+ meshDataList = []
+ node = sceneGraph.get_root()
+ children = []
+ paths = []
+
+ while node.IsValid():
+ # store children to process after siblings
+ if sceneGraph.has_node_child(node):
+ children.append(sceneGraph.get_node_child(node))
+
+ nodeName = sceneData.SceneGraphName(sceneGraph.get_node_name(node))
+ paths.append(nodeName.get_path())
+
+ # store any node that has mesh data content
+ nodeContent = sceneGraph.get_node_content(node)
+ if nodeContent.CastWithTypeName('MeshData'):
+ if sceneGraph.is_node_end_point(node) is False:
+ if (len(nodeName.get_path())):
+ meshDataList.append(sceneData.SceneGraphName(sceneGraph.get_node_name(node)))
+
+ # advance to next node
+ if sceneGraph.has_node_sibling(node):
+ node = sceneGraph.get_node_sibling(node)
+ elif children:
+ node = children.pop()
+ else:
+ node = azlmbr.scene.graph.NodeIndex()
+
+ return meshDataList, paths
+
+def update_manifest(scene):
+ import json
+ import uuid, os
+ import azlmbr.scene as sceneApi
+ import azlmbr.scene.graph
+ from scene_api import scene_data as sceneData
+
+ graph = sceneData.SceneGraph(scene.graph)
+ # Get a list of all the mesh nodes, as well as all the nodes
+ mesh_name_list, all_node_paths = get_mesh_node_names(graph)
+ scene_manifest = sceneData.SceneManifest()
+
+ clean_filename = scene.sourceFilename.replace('.', '_')
+
+ # Compute the filename of the scene file
+ source_basepath = scene.watchFolder
+ source_relative_path = os.path.dirname(os.path.relpath(clean_filename, source_basepath))
+ source_filename_only = os.path.basename(clean_filename)
+
+ created_entities = []
+
+ # Loop every mesh node in the scene
+ for activeMeshIndex in range(len(mesh_name_list)):
+ mesh_name = mesh_name_list[activeMeshIndex]
+ mesh_path = mesh_name.get_path()
+ # Create a unique mesh group name using the filename + node name
+ mesh_group_name = '{}_{}'.format(source_filename_only, mesh_name.get_name())
+ # Remove forbidden filename characters from the name since this will become a file on disk later
+ mesh_group_name = "".join(char for char in mesh_group_name if char not in "|<>:\"/?*\\")
+ # Add the MeshGroup to the manifest and give it a unique ID
+ mesh_group = scene_manifest.add_mesh_group(mesh_group_name)
+ mesh_group['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, source_filename_only + mesh_path)) + '}'
+ # Set our current node as the only node that is included in this MeshGroup
+ scene_manifest.mesh_group_select_node(mesh_group, mesh_path)
+
+ # Explicitly remove all other nodes to prevent implicit inclusions
+ for node in all_node_paths:
+ if node != mesh_path:
+ scene_manifest.mesh_group_unselect_node(mesh_group, node)
+
+ # Create an editor entity
+ entity_id = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "CreateEditorReadyEntity", mesh_group_name)
+ # Add an EditorMeshComponent to the entity
+ editor_mesh_component = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "GetOrAddComponentByTypeName", entity_id, "AZ::Render::EditorMeshComponent")
+ # Set the ModelAsset assetHint to the relative path of the input asset + the name of the MeshGroup we just created + the azmodel extension
+ # The MeshGroup we created will be output as a product in the asset's path named mesh_group_name.azmodel
+ # The assetHint will be converted to an AssetId later during prefab loading
+ json_update = json.dumps({
+ "Controller": { "Configuration": { "ModelAsset": {
+ "assetHint": os.path.join(source_relative_path, mesh_group_name) + ".azmodel" }}}
+ });
+ # Apply the JSON above to the component we created
+ result = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "UpdateComponentForEntity", entity_id, editor_mesh_component, json_update)
+
+ if not result:
+ raise RuntimeError("UpdateComponentForEntity failed")
+
+ # Keep track of the entity we set up, we'll add them all to the prefab we're creating later
+ created_entities.append(entity_id)
+
+ # Create a prefab with all our entities
+ prefab_filename = source_filename_only + ".prefab"
+ created_template_id = azlmbr.prefab.PrefabSystemScriptingBus(azlmbr.bus.Broadcast, "CreatePrefab", created_entities, prefab_filename)
+
+ if created_template_id == azlmbr.prefab.InvalidTemplateId:
+ raise RuntimeError("CreatePrefab {} failed".format(prefab_filename))
+
+ # Convert the prefab to a JSON string
+ output = azlmbr.prefab.PrefabLoaderScriptingBus(azlmbr.bus.Broadcast, "SaveTemplateToString", created_template_id)
+
+ if output.IsSuccess():
+ jsonString = output.GetValue()
+ uuid = azlmbr.math.Uuid_CreateRandom().ToString()
+ jsonResult = json.loads(jsonString)
+ # Add a PrefabGroup to the manifest and store the JSON on it
+ scene_manifest.add_prefab_group(source_filename_only, uuid, jsonResult)
+ else:
+ raise RuntimeError("SaveTemplateToString failed for template id {}, prefab {}".format(created_template_id, prefab_filename))
+
+ # Convert the manifest to a JSON string and return it
+ new_manifest = scene_manifest.export()
+
+ return new_manifest
+
+sceneJobHandler = None
+
+def on_update_manifest(args):
+ try:
+ scene = args[0]
+ return update_manifest(scene)
+ except RuntimeError as err:
+ print (f'ERROR - {err}')
+ log_exception_traceback()
+
+ global sceneJobHandler
+ sceneJobHandler = None
+
+# try to create SceneAPI handler for processing
+try:
+ import azlmbr.scene as sceneApi
+ if (sceneJobHandler == None):
+ sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler()
+ sceneJobHandler.connect()
+ sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
+except:
+ sceneJobHandler = None
diff --git a/AutomatedTesting/Gem/Code/enabled_gems.cmake b/AutomatedTesting/Gem/Code/enabled_gems.cmake
index bae8afabb1..30740a489d 100644
--- a/AutomatedTesting/Gem/Code/enabled_gems.cmake
+++ b/AutomatedTesting/Gem/Code/enabled_gems.cmake
@@ -21,9 +21,9 @@ set(ENABLED_GEMS
QtForPython
PythonAssetBuilder
Metastream
-
Camera
EMotionFX
+ AtomTressFX
PhysX
CameraFramework
StartingPointMovement
@@ -52,9 +52,6 @@ set(ENABLED_GEMS
AWSCore
AWSClientAuth
AWSMetrics
-
-
-
-
+ PrefabBuilder
AudioSystem
)
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt
index 02aaa42597..ff3cd5c465 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt
+++ b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt
@@ -6,12 +6,7 @@
#
#
-################################################################################
-# Atom Renderer: Automated Tests
-# Runs EditorPythonBindings (hydra) scripts inside the Editor to verify test results for the Atom renderer.
-################################################################################
-
-if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedTesting IN_LIST LY_PROJECTS)
+if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_pytest(
NAME AutomatedTesting::Atom_TestSuite_Main
TEST_SUITE main
@@ -65,4 +60,18 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT
COMPONENT
Atom
)
+ ly_add_pytest(
+ NAME AutomatedTesting::Atom_TestSuite_Main_GPU_Optimized
+ TEST_SUITE main
+ TEST_REQUIRES gpu
+ TEST_SERIAL
+ TIMEOUT 1200
+ PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_GPU_Optimized.py
+ RUNTIME_DEPENDENCIES
+ AssetProcessor
+ AutomatedTesting.Assets
+ Editor
+ COMPONENT
+ Atom
+ )
endif()
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py
index 4c5716d365..b148ffdcdb 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py
@@ -25,6 +25,7 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests")
class TestAtomEditorComponentsMain(object):
"""Holds tests for Atom components."""
+ @pytest.mark.xfail(reason="This test is being marked xfail as it failed during an unrelated development run. See LYN-7530 for more details.")
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.
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py
index 3403d8e9b1..220623af8b 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py
@@ -220,20 +220,18 @@ class TestPerformanceBenchmarkSuite(object):
@pytest.mark.system
class TestMaterialEditor(object):
- @pytest.mark.parametrize("cfg_args", ["-rhi=dx12", "-rhi=Vulkan"])
+ @pytest.mark.parametrize("cfg_args,expected_lines", [
+ pytest.param("-rhi=dx12", ["Registering dx12 RHI"]),
+ pytest.param("-rhi=Vulkan", ["Registering vulkan RHI"])
+ ])
@pytest.mark.parametrize("exe_file_name", ["MaterialEditor"])
def test_MaterialEditorLaunch_AllRHIOptionsSucceed(
- self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args):
+ self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args,
+ expected_lines):
"""
Tests each valid RHI option (Null RHI excluded) can be launched with the MaterialEditor.
- Checks for the "Finished loading viewport configurations." success message post launch.
+ Checks for the specific expected_lines messaging for each RHI type.
"""
- expected_lines = ["Finished loading viewport configurations."]
- unexpected_lines = [
- # "Trace::Assert",
- # "Trace::Error",
- "Traceback (most recent call last):",
- ]
hydra.launch_and_validate_results(
request,
@@ -243,7 +241,7 @@ class TestMaterialEditor(object):
run_python="--runpython",
timeout=60,
expected_lines=expected_lines,
- unexpected_lines=unexpected_lines,
+ unexpected_lines=[],
halt_on_unexpected=False,
null_renderer=False,
cfg_args=[cfg_args],
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py
new file mode 100644
index 0000000000..ef572d6e5c
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py
@@ -0,0 +1,43 @@
+"""
+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 pytest
+
+import ly_test_tools.environment.file_system as file_system
+from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
+from ly_test_tools.image.screenshot_compare_qssim import qssim as compare_screenshots
+from .atom_utils.atom_component_helper import create_screenshots_archive, golden_images_directory
+
+DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots'
+
+
+@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):
+ # Remove -autotest_mode from global_extra_cmdline_args since we need rendering for these tests.
+ global_extra_cmdline_args = ["-BatchMode"] # Default is ["-BatchMode", "-autotest_mode"]
+
+ class AtomGPU_BasicLevelSetup_SetsUpLevel(EditorSharedTest):
+ use_null_renderer = False # Default is True
+ screenshot_name = "AtomBasicLevelSetup.ppm"
+ test_screenshots = [] # Gets set by setup()
+ screenshot_directory = "" # Gets set by setup()
+
+ # Clear existing test screenshots before starting test.
+ def setup(self, workspace):
+ screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
+ test_screenshots = [os.path.join(screenshot_directory, self.screenshot_name)]
+ file_system.delete(test_screenshots, True, True)
+
+ from Atom.tests import hydra_AtomGPU_BasicLevelSetup as test_module
+
+ golden_images = [os.path.join(golden_images_directory(), screenshot_name)]
+ for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images):
+ compare_screenshots(test_screenshot, golden_screenshot)
+ create_screenshots_archive(screenshot_directory)
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py
index 47b2204d56..90436fbe27 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py
@@ -14,33 +14,50 @@ from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
@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
+
class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest):
from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module
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 58b72ef01a..821e0acfdb 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py
@@ -3,13 +3,54 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright
SPDX-License-Identifier: Apache-2.0 OR MIT
-File to assist with common hydra component functions used across various Atom tests.
"""
+import datetime
import os
+import zipfile
-from editor_python_test_tools.editor_test_helper import EditorTestHelper
-helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper")
+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.
+ """
+ files_to_archive = []
+
+ # Search for .png and .ppm files to add to the zip archive file.
+ for (folder_name, sub_folders, file_names) in os.walk(screenshot_path):
+ for file_name in file_names:
+ if file_name.endswith(".png") or file_name.endswith(".ppm"):
+ file_path = os.path.join(folder_name, file_name)
+ files_to_archive.append(file_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')
+
+ # 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:
+ for file_path in files_to_archive:
+ file_name = os.path.basename(file_path)
+ zip_archive.write(file_path, file_name)
+
+
+def golden_images_directory():
+ """
+ Uses this file location to return the valid location for golden image files.
+ :return: The path to the golden_images directory, but raises an IOError if the golden_images directory is missing.
+ """
+ current_file_directory = os.path.join(os.path.dirname(__file__))
+ golden_images_dir = os.path.join(current_file_directory, '..', 'golden_images')
+
+ if not os.path.exists(golden_images_dir):
+ raise IOError(
+ f'golden_images" directory was not found at path "{golden_images_dir}"'
+ f'Please add a "golden_images" directory inside: "{current_file_directory}"'
+ )
+
+ return golden_images_dir
def create_basic_atom_level(level_name):
@@ -31,6 +72,9 @@ def create_basic_atom_level(level_name):
import azlmbr.object
import editor_python_test_tools.hydra_editor_utils as hydra
+ from editor_python_test_tools.editor_test_helper import EditorTestHelper
+
+ helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper")
# Create a new level.
new_level_name = level_name
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py
index ef0a592df0..b21c74de19 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py
@@ -211,7 +211,7 @@ class Timeout:
return time.time() > self.die_after
-screenshotsFolder = os.path.join(azlmbr.paths.devroot, "AtomTest", "Cache" "pc", "Screenshots")
+screenshotsFolder = os.path.join(azlmbr.paths.products, "Screenshots")
class ScreenshotHelper:
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py
index 602e7564b3..bbc8463152 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py
@@ -17,7 +17,7 @@ import azlmbr.legacy.general as general
import azlmbr.editor as editor
import azlmbr.render as render
-sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
+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
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py
index 751f425916..7ecdc6859b 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py
@@ -14,7 +14,7 @@ import azlmbr.math as math
import azlmbr.paths
import azlmbr.legacy.general as general
-sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests"))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from Atom.atom_utils.atom_constants import LIGHT_TYPES
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py
new file mode 100644
index 0000000000..fbf5c987d1
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_MeshAdded.py
@@ -0,0 +1,171 @@
+"""
+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")
+ mesh_entity_creation = (
+ "Mesh Entity successfully created",
+ "Mesh Entity failed to be created")
+ mesh_component_added = (
+ "Entity has a Mesh component",
+ "Entity failed to find Mesh component")
+ mesh_asset_specified = (
+ "Mesh asset set",
+ "Mesh asset 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_Mesh_AddedToEntity():
+ """
+ Summary:
+ Tests the Mesh 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 Mesh entity with no components.
+ 2) Add a Mesh component to Mesh entity.
+ 3) UNDO the entity creation and component addition.
+ 4) REDO the entity creation and component addition.
+ 5) Specify the Mesh component asset
+ 6) Enter/Exit game mode.
+ 7) Test IsHidden.
+ 8) Test IsVisible.
+ 9) Delete Mesh entity.
+ 10) UNDO deletion.
+ 11) REDO deletion.
+ 12) Look for errors.
+
+ :return: None
+ """
+
+ import os
+
+ import azlmbr.legacy.general as general
+
+ 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
+
+ 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")
+
+ # Test steps begin.
+ # 1. Create a Mesh entity with no components.
+ mesh_name = "Mesh"
+ mesh_entity = EditorEntity.create_editor_entity(mesh_name)
+ Report.critical_result(Tests.mesh_entity_creation, mesh_entity.exists())
+
+ # 2. Add a Mesh component to Mesh entity.
+ mesh_component = mesh_entity.add_component(mesh_name)
+ Report.critical_result(
+ Tests.mesh_component_added,
+ mesh_entity.has_component(mesh_name))
+
+ # 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 mesh_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, mesh_entity.exists())
+
+ # 5. Set Mesh component asset property
+ mesh_property_asset = 'Controller|Configuration|Mesh Asset'
+ 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(mesh_property_asset, model.id)
+ Report.result(Tests.mesh_asset_specified,
+ mesh_component.get_component_property_value(mesh_property_asset) == model.id)
+
+ # 6. 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)
+
+ # 7. Test IsHidden.
+ mesh_entity.set_visibility_state(False)
+ Report.result(Tests.is_hidden, mesh_entity.is_hidden() is True)
+
+ # 8. Test IsVisible.
+ mesh_entity.set_visibility_state(True)
+ general.idle_wait_frames(1)
+ Report.result(Tests.is_visible, mesh_entity.is_visible() is True)
+
+ # 9. Delete Mesh entity.
+ mesh_entity.delete()
+ Report.result(Tests.entity_deleted, not mesh_entity.exists())
+
+ # 10. UNDO deletion.
+ general.undo()
+ Report.result(Tests.deletion_undo, mesh_entity.exists())
+
+ # 11. REDO deletion.
+ general.redo()
+ Report.result(Tests.deletion_redo, not mesh_entity.exists())
+
+ # 12. Look for errors or asserts.
+ helper.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_Mesh_AddedToEntity)
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py
new file mode 100644
index 0000000000..9b13eb2c7e
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ReflectionProbeAdded.py
@@ -0,0 +1,194 @@
+"""
+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")
+ reflection_probe_creation = (
+ "Reflection Probe Entity successfully created",
+ "Reflection Probe Entity failed to be created")
+ reflection_probe_component = (
+ "Entity has a Reflection Probe component",
+ "Entity failed to find Reflection Probe component")
+ reflection_probe_disabled = (
+ "Reflection Probe component disabled",
+ "Reflection Probe component was not disabled.")
+ reflection_map_generated = (
+ "Reflection Probe cubemap generated",
+ "Reflection Probe cubemap not generated")
+ box_shape_component = (
+ "Entity has a Box Shape component",
+ "Entity did not have a Box Shape component")
+ reflection_probe_enabled = (
+ "Reflection Probe component enabled",
+ "Reflection Probe 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_ReflectionProbe_AddedToEntity():
+ """
+ Summary:
+ Tests the Reflection Probe 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 Reflection Probe entity with no components.
+ 2) Add a Reflection Probe component to Reflection Probe entity.
+ 3) UNDO the entity creation and component addition.
+ 4) REDO the entity creation and component addition.
+ 5) Verify Reflection Probe component not enabled.
+ 6) Add Shape component since it is required by the Reflection Probe component.
+ 7) Verify Reflection Probe component is enabled.
+ 8) Enter/Exit game mode.
+ 9) Test IsHidden.
+ 10) Test IsVisible.
+ 11) Verify cubemap generation
+ 12) Delete Reflection Probe entity.
+ 13) UNDO deletion.
+ 14) REDO deletion.
+ 15) Look for errors.
+
+ :return: None
+ """
+
+ 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
+
+ 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")
+
+ # 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)
+ 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)
+ Report.critical_result(
+ Tests.reflection_probe_component,
+ reflection_probe_entity.has_component(reflection_probe_name))
+
+ # 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 reflection_probe_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, reflection_probe_entity.exists())
+
+ # 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))
+
+ # 7. Verify Reflection Probe component is enabled.
+ Report.result(Tests.reflection_probe_enabled, reflection_probe_component.is_enabled())
+
+ # 8. 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)
+
+ # 9. Test IsHidden.
+ reflection_probe_entity.set_visibility_state(False)
+ Report.result(Tests.is_hidden, reflection_probe_entity.is_hidden() is True)
+
+ # 10. Test IsVisible.
+ reflection_probe_entity.set_visibility_state(True)
+ general.idle_wait_frames(1)
+ Report.result(Tests.is_visible, reflection_probe_entity.is_visible() is True)
+
+ # 11. Verify cubemap generation
+ 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") != "",
+ 20.0))
+
+ # 12. Delete Reflection Probe entity.
+ reflection_probe_entity.delete()
+ Report.result(Tests.entity_deleted, not reflection_probe_entity.exists())
+
+ # 13. UNDO deletion.
+ general.undo()
+ Report.result(Tests.deletion_undo, reflection_probe_entity.exists())
+
+ # 14. REDO deletion.
+ general.redo()
+ 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)
+ 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_ReflectionProbe_AddedToEntity)
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py
new file mode 100644
index 0000000000..92c555127a
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py
@@ -0,0 +1,292 @@
+"""
+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
+"""
+
+
+# fmt: off
+class Tests :
+ camera_component_added = ("Camera component was added", "Camera component wasn't added")
+ camera_fov_set = ("Camera component FOV property set", "Camera component FOV property wasn't set")
+ directional_light_component_added = ("Directional Light component added", "Directional Light component wasn't added")
+ enter_game_mode = ("Entered game mode", "Failed to enter game mode")
+ exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
+ global_skylight_component_added = ("Global Skylight (IBL) component added", "Global Skylight (IBL) component wasn't added")
+ global_skylight_diffuse_image_set = ("Global Skylight Diffuse Image property set", "Global Skylight Diffuse Image property wasn't set")
+ global_skylight_specular_image_set = ("Global Skylight Specular Image property set", "Global Skylight Specular Image property wasn't set")
+ ground_plane_material_asset_set = ("Ground Plane Material Asset was set", "Ground Plane Material Asset wasn't set")
+ ground_plane_material_component_added = ("Ground Plane Material component added", "Ground Plane Material component wasn't added")
+ ground_plane_mesh_asset_set = ("Ground Plane Mesh Asset property was set", "Ground Plane Mesh Asset property wasn't set")
+ hdri_skybox_component_added = ("HDRi Skybox component added", "HDRi Skybox component wasn't added")
+ hdri_skybox_cubemap_texture_set = ("HDRi Skybox Cubemap Texture property set", "HDRi Skybox Cubemap Texture property wasn't set")
+ mesh_component_added = ("Mesh component added", "Mesh component wasn't added")
+ no_assert_occurred = ("No asserts detected", "Asserts were detected")
+ no_error_occurred = ("No errors detected", "Errors were detected")
+ secondary_grid_spacing = ("Secondary Grid Spacing set", "Secondary Grid Spacing not set")
+ sphere_material_component_added = ("Sphere Material component added", "Sphere Material component wasn't added")
+ sphere_material_set = ("Sphere Material Asset was set", "Sphere Material Asset wasn't set")
+ sphere_mesh_asset_set = ("Sphere Mesh Asset was set", "Sphere Mesh Asset wasn't set")
+ viewport_set = ("Viewport set to correct size", "Viewport not set to correct size")
+# fmt: on
+
+
+def AtomGPU_BasicLevelSetup_SetsUpLevel():
+ """
+ Summary:
+ Sets up a level to match the AtomBasicLevelSetup.ppm golden image then takes a screenshot to verify the setup.
+
+ Test setup:
+ - Wait for Editor idle loop.
+ - Open the "Base" level.
+
+ Expected Behavior:
+ The scene can be setup for a basic level.
+ The test screenshot matches the appearance of the AtomBasicLevelSetup.ppm golden image.
+
+ Test Steps:
+ 1. Close error windows and display helpers then update the viewport size.
+ 2. Create Default Level Entity.
+ 3. Create Grid Entity as a child entity of the Default Level Entity.
+ 4. Add Grid component to Grid Entity and set Secondary Grid Spacing.
+ 5. Create Global Skylight (IBL) Entity as a child entity of the Default Level Entity.
+ 6. Add HDRi Skybox component to the Global Skylight (IBL) Entity.
+ 7. Add Global Skylight (IBL) component to the Global Skylight (IBL) Entity.
+ 8. Set the Cubemap Texture property of the HDRi Skybox component.
+ 9. Set the Diffuse Image property of the Global Skylight (IBL) component.
+ 10. Set the Specular Image property of the Global Skylight (IBL) component.
+ 11. Create a Ground Plane Entity with a Material component that is a child entity of the Default Level Entity.
+ 12. Set the Material Asset property of the Material component for the Ground Plane Entity.
+ 13. Add the Mesh component to the Ground Plane Entity and set the Mesh component Mesh Asset property.
+ 14. Create a Directional Light Entity as a child entity of the Default Level Entity.
+ 15. Add Directional Light component to Directional Light Entity and set entity rotation.
+ 16. Create a Sphere Entity as a child entity of the Default Level Entity then add a Material component.
+ 17. Set the Material Asset property of the Material component for the Sphere Entity.
+ 18. Add Mesh component to Sphere Entity and set the Mesh Asset property for the Mesh component.
+ 19. Create a Camera Entity as a child entity of the Default Level Entity then add a Camera component.
+ 20. Set the Camera Entity rotation value and set the Camera component Field of View value.
+ 21. Enter game mode.
+ 22. Take screenshot.
+ 23. Exit game mode.
+ 24. Look for errors.
+
+ :return: None
+ """
+
+ import os
+ from math import isclose
+
+ import azlmbr.asset as asset
+ import azlmbr.bus as bus
+ import azlmbr.legacy.general as general
+ import azlmbr.math as math
+ import azlmbr.paths
+
+ from editor_python_test_tools.editor_entity_utils import EditorEntity
+ from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
+
+ from Atom.atom_utils.screenshot_utils import ScreenshotHelper
+
+ MATERIAL_COMPONENT_NAME = "Material"
+ MESH_COMPONENT_NAME = "Mesh"
+ SCREENSHOT_NAME = "AtomBasicLevelSetup"
+ SCREEN_WIDTH = 1280
+ SCREEN_HEIGHT = 720
+ DEGREE_RADIAN_FACTOR = 0.0174533
+
+ def initial_viewport_setup(screen_width, screen_height):
+ general.set_viewport_size(screen_width, screen_height)
+ general.update_viewport()
+ result = isclose(
+ a=general.get_viewport_size().x, b=SCREEN_WIDTH, rel_tol=0.1) and isclose(
+ a=general.get_viewport_size().y, b=SCREEN_HEIGHT, rel_tol=0.1)
+
+ return result
+
+ 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")
+
+ # Test steps begin.
+ # 1. Close error windows and display helpers then update the viewport size.
+ helper.close_error_windows()
+ helper.close_display_helpers()
+ general.update_viewport()
+ Report.critical_result(Tests.viewport_set, initial_viewport_setup(SCREEN_WIDTH, SCREEN_HEIGHT))
+
+ # 2. Create Default Level Entity.
+ default_level_entity_name = "Default Level"
+ default_level_entity = EditorEntity.create_editor_entity_at(
+ math.Vector3(0.0, 0.0, 0.0), default_level_entity_name)
+
+ # 3. Create Grid Entity as a child entity of the Default Level Entity.
+ grid_name = "Grid"
+ grid_entity = EditorEntity.create_editor_entity(grid_name, default_level_entity.id)
+
+ # 4. Add Grid component to Grid Entity and set Secondary Grid Spacing.
+ grid_component = grid_entity.add_component(grid_name)
+ secondary_grid_spacing_property = "Controller|Configuration|Secondary Grid Spacing"
+ secondary_grid_spacing_value = 1.0
+ grid_component.set_component_property_value(secondary_grid_spacing_property, secondary_grid_spacing_value)
+ secondary_grid_spacing_set = grid_component.get_component_property_value(
+ secondary_grid_spacing_property) == secondary_grid_spacing_value
+ Report.result(Tests.secondary_grid_spacing, secondary_grid_spacing_set)
+
+ # 5. Create Global Skylight (IBL) Entity as a child entity of the Default Level Entity.
+ global_skylight_name = "Global Skylight (IBL)"
+ global_skylight_entity = EditorEntity.create_editor_entity(global_skylight_name, default_level_entity.id)
+
+ # 6. Add HDRi Skybox component to the Global Skylight (IBL) Entity.
+ hdri_skybox_name = "HDRi Skybox"
+ hdri_skybox_component = global_skylight_entity.add_component(hdri_skybox_name)
+ Report.result(Tests.hdri_skybox_component_added, global_skylight_entity.has_component(hdri_skybox_name))
+
+ # 7. Add Global Skylight (IBL) component to the Global Skylight (IBL) Entity.
+ global_skylight_component = global_skylight_entity.add_component(global_skylight_name)
+ Report.result(Tests.global_skylight_component_added, global_skylight_entity.has_component(global_skylight_name))
+
+ # 8. Set the Cubemap Texture property of the HDRi Skybox component.
+ global_skylight_image_asset_path = os.path.join(
+ "LightingPresets", "greenwich_park_02_4k_iblskyboxcm_iblspecular.exr.streamingimage")
+ global_skylight_image_asset = asset.AssetCatalogRequestBus(
+ bus.Broadcast, "GetAssetIdByPath", global_skylight_image_asset_path, math.Uuid(), False)
+ hdri_skybox_cubemap_texture_property = "Controller|Configuration|Cubemap Texture"
+ hdri_skybox_component.set_component_property_value(
+ hdri_skybox_cubemap_texture_property, global_skylight_image_asset)
+ Report.result(
+ Tests.hdri_skybox_cubemap_texture_set,
+ hdri_skybox_component.get_component_property_value(
+ hdri_skybox_cubemap_texture_property) == global_skylight_image_asset)
+
+ # 9. Set the Diffuse Image property of the Global Skylight (IBL) component.
+ # Re-use the same image that was used in the previous test step.
+ global_skylight_diffuse_image_property = "Controller|Configuration|Diffuse Image"
+ global_skylight_component.set_component_property_value(
+ global_skylight_diffuse_image_property, global_skylight_image_asset)
+ Report.result(
+ Tests.global_skylight_diffuse_image_set,
+ global_skylight_component.get_component_property_value(
+ global_skylight_diffuse_image_property) == global_skylight_image_asset)
+
+ # 10. Set the Specular Image property of the Global Skylight (IBL) component.
+ # Re-use the same image that was used in the previous test step.
+ global_skylight_specular_image_property = "Controller|Configuration|Specular Image"
+ global_skylight_component.set_component_property_value(
+ global_skylight_specular_image_property, global_skylight_image_asset)
+ global_skylight_specular_image_set = global_skylight_component.get_component_property_value(
+ global_skylight_specular_image_property)
+ Report.result(
+ Tests.global_skylight_specular_image_set, global_skylight_specular_image_set == global_skylight_image_asset)
+
+ # 11. Create a Ground Plane Entity with a Material component that is a child entity of the Default Level Entity.
+ ground_plane_name = "Ground Plane"
+ ground_plane_entity = EditorEntity.create_editor_entity(ground_plane_name, default_level_entity.id)
+ ground_plane_material_component = ground_plane_entity.add_component(MATERIAL_COMPONENT_NAME)
+ Report.result(
+ Tests.ground_plane_material_component_added, ground_plane_entity.has_component(MATERIAL_COMPONENT_NAME))
+
+ # 12. Set the Material Asset property of the Material component for the Ground Plane Entity.
+ ground_plane_entity.set_local_uniform_scale(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_material_asset_property = "Default Material|Material Asset"
+ ground_plane_material_component.set_component_property_value(
+ ground_plane_material_asset_property, ground_plane_material_asset)
+ Report.result(
+ Tests.ground_plane_material_asset_set,
+ ground_plane_material_component.get_component_property_value(
+ ground_plane_material_asset_property) == ground_plane_material_asset)
+
+ # 13. Add the Mesh component to the Ground Plane Entity and set the Mesh component Mesh Asset property.
+ ground_plane_mesh_component = ground_plane_entity.add_component(MESH_COMPONENT_NAME)
+ Report.result(Tests.mesh_component_added, ground_plane_entity.has_component(MESH_COMPONENT_NAME))
+ ground_plane_mesh_asset_path = os.path.join("Objects", "plane.azmodel")
+ ground_plane_mesh_asset = asset.AssetCatalogRequestBus(
+ bus.Broadcast, "GetAssetIdByPath", ground_plane_mesh_asset_path, math.Uuid(), False)
+ ground_plane_mesh_asset_property = "Controller|Configuration|Mesh Asset"
+ ground_plane_mesh_component.set_component_property_value(
+ ground_plane_mesh_asset_property, ground_plane_mesh_asset)
+ Report.result(
+ Tests.ground_plane_mesh_asset_set,
+ ground_plane_mesh_component.get_component_property_value(
+ ground_plane_mesh_asset_property) == ground_plane_mesh_asset)
+
+ # 14. Create a Directional Light Entity as a child entity of the Default Level Entity.
+ directional_light_name = "Directional Light"
+ directional_light_entity = EditorEntity.create_editor_entity_at(
+ math.Vector3(0.0, 0.0, 10.0), directional_light_name, default_level_entity.id)
+
+ # 15. Add Directional Light component to Directional Light Entity and set entity rotation.
+ directional_light_entity.add_component(directional_light_name)
+ directional_light_entity_rotation = math.Vector3(DEGREE_RADIAN_FACTOR * -90.0, 0.0, 0.0)
+ directional_light_entity.set_local_rotation(directional_light_entity_rotation)
+ Report.result(
+ Tests.directional_light_component_added, directional_light_entity.has_component(directional_light_name))
+
+ # 16. Create a Sphere Entity as a child entity of the Default Level Entity then add a Material component.
+ sphere_entity = EditorEntity.create_editor_entity_at(
+ math.Vector3(0.0, 0.0, 1.0), "Sphere", default_level_entity.id)
+ sphere_material_component = sphere_entity.add_component(MATERIAL_COMPONENT_NAME)
+ Report.result(Tests.sphere_material_component_added, sphere_entity.has_component(MATERIAL_COMPONENT_NAME))
+
+ # 17. Set the Material Asset property of the Material component for the Sphere Entity.
+ 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_material_asset_property = "Default Material|Material Asset"
+ sphere_material_component.set_component_property_value(sphere_material_asset_property, sphere_material_asset)
+ Report.result(Tests.sphere_material_set, sphere_material_component.get_component_property_value(
+ sphere_material_asset_property) == sphere_material_asset)
+
+ # 18. Add Mesh component to Sphere Entity and set the Mesh Asset property for the Mesh component.
+ sphere_mesh_component = sphere_entity.add_component(MESH_COMPONENT_NAME)
+ sphere_mesh_asset_path = os.path.join("Models", "sphere.azmodel")
+ sphere_mesh_asset = asset.AssetCatalogRequestBus(
+ bus.Broadcast, "GetAssetIdByPath", sphere_mesh_asset_path, math.Uuid(), False)
+ sphere_mesh_asset_property = "Controller|Configuration|Mesh Asset"
+ sphere_mesh_component.set_component_property_value(sphere_mesh_asset_property, sphere_mesh_asset)
+ Report.result(Tests.sphere_mesh_asset_set, sphere_mesh_component.get_component_property_value(
+ sphere_mesh_asset_property) == sphere_mesh_asset)
+
+ # 19. Create a Camera Entity as a child entity of the Default Level Entity then add a Camera component.
+ camera_name = "Camera"
+ camera_entity = EditorEntity.create_editor_entity_at(
+ math.Vector3(5.5, -12.0, 9.0), camera_name, default_level_entity.id)
+ camera_component = camera_entity.add_component(camera_name)
+ Report.result(Tests.camera_component_added, camera_entity.has_component(camera_name))
+
+ # 20. Set the Camera Entity rotation value and set the Camera component Field of View value.
+ camera_entity_rotation = math.Vector3(
+ DEGREE_RADIAN_FACTOR * -27.0, DEGREE_RADIAN_FACTOR * -12.0, DEGREE_RADIAN_FACTOR * 25.0)
+ camera_entity.set_local_rotation(camera_entity_rotation)
+ camera_fov_property = "Controller|Configuration|Field of view"
+ camera_fov_value = 60.0
+ camera_component.set_component_property_value(camera_fov_property, camera_fov_value)
+ azlmbr.camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id)
+ Report.result(Tests.camera_fov_set, camera_component.get_component_property_value(
+ camera_fov_property) == camera_fov_value)
+
+ # 21. Enter game mode.
+ helper.enter_game_mode(Tests.enter_game_mode)
+ helper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=4.0)
+
+ # 22. Take screenshot.
+ ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(f"{SCREENSHOT_NAME}.ppm")
+
+ # 23. Exit game mode.
+ helper.exit_game_mode(Tests.exit_game_mode)
+ helper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=4.0)
+
+ # 24. Look for errors.
+ helper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+ Report.result(Tests.no_assert_occurred, not error_tracer.has_asserts)
+ Report.result(Tests.no_error_occurred, not error_tracer.has_errors)
+
+
+if __name__ == "__main__":
+ from editor_python_test_tools.utils import Report
+ Report.start_test(AtomGPU_BasicLevelSetup_SetsUpLevel)
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py
index 88a1ef4c7b..9f8f6c44b2 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py
@@ -16,7 +16,7 @@ import time
import azlmbr.math as math
import azlmbr.paths
-sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests"))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
import Atom.atom_utils.material_editor_utils as material_editor
@@ -27,10 +27,10 @@ TEST_MATERIAL_1 = "001_DefaultWhite.material"
TEST_MATERIAL_2 = "002_BaseColorLerp.material"
TEST_MATERIAL_3 = "003_MetalMatte.material"
TEST_DATA_PATH = os.path.join(
- azlmbr.paths.devroot, "Gems", "Atom", "TestData", "TestData", "Materials", "StandardPbrTestCases"
+ azlmbr.paths.engroot, "Gems", "Atom", "TestData", "TestData", "Materials", "StandardPbrTestCases"
)
MATERIAL_TYPE_PATH = os.path.join(
- azlmbr.paths.devroot, "Gems", "Atom", "Feature", "Common", "Assets",
+ azlmbr.paths.engroot, "Gems", "Atom", "Feature", "Common", "Assets",
"Materials", "Types", "StandardPBR.materialtype",
)
CACHE_FILE_EXTENSION = ".azmaterial"
@@ -61,7 +61,7 @@ def run():
print(f"Material opened: {material_editor.is_open(document_id)}")
# Verify if the test material exists initially
- target_path = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL)
+ target_path = os.path.join(azlmbr.paths.projectroot, "Materials", NEW_MATERIAL)
print(f"Test asset doesn't exist initially: {not os.path.exists(target_path)}")
# 2) Test Case: Creating a New Material Using Existing One
@@ -109,10 +109,10 @@ def run():
# Assign new color to the material file and save the document as copy
expected_color_1 = math.Color(0.5, 0.5, 0.5, 1.0)
material_editor.set_property(document_id, property_name, expected_color_1)
- target_path_1 = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL_1)
+ target_path_1 = os.path.join(azlmbr.paths.projectroot, "Materials", NEW_MATERIAL_1)
cache_file_name_1 = os.path.splitext(NEW_MATERIAL_1) # Example output: ('test_material_1', '.material')
cache_file_1 = f"{cache_file_name_1[0]}{CACHE_FILE_EXTENSION}"
- target_path_1_cache = os.path.join(azlmbr.paths.devassets, "Cache", "pc", "materials", cache_file_1)
+ target_path_1_cache = os.path.join(azlmbr.paths.products, "materials", cache_file_1)
material_editor.save_document_as_copy(document_id, target_path_1)
material_editor.wait_for_condition(lambda: os.path.exists(target_path_1_cache), 4.0)
@@ -120,10 +120,10 @@ def run():
# Assign new color to the material file save the document as child
expected_color_2 = math.Color(0.75, 0.75, 0.75, 1.0)
material_editor.set_property(document_id, property_name, expected_color_2)
- target_path_2 = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL_2)
+ target_path_2 = os.path.join(azlmbr.paths.projectroot, "Materials", NEW_MATERIAL_2)
cache_file_name_2 = os.path.splitext(NEW_MATERIAL_1) # Example output: ('test_material_2', '.material')
cache_file_2 = f"{cache_file_name_2[0]}{CACHE_FILE_EXTENSION}"
- target_path_2_cache = os.path.join(azlmbr.paths.devassets, "Cache", "pc", "materials", cache_file_2)
+ target_path_2_cache = os.path.join(azlmbr.paths.products, "materials", cache_file_2)
material_editor.save_document_as_child(document_id, target_path_2)
material_editor.wait_for_condition(lambda: os.path.exists(target_path_2_cache), 4.0)
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py
index 4f7edeba75..92199bf196 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py
@@ -10,7 +10,7 @@ import sys
import azlmbr.legacy.general as general
-sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
+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
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py
index 62a122a723..ac28e67fa1 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py
@@ -17,7 +17,7 @@ import azlmbr.math as math
import azlmbr.paths
import azlmbr.editor as editor
-sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
+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
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py
index 4a3ae8c85d..1c3e6226c1 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py
@@ -14,7 +14,7 @@ import azlmbr.math as math
import azlmbr.paths
import azlmbr.legacy.general as general
-sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from Atom.atom_utils import atom_component_helper, atom_constants, screenshot_utils
diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test.py b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test.py
index 1d3ca11618..fcce6eab37 100755
--- a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test.py
+++ b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test.py
@@ -42,7 +42,6 @@ class TestEditorAutomation(object):
"editor command line arg bar",
"editor command line arg baz",
"editor engroot set",
- "editor devroot set",
"path resolved worked"
]
diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test_case.py b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test_case.py
index bd8791fad6..c6ae65612f 100755
--- a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test_case.py
+++ b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test_case.py
@@ -20,12 +20,6 @@ if (engroot is not None and len(engroot) is not 0):
print ('engroot is {}'.format(engroot))
print ('editor engroot set')
-# make sure the @devroot@ exists as a azlmbr.paths property
-devroot = azlmbr.paths.devroot
-if (devroot is not None and len(devroot) != 0):
- print ('devroot is {}'.format(devroot))
- print ('editor devroot set')
-
# resolving a basic path
path = azlmbr.paths.resolve_path('@engroot@/engineassets/texturemsg/defaultsolids.mtl')
if (len(path) != 0 and path.find('@engroot@') == -1):
diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorScripts/ComponentUpdateListProperty_test_case.py b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorScripts/ComponentUpdateListProperty_test_case.py
index 5b7f2f42c1..c5ca4de603 100644
--- a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorScripts/ComponentUpdateListProperty_test_case.py
+++ b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorScripts/ComponentUpdateListProperty_test_case.py
@@ -16,7 +16,7 @@ import azlmbr.entity as entity
import azlmbr.math as math
import azlmbr.paths
-sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, 'Gem', 'PythonTests'))
from automatedtesting_shared.editor_test_helper import EditorTestHelper
diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/LevelComponentCommands.cfg b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/LevelComponentCommands.cfg
index 3adc32d20a..ccc605cec9 100644
--- a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/LevelComponentCommands.cfg
+++ b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/LevelComponentCommands.cfg
@@ -1,2 +1,2 @@
# this file is copied to $/dev/editor_autoexec.cfg so the the Editor automation runs for this Hydra test
-pyRunFile @devroot@/Tests/hydra/LevelComponentCommands_test_case.py exit_when_done
\ No newline at end of file
+pyRunFile @engroot@/Tests/hydra/LevelComponentCommands_test_case.py exit_when_done
\ No newline at end of file
diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/ViewportTitleDlgCommands.cfg b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/ViewportTitleDlgCommands.cfg
index 6230dfe0fa..3cbd84c1b5 100644
--- a/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/ViewportTitleDlgCommands.cfg
+++ b/AutomatedTesting/Gem/PythonTests/EditorPythonBindings/ViewportTitleDlgCommands.cfg
@@ -1,2 +1,2 @@
# this file is copied to $/dev/editor_autoexec.cfg so the the Editor automation runs for this Hydra test
-pyRunFile @devroot@/Tests/hydra/ViewportTitleDlgCommands_test_case.py
\ No newline at end of file
+pyRunFile @engroot@/Tests/hydra/ViewportTitleDlgCommands_test_case.py
\ No newline at end of file
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 154b5730d7..783f71e06c 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
@@ -361,3 +361,19 @@ class EditorEntity:
:return: True if "isVisible" is enabled, False otherwise.
"""
return editor.EditorEntityInfoRequestBus(bus.Event, "IsVisible", self.id)
+
+ def set_local_uniform_scale(self, scale_float) -> None:
+ """
+ Sets the "SetLocalUniformScale" value 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:
+ """
+ Sets the "SetLocalRotation" value on the 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)
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 cc217f81da..ef3048d8d4 100644
--- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py
+++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py
@@ -4,18 +4,18 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
+
+import json
+import math
import os
import time
-import math
+import traceback
+from typing import Callable, Tuple
import azlmbr
import azlmbr.legacy.general as general
import azlmbr.debug
-import json
-import traceback
-
-from typing import Callable, Tuple
class FailFast(Exception):
"""
@@ -127,6 +127,31 @@ class TestHelper:
if ret:
return True
+ @staticmethod
+ def close_error_windows():
+ """
+ Closes Error Report and Error Log windows that block focus if they are visible.
+ :return: None
+ """
+ if general.is_pane_visible("Error Report"):
+ general.close_pane("Error Report")
+ if general.is_pane_visible("Error Log"):
+ general.close_pane("Error Log")
+
+ @staticmethod
+ def close_display_helpers():
+ """
+ Closes helper gizmos, anti-aliasing, and FPS meters.
+ :return: None
+ """
+ if general.is_helpers_shown():
+ general.toggle_helpers()
+ general.idle_wait(1.0)
+ general.idle_wait(1.0)
+ general.run_console("r_displayInfo=0")
+ general.run_console("r_antialiasingmode=0")
+ general.idle_wait(1.0)
+
class Timeout:
# type: (float) -> None
@@ -149,6 +174,7 @@ class Timeout:
def timed_out(self):
return time.time() > self.die_after
+
class Report:
_results = []
_exception = None
@@ -290,8 +316,8 @@ class Report:
Report.info(" x: {:.2f}, y: {:.2f}, z: {:.2f}".format(vector3.x, vector3.y, vector3.z))
if magnitude is not None:
Report.info(" magnitude: {:.2f}".format(magnitude))
-
-
+
+
'''
Utility for scope tracing errors and warnings.
Usage:
@@ -303,7 +329,7 @@ Usage:
Report.result(Tests.warnings_not_found_in_section, not section_tracer.has_warnings)
-'''
+'''
class Tracer:
def __init__(self):
self.warnings = []
@@ -349,10 +375,10 @@ class Tracer:
self.line = args[1]
self.function = args[2]
self.message = args[3]
-
+
def __str__(self):
return f"Assert: [{self.filename}:{self.function}:{self.line}]: {self.message}"
-
+
def __repr__(self):
return f"[Assert: {self.message}]"
@@ -360,21 +386,21 @@ class Tracer:
def __init__(self, args):
self.window = args[0]
self.message = args[1]
-
+
def _on_warning(self, args):
warningInfo = Tracer.WarningInfo(args)
self.warnings.append(warningInfo)
Report.info("Tracer caught Warning: %s" % warningInfo.message)
self.has_warnings = True
return False
-
+
def _on_error(self, args):
errorInfo = Tracer.ErrorInfo(args)
self.errors.append(errorInfo)
Report.info("Tracer caught Error: %s" % errorInfo.message)
self.has_errors = True
return False
-
+
def _on_assert(self, args):
assertInfo = Tracer.AssertInfo(args)
self.asserts.append(assertInfo)
@@ -436,6 +462,7 @@ class AngleHelper:
def vector3_str(vector3):
return "(x: {:.2f}, y: {:.2f}, z: {:.2f})".format(vector3.x, vector3.y, vector3.z)
-
+
+
def aabb_str(aabb):
return "[Min: %s, Max: %s]" % (vector3_str(aabb.min), vector3_str(aabb.max))
diff --git a/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Periodic.py
index da89316993..e5dd9adbb9 100755
--- a/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Periodic.py
+++ b/AutomatedTesting/Gem/PythonTests/Physics/TestSuite_Periodic.py
@@ -525,10 +525,6 @@ class TestAutomation(TestAutomationBase):
from .tests.joints import Joints_BallNoLimitsConstrained as test_module
self._run_test(request, workspace, editor, test_module)
- def test_Joints_FixedLeadFollowerCollide(self, request, workspace, editor, launcher_platform):
- from .tests.joints import Joints_FixedLeadFollowerCollide as test_module
- self._run_test(request, workspace, editor, test_module)
-
def test_Joints_GlobalFrameConstrained(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_GlobalFrameConstrained as test_module
self._run_test(request, workspace, editor, test_module)
diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_SplineRegionWithModifiedTransform.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_SplineRegionWithModifiedTransform.py
index abc58779a2..e949d2d8f9 100644
--- a/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_SplineRegionWithModifiedTransform.py
+++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_SplineRegionWithModifiedTransform.py
@@ -82,7 +82,7 @@ def ForceRegion_SplineRegionWithModifiedTransform():
import azlmbr.bus as bus
# region Constants
- TIMEOUT = 5.0
+ TIMEOUT = 10.0
MIN_TRIGGER_DISTANCE = 2.0
# endregion
diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_ZeroPointForceDoesNothing.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_ZeroPointForceDoesNothing.py
index 91190f83e5..6582628ff9 100644
--- a/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_ZeroPointForceDoesNothing.py
+++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_ZeroPointForceDoesNothing.py
@@ -77,7 +77,7 @@ def ForceRegion_ZeroPointForceDoesNothing():
helper.init_idle()
- TIMEOUT_SECONDS = 3.0
+ TIMEOUT_SECONDS = 5.0
X_Y_Z_TOLERANCE = 1.5
REGION_HEIGHT = 3.0
TERRAIN_HEIGHT = 32.0
diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/JointsHelper.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/JointsHelper.py
index 6226f42534..a85e460659 100644
--- a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/JointsHelper.py
+++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/JointsHelper.py
@@ -45,8 +45,13 @@ class JointEntity:
# Entity class that sets a flag when an instance receives collision events.
class JointEntityCollisionAware(JointEntity):
def on_collision_begin(self, args):
- if not self.collided:
- self.collided = True
+ self.collided = True
+
+ def on_collision_persist(self, args):
+ self.collided = True
+
+ def on_collision_end(self, args):
+ self.collided = True
def __init__(self, name):
self.id = general.find_game_entity(name)
@@ -58,3 +63,5 @@ class JointEntityCollisionAware(JointEntity):
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
self.handler.connect(self.id)
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
+ self.handler.add_callback("OnCollisionPresist", self.on_collision_persist)
+ self.handler.add_callback("OnCollisionEnd", self.on_collision_end)
diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_FixedLeadFollowerCollide.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_FixedLeadFollowerCollide.py
deleted file mode 100644
index e142941c5a..0000000000
--- a/AutomatedTesting/Gem/PythonTests/Physics/tests/joints/Joints_FixedLeadFollowerCollide.py
+++ /dev/null
@@ -1,92 +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
-"""
-
-# Test case ID : C18243582
-# Test Case Title : Check that fixed joint allows lead-follower collision
-
-
-# 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")
- lead_found = ("Found lead", "Did not find lead")
- follower_found = ("Found follower", "Did not find follower")
- check_collision_happened = ("Lead and follower collided", "Lead and follower did not collide")
-# fmt: on
-
-
-def Joints_FixedLeadFollowerCollide():
- """
- Summary: Check that fixed joint allows lead-follower collision
-
- Level Description:
- lead - Starts above follower entity
- follower - Starts below lead entity. Constrained to lead entity with fixed joint. Starts with initial velocity of (5, 0, 0) in positive X direction.
-
- Expected Behavior:
- The follower entity moves in the positive X direction and the lead entity is dragged along towards the positive X direction.
- The x position of the lead entity is incremented from its original.
- The lead and follower entities are kept apart at a distance of approximately 1.0 due to collision.
-
- Test Steps:
- 1) Open Level
- 2) Enter Game Mode
- 3) Create and Validate Entities
- 4) Wait for several seconds
- 5) Check to see if lead and follower behaved as expected.
- 6) Exit Game Mode
- 7) Close Editor
-
- Note:
- - This test file must be called from the Open 3D Engine Editor command terminal
- - Any passed and failed tests are written to the Editor.log file.
- Parsing the file or running a log_monitor are required to observe the test results.
-
- :return: None
- """
- import os
- import sys
- from editor_python_test_tools.utils import Report
- from editor_python_test_tools.utils import TestHelper as helper
-
- import azlmbr.legacy.general as general
- import azlmbr.bus
-
- from JointsHelper import JointEntityCollisionAware
-
- # Helper Entity class - self.collided flag is set when instance receives collision event.
- class Entity(JointEntityCollisionAware):
- def criticalEntityFound(self): # Override function to use local Test dictionary
- Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
-
- # Main Script
- helper.init_idle()
-
- # 1) Open Level
- helper.open_level("Physics", "Joints_FixedLeadFollowerCollide")
-
- # 2) Enter Game Mode
- helper.enter_game_mode(Tests.enter_game_mode)
-
- # 3) Create and Validate Entities
- lead = Entity("lead")
- follower = Entity("follower")
-
- # 4) Wait for several seconds
- general.idle_wait(2.0) # wait for lead and follower to move
-
- # 5) Check to see if lead entity and follower collided
- Report.critical_result(Tests.check_collision_happened, lead.collided and follower.collided)
-
- # 6) 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(Joints_FixedLeadFollowerCollide)
diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/rigid_body/RigidBody_KinematicModeWorks.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/rigid_body/RigidBody_KinematicModeWorks.py
index 1c53854bab..05671389c2 100644
--- a/AutomatedTesting/Gem/PythonTests/Physics/tests/rigid_body/RigidBody_KinematicModeWorks.py
+++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/rigid_body/RigidBody_KinematicModeWorks.py
@@ -80,6 +80,20 @@ def RigidBody_KinematicModeWorks():
ramp_id = general.find_game_entity("Ramp")
Report.result(Tests.find_ramp, ramp_id.IsValid())
+ # 2.1) setup collision handler
+ class RampTouched:
+ value = False
+
+ def on_collision_begin(args):
+ other_id = args[0]
+ if other_id.Equal(ramp_id):
+ Report.info("Box touched ramp")
+ RampTouched.value = True
+
+ handler = azlmbr.physics.CollisionNotificationBusHandler()
+ handler.connect(box_id)
+ handler.add_callback("OnCollisionBegin", on_collision_begin)
+
# 3) Check for kinematic ramp and not kinematic box
box_kinematic = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsKinematic", box_id)
Report.result(Tests.box_is_not_kinematic, not box_kinematic)
@@ -98,21 +112,7 @@ def RigidBody_KinematicModeWorks():
ramp_pos_start = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", ramp_id)
Report.info("Ramp's initial position: {}".format(ramp_pos_start))
- # 6) Check to see that the box hits the ramp
- class RampTouched:
- value = False
-
- def on_collision_begin(args):
- other_id = args[0]
- if other_id.Equal(ramp_id):
- Report.info("Box touched ramp")
- RampTouched.value = True
-
- handler = azlmbr.physics.CollisionNotificationBusHandler()
- handler.connect(box_id)
- handler.add_callback("OnCollisionBegin", on_collision_begin)
-
- # 6.5) Wait for the box to touch the ramp or timeout
+ # 6) Wait for the box to touch the ramp or timeout
helper.wait_for_condition(lambda: RampTouched.value, TIME_OUT)
Report.result(Tests.box_touched_ramp, RampTouched.value)
diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt
index f1299ddc2d..d5c7fd5109 100644
--- a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt
+++ b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt
@@ -96,19 +96,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
## GradientSignal ##
- ly_add_pytest(
- NAME AutomatedTesting::GradientSignalTests_Periodic
- TEST_SERIAL
- TEST_SUITE periodic
- PATH ${CMAKE_CURRENT_LIST_DIR}/gradient_signal/TestSuite_Periodic.py
- RUNTIME_DEPENDENCIES
- AZ::AssetProcessor
- Legacy::Editor
- AutomatedTesting.Assets
- COMPONENT
- LargeWorlds
- )
-
ly_add_pytest(
NAME AutomatedTesting::GradientSignalTests_Periodic_Optimized
TEST_SERIAL
diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py
index e51be58ec6..84c661873c 100755
--- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py
+++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py
@@ -109,7 +109,7 @@ def DynamicSliceInstanceSpawner_Embedded_E2E():
# 6) Save and export to engine
general.save_level()
general.export_to_engine()
- pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak")
+ pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak")
Report.result(Tests.saved_and_exported, os.path.exists(pak_path))
diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py
index 7a0abdd969..de2554034f 100755
--- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py
+++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py
@@ -131,7 +131,7 @@ def DynamicSliceInstanceSpawner_External_E2E():
# 6) Save and export to engine
general.save_level()
general.export_to_engine()
- pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak")
+ pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak")
Report.result(Tests.saved_and_exported, os.path.exists(pak_path))
diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py
index f56c0b836e..bf6501f469 100755
--- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py
+++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py
@@ -155,7 +155,7 @@ def LayerBlender_E2E_Editor():
# 6) Save and export to engine
general.save_level()
general.export_to_engine()
- pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak")
+ pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak")
Report.result(Tests.saved_and_exported, os.path.exists(pak_path))
diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic_Optimized.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic_Optimized.py
index f996a1f8c3..514504d324 100644
--- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic_Optimized.py
+++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic_Optimized.py
@@ -10,7 +10,6 @@ import pytest
from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite
-@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.")
@pytest.mark.SUITE_periodic
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py b/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py
index 65ebf8e59e..957536fffb 100755
--- a/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py
+++ b/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py
@@ -17,7 +17,7 @@ import azlmbr.vegetation as vegetation
import azlmbr.areasystem as areasystem
import azlmbr.paths
-sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
@@ -25,7 +25,7 @@ def create_surface_entity(name, center_point, box_size_x, box_size_y, box_size_z
# Create a "flat surface" entity to use as a plantable vegetation surface
surface_entity = hydra.Entity(name)
surface_entity.create_entity(
- center_point,
+ center_point,
["Box Shape", "Shape Surface Tag Emitter"]
)
if surface_entity.id.IsValid():
@@ -56,7 +56,7 @@ def create_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_
# Create a vegetation area entity to use as our test vegetation spawner
spawner_entity = hydra.Entity(name)
spawner_entity.create_entity(
- center_point,
+ center_point,
["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"]
)
if spawner_entity.id.IsValid():
diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/Joints_FixedLeadFollowerCollide.ly b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/Joints_FixedLeadFollowerCollide.ly
deleted file mode 100644
index 0c3ac9e668..0000000000
--- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/Joints_FixedLeadFollowerCollide.ly
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:3093fee5317ba6fb353a435c09f43f13c5e722421aae62a297f699c202d6129e
-size 6699
diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/filelist.xml b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/filelist.xml
deleted file mode 100644
index d0960f8154..0000000000
--- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/filelist.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/level.pak b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/level.pak
deleted file mode 100644
index 532be2c1bd..0000000000
--- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/level.pak
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b1daa050e732ff0594601bbfca8ac9ed05972c2f9fa3e43dbc8645e802ed2730
-size 7959
diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/Environment.xml b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/Environment.xml
deleted file mode 100644
index 4ba36f66ae..0000000000
--- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/Environment.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/TerrainTexture.xml b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/TerrainTexture.xml
deleted file mode 100644
index f43df05b22..0000000000
--- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/TerrainTexture.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/TimeOfDay.xml b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/TimeOfDay.xml
deleted file mode 100644
index 456d609b8a..0000000000
--- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/TimeOfDay.xml
+++ /dev/null
@@ -1,356 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/VegetationMap.dat b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/VegetationMap.dat
deleted file mode 100644
index dce5631cd0..0000000000
--- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/leveldata/VegetationMap.dat
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0e6a5435c928079b27796f6b202bbc2623e7e454244ddc099a3cadf33b7cb9e9
-size 63
diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/tags.txt b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/tags.txt
deleted file mode 100644
index 0d6c1880e7..0000000000
--- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/tags.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-0,0,0,0,0,0
-0,0,0,0,0,0
-0,0,0,0,0,0
-0,0,0,0,0,0
-0,0,0,0,0,0
-0,0,0,0,0,0
-0,0,0,0,0,0
-0,0,0,0,0,0
-0,0,0,0,0,0
-0,0,0,0,0,0
-0,0,0,0,0,0
-0,0,0,0,0,0
diff --git a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/terraintexture.pak b/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/terraintexture.pak
deleted file mode 100644
index fe3604a050..0000000000
--- a/AutomatedTesting/Levels/Physics/Joints_FixedLeadFollowerCollide/terraintexture.pak
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8739c76e681f900923b900c9df0ef75cf421d39cabb54650c4b9ad19b6a76d85
-size 22
diff --git a/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly b/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly
index 3d17ae7ccc..86dd941423 100644
--- a/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly
+++ b/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:012caffa9354f6253d2e626ba183b44eb02a74eba286fde0430227ef024411e2
-size 8961
+oid sha256:817bd8dd22e185136418b60fba5b3552993687515d3d5ae96791f2c3be907b92
+size 7233
diff --git a/AutomatedTesting/Levels/Physics/Material_DefaultLibraryConsistentOnAllFeatures/cowboy.emfxworkspace b/AutomatedTesting/Levels/Physics/Material_DefaultLibraryConsistentOnAllFeatures/cowboy.emfxworkspace
index 188fee9f05..05140f340b 100644
--- a/AutomatedTesting/Levels/Physics/Material_DefaultLibraryConsistentOnAllFeatures/cowboy.emfxworkspace
+++ b/AutomatedTesting/Levels/Physics/Material_DefaultLibraryConsistentOnAllFeatures/cowboy.emfxworkspace
@@ -1,3 +1,3 @@
[General]
version=1
-startScript="ImportActor -filename \"@assets@/characters/cowboy/actor/cowboy_01.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\n"
+startScript="ImportActor -filename \"@products@/characters/cowboy/actor/cowboy_01.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\n"
diff --git a/AutomatedTesting/Levels/Physics/Material_DefaultMaterialLibraryChangesWork/ws.emfxworkspace b/AutomatedTesting/Levels/Physics/Material_DefaultMaterialLibraryChangesWork/ws.emfxworkspace
index e429d74a57..870b9a8579 100644
--- a/AutomatedTesting/Levels/Physics/Material_DefaultMaterialLibraryChangesWork/ws.emfxworkspace
+++ b/AutomatedTesting/Levels/Physics/Material_DefaultMaterialLibraryChangesWork/ws.emfxworkspace
@@ -1,3 +1,3 @@
[General]
version=1
-startScript="ImportActor -filename \"@assets@/levels/physics/c15096734_physxmaterials_defaultmateriallibrary/rin_skeleton_newgeo.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\nLoadMotionSet -filename \"@assets@/Levels/Physics/C15096734_PhysxMaterials_DefaultMaterialLibrary/custom_motionset.motionset\"\nLoadAnimGraph -filename \"@assets@/Levels/Physics/C15096734_PhysxMaterials_DefaultMaterialLibrary/rin_physics.animgraph\"\n"
+startScript="ImportActor -filename \"@products@/levels/physics/c15096734_physxmaterials_defaultmateriallibrary/rin_skeleton_newgeo.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\nLoadMotionSet -filename \"@products@/levels/physics/c15096734_physxmaterials_defaultmateriallibrary/custom_motionset.motionset\"\nLoadAnimGraph -filename \"@products@/levels/physics/c15096734_physxmaterials_defaultmateriallibrary/rin_physics.animgraph\"\n"
diff --git a/AutomatedTesting/Levels/Physics/RigidBody_KinematicModeWorks/RigidBody_KinematicModeWorks.ly b/AutomatedTesting/Levels/Physics/RigidBody_KinematicModeWorks/RigidBody_KinematicModeWorks.ly
index 92e6788bf0..d6052ffa7c 100644
--- a/AutomatedTesting/Levels/Physics/RigidBody_KinematicModeWorks/RigidBody_KinematicModeWorks.ly
+++ b/AutomatedTesting/Levels/Physics/RigidBody_KinematicModeWorks/RigidBody_KinematicModeWorks.ly
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:619bcf0c2a2b08f4ffe05e60858c479828fe39d5530f4e488b9c0ec8a585ccb7
-size 7735
+oid sha256:cb97ada674d123c67d7a32eb65e81fa66472cf51f748054c4a4297649f2a0f40
+size 5568
diff --git a/AutomatedTesting/Registry/assets_scan_folders.setreg b/AutomatedTesting/Registry/assets_scan_folders.setreg
index 91061f3337..c74ba6703e 100644
--- a/AutomatedTesting/Registry/assets_scan_folders.setreg
+++ b/AutomatedTesting/Registry/assets_scan_folders.setreg
@@ -51,6 +51,14 @@
[
"Gems/UiBasics"
]
+ },
+ "Hair":
+ {
+ "SourcePaths":
+ [
+ "Gems/AtomTressFX/Assets",
+ "Gems/AtomTressFX/Assets/Passes"
+ ]
}
}
}
diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_AddingNewGroupWorks.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_AddingNewGroupWorks.setreg_override
index 7065f0dfeb..209e9a9feb 100644
--- a/AutomatedTesting/Registry/physx_overrides/Collider_AddingNewGroupWorks.setreg_override
+++ b/AutomatedTesting/Registry/physx_overrides/Collider_AddingNewGroupWorks.setreg_override
@@ -109,7 +109,7 @@
},
"MaterialLibrary": {
"assetId": {
- "guid": "{62446378-67F8-5E49-AC31-761DD5942695}"
+ "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}"
},
"loadBehavior": "QueueLoad",
"assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial"
diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_CollisionGroupsWorkflow.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_CollisionGroupsWorkflow.setreg_override
index b82acaf0ae..65f15f8554 100644
--- a/AutomatedTesting/Registry/physx_overrides/Collider_CollisionGroupsWorkflow.setreg_override
+++ b/AutomatedTesting/Registry/physx_overrides/Collider_CollisionGroupsWorkflow.setreg_override
@@ -121,7 +121,7 @@
},
"MaterialLibrary": {
"assetId": {
- "guid": "{62446378-67F8-5E49-AC31-761DD5942695}"
+ "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}"
},
"loadBehavior": "QueueLoad",
"assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial"
diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override
index b82acaf0ae..65f15f8554 100644
--- a/AutomatedTesting/Registry/physx_overrides/Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override
+++ b/AutomatedTesting/Registry/physx_overrides/Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override
@@ -121,7 +121,7 @@
},
"MaterialLibrary": {
"assetId": {
- "guid": "{62446378-67F8-5E49-AC31-761DD5942695}"
+ "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}"
},
"loadBehavior": "QueueLoad",
"assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial"
diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override
index b82acaf0ae..65f15f8554 100644
--- a/AutomatedTesting/Registry/physx_overrides/Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override
+++ b/AutomatedTesting/Registry/physx_overrides/Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override
@@ -121,7 +121,7 @@
},
"MaterialLibrary": {
"assetId": {
- "guid": "{62446378-67F8-5E49-AC31-761DD5942695}"
+ "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}"
},
"loadBehavior": "QueueLoad",
"assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial"
diff --git a/AutomatedTesting/Registry/physx_overrides/Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override b/AutomatedTesting/Registry/physx_overrides/Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override
index b82acaf0ae..65f15f8554 100644
--- a/AutomatedTesting/Registry/physx_overrides/Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override
+++ b/AutomatedTesting/Registry/physx_overrides/Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override
@@ -121,7 +121,7 @@
},
"MaterialLibrary": {
"assetId": {
- "guid": "{62446378-67F8-5E49-AC31-761DD5942695}"
+ "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}"
},
"loadBehavior": "QueueLoad",
"assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial"
diff --git a/AutomatedTesting/Registry/physxsystemconfiguration.setreg b/AutomatedTesting/Registry/physxsystemconfiguration.setreg
index 83aad307a6..2ade83d769 100644
--- a/AutomatedTesting/Registry/physxsystemconfiguration.setreg
+++ b/AutomatedTesting/Registry/physxsystemconfiguration.setreg
@@ -103,7 +103,7 @@
},
"MaterialLibrary": {
"assetId": {
- "guid": "{62446378-67F8-5E49-AC31-761DD5942695}"
+ "guid": "{7CDF49C3-91A2-5C4E-B642-6D1AEC80E70E}"
},
"loadBehavior": "QueueLoad",
"assetHint": "assets/physics/surfacetypemateriallibrary.physmaterial"
diff --git a/AutomatedTesting/multiple_mesh_one_material/FBXTestTexture.png b/AutomatedTesting/multiple_mesh_one_material/FBXTestTexture.png
new file mode 100644
index 0000000000..4f52364cb3
--- /dev/null
+++ b/AutomatedTesting/multiple_mesh_one_material/FBXTestTexture.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:827a63985273229050bf4f63030bcc666f045091fe81cf8157a9ca23b40074b6
+size 3214
diff --git a/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx b/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx
new file mode 100644
index 0000000000..d9deb899f0
--- /dev/null
+++ b/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d8d24963e6e8765205bc79cbe2304fc39f1245ee75249e2834a71c96c3cab824
+size 22700
diff --git a/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx.assetinfo b/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx.assetinfo
new file mode 100644
index 0000000000..fe18a16cb5
--- /dev/null
+++ b/AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx.assetinfo
@@ -0,0 +1,8 @@
+{
+ "values": [
+ {
+ "$type": "ScriptProcessorRule",
+ "scriptFilename": "Editor/Scripts/scene_mesh_to_prefab.py"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Code/Editor/CMakeLists.txt b/Code/Editor/CMakeLists.txt
index 5884795413..2ea0aa1a74 100644
--- a/Code/Editor/CMakeLists.txt
+++ b/Code/Editor/CMakeLists.txt
@@ -33,7 +33,6 @@ ly_add_target(
BUILD_DEPENDENCIES
PRIVATE
Legacy::CryCommon
- 3rdParty::zlib
PUBLIC
3rdParty::Qt::Core
3rdParty::Qt::Gui
@@ -105,7 +104,6 @@ ly_add_target(
3rdParty::Qt::Concurrent
3rdParty::tiff
3rdParty::squish-ccr
- 3rdParty::zlib
3rdParty::AWSNativeSDK::STS
Legacy::CryCommon
Legacy::EditorCommon
diff --git a/Code/Editor/Controls/FolderTreeCtrl.cpp b/Code/Editor/Controls/FolderTreeCtrl.cpp
index b1cbb9414e..4088ab976f 100644
--- a/Code/Editor/Controls/FolderTreeCtrl.cpp
+++ b/Code/Editor/Controls/FolderTreeCtrl.cpp
@@ -278,17 +278,16 @@ void CFolderTreeCtrl::LoadTreeRec(const QString& currentFolder)
void CFolderTreeCtrl::AddItem(const QString& path)
{
- QString folder;
- QString fileNameWithoutExtension;
- QString ext;
-
- Path::Split(path, folder, fileNameWithoutExtension, ext);
+ AZ::IO::FixedMaxPath folder{ AZ::IO::PathView(path.toUtf8().constData()) };
+ AZ::IO::FixedMaxPath fileNameWithoutExtension = folder.Extension();
+ folder = folder.ParentPath();
auto regex = QRegExp(m_fileNameSpec, Qt::CaseInsensitive, QRegExp::Wildcard);
if (regex.exactMatch(path))
{
- CTreeItem* folderTreeItem = CreateFolderItems(folder);
- folderTreeItem->AddChild(fileNameWithoutExtension, path, eTreeImage_File);
+ CTreeItem* folderTreeItem = CreateFolderItems(QString::fromUtf8(folder.c_str(), static_cast(folder.Native().size())));
+ folderTreeItem->AddChild(QString::fromUtf8(fileNameWithoutExtension.c_str(),
+ static_cast(fileNameWithoutExtension.Native().size())), path, eTreeImage_File);
}
}
diff --git a/Code/Editor/Core/LevelEditorMenuHandler.cpp b/Code/Editor/Core/LevelEditorMenuHandler.cpp
index e568f05167..fdde1ac305 100644
--- a/Code/Editor/Core/LevelEditorMenuHandler.cpp
+++ b/Code/Editor/Core/LevelEditorMenuHandler.cpp
@@ -32,6 +32,7 @@
#include
// AzToolsFramework
+#include
#include
// AzQtComponents
@@ -166,15 +167,14 @@ LevelEditorMenuHandler::LevelEditorMenuHandler(MainWindow* mainWindow, QtViewPan
m_mainWindow->menuBar()->setNativeMenuBar(true);
#endif
- ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
- AzToolsFramework::GetEntityContextId());
+ ViewportEditorModeNotificationsBus::Handler::BusConnect(GetEntityContextId());
EditorMenuRequestBus::Handler::BusConnect();
}
LevelEditorMenuHandler::~LevelEditorMenuHandler()
{
EditorMenuRequestBus::Handler::BusDisconnect();
- ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
+ ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
}
void LevelEditorMenuHandler::Initialize()
@@ -487,8 +487,6 @@ void LevelEditorMenuHandler::PopulateEditMenu(ActionManager::MenuWrapper& editMe
editMenu.AddAction(AzToolsFramework::EditPivot);
editMenu.AddAction(AzToolsFramework::EditReset);
editMenu.AddAction(AzToolsFramework::EditResetManipulator);
- editMenu.AddAction(AzToolsFramework::EditResetLocal);
- editMenu.AddAction(AzToolsFramework::EditResetWorld);
// Hide Selection
editMenu.AddAction(AzToolsFramework::HideSelection);
@@ -1186,36 +1184,44 @@ void LevelEditorMenuHandler::AddDisableActionInSimModeListener(QAction* action)
}));
}
-void LevelEditorMenuHandler::EnteredComponentMode(const AZStd::vector& /*componentModeTypes*/)
+void LevelEditorMenuHandler::OnEditorModeActivated(
+ [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
- auto menuWrapper = m_actionManager->FindMenu(s_editMenuId);
- if (!menuWrapper.isNull())
+ if (mode == ViewportEditorMode::Component)
{
- // copy of menu actions
- auto actions = menuWrapper.Get()->actions();
- // remove all non-reserved edit menu options
- actions.erase(
- std::remove_if(actions.begin(), actions.end(), [](QAction* action)
- {
- return !action->property("Reserved").toBool();
- }),
- actions.end());
+ if (auto menuWrapper = m_actionManager->FindMenu(s_editMenuId);
+ !menuWrapper.isNull())
+ {
+ // copy of menu actions
+ auto actions = menuWrapper.Get()->actions();
+ // remove all non-reserved edit menu options
+ actions.erase(
+ std::remove_if(actions.begin(), actions.end(), [](QAction* action)
+ {
+ return !action->property("Reserved").toBool();
+ }),
+ actions.end());
- // clear and update the menu with new actions
- menuWrapper.Get()->clear();
- menuWrapper.Get()->addActions(actions);
+ // clear and update the menu with new actions
+ menuWrapper.Get()->clear();
+ menuWrapper.Get()->addActions(actions);
+ }
}
}
-void LevelEditorMenuHandler::LeftComponentMode(const AZStd::vector& /*componentModeTypes*/)
+void LevelEditorMenuHandler::OnEditorModeDeactivated(
+ [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
- RestoreEditMenuToDefault();
+ if (mode == ViewportEditorMode::Component)
+ {
+ RestoreEditMenuToDefault();
+ }
}
void LevelEditorMenuHandler::AddEditMenuAction(QAction* action)
{
- auto menuWrapper = m_actionManager->FindMenu(s_editMenuId);
- if (!menuWrapper.isNull())
+ if (auto menuWrapper = m_actionManager->FindMenu(s_editMenuId);
+ !menuWrapper.isNull())
{
menuWrapper.Get()->addAction(action);
}
@@ -1239,8 +1245,8 @@ void LevelEditorMenuHandler::AddMenuAction(AZStd::string_view categoryId, QActio
void LevelEditorMenuHandler::RestoreEditMenuToDefault()
{
- auto menuWrapper = m_actionManager->FindMenu(s_editMenuId);
- if (!menuWrapper.isNull())
+ if (auto menuWrapper = m_actionManager->FindMenu(s_editMenuId);
+ !menuWrapper.isNull())
{
menuWrapper.Get()->clear();
PopulateEditMenu(menuWrapper);
diff --git a/Code/Editor/Core/LevelEditorMenuHandler.h b/Code/Editor/Core/LevelEditorMenuHandler.h
index 5ac8e63786..ff03c7748c 100644
--- a/Code/Editor/Core/LevelEditorMenuHandler.h
+++ b/Code/Editor/Core/LevelEditorMenuHandler.h
@@ -18,7 +18,7 @@
#include
#include "ActionManager.h"
#include "QtViewPaneManager.h"
-#include
+#include
#endif
class MainWindow;
@@ -28,7 +28,7 @@ struct QtViewPane;
class LevelEditorMenuHandler
: public QObject
- , private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
+ , private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
, private AzToolsFramework::EditorMenuRequestBus::Handler
{
Q_OBJECT
@@ -88,9 +88,11 @@ private:
void AddDisableActionInSimModeListener(QAction* action);
- // EditorComponentModeNotificationBus
- void EnteredComponentMode(const AZStd::vector& componentModeTypes) override;
- void LeftComponentMode(const AZStd::vector& componentModeTypes) override;
+ // ViewportEditorModeNotificationsBus overrides ...
+ void OnEditorModeActivated(
+ const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
+ void OnEditorModeDeactivated(
+ const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
// EditorMenuRequestBus
void AddEditMenuAction(QAction* action) override;
diff --git a/Code/Editor/Core/Tests/test_Main.cpp b/Code/Editor/Core/Tests/test_Main.cpp
index 3d3e286f18..16b0aa92cf 100644
--- a/Code/Editor/Core/Tests/test_Main.cpp
+++ b/Code/Editor/Core/Tests/test_Main.cpp
@@ -33,6 +33,7 @@ public:
protected:
void SetupEnvironment() override
{
+ AttachEditorCoreAZEnvironment(AZ::Environment::GetInstance());
m_allocatorScope.ActivateAllocators();
m_cryPak = new NiceMock();
@@ -49,6 +50,7 @@ protected:
{
delete m_cryPak;
m_allocatorScope.DeactivateAllocators();
+ DetachEditorCoreAZEnvironment();
}
private:
diff --git a/Code/Editor/Core/Tests/test_PathUtil.cpp b/Code/Editor/Core/Tests/test_PathUtil.cpp
index 83e50a5947..cade19eb81 100644
--- a/Code/Editor/Core/Tests/test_PathUtil.cpp
+++ b/Code/Editor/Core/Tests/test_PathUtil.cpp
@@ -5,22 +5,29 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
-#include "EditorDefs.h"
-#include
-#include "Util/PathUtil.h"
-#include
-
-TEST(PathUtil, GamePathToFullPath_DoesNotBufferOverflow)
+#include
+#include
+namespace UnitTest
{
- // There are no test assertions in this test because the purpose is just to verify that the test runs without crashing
- QString pngExtension(".png");
+ class PathUtil
+ : public ScopedAllocatorSetupFixture
+ {
+ };
- // Create a string of lenth AZ_MAX_PATH_LEN that ends in .png
- QString longStringMaxPath(AZ_MAX_PATH_LEN, 'x');
- longStringMaxPath.replace(longStringMaxPath.length() - pngExtension.length(), longStringMaxPath.length(), pngExtension);
- Path::GamePathToFullPath(longStringMaxPath);
+ TEST_F(PathUtil, GamePathToFullPath_DoesNotBufferOverflow)
+ {
+ // There are no test assertions in this test because the purpose is just to verify that the test runs without crashing
+ QString pngExtension(".png");
- QString longStringMaxPathPlusOne(AZ_MAX_PATH_LEN + 1, 'x');
- longStringMaxPathPlusOne.replace(longStringMaxPathPlusOne.length() - pngExtension.length(), longStringMaxPathPlusOne.length(), pngExtension);
- Path::GamePathToFullPath(longStringMaxPathPlusOne);
+ // Create a string of length AZ_MAX_PATH_LEN that ends in .png
+ QString longStringMaxPath(AZ_MAX_PATH_LEN, 'x');
+ longStringMaxPath.replace(longStringMaxPath.length() - pngExtension.length(), longStringMaxPath.length(), pngExtension);
+ AZ_TEST_START_TRACE_SUPPRESSION;
+ Path::GamePathToFullPath(longStringMaxPath);
+ AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT;
+
+ QString longStringMaxPathPlusOne(AZ_MAX_PATH_LEN + 1, 'x');
+ longStringMaxPathPlusOne.replace(longStringMaxPathPlusOne.length() - pngExtension.length(), longStringMaxPathPlusOne.length(), pngExtension);
+ Path::GamePathToFullPath(longStringMaxPathPlusOne);
+ }
}
diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp
index b5eb4229df..abba2bb9c6 100644
--- a/Code/Editor/CryEdit.cpp
+++ b/Code/Editor/CryEdit.cpp
@@ -2124,6 +2124,8 @@ bool CCryEditApp::FixDanglingSharedMemory(const QString& sharedMemName) const
int CCryEditApp::ExitInstance(int exitCode)
{
+ AZ_TracePrintf("Exit", "Called ExitInstance() with exit code: 0x%x", exitCode);
+
if (m_pEditor)
{
m_pEditor->OnBeginShutdownSequence();
@@ -2642,7 +2644,7 @@ void CCryEditApp::OnFileResaveSlices()
sliceAssetInfos.reserve(5000);
AZ::Data::AssetCatalogRequests::AssetEnumerationCB sliceCountCb = [&sliceAssetInfos]([[maybe_unused]] const AZ::Data::AssetId id, const AZ::Data::AssetInfo& info)
{
- // Only add slices and nothing that has been temporarily added to the catalog with a macro in it (ie @devroot@)
+ // Only add slices and nothing that has been temporarily added to the catalog with a macro in it (ie @engroot@)
if (info.m_assetType == azrtti_typeid() && info.m_relativePath[0] != '@')
{
sliceAssetInfos.push_back(info);
diff --git a/Code/Editor/CryEditDoc.cpp b/Code/Editor/CryEditDoc.cpp
index 07d946c609..75bd0ad270 100644
--- a/Code/Editor/CryEditDoc.cpp
+++ b/Code/Editor/CryEditDoc.cpp
@@ -1108,7 +1108,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
if (QFileInfo(filename).isRelative())
{
// Resolving the path through resolvepath would normalize and lowcase it, and in this case, we don't want that.
- fullPathName = Path::ToUnixPath(QDir(QString::fromUtf8(gEnv->pFileIO->GetAlias("@devassets@"))).absoluteFilePath(fullPathName));
+ fullPathName = Path::ToUnixPath(QDir(QString::fromUtf8(gEnv->pFileIO->GetAlias("@projectroot@"))).absoluteFilePath(fullPathName));
}
if (!CFileUtil::OverwriteFile(fullPathName))
@@ -2159,7 +2159,7 @@ bool CCryEditDoc::LoadXmlArchiveArray(TDocMultiArchive& arrXmlAr, const QString&
xmlAr.bLoading = true;
// bound to the level folder, as if it were the assets folder.
- // this mounts (whateverlevelname.ly) as @assets@/Levels/whateverlevelname/ and thus it works...
+ // this mounts (whateverlevelname.ly) as @products@/Levels/whateverlevelname/ and thus it works...
bool openLevelPakFileSuccess = pIPak->OpenPack(levelPath.toUtf8().data(), absoluteLevelPath.toUtf8().data());
if (!openLevelPakFileSuccess)
{
diff --git a/Code/Editor/Dialogs/PythonScriptsDialog.cpp b/Code/Editor/Dialogs/PythonScriptsDialog.cpp
index e95fb90c0a..35047947ac 100644
--- a/Code/Editor/Dialogs/PythonScriptsDialog.cpp
+++ b/Code/Editor/Dialogs/PythonScriptsDialog.cpp
@@ -91,7 +91,7 @@ CPythonScriptsDialog::CPythonScriptsDialog(QWidget* parent)
{
AZ::IO::Path newSourcePath = jsonSourcePathPointer;
// Resolve any file aliases first - Do not use ResolvePath() as that assumes
- // any relative path is underneath the @assets@ alias
+ // any relative path is underneath the @products@ alias
if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr)
{
AZ::IO::FixedMaxPath replacedAliasPath;
diff --git a/Code/Editor/EditorFileMonitor.cpp b/Code/Editor/EditorFileMonitor.cpp
index 7feb9d32a8..311c42befa 100644
--- a/Code/Editor/EditorFileMonitor.cpp
+++ b/Code/Editor/EditorFileMonitor.cpp
@@ -14,6 +14,8 @@
// Editor
#include "CryEdit.h"
+#include
+
//////////////////////////////////////////////////////////////////////////
CEditorFileMonitor::CEditorFileMonitor()
{
@@ -177,26 +179,14 @@ void CEditorFileMonitor::OnFileMonitorChange(const SFileChangeInfo& rChange)
// Make file relative to PrimaryCD folder.
QString filename = rChange.filename;
- // Remove game directory if present in path.
- const QString rootPath =
- QDir::fromNativeSeparators(QString::fromLatin1(Path::GetEditingRootFolder().c_str()));
- if (filename.startsWith(rootPath, Qt::CaseInsensitive))
- {
- filename = filename.right(filename.length() - rootPath.length());
- }
+ // Make path relative to the the project directory
+ AZ::IO::Path projectPath{ AZ::Utils::GetProjectPath() };
+ AZ::IO::FixedMaxPath projectRelativeFilePath = AZ::IO::PathView(filename.toUtf8().constData()).LexicallyProximate(
+ projectPath);
- // Make sure there is no leading slash
- if (!filename.isEmpty() && (filename[0] == '\\' || filename[0] == '/'))
+ if (!projectRelativeFilePath.empty())
{
- filename = filename.mid(1);
- }
-
- if (!filename.isEmpty())
- {
- //remove game name. Make it relative to the game folder
- const QString filenameRelGame = RemoveGameName(filename);
- const int extIndex = filename.lastIndexOf('.');
- const QString ext = filename.right(filename.length() - 1 - extIndex);
+ AZ::IO::PathView ext = projectRelativeFilePath.Extension();
// Check for File Monitor callback
std::vector::iterator iter;
@@ -207,15 +197,11 @@ void CEditorFileMonitor::OnFileMonitorChange(const SFileChangeInfo& rChange)
// We compare against length of callback string, so we get directory matches as well as full filenames
if (sCallback.pListener)
{
- if (sCallback.extension == "*" || ext.compare(sCallback.extension, Qt::CaseInsensitive) == 0)
+ if (sCallback.extension == "*" || AZ::IO::PathView(sCallback.extension.toUtf8().constData()) == ext)
{
- if (filenameRelGame.compare(sCallback.item, Qt::CaseInsensitive) == 0)
+ if (AZ::IO::PathView(sCallback.item.toUtf8().constData()) == projectRelativeFilePath)
{
- sCallback.pListener->OnFileChange(qPrintable(filenameRelGame), IFileChangeListener::EChangeType(rChange.changeType));
- }
- else if (filename.compare(sCallback.item, Qt::CaseInsensitive) == 0)
- {
- sCallback.pListener->OnFileChange(qPrintable(filename), IFileChangeListener::EChangeType(rChange.changeType));
+ sCallback.pListener->OnFileChange(qPrintable(projectRelativeFilePath.c_str()), IFileChangeListener::EChangeType(rChange.changeType));
}
}
}
diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp
index 29cf348ab5..ce4a0a2e33 100644
--- a/Code/Editor/EditorModularViewportCameraComposer.cpp
+++ b/Code/Editor/EditorModularViewportCameraComposer.cpp
@@ -95,7 +95,8 @@ namespace SandboxEditor
cameras.AddCamera(m_firstPersonPanCamera);
cameras.AddCamera(m_firstPersonTranslateCamera);
cameras.AddCamera(m_firstPersonScrollCamera);
- cameras.AddCamera(m_pivotCamera);
+ cameras.AddCamera(m_firstPersonFocusCamera);
+ cameras.AddCamera(m_orbitCamera);
});
return controller;
@@ -111,6 +112,7 @@ namespace SandboxEditor
viewportId, &AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Events::BeginCursorCapture);
}
};
+
const auto showCursor = [viewportId = m_viewportId]
{
if (SandboxEditor::CameraCaptureCursorForLook())
@@ -133,7 +135,7 @@ namespace SandboxEditor
m_firstPersonRotateCamera->SetActivationEndedFn(showCursor);
m_firstPersonPanCamera = AZStd::make_shared(
- SandboxEditor::CameraFreePanChannelId(), AzFramework::LookPan, AzFramework::TranslatePivot);
+ SandboxEditor::CameraFreePanChannelId(), AzFramework::LookPan, AzFramework::TranslatePivotLook);
m_firstPersonPanCamera->m_panSpeedFn = []
{
@@ -153,7 +155,7 @@ namespace SandboxEditor
const auto translateCameraInputChannelIds = BuildTranslateCameraInputChannelIds();
m_firstPersonTranslateCamera = AZStd::make_shared(
- translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivot);
+ translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivotLook);
m_firstPersonTranslateCamera->m_translateSpeedFn = []
{
@@ -165,90 +167,111 @@ namespace SandboxEditor
return SandboxEditor::CameraBoostMultiplier();
};
- m_firstPersonScrollCamera = AZStd::make_shared();
+ m_firstPersonScrollCamera = AZStd::make_shared();
m_firstPersonScrollCamera->m_scrollSpeedFn = []
{
return SandboxEditor::CameraScrollSpeed();
};
- m_pivotCamera = AZStd::make_shared(SandboxEditor::CameraPivotChannelId());
+ const auto pivotFn = []
+ {
+ // use the manipulator transform as the pivot point
+ AZStd::optional entityPivot;
+ AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
+ entityPivot, AzToolsFramework::GetEntityContextId(),
+ &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
- m_pivotCamera->SetPivotFn(
- []([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
+ if (entityPivot.has_value())
{
- // use the manipulator transform as the pivot point
- AZStd::optional entityPivot;
- AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
- entityPivot, AzToolsFramework::GetEntityContextId(),
- &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
+ return entityPivot->GetTranslation();
+ }
- // otherwise just use the identity
- return entityPivot.value_or(AZ::Transform::CreateIdentity()).GetTranslation();
+ // otherwise just use the identity
+ return AZ::Vector3::CreateZero();
+ };
+
+ m_firstPersonFocusCamera =
+ AZStd::make_shared(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusLook);
+
+ m_firstPersonFocusCamera->SetPivotFn(pivotFn);
+
+ m_orbitCamera = AZStd::make_shared(SandboxEditor::CameraOrbitChannelId());
+
+ m_orbitCamera->SetPivotFn(
+ [pivotFn]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
+ {
+ return pivotFn();
});
- m_pivotRotateCamera = AZStd::make_shared(SandboxEditor::CameraPivotLookChannelId());
+ m_orbitRotateCamera = AZStd::make_shared(SandboxEditor::CameraOrbitLookChannelId());
- m_pivotRotateCamera->m_rotateSpeedFn = []
+ m_orbitRotateCamera->m_rotateSpeedFn = []
{
return SandboxEditor::CameraRotateSpeed();
};
- m_pivotRotateCamera->m_invertYawFn = []
+ m_orbitRotateCamera->m_invertYawFn = []
{
- return SandboxEditor::CameraPivotYawRotationInverted();
+ return SandboxEditor::CameraOrbitYawRotationInverted();
};
- m_pivotTranslateCamera = AZStd::make_shared(
- translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslateOffset);
+ m_orbitTranslateCamera = AZStd::make_shared(
+ translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslateOffsetOrbit);
- m_pivotTranslateCamera->m_translateSpeedFn = []
+ m_orbitTranslateCamera->m_translateSpeedFn = []
{
return SandboxEditor::CameraTranslateSpeed();
};
- m_pivotTranslateCamera->m_boostMultiplierFn = []
+ m_orbitTranslateCamera->m_boostMultiplierFn = []
{
return SandboxEditor::CameraBoostMultiplier();
};
- m_pivotDollyScrollCamera = AZStd::make_shared();
+ m_orbitDollyScrollCamera = AZStd::make_shared();
- m_pivotDollyScrollCamera->m_scrollSpeedFn = []
+ m_orbitDollyScrollCamera->m_scrollSpeedFn = []
{
return SandboxEditor::CameraScrollSpeed();
};
- m_pivotDollyMoveCamera = AZStd::make_shared(SandboxEditor::CameraPivotDollyChannelId());
+ m_orbitDollyMoveCamera = AZStd::make_shared(SandboxEditor::CameraOrbitDollyChannelId());
- m_pivotDollyMoveCamera->m_motionSpeedFn = []
+ m_orbitDollyMoveCamera->m_motionSpeedFn = []
{
return SandboxEditor::CameraDollyMotionSpeed();
};
- m_pivotPanCamera = AZStd::make_shared(
- SandboxEditor::CameraPivotPanChannelId(), AzFramework::LookPan, AzFramework::TranslateOffset);
+ m_orbitPanCamera = AZStd::make_shared(
+ SandboxEditor::CameraOrbitPanChannelId(), AzFramework::LookPan, AzFramework::TranslateOffsetOrbit);
- m_pivotPanCamera->m_panSpeedFn = []
+ m_orbitPanCamera->m_panSpeedFn = []
{
return SandboxEditor::CameraPanSpeed();
};
- m_pivotPanCamera->m_invertPanXFn = []
+ m_orbitPanCamera->m_invertPanXFn = []
{
return SandboxEditor::CameraPanInvertedX();
};
- m_pivotPanCamera->m_invertPanYFn = []
+ m_orbitPanCamera->m_invertPanYFn = []
{
return SandboxEditor::CameraPanInvertedY();
};
- m_pivotCamera->m_pivotCameras.AddCamera(m_pivotRotateCamera);
- m_pivotCamera->m_pivotCameras.AddCamera(m_pivotTranslateCamera);
- m_pivotCamera->m_pivotCameras.AddCamera(m_pivotDollyScrollCamera);
- m_pivotCamera->m_pivotCameras.AddCamera(m_pivotDollyMoveCamera);
- m_pivotCamera->m_pivotCameras.AddCamera(m_pivotPanCamera);
+ m_orbitFocusCamera =
+ AZStd::make_shared(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusOrbit);
+
+ m_orbitFocusCamera->SetPivotFn(pivotFn);
+
+ m_orbitCamera->m_orbitCameras.AddCamera(m_orbitRotateCamera);
+ m_orbitCamera->m_orbitCameras.AddCamera(m_orbitTranslateCamera);
+ m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyScrollCamera);
+ m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyMoveCamera);
+ m_orbitCamera->m_orbitCameras.AddCamera(m_orbitPanCamera);
+ m_orbitCamera->m_orbitCameras.AddCamera(m_orbitFocusCamera);
}
void EditorModularViewportCameraComposer::OnEditorModularViewportCameraComposerSettingsChanged()
@@ -257,12 +280,14 @@ namespace SandboxEditor
m_firstPersonTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
m_firstPersonPanCamera->SetPanInputChannelId(SandboxEditor::CameraFreePanChannelId());
m_firstPersonRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraFreeLookChannelId());
+ m_firstPersonFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId());
- m_pivotCamera->SetPivotInputChannelId(SandboxEditor::CameraPivotChannelId());
- m_pivotTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
- m_pivotPanCamera->SetPanInputChannelId(SandboxEditor::CameraPivotPanChannelId());
- m_pivotRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraPivotLookChannelId());
- m_pivotDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraPivotDollyChannelId());
+ m_orbitCamera->SetOrbitInputChannelId(SandboxEditor::CameraOrbitChannelId());
+ m_orbitTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
+ m_orbitPanCamera->SetPanInputChannelId(SandboxEditor::CameraOrbitPanChannelId());
+ m_orbitRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraOrbitLookChannelId());
+ m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId());
+ m_orbitFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId());
}
void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId)
diff --git a/Code/Editor/EditorModularViewportCameraComposer.h b/Code/Editor/EditorModularViewportCameraComposer.h
index e691ca1c89..9cfd6f3554 100644
--- a/Code/Editor/EditorModularViewportCameraComposer.h
+++ b/Code/Editor/EditorModularViewportCameraComposer.h
@@ -41,13 +41,15 @@ namespace SandboxEditor
AZStd::shared_ptr m_firstPersonRotateCamera;
AZStd::shared_ptr m_firstPersonPanCamera;
AZStd::shared_ptr m_firstPersonTranslateCamera;
- AZStd::shared_ptr m_firstPersonScrollCamera;
- AZStd::shared_ptr m_pivotCamera;
- AZStd::shared_ptr m_pivotRotateCamera;
- AZStd::shared_ptr m_pivotTranslateCamera;
- AZStd::shared_ptr m_pivotDollyScrollCamera;
- AZStd::shared_ptr m_pivotDollyMoveCamera;
- AZStd::shared_ptr m_pivotPanCamera;
+ AZStd::shared_ptr m_firstPersonScrollCamera;
+ AZStd::shared_ptr m_firstPersonFocusCamera;
+ AZStd::shared_ptr m_orbitCamera;
+ AZStd::shared_ptr m_orbitRotateCamera;
+ AZStd::shared_ptr m_orbitTranslateCamera;
+ AZStd::shared_ptr m_orbitDollyScrollCamera;
+ AZStd::shared_ptr m_orbitDollyMoveCamera;
+ AZStd::shared_ptr m_orbitPanCamera;
+ AZStd::shared_ptr m_orbitFocusCamera;
AzFramework::ViewportId m_viewportId;
};
diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.cpp b/Code/Editor/EditorPreferencesPageViewportCamera.cpp
index 55b631e1f6..16176bc241 100644
--- a/Code/Editor/EditorPreferencesPageViewportCamera.cpp
+++ b/Code/Editor/EditorPreferencesPageViewportCamera.cpp
@@ -73,7 +73,7 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
->Field("TranslateSmoothing", &CameraMovementSettings::m_translateSmoothing)
->Field("TranslateSmoothness", &CameraMovementSettings::m_translateSmoothness)
->Field("CaptureCursorLook", &CameraMovementSettings::m_captureCursorLook)
- ->Field("PivotYawRotationInverted", &CameraMovementSettings::m_pivotYawRotationInverted)
+ ->Field("OrbitYawRotationInverted", &CameraMovementSettings::m_orbitYawRotationInverted)
->Field("PanInvertedX", &CameraMovementSettings::m_panInvertedX)
->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY);
@@ -86,12 +86,13 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
->Field("TranslateUp", &CameraInputSettings::m_translateUpChannelId)
->Field("TranslateDown", &CameraInputSettings::m_translateDownChannelId)
->Field("Boost", &CameraInputSettings::m_boostChannelId)
- ->Field("Pivot", &CameraInputSettings::m_pivotChannelId)
+ ->Field("Orbit", &CameraInputSettings::m_orbitChannelId)
->Field("FreeLook", &CameraInputSettings::m_freeLookChannelId)
->Field("FreePan", &CameraInputSettings::m_freePanChannelId)
- ->Field("PivotLook", &CameraInputSettings::m_pivotLookChannelId)
- ->Field("PivotDolly", &CameraInputSettings::m_pivotDollyChannelId)
- ->Field("PivotPan", &CameraInputSettings::m_pivotPanChannelId);
+ ->Field("OrbitLook", &CameraInputSettings::m_orbitLookChannelId)
+ ->Field("OrbitDolly", &CameraInputSettings::m_orbitDollyChannelId)
+ ->Field("OrbitPan", &CameraInputSettings::m_orbitPanChannelId)
+ ->Field("Focus", &CameraInputSettings::m_focusChannelId);
serialize.Class()
->Version(1)
@@ -143,8 +144,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
->Attribute(AZ::Edit::Attributes::Min, minValue)
->Attribute(AZ::Edit::Attributes::Visibility, &CameraMovementSettings::TranslateSmoothingVisibility)
->DataElement(
- AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_pivotYawRotationInverted, "Camera Pivot Yaw Inverted",
- "Inverted yaw rotation while pivoting")
+ AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_orbitYawRotationInverted, "Camera Orbit Yaw Inverted",
+ "Inverted yaw rotation while orbiting")
->DataElement(
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_panInvertedX, "Invert Pan X",
"Invert direction of pan in local X axis")
@@ -185,8 +186,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
"Key/button to move the camera more quickly")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
- AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotChannelId, "Pivot",
- "Key/button to begin the camera pivot behavior")
+ AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitChannelId, "Orbit",
+ "Key/button to begin the camera orbit behavior")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_freeLookChannelId, "Free Look",
@@ -196,24 +197,27 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_freePanChannelId, "Free Pan", "Key/button to begin camera free pan")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
- AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotLookChannelId, "Pivot Look",
- "Key/button to begin camera pivot look")
+ AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitLookChannelId, "Orbit Look",
+ "Key/button to begin camera orbit look")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
- AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotDollyChannelId, "Pivot Dolly",
- "Key/button to begin camera pivot dolly")
+ AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitDollyChannelId, "Orbit Dolly",
+ "Key/button to begin camera orbit dolly")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
- AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotPanChannelId, "Pivot Pan",
- "Key/button to begin camera pivot pan")
+ AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitPanChannelId, "Orbit Pan",
+ "Key/button to begin camera orbit pan")
+ ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
+ ->DataElement(
+ AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_focusChannelId, "Focus", "Key/button to focus camera orbit")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames);
editContext->Class("Viewport Preferences", "Viewport Preferences")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20))
->DataElement(
- AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportCamera::m_cameraMovementSettings,
- "Camera Movement Settings", "Camera Movement Settings")
+ AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportCamera::m_cameraMovementSettings, "Camera Movement Settings",
+ "Camera Movement Settings")
->DataElement(
AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportCamera::m_cameraInputSettings, "Camera Input Settings",
"Camera Input Settings");
@@ -264,7 +268,7 @@ void CEditorPreferencesPage_ViewportCamera::OnApply()
SandboxEditor::SetCameraTranslateSmoothness(m_cameraMovementSettings.m_translateSmoothness);
SandboxEditor::SetCameraTranslateSmoothingEnabled(m_cameraMovementSettings.m_translateSmoothing);
SandboxEditor::SetCameraCaptureCursorForLook(m_cameraMovementSettings.m_captureCursorLook);
- SandboxEditor::SetCameraPivotYawRotationInverted(m_cameraMovementSettings.m_pivotYawRotationInverted);
+ SandboxEditor::SetCameraOrbitYawRotationInverted(m_cameraMovementSettings.m_orbitYawRotationInverted);
SandboxEditor::SetCameraPanInvertedX(m_cameraMovementSettings.m_panInvertedX);
SandboxEditor::SetCameraPanInvertedY(m_cameraMovementSettings.m_panInvertedY);
@@ -275,12 +279,13 @@ void CEditorPreferencesPage_ViewportCamera::OnApply()
SandboxEditor::SetCameraTranslateUpChannelId(m_cameraInputSettings.m_translateUpChannelId);
SandboxEditor::SetCameraTranslateDownChannelId(m_cameraInputSettings.m_translateDownChannelId);
SandboxEditor::SetCameraTranslateBoostChannelId(m_cameraInputSettings.m_boostChannelId);
- SandboxEditor::SetCameraPivotChannelId(m_cameraInputSettings.m_pivotChannelId);
+ SandboxEditor::SetCameraOrbitChannelId(m_cameraInputSettings.m_orbitChannelId);
SandboxEditor::SetCameraFreeLookChannelId(m_cameraInputSettings.m_freeLookChannelId);
SandboxEditor::SetCameraFreePanChannelId(m_cameraInputSettings.m_freePanChannelId);
- SandboxEditor::SetCameraPivotLookChannelId(m_cameraInputSettings.m_pivotLookChannelId);
- SandboxEditor::SetCameraPivotDollyChannelId(m_cameraInputSettings.m_pivotDollyChannelId);
- SandboxEditor::SetCameraPivotPanChannelId(m_cameraInputSettings.m_pivotPanChannelId);
+ SandboxEditor::SetCameraOrbitLookChannelId(m_cameraInputSettings.m_orbitLookChannelId);
+ SandboxEditor::SetCameraOrbitDollyChannelId(m_cameraInputSettings.m_orbitDollyChannelId);
+ SandboxEditor::SetCameraOrbitPanChannelId(m_cameraInputSettings.m_orbitPanChannelId);
+ SandboxEditor::SetCameraFocusChannelId(m_cameraInputSettings.m_focusChannelId);
SandboxEditor::EditorModularViewportCameraComposerNotificationBus::Broadcast(
&SandboxEditor::EditorModularViewportCameraComposerNotificationBus::Events::OnEditorModularViewportCameraComposerSettingsChanged);
@@ -299,7 +304,7 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings()
m_cameraMovementSettings.m_translateSmoothness = SandboxEditor::CameraTranslateSmoothness();
m_cameraMovementSettings.m_translateSmoothing = SandboxEditor::CameraTranslateSmoothingEnabled();
m_cameraMovementSettings.m_captureCursorLook = SandboxEditor::CameraCaptureCursorForLook();
- m_cameraMovementSettings.m_pivotYawRotationInverted = SandboxEditor::CameraPivotYawRotationInverted();
+ m_cameraMovementSettings.m_orbitYawRotationInverted = SandboxEditor::CameraOrbitYawRotationInverted();
m_cameraMovementSettings.m_panInvertedX = SandboxEditor::CameraPanInvertedX();
m_cameraMovementSettings.m_panInvertedY = SandboxEditor::CameraPanInvertedY();
@@ -310,10 +315,11 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings()
m_cameraInputSettings.m_translateUpChannelId = SandboxEditor::CameraTranslateUpChannelId().GetName();
m_cameraInputSettings.m_translateDownChannelId = SandboxEditor::CameraTranslateDownChannelId().GetName();
m_cameraInputSettings.m_boostChannelId = SandboxEditor::CameraTranslateBoostChannelId().GetName();
- m_cameraInputSettings.m_pivotChannelId = SandboxEditor::CameraPivotChannelId().GetName();
+ m_cameraInputSettings.m_orbitChannelId = SandboxEditor::CameraOrbitChannelId().GetName();
m_cameraInputSettings.m_freeLookChannelId = SandboxEditor::CameraFreeLookChannelId().GetName();
m_cameraInputSettings.m_freePanChannelId = SandboxEditor::CameraFreePanChannelId().GetName();
- m_cameraInputSettings.m_pivotLookChannelId = SandboxEditor::CameraPivotLookChannelId().GetName();
- m_cameraInputSettings.m_pivotDollyChannelId = SandboxEditor::CameraPivotDollyChannelId().GetName();
- m_cameraInputSettings.m_pivotPanChannelId = SandboxEditor::CameraPivotPanChannelId().GetName();
+ m_cameraInputSettings.m_orbitLookChannelId = SandboxEditor::CameraOrbitLookChannelId().GetName();
+ m_cameraInputSettings.m_orbitDollyChannelId = SandboxEditor::CameraOrbitDollyChannelId().GetName();
+ m_cameraInputSettings.m_orbitPanChannelId = SandboxEditor::CameraOrbitPanChannelId().GetName();
+ m_cameraInputSettings.m_focusChannelId = SandboxEditor::CameraFocusChannelId().GetName();
}
diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.h b/Code/Editor/EditorPreferencesPageViewportCamera.h
index 01dc4664f6..fdc86b0f89 100644
--- a/Code/Editor/EditorPreferencesPageViewportCamera.h
+++ b/Code/Editor/EditorPreferencesPageViewportCamera.h
@@ -54,7 +54,7 @@ private:
float m_translateSmoothness;
bool m_translateSmoothing;
bool m_captureCursorLook;
- bool m_pivotYawRotationInverted;
+ bool m_orbitYawRotationInverted;
bool m_panInvertedX;
bool m_panInvertedY;
@@ -80,12 +80,13 @@ private:
AZStd::string m_translateUpChannelId;
AZStd::string m_translateDownChannelId;
AZStd::string m_boostChannelId;
- AZStd::string m_pivotChannelId;
+ AZStd::string m_orbitChannelId;
AZStd::string m_freeLookChannelId;
AZStd::string m_freePanChannelId;
- AZStd::string m_pivotLookChannelId;
- AZStd::string m_pivotDollyChannelId;
- AZStd::string m_pivotPanChannelId;
+ AZStd::string m_orbitLookChannelId;
+ AZStd::string m_orbitDollyChannelId;
+ AZStd::string m_orbitPanChannelId;
+ AZStd::string m_focusChannelId;
};
CameraMovementSettings m_cameraMovementSettings;
diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp
index fe8efecd17..2354c6d63a 100644
--- a/Code/Editor/EditorViewportSettings.cpp
+++ b/Code/Editor/EditorViewportSettings.cpp
@@ -28,7 +28,7 @@ namespace SandboxEditor
constexpr AZStd::string_view CameraRotateSpeedSetting = "/Amazon/Preferences/Editor/Camera/RotateSpeed";
constexpr AZStd::string_view CameraScrollSpeedSetting = "/Amazon/Preferences/Editor/Camera/DollyScrollSpeed";
constexpr AZStd::string_view CameraDollyMotionSpeedSetting = "/Amazon/Preferences/Editor/Camera/DollyMotionSpeed";
- constexpr AZStd::string_view CameraPivotYawRotationInvertedSetting = "/Amazon/Preferences/Editor/Camera/YawRotationInverted";
+ constexpr AZStd::string_view CameraOrbitYawRotationInvertedSetting = "/Amazon/Preferences/Editor/Camera/YawRotationInverted";
constexpr AZStd::string_view CameraPanInvertedXSetting = "/Amazon/Preferences/Editor/Camera/PanInvertedX";
constexpr AZStd::string_view CameraPanInvertedYSetting = "/Amazon/Preferences/Editor/Camera/PanInvertedY";
constexpr AZStd::string_view CameraPanSpeedSetting = "/Amazon/Preferences/Editor/Camera/PanSpeed";
@@ -44,12 +44,13 @@ namespace SandboxEditor
constexpr AZStd::string_view CameraTranslateUpIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpId";
constexpr AZStd::string_view CameraTranslateDownIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpDownId";
constexpr AZStd::string_view CameraTranslateBoostIdSetting = "/Amazon/Preferences/Editor/Camera/TranslateBoostId";
- constexpr AZStd::string_view CameraPivotIdSetting = "/Amazon/Preferences/Editor/Camera/PivotId";
+ constexpr AZStd::string_view CameraOrbitIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitId";
constexpr AZStd::string_view CameraFreeLookIdSetting = "/Amazon/Preferences/Editor/Camera/FreeLookId";
constexpr AZStd::string_view CameraFreePanIdSetting = "/Amazon/Preferences/Editor/Camera/FreePanId";
- constexpr AZStd::string_view CameraPivotLookIdSetting = "/Amazon/Preferences/Editor/Camera/PivotLookId";
- constexpr AZStd::string_view CameraPivotDollyIdSetting = "/Amazon/Preferences/Editor/Camera/PivotDollyId";
- constexpr AZStd::string_view CameraPivotPanIdSetting = "/Amazon/Preferences/Editor/Camera/PivotPanId";
+ constexpr AZStd::string_view CameraOrbitLookIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitLookId";
+ constexpr AZStd::string_view CameraOrbitDollyIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitDollyId";
+ constexpr AZStd::string_view CameraOrbitPanIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitPanId";
+ constexpr AZStd::string_view CameraFocusIdSetting = "/Amazon/Preferences/Editor/Camera/FocusId";
template
void SetRegistry(const AZStd::string_view setting, T&& value)
@@ -239,14 +240,14 @@ namespace SandboxEditor
SetRegistry(CameraDollyMotionSpeedSetting, speed);
}
- bool CameraPivotYawRotationInverted()
+ bool CameraOrbitYawRotationInverted()
{
- return GetRegistry(CameraPivotYawRotationInvertedSetting, false);
+ return GetRegistry(CameraOrbitYawRotationInvertedSetting, false);
}
- void SetCameraPivotYawRotationInverted(const bool inverted)
+ void SetCameraOrbitYawRotationInverted(const bool inverted)
{
- SetRegistry(CameraPivotYawRotationInvertedSetting, inverted);
+ SetRegistry(CameraOrbitYawRotationInvertedSetting, inverted);
}
bool CameraPanInvertedX()
@@ -403,14 +404,14 @@ namespace SandboxEditor
SetRegistry(CameraTranslateBoostIdSetting, cameraTranslateBoostId);
}
- AzFramework::InputChannelId CameraPivotChannelId()
+ AzFramework::InputChannelId CameraOrbitChannelId()
{
- return AzFramework::InputChannelId(GetRegistry(CameraPivotIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str());
+ return AzFramework::InputChannelId(GetRegistry(CameraOrbitIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str());
}
- void SetCameraPivotChannelId(AZStd::string_view cameraPivotId)
+ void SetCameraOrbitChannelId(AZStd::string_view cameraOrbitId)
{
- SetRegistry(CameraPivotIdSetting, cameraPivotId);
+ SetRegistry(CameraOrbitIdSetting, cameraOrbitId);
}
AzFramework::InputChannelId CameraFreeLookChannelId()
@@ -433,33 +434,43 @@ namespace SandboxEditor
SetRegistry(CameraFreePanIdSetting, cameraFreePanId);
}
- AzFramework::InputChannelId CameraPivotLookChannelId()
+ AzFramework::InputChannelId CameraOrbitLookChannelId()
{
- return AzFramework::InputChannelId(GetRegistry(CameraPivotLookIdSetting, AZStd::string("mouse_button_left")).c_str());
+ return AzFramework::InputChannelId(GetRegistry(CameraOrbitLookIdSetting, AZStd::string("mouse_button_left")).c_str());
}
- void SetCameraPivotLookChannelId(AZStd::string_view cameraPivotLookId)
+ void SetCameraOrbitLookChannelId(AZStd::string_view cameraOrbitLookId)
{
- SetRegistry(CameraPivotLookIdSetting, cameraPivotLookId);
+ SetRegistry(CameraOrbitLookIdSetting, cameraOrbitLookId);
}
- AzFramework::InputChannelId CameraPivotDollyChannelId()
+ AzFramework::InputChannelId CameraOrbitDollyChannelId()
{
- return AzFramework::InputChannelId(GetRegistry(CameraPivotDollyIdSetting, AZStd::string("mouse_button_right")).c_str());
+ return AzFramework::InputChannelId(GetRegistry(CameraOrbitDollyIdSetting, AZStd::string("mouse_button_right")).c_str());
}
- void SetCameraPivotDollyChannelId(AZStd::string_view cameraPivotDollyId)
+ void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId)
{
- SetRegistry(CameraPivotDollyIdSetting, cameraPivotDollyId);
+ SetRegistry(CameraOrbitDollyIdSetting, cameraOrbitDollyId);
}
- AzFramework::InputChannelId CameraPivotPanChannelId()
+ AzFramework::InputChannelId CameraOrbitPanChannelId()
{
- return AzFramework::InputChannelId(GetRegistry(CameraPivotPanIdSetting, AZStd::string("mouse_button_middle")).c_str());
+ return AzFramework::InputChannelId(GetRegistry(CameraOrbitPanIdSetting, AZStd::string("mouse_button_middle")).c_str());
}
- void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId)
+ void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId)
{
- SetRegistry(CameraPivotPanIdSetting, cameraPivotPanId);
+ SetRegistry(CameraOrbitPanIdSetting, cameraOrbitPanId);
+ }
+
+ AzFramework::InputChannelId CameraFocusChannelId()
+ {
+ return AzFramework::InputChannelId(GetRegistry(CameraFocusIdSetting, AZStd::string("keyboard_key_alphanumeric_X")).c_str());
+ }
+
+ void SetCameraFocusChannelId(AZStd::string_view cameraFocusId)
+ {
+ SetRegistry(CameraFocusIdSetting, cameraFocusId);
}
} // namespace SandboxEditor
diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h
index d1271017c6..c1394f7404 100644
--- a/Code/Editor/EditorViewportSettings.h
+++ b/Code/Editor/EditorViewportSettings.h
@@ -71,8 +71,8 @@ namespace SandboxEditor
SANDBOX_API float CameraDollyMotionSpeed();
SANDBOX_API void SetCameraDollyMotionSpeed(float speed);
- SANDBOX_API bool CameraPivotYawRotationInverted();
- SANDBOX_API void SetCameraPivotYawRotationInverted(bool inverted);
+ SANDBOX_API bool CameraOrbitYawRotationInverted();
+ SANDBOX_API void SetCameraOrbitYawRotationInverted(bool inverted);
SANDBOX_API bool CameraPanInvertedX();
SANDBOX_API void SetCameraPanInvertedX(bool inverted);
@@ -119,8 +119,8 @@ namespace SandboxEditor
SANDBOX_API AzFramework::InputChannelId CameraTranslateBoostChannelId();
SANDBOX_API void SetCameraTranslateBoostChannelId(AZStd::string_view cameraTranslateBoostId);
- SANDBOX_API AzFramework::InputChannelId CameraPivotChannelId();
- SANDBOX_API void SetCameraPivotChannelId(AZStd::string_view cameraPivotId);
+ SANDBOX_API AzFramework::InputChannelId CameraOrbitChannelId();
+ SANDBOX_API void SetCameraOrbitChannelId(AZStd::string_view cameraOrbitId);
SANDBOX_API AzFramework::InputChannelId CameraFreeLookChannelId();
SANDBOX_API void SetCameraFreeLookChannelId(AZStd::string_view cameraFreeLookId);
@@ -128,12 +128,15 @@ namespace SandboxEditor
SANDBOX_API AzFramework::InputChannelId CameraFreePanChannelId();
SANDBOX_API void SetCameraFreePanChannelId(AZStd::string_view cameraFreePanId);
- SANDBOX_API AzFramework::InputChannelId CameraPivotLookChannelId();
- SANDBOX_API void SetCameraPivotLookChannelId(AZStd::string_view cameraPivotLookId);
+ SANDBOX_API AzFramework::InputChannelId CameraOrbitLookChannelId();
+ SANDBOX_API void SetCameraOrbitLookChannelId(AZStd::string_view cameraOrbitLookId);
- SANDBOX_API AzFramework::InputChannelId CameraPivotDollyChannelId();
- SANDBOX_API void SetCameraPivotDollyChannelId(AZStd::string_view cameraPivotDollyId);
+ SANDBOX_API AzFramework::InputChannelId CameraOrbitDollyChannelId();
+ SANDBOX_API void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId);
- SANDBOX_API AzFramework::InputChannelId CameraPivotPanChannelId();
- SANDBOX_API void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId);
+ SANDBOX_API AzFramework::InputChannelId CameraOrbitPanChannelId();
+ SANDBOX_API void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId);
+
+ SANDBOX_API AzFramework::InputChannelId CameraFocusChannelId();
+ SANDBOX_API void SetCameraFocusChannelId(AZStd::string_view cameraFocusId);
} // namespace SandboxEditor
diff --git a/Code/Editor/GameExporter.cpp b/Code/Editor/GameExporter.cpp
index 9315505e08..0324629877 100644
--- a/Code/Editor/GameExporter.cpp
+++ b/Code/Editor/GameExporter.cpp
@@ -28,6 +28,7 @@
#include "Objects/EntityObject.h"
#include
+#include
//////////////////////////////////////////////////////////////////////////
#define MUSIC_LEVEL_LIBRARY_FILE "Music.xml"
diff --git a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp
index 157291cfc2..ea3653f663 100644
--- a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp
+++ b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp
@@ -69,6 +69,7 @@ namespace UnitTest
m_rootWidget = AZStd::make_unique();
m_rootWidget->setFixedSize(WidgetSize);
+ m_rootWidget->move(0, 0); // explicitly set the widget to be in the upper left corner
m_controllerList = AZStd::make_shared();
m_controllerList->RegisterViewportContext(TestViewportId);
@@ -344,4 +345,51 @@ namespace UnitTest
// Clean-up
HaltCollaborators();
}
+
+ // test to verify deltas and cursor positions are handled correctly when the widget is moved
+ TEST_F(ModularViewportCameraControllerFixture, CameraDoesNotStutterAfterWidgetIsMoved)
+ {
+ // Given
+ PrepareCollaborators();
+ SandboxEditor::SetCameraCaptureCursorForLook(true);
+
+ const float deltaTime = 1.0f / 60.0f;
+
+ // When
+ // move cursor to the center of the screen
+ auto start = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
+ MouseMove(m_rootWidget.get(), start, QPoint(0, 0));
+ m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
+
+ // move camera right
+ const auto mouseDelta = QPoint(200, 0);
+ MousePressAndMove(m_rootWidget.get(), start, mouseDelta, Qt::MouseButton::RightButton);
+ m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
+
+ QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::RightButton, Qt::NoModifier, start + mouseDelta);
+
+ // update the position of the widget
+ const auto offset = QPoint(500, 500);
+ m_rootWidget->move(offset);
+
+ // move cursor back to widget center
+ MouseMove(m_rootWidget.get(), start, QPoint(0, 0));
+ m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
+
+ // move camera left
+ MousePressAndMove(m_rootWidget.get(), start, -mouseDelta, Qt::MouseButton::RightButton);
+ m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
+
+ // Then
+ // ensure the camera rotation has returned to the identity
+ const AZ::Quaternion cameraRotation = m_cameraViewportContextView->GetCameraTransform().GetRotation();
+ const auto eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromQuaternion(cameraRotation));
+
+ using ::testing::FloatNear;
+ EXPECT_THAT(eulerAngles.GetX(), FloatNear(0.0f, 0.001f));
+ EXPECT_THAT(eulerAngles.GetZ(), FloatNear(0.0f, 0.001f));
+
+ // Clean-up
+ HaltCollaborators();
+ }
} // namespace UnitTest
diff --git a/Code/Editor/MainWindow.cpp b/Code/Editor/MainWindow.cpp
index 05477bb0c7..ed72cd9170 100644
--- a/Code/Editor/MainWindow.cpp
+++ b/Code/Editor/MainWindow.cpp
@@ -98,6 +98,7 @@ AZ_POP_DISABLE_WARNING
#include "ActionManager.h"
#include
+#include
#include
using namespace AZ;
diff --git a/Code/Editor/Objects/ObjectManager.cpp b/Code/Editor/Objects/ObjectManager.cpp
index f67cf24553..7057cc5b7b 100644
--- a/Code/Editor/Objects/ObjectManager.cpp
+++ b/Code/Editor/Objects/ObjectManager.cpp
@@ -30,6 +30,8 @@
#include "Plugins/ComponentEntityEditorPlugin/Objects/ComponentEntityObject.h"
#include
+#include
+#include
AZ_CVAR_EXTERNED(bool, ed_visibility_logTiming);
@@ -107,14 +109,13 @@ CObjectManager::CObjectManager()
m_objectsByName.reserve(1024);
LoadRegistry();
- AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
- AzToolsFramework::GetEntityContextId());
+ AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
}
//////////////////////////////////////////////////////////////////////////
CObjectManager::~CObjectManager()
{
- AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
+ AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
m_bExiting = true;
SaveRegistry();
@@ -2306,25 +2307,33 @@ void CObjectManager::SelectObjectInRect(CBaseObject* pObj, CViewport* view, HitC
}
}
-void CObjectManager::EnteredComponentMode(const AZStd::vector& /*componentModeTypes*/)
+void CObjectManager::OnEditorModeActivated(
+ [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
- // hide current gizmo for entity (translate/rotate/scale)
- IGizmoManager* gizmoManager = GetGizmoManager();
- const size_t gizmoCount = static_cast(gizmoManager->GetGizmoCount());
- for (size_t i = 0; i < gizmoCount; ++i)
+ if (mode == AzToolsFramework::ViewportEditorMode::Component)
{
- gizmoManager->RemoveGizmo(gizmoManager->GetGizmoByIndex(static_cast(i)));
+ // hide current gizmo for entity (translate/rotate/scale)
+ IGizmoManager* gizmoManager = GetGizmoManager();
+ const size_t gizmoCount = static_cast(gizmoManager->GetGizmoCount());
+ for (size_t i = 0; i < gizmoCount; ++i)
+ {
+ gizmoManager->RemoveGizmo(gizmoManager->GetGizmoByIndex(static_cast(i)));
+ }
}
}
-void CObjectManager::LeftComponentMode(const AZStd::vector& /*componentModeTypes*/)
+void CObjectManager::OnEditorModeDeactivated(
+ [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
- // show translate/rotate/scale gizmo again
- if (IGizmoManager* gizmoManager = GetGizmoManager())
+ if (mode == AzToolsFramework::ViewportEditorMode::Component)
{
- if (CBaseObject* selectedObject = GetIEditor()->GetSelectedObject())
+ // show translate/rotate/scale gizmo again
+ if (IGizmoManager* gizmoManager = GetGizmoManager())
{
- gizmoManager->AddGizmo(new CAxisGizmo(selectedObject));
+ if (CBaseObject* selectedObject = GetIEditor()->GetSelectedObject())
+ {
+ gizmoManager->AddGizmo(new CAxisGizmo(selectedObject));
+ }
}
}
}
diff --git a/Code/Editor/Objects/ObjectManager.h b/Code/Editor/Objects/ObjectManager.h
index 0ad5d8323e..7fb2342e40 100644
--- a/Code/Editor/Objects/ObjectManager.h
+++ b/Code/Editor/Objects/ObjectManager.h
@@ -20,8 +20,9 @@
#include "ObjectManagerEventBus.h"
#include
-#include
+#include
#include
+#include
#include
// forward declarations.
@@ -58,7 +59,7 @@ public:
*/
class CObjectManager
: public IObjectManager
- , private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
+ , private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
{
public:
//! Selection functor callback.
@@ -329,9 +330,11 @@ private:
void FindDisplayableObjects(DisplayContext& dc, bool bDisplay);
- // EditorComponentModeNotificationBus
- void EnteredComponentMode(const AZStd::vector& componentModeTypes) override;
- void LeftComponentMode(const AZStd::vector& componentModeTypes) override;
+ // ViewportEditorModeNotificationsBus overrides ...
+ void OnEditorModeActivated(
+ const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
+ void OnEditorModeDeactivated(
+ const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
private:
typedef std::map Objects;
diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
index 666700a874..41e950a058 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
@@ -142,8 +142,7 @@ void GetSelectedEntitiesSetWithFlattenedHierarchy(AzToolsFramework::EntityIdSet&
}
SandboxIntegrationManager::SandboxIntegrationManager()
- : m_inObjectPickMode(false)
- , m_startedUndoRecordingNestingLevel(0)
+ : m_startedUndoRecordingNestingLevel(0)
, m_dc(nullptr)
, m_notificationWindowManager(new AzToolsFramework::SliceOverridesNotificationWindowManager())
{
@@ -1000,62 +999,6 @@ void SandboxIntegrationManager::SetupSliceContextMenu_Modify(QMenu* menu, const
revertAction->setEnabled(canRevert);
}
-void SandboxIntegrationManager::HandleObjectModeSelection(const AZ::Vector2& point, [[maybe_unused]] int flags, bool& handled)
-{
- // Todo - Use a custom "edit tool". This will eliminate the need for this bus message entirely, which technically
- // makes this feature less intrusive on Sandbox.
- // UPDATE: This is now provided by EditorPickEntitySelection when the new Viewport Interaction Model changes are enabled.
- if (m_inObjectPickMode)
- {
- CViewport* view = GetIEditor()->GetViewManager()->GetGameViewport();
- const QPoint viewPoint(static_cast(point.GetX()), static_cast(point.GetY()));
-
- HitContext hitInfo;
- hitInfo.view = view;
- if (view->HitTest(viewPoint, hitInfo))
- {
- if (hitInfo.object && (hitInfo.object->GetType() == OBJTYPE_AZENTITY))
- {
- CComponentEntityObject* entityObject = static_cast(hitInfo.object);
- AzToolsFramework::EditorPickModeRequestBus::Broadcast(
- &AzToolsFramework::EditorPickModeRequests::PickModeSelectEntity, entityObject->GetAssociatedEntityId());
- }
- }
-
- AzToolsFramework::EditorPickModeRequestBus::Broadcast(
- &AzToolsFramework::EditorPickModeRequests::StopEntityPickMode);
-
- handled = true;
- }
-}
-
-void SandboxIntegrationManager::UpdateObjectModeCursor(AZ::u32& cursorId, AZStd::string& cursorStr)
-{
- if (m_inObjectPickMode)
- {
- cursorId = static_cast(STD_CURSOR_HAND);
- cursorStr = "Pick an entity...";
- }
-}
-
-void SandboxIntegrationManager::OnEntityPickModeStarted()
-{
- m_inObjectPickMode = true;
-
- // Currently this object pick mode is activated only via PropertyEntityIdCtrl picker.
- // When the picker button is clicked, we transfer focus to the viewport so the
- // spacebar can still be used to activate selection helpers.
- if (CViewport* view = GetIEditor()->GetViewManager()->GetGameViewport())
- {
- view->SetFocus();
- }
-}
-
-void SandboxIntegrationManager::OnEntityPickModeStopped()
-{
- m_inObjectPickMode = false;
-}
-
void SandboxIntegrationManager::CreateEditorRepresentation(AZ::Entity* entity)
{
IEditor* editor = GetIEditor();
@@ -1952,7 +1895,7 @@ void SandboxIntegrationManager::MakeSliceFromEntities(const AzToolsFramework::En
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(entitiesAndDescendants,
&AzToolsFramework::ToolsApplicationRequestBus::Events::GatherEntitiesAndAllDescendents, entities);
- const AZStd::string slicesAssetsPath = "@devassets@/Slices";
+ const AZStd::string slicesAssetsPath = "@projectroot@/Slices";
if (!gEnv->pFileIO->Exists(slicesAssetsPath.c_str()))
{
diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h
index 617c5cb2c8..40962409a3 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h
@@ -93,7 +93,6 @@ namespace AzToolsFramework
class SandboxIntegrationManager
: private AzToolsFramework::ToolsApplicationEvents::Bus::Handler
, private AzToolsFramework::EditorRequests::Bus::Handler
- , private AzToolsFramework::EditorPickModeNotificationBus::Handler
, private AzToolsFramework::EditorContextMenuBus::Handler
, private AzToolsFramework::EditorWindowRequests::Bus::Handler
, private AzFramework::AssetCatalogEventBus::Handler
@@ -140,8 +139,6 @@ private:
QDockWidget* InstanceViewPane(const char* paneName) override;
void CloseViewPane(const char* paneName) override;
void BrowseForAssets(AzToolsFramework::AssetBrowser::AssetSelectionModel& selection) override;
- void HandleObjectModeSelection(const AZ::Vector2& point, int flags, bool& handled) override;
- void UpdateObjectModeCursor(AZ::u32& cursorId, AZStd::string& cursorStr) override;
void CreateEditorRepresentation(AZ::Entity* entity) override;
bool DestroyEditorRepresentation(AZ::EntityId entityId, bool deleteAZEntity) override;
void CloneSelection(bool& handled) override;
@@ -175,10 +172,6 @@ private:
QWidget* GetAppMainWindow() override;
//////////////////////////////////////////////////////////////////////////
- // EditorPickModeNotificationBus
- void OnEntityPickModeStarted() override;
- void OnEntityPickModeStopped() override;
-
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::EditorContextMenu::Bus::Handler overrides
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override;
@@ -281,7 +274,6 @@ private:
private:
AZ::Vector2 m_contextMenuViewPoint;
- int m_inObjectPickMode;
short m_startedUndoRecordingNestingLevel; // used in OnBegin/EndUndo to ensure we only accept undo's we started recording
AzToolsFramework::SliceOverridesNotificationWindowManager* m_notificationWindowManager;
diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.cpp
index 9ed7c4a144..803deb3509 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.cpp
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.cpp
@@ -36,6 +36,7 @@
#include
#include
#include
+#include
#include
#include
@@ -267,8 +268,7 @@ OutlinerWidget::OutlinerWidget(QWidget* pParent, Qt::WindowFlags flags)
ToolsApplicationEvents::Bus::Handler::BusConnect();
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusConnect();
- AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
- AzToolsFramework::GetEntityContextId());
+ AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusConnect();
AzToolsFramework::EditorWindowUIRequestBus::Handler::BusConnect();
}
@@ -276,7 +276,7 @@ OutlinerWidget::OutlinerWidget(QWidget* pParent, Qt::WindowFlags flags)
OutlinerWidget::~OutlinerWidget()
{
AzToolsFramework::EditorWindowUIRequestBus::Handler::BusDisconnect();
- AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
+ AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect();
EntityHighlightMessages::Bus::Handler::BusDisconnect();
@@ -1335,14 +1335,22 @@ void OutlinerWidget::SetEditorUiEnabled(bool enable)
EnableUi(enable);
}
-void OutlinerWidget::EnteredComponentMode([[maybe_unused]] const AZStd::vector& componentModeTypes)
+void OutlinerWidget::OnEditorModeActivated(
+ [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
- EnableUi(false);
+ if (mode == AzToolsFramework::ViewportEditorMode::Component)
+ {
+ EnableUi(false);
+ }
}
-void OutlinerWidget::LeftComponentMode([[maybe_unused]] const AZStd::vector& componentModeTypes)
+void OutlinerWidget::OnEditorModeDeactivated(
+ [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
- EnableUi(true);
+ if (mode == AzToolsFramework::ViewportEditorMode::Component)
+ {
+ EnableUi(true);
+ }
}
void OutlinerWidget::OnSliceInstantiated(const AZ::Data::AssetId& /*sliceAssetId*/, AZ::SliceComponent::SliceInstanceAddress& sliceAddress, const AzFramework::SliceInstantiationTicket& /*ticket*/)
diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.hxx b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.hxx
index b29032a760..364ab05eb7 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.hxx
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.hxx
@@ -16,7 +16,7 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
@@ -58,7 +58,7 @@ class OutlinerWidget
, private AzToolsFramework::EditorEntityContextNotificationBus::Handler
, private AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler
, private AzToolsFramework::EditorEntityInfoNotificationBus::Handler
- , private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
+ , private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
, private AzToolsFramework::EditorWindowUIRequestBus::Handler
{
Q_OBJECT;
@@ -105,9 +105,11 @@ private:
void OnEntityInfoUpdatedAddChildEnd(AZ::EntityId /*parentId*/, AZ::EntityId /*childId*/) override;
void OnEntityInfoUpdatedName(AZ::EntityId entityId, const AZStd::string& /*name*/) override;
- // EditorComponentModeNotificationBus
- void EnteredComponentMode(const AZStd::vector& componentModeTypes) override;
- void LeftComponentMode(const AZStd::vector& componentModeTypes) override;
+ // ViewportEditorModeNotificationsBus overrides ...
+ void OnEditorModeActivated(
+ const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
+ void OnEditorModeDeactivated(
+ const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
// EditorWindowUIRequestBus overrides
void SetEditorUiEnabled(bool enable) override;
diff --git a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp
index 98bf228391..0ff9467f33 100644
--- a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp
+++ b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp
@@ -30,6 +30,7 @@ class CXTPDockingPaneLayout; // Needed for settings.h
#include
#include
#include
+#include
#include
#include
#include
@@ -47,41 +48,6 @@ class CXTPDockingPaneLayout; // Needed for settings.h
const char* AssetImporterWindow::s_documentationWebAddress = "http://docs.aws.amazon.com/lumberyard/latest/userguide/char-fbx-importer.html";
const AZ::Uuid AssetImporterWindow::s_browseTag = AZ::Uuid::CreateString("{C240D2E1-BFD2-4FFA-BB5B-CC0FA389A5D3}");
-void MakeUserFriendlySourceAssetPath(QString& out, const QString& sourcePath)
-{
- char devAssetsRoot[AZ_MAX_PATH_LEN] = { 0 };
- if (!gEnv->pFileIO->ResolvePath("@devroot@", devAssetsRoot, AZ_MAX_PATH_LEN))
- {
- out = sourcePath;
- return;
- }
-
- AZStd::replace(devAssetsRoot, devAssetsRoot + AZ_MAX_PATH_LEN- 1, AZ_WRONG_FILESYSTEM_SEPARATOR, AZ_CORRECT_FILESYSTEM_SEPARATOR);
-
- // Find if the sourcePathArray is a sub directory of the devAssets folder
- // Keep reference to sourcePathArray long enough to use in PathView
- QByteArray sourcePathArray = sourcePath.toUtf8();
- AZ::IO::PathView sourcePathRootView(sourcePathArray.data());
- AZ::IO::PathView devAssetsRootView(devAssetsRoot);
- auto [sourcePathIter, devAssetsIter] = AZStd::mismatch(sourcePathRootView.begin(), sourcePathRootView.end(),
- devAssetsRootView.begin(), devAssetsRootView.end());
- // If the devAssets path iterator is not equal to the end, then there was a mismistch while comparing it
- // against the source path indicating that the source path is not a sub-directory
- if (devAssetsIter != devAssetsRootView.end())
- {
- out = sourcePath;
- return;
- }
-
- int offset = aznumeric_cast(strlen(devAssetsRoot));
- if (sourcePath.at(offset) == AZ_CORRECT_FILESYSTEM_SEPARATOR)
- {
- ++offset;
- }
- out = sourcePath.right(sourcePath.length() - offset);
-
-}
-
AssetImporterWindow::AssetImporterWindow()
: AssetImporterWindow(nullptr)
{
@@ -102,7 +68,7 @@ AssetImporterWindow::AssetImporterWindow(QWidget* parent)
AssetImporterWindow::~AssetImporterWindow()
{
- AZ_Assert(m_processingOverlayIndex == AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex,
+ AZ_Assert(m_processingOverlayIndex == AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex,
"Processing overlay (and potentially background thread) still active at destruction.");
AZ_Assert(!m_processingOverlay, "Processing overlay (and potentially background thread) still active at destruction.");
}
@@ -133,7 +99,7 @@ void AssetImporterWindow::OpenFile(const AZStd::string& filePath)
QMessageBox::warning(this, "In progress", "Unable to close one or more windows at this time.");
return;
}
-
+
OpenFileInternal(filePath);
}
@@ -146,7 +112,7 @@ void AssetImporterWindow::closeEvent(QCloseEvent* ev)
if (m_processingOverlay)
{
- AZ_Assert(m_processingOverlayIndex != AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex,
+ AZ_Assert(m_processingOverlayIndex != AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex,
"Processing overlay present, but not the index in the overlay for it.");
if (m_processingOverlay->HasProcessingCompleted())
{
@@ -157,7 +123,7 @@ void AssetImporterWindow::closeEvent(QCloseEvent* ev)
}
else
{
- QMessageBox::critical(this, "Processing In Progress", "Unable to close the result window at this time.",
+ QMessageBox::critical(this, "Processing In Progress", "Unable to close the result window at this time.",
QMessageBox::Ok, QMessageBox::Ok);
ev->ignore();
return;
@@ -165,7 +131,7 @@ void AssetImporterWindow::closeEvent(QCloseEvent* ev)
}
else
{
- QMessageBox::critical(this, "Processing In Progress", "Please wait until processing has completed to try again.",
+ QMessageBox::critical(this, "Processing In Progress", "Please wait until processing has completed to try again.",
QMessageBox::Ok, QMessageBox::Ok);
ev->ignore();
return;
@@ -199,7 +165,9 @@ void AssetImporterWindow::Init()
// Load the style sheets
AzQtComponents::StylesheetPreprocessor styleSheetProcessor(nullptr);
- AZStd::string mainWindowQSSPath = Path::GetEditingRootFolder() + "\\Editor\\Styles\\AssetImporterWindow.qss";
+ auto mainWindowQSSPath = AZ::IO::Path(AZ::Utils::GetEnginePath()) / "Assets";
+ mainWindowQSSPath /= "Editor/Styles/AssetImporterWindow.qss";
+ mainWindowQSSPath.MakePreferred();
QFile mainWindowStyleSheetFile(mainWindowQSSPath.c_str());
if (mainWindowStyleSheetFile.open(QFile::ReadOnly))
{
@@ -212,7 +180,7 @@ void AssetImporterWindow::Init()
{
ui->m_actionInspect->setVisible(false);
}
-
+
ResetMenuAccess(WindowState::InitialNothingLoaded);
// Setup the overlay system, and set the root to be the root display. The root display has the browse,
@@ -220,7 +188,7 @@ void AssetImporterWindow::Init()
m_overlay.reset(aznew AZ::SceneAPI::UI::OverlayWidget(this));
m_rootDisplay.reset(aznew ImporterRootDisplay(m_serializeContext));
connect(m_rootDisplay.data(), &ImporterRootDisplay::UpdateClicked, this, &AssetImporterWindow::UpdateClicked);
-
+
connect(m_overlay.data(), &AZ::SceneAPI::UI::OverlayWidget::LayerAdded, this, &AssetImporterWindow::OverlayLayerAdded);
connect(m_overlay.data(), &AZ::SceneAPI::UI::OverlayWidget::LayerRemoved, this, &AssetImporterWindow::OverlayLayerRemoved);
@@ -242,7 +210,7 @@ void AssetImporterWindow::Init()
AZStd::string joinedExtensions;
AzFramework::StringFunc::Join(joinedExtensions, extensions.begin(), extensions.end(), " or ");
- AZStd::string firstLineText =
+ AZStd::string firstLineText =
AZStd::string::format(
"%s files are available for use after placing them in any folder within your game project. "
"These files will automatically be processed and may be accessed via the Asset Browser. Learn more...",
@@ -250,13 +218,13 @@ void AssetImporterWindow::Init()
ui->m_initialPromptFirstLine->setText(firstLineText.c_str());
- AZStd::string secondLineText =
+ AZStd::string secondLineText =
AZStd::string::format("To adjust the %s settings, right-click the file in the Asset Browser and select \"Edit Settings\" from the context menu.", joinedExtensions.c_str());
ui->m_initialPromptSecondLine->setText(secondLineText.c_str());
}
else
{
- AZStd::string firstLineText =
+ AZStd::string firstLineText =
AZStd::string::format(
"Files are available for use after placing them in any folder within your game project. "
"These files will automatically be processed and may be accessed via the Asset Browser. Learn more...", s_documentationWebAddress);
@@ -282,12 +250,12 @@ void AssetImporterWindow::OpenFileInternal(const AZStd::string& filePath)
auto asyncLoadHandler = AZStd::make_shared(
s_browseTag,
[this, filePath]()
- {
- m_assetImporterDocument->LoadScene(filePath);
+ {
+ m_assetImporterDocument->LoadScene(filePath);
},
[this]()
{
- HandleAssetLoadingCompleted();
+ HandleAssetLoadingCompleted();
}, this);
m_processingOverlay.reset(new ProcessingOverlayWidget(m_overlay.data(), ProcessingOverlayWidget::Layout::Loading, s_browseTag));
@@ -304,7 +272,7 @@ bool AssetImporterWindow::IsAllowedToChangeSourceFile()
return true;
}
- QMessageBox messageBox(QMessageBox::Icon::NoIcon, "Unsaved changes",
+ QMessageBox messageBox(QMessageBox::Icon::NoIcon, "Unsaved changes",
"You have unsaved changes. Do you want to discard those changes?",
QMessageBox::StandardButton::Discard | QMessageBox::StandardButton::Cancel, this);
messageBox.exec();
@@ -406,7 +374,7 @@ void AssetImporterWindow::OnSceneResetRequested()
else
{
m_assetImporterDocument->ClearScene();
- AZ_TracePrintf(ErrorWindow, "Manifest reset returned in '%s'",
+ AZ_TracePrintf(ErrorWindow, "Manifest reset returned in '%s'",
result.GetResult() == ProcessingResult::Failure ? "Failure" : "Ignored");
}
},
@@ -456,7 +424,7 @@ void AssetImporterWindow::OnInspect()
// make sure the inspector doesn't outlive the AssetImporterWindow, since we own the data it will be inspecting.
auto* theInspectWidget = aznew AZ::SceneAPI::UI::SceneGraphInspectWidget(*m_assetImporterDocument->GetScene());
QObject::connect(this, &QObject::destroyed, theInspectWidget, [theInspectWidget]() { theInspectWidget->window()->close(); } );
-
+
m_overlay->PushLayer(label, theInspectWidget, "Scene Inspector", buttons);
}
@@ -483,7 +451,7 @@ void AssetImporterWindow::OverlayLayerRemoved()
else
{
ResetMenuAccess(WindowState::InitialNothingLoaded);
-
+
ui->m_initialBrowseContainer->show();
m_rootDisplay->hide();
}
@@ -533,8 +501,9 @@ void AssetImporterWindow::HandleAssetLoadingCompleted()
m_fullSourcePath = m_assetImporterDocument->GetScene()->GetSourceFilename();
SetTitle(m_fullSourcePath.c_str());
- QString userFriendlyFileName;
- MakeUserFriendlySourceAssetPath(userFriendlyFileName, m_fullSourcePath.c_str());
+ AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath();
+ AZ::IO::FixedMaxPath relativeSourcePath = AZ::IO::PathView(m_fullSourcePath).LexicallyProximate(projectPath);
+ auto userFriendlyFileName = QString::fromUtf8(relativeSourcePath.c_str(), static_cast(relativeSourcePath.Native().size()));
m_rootDisplay->SetSceneDisplay(userFriendlyFileName, m_assetImporterDocument->GetScene());
// Once we've browsed to something successfully, we need to hide the initial browse button layer and
diff --git a/Code/Editor/Plugins/EditorAssetImporter/SceneSerializationHandler.cpp b/Code/Editor/Plugins/EditorAssetImporter/SceneSerializationHandler.cpp
index b1d07af41c..5ee54667b9 100644
--- a/Code/Editor/Plugins/EditorAssetImporter/SceneSerializationHandler.cpp
+++ b/Code/Editor/Plugins/EditorAssetImporter/SceneSerializationHandler.cpp
@@ -7,9 +7,11 @@
*/
#include
+#include
#include
#include
#include
+#include
#include
#include
#include
@@ -50,22 +52,15 @@ namespace AZ
return nullptr;
}
- AZStd::string cleanPath = filePath;
- if (AzFramework::StringFunc::Path::IsRelative(filePath.c_str()))
+ AZ::IO::Path enginePath;
+ if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
- const char* absolutePath = nullptr;
- AzToolsFramework::AssetSystemRequestBus::BroadcastResult(absolutePath,
- &AzToolsFramework::AssetSystemRequestBus::Events::GetAbsoluteDevRootFolderPath);
- AZ_Assert(absolutePath, "Unable to retrieve the dev folder path");
- AzFramework::StringFunc::Path::Join(absolutePath, cleanPath.c_str(), cleanPath);
- }
- else
- {
- // Normalizing is not needed if the path is relative as Join(...) will also normalize.
- AzFramework::StringFunc::Path::Normalize(cleanPath);
+ settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
}
- auto sceneIt = m_scenes.find(cleanPath);
+ AZ::IO::Path cleanPath = (enginePath / filePath).LexicallyNormal();
+
+ auto sceneIt = m_scenes.find(cleanPath.Native());
if (sceneIt != m_scenes.end())
{
AZStd::shared_ptr scene = sceneIt->second.lock();
@@ -98,14 +93,14 @@ namespace AZ
}
AZStd::shared_ptr scene =
- AssetImportRequest::LoadSceneFromVerifiedPath(cleanPath, sceneSourceGuid, AssetImportRequest::RequestingApplication::Editor, SceneAPI::SceneCore::LoadingComponent::TYPEINFO_Uuid());
+ AssetImportRequest::LoadSceneFromVerifiedPath(cleanPath.Native(), sceneSourceGuid, AssetImportRequest::RequestingApplication::Editor, SceneAPI::SceneCore::LoadingComponent::TYPEINFO_Uuid());
if (!scene)
{
AZ_TracePrintf(Utilities::ErrorWindow, "Failed to load the requested scene.");
return nullptr;
}
- m_scenes.emplace(AZStd::move(cleanPath), scene);
+ m_scenes.emplace(AZStd::move(cleanPath.Native()), scene);
return scene;
}
diff --git a/Code/Editor/Plugins/EditorCommon/CMakeLists.txt b/Code/Editor/Plugins/EditorCommon/CMakeLists.txt
index cd9f2e79c7..609482b581 100644
--- a/Code/Editor/Plugins/EditorCommon/CMakeLists.txt
+++ b/Code/Editor/Plugins/EditorCommon/CMakeLists.txt
@@ -39,7 +39,6 @@ ly_add_target(
EDITOR_COMMON_IMPORTS
BUILD_DEPENDENCIES
PRIVATE
- 3rdParty::zlib
3rdParty::Qt::Core
3rdParty::Qt::Widgets
Legacy::CryCommon
diff --git a/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp b/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp
index 6b454474be..a53ce966c4 100644
--- a/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp
+++ b/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp
@@ -46,7 +46,6 @@ namespace ProjectSettingsTool
, LastPathBus::Handler()
, m_ui(new Ui::ProjectSettingsToolWidget())
, m_reconfigureProcess()
- , m_devRoot(GetDevRoot())
, m_projectRoot(GetProjectRoot())
, m_projectName(GetProjectName())
, m_plistsInitVector(
diff --git a/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.h b/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.h
index 1c9aa4b1bf..daa5bea27c 100644
--- a/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.h
+++ b/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.h
@@ -147,7 +147,6 @@ namespace ProjectSettingsTool
// The process used to reconfigure settings
QProcess m_reconfigureProcess;
- AZStd::string m_devRoot;
AZStd::string m_projectRoot;
AZStd::string m_projectName;
diff --git a/Code/Editor/Plugins/ProjectSettingsTool/Utils.cpp b/Code/Editor/Plugins/ProjectSettingsTool/Utils.cpp
index 0e171adf04..d5b94469cc 100644
--- a/Code/Editor/Plugins/ProjectSettingsTool/Utils.cpp
+++ b/Code/Editor/Plugins/ProjectSettingsTool/Utils.cpp
@@ -27,37 +27,31 @@ namespace
}
template
- StringType GetAbsoluteDevRoot()
+ StringType GetAbsoluteEngineRoot()
{
- const char* devRoot = nullptr;
- AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
- devRoot,
- &AzToolsFramework::AssetSystemRequestBus::Handler::GetAbsoluteDevRootFolderPath);
+ AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
- if (!devRoot)
+ if (engineRoot.empty())
{
return "";
}
- StringType devRootString(devRoot);
- ToUnixPath(devRootString);
- return devRootString;
+ StringType engineRootString(engineRoot.c_str());
+ ToUnixPath(engineRootString);
+ return engineRootString;
}
template
StringType GetAbsoluteProjectRoot()
{
- const char* projectRoot = nullptr;
- AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
- projectRoot,
- &AzToolsFramework::AssetSystemRequestBus::Handler::GetAbsoluteDevGameFolderPath);
+ AZ::IO::FixedMaxPath projectRoot = AZ::Utils::GetProjectPath();
- if (!projectRoot)
+ if (projectRoot.empty())
{
return "";
}
- StringType projectRootString(projectRoot);
+ StringType projectRootString(projectRoot.c_str());
ToUnixPath(projectRootString);
return projectRootString;
}
@@ -87,9 +81,9 @@ namespace ProjectSettingsTool
return reinterpret_cast(func);
}
- AZStd::string GetDevRoot()
+ AZStd::string GetEngineRoot()
{
- return GetAbsoluteDevRoot();
+ return GetAbsoluteEngineRoot();
}
AZStd::string GetProjectRoot()
{
@@ -104,7 +98,7 @@ namespace ProjectSettingsTool
QString SelectXmlFromFileDialog(const QString& currentFile)
{
// The selected file must be relative to this path
- QString defaultPath = GetAbsoluteDevRoot();
+ QString defaultPath = GetAbsoluteEngineRoot();
QString startPath;
// Choose the starting path for file dialog
@@ -139,7 +133,7 @@ namespace ProjectSettingsTool
QString SelectImageFromFileDialog(const QString& currentFile)
{
- QString defaultPath = QStringLiteral("%1Code%2/Resources/").arg(GetAbsoluteDevRoot(), ::GetProjectName());
+ QString defaultPath = QStringLiteral("%1Code%2/Resources/").arg(GetAbsoluteEngineRoot(), ::GetProjectName());
QString startPath;
@@ -188,7 +182,7 @@ namespace ProjectSettingsTool
// Android
if (group <= ImageGroup::AndroidPortrait)
{
- root = GetDevRoot() + "/Code/Tools/Android/ProjectBuilder/app_";
+ root = GetEngineRoot() + "/Code/Tools/Android/ProjectBuilder/app_";
}
//Ios
else
diff --git a/Code/Editor/Plugins/ProjectSettingsTool/Utils.h b/Code/Editor/Plugins/ProjectSettingsTool/Utils.h
index 4226d534a6..050fb3b85b 100644
--- a/Code/Editor/Plugins/ProjectSettingsTool/Utils.h
+++ b/Code/Editor/Plugins/ProjectSettingsTool/Utils.h
@@ -17,7 +17,7 @@
namespace ProjectSettingsTool
{
void* ConvertFunctorToVoid(AZStd::pair(*func)(const QString&));
- AZStd::string GetDevRoot();
+ AZStd::string GetEngineRoot();
AZStd::string GetProjectRoot();
AZStd::string GetProjectName();
diff --git a/Code/Editor/QtViewPaneManager.cpp b/Code/Editor/QtViewPaneManager.cpp
index eff3a7331e..1ac4b9cfe3 100644
--- a/Code/Editor/QtViewPaneManager.cpp
+++ b/Code/Editor/QtViewPaneManager.cpp
@@ -37,6 +37,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -44,11 +45,54 @@
#include
#include
#include
-
#include
#include "ShortcutDispatcher.h"
+// Helper for EditorComponentModeNotifications to be used
+// as a member instead of inheriting from EBus directly.
+class ViewportEditorModeNotificationsBusImpl
+ : public AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
+{
+ public:
+ // Set the function to be called when entering ComponentMode.
+ void SetEnteredComponentModeFunc(
+ const AZStd::function& enteredComponentModeFunc)
+ {
+ m_enteredComponentModeFunc = enteredComponentModeFunc;
+ }
+
+ // Set the function to be called when leaving ComponentMode.
+ void SetLeftComponentModeFunc(
+ const AZStd::function& leftComponentModeFunc)
+ {
+ m_leftComponentModeFunc = leftComponentModeFunc;
+ }
+
+ private:
+ // ViewportEditorModeNotificationsBus overrides ...
+ void OnEditorModeActivated(
+ const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override
+ {
+ if (mode == AzToolsFramework::ViewportEditorMode::Component)
+ {
+ m_enteredComponentModeFunc(editorModeState);
+ }
+ }
+
+ void OnEditorModeDeactivated(
+ const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override
+ {
+ if (mode == AzToolsFramework::ViewportEditorMode::Component)
+ {
+ m_leftComponentModeFunc(editorModeState);
+ }
+ }
+
+ AZStd::function m_enteredComponentModeFunc; ///< Function to call when entering ComponentMode.
+ AZStd::function m_leftComponentModeFunc; ///< Function to call when leaving ComponentMode.
+};
+
struct ViewLayoutState
{
QVector viewPanes;
@@ -519,16 +563,17 @@ QtViewPaneManager::QtViewPaneManager(QObject* parent)
, m_settings(nullptr)
, m_restoreInProgress(false)
, m_advancedDockManager(nullptr)
+ , m_componentModeNotifications(AZStd::make_unique())
{
qRegisterMetaTypeStreamOperators("ViewLayoutState");
qRegisterMetaTypeStreamOperators >("QVector");
// view pane manager is interested when we enter/exit ComponentMode
- m_componentModeNotifications.BusConnect(AzToolsFramework::GetEntityContextId());
+ m_componentModeNotifications->BusConnect(AzToolsFramework::GetEntityContextId());
m_windowRequest.BusConnect();
- m_componentModeNotifications.SetEnteredComponentModeFunc(
- [this](const AZStd::vector& /*componentModeTypes*/)
+ m_componentModeNotifications->SetEnteredComponentModeFunc(
+ [this](const AzToolsFramework::ViewportEditorModesInterface&)
{
// gray out panels when entering ComponentMode
SetDefaultActionsEnabled(false, m_registeredPanes, [](QWidget* widget, bool on)
@@ -537,8 +582,8 @@ QtViewPaneManager::QtViewPaneManager(QObject* parent)
});
});
- m_componentModeNotifications.SetLeftComponentModeFunc(
- [this](const AZStd::vector& /*componentModeTypes*/)
+ m_componentModeNotifications->SetLeftComponentModeFunc(
+ [this](const AzToolsFramework::ViewportEditorModesInterface&)
{
// enable panels again when leaving ComponentMode
SetDefaultActionsEnabled(true, m_registeredPanes, [](QWidget* widget, bool on)
@@ -563,7 +608,7 @@ QtViewPaneManager::QtViewPaneManager(QObject* parent)
QtViewPaneManager::~QtViewPaneManager()
{
m_windowRequest.BusDisconnect();
- m_componentModeNotifications.BusDisconnect();
+ m_componentModeNotifications->BusDisconnect();
}
static bool lessThan(const QtViewPane& v1, const QtViewPane& v2)
diff --git a/Code/Editor/QtViewPaneManager.h b/Code/Editor/QtViewPaneManager.h
index 3ad1cc9cf7..0515ae726e 100644
--- a/Code/Editor/QtViewPaneManager.h
+++ b/Code/Editor/QtViewPaneManager.h
@@ -17,7 +17,6 @@
#include
#include
#include
-#include
#include
#include
@@ -34,6 +33,7 @@
#endif
class QMainWindow;
+class ViewportEditorModeNotificationsBusImpl;
struct ViewLayoutState;
namespace AzQtComponents
@@ -245,9 +245,9 @@ private:
QPointer m_advancedDockManager;
- using EditorComponentModeNotificationBusImpl = AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBusImpl;
- EditorComponentModeNotificationBusImpl m_componentModeNotifications; //!< Helper for EditorComponentModeNotificationBus so
- //!< QtViewPaneManager does not need to inherit directly from it. */
+ AZStd::unique_ptr
+ m_componentModeNotifications; //!< Helper for EditorComponentModeNotificationBus so
+ //!< QtViewPaneManager does not need to inherit directly from it. */
using EditorWindowRequestBusImpl = AzToolsFramework::EditorWindowRequestBusImpl;
EditorWindowRequestBusImpl m_windowRequest; //!< Helper for EditorWindowRequestBus so
diff --git a/Code/Editor/Settings.cpp b/Code/Editor/Settings.cpp
index 05a6960695..47743d1c42 100644
--- a/Code/Editor/Settings.cpp
+++ b/Code/Editor/Settings.cpp
@@ -935,8 +935,9 @@ void SEditorSettings::LoadDefaultGamePaths()
searchPaths[EDITOR_PATH_MATERIALS].push_back((Path::GetEditingGameDataFolder() + "/Materials").c_str());
}
- AZStd::string iconsPath;
- AZ::StringFunc::Path::Join(Path::GetEditingRootFolder().c_str(), "Editor/UI/Icons", iconsPath);
+ auto iconsPath = AZ::IO::Path(AZ::Utils::GetEnginePath()) / "Assets";
+ iconsPath /= "Editor/UI/Icons";
+ iconsPath.MakePreferred();
searchPaths[EDITOR_PATH_UI_ICONS].push_back(iconsPath.c_str());
}
diff --git a/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp b/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp
index b510315995..d7901e338a 100644
--- a/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp
+++ b/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp
@@ -269,7 +269,7 @@ void CSequenceBatchRenderDialog::OnRenderItemSelChange()
// Enable/disable the 'remove'/'update' button properly.
bool bNoSelection = !m_ui->m_renderList->selectionModel()->hasSelection();
m_ui->BATCH_RENDER_REMOVE_SEQ->setEnabled(bNoSelection ? false : true);
-
+
CheckForEnableUpdateButton();
if (bNoSelection)
@@ -360,7 +360,7 @@ void CSequenceBatchRenderDialog::OnRenderItemSelChange()
cvarsText += item.cvars[static_cast(i)];
cvarsText += "\r\n";
}
- m_ui->m_cvarsEdit->setPlainText(cvarsText);
+ m_ui->m_cvarsEdit->setPlainText(cvarsText);
}
void CSequenceBatchRenderDialog::CheckForEnableUpdateButton()
@@ -494,7 +494,7 @@ void CSequenceBatchRenderDialog::OnSavePreset()
}
void CSequenceBatchRenderDialog::stashActiveViewportResolution()
-{
+{
// stash active resolution in global vars
activeViewportWidth = resolutions[0][0];
activeViewportHeight = resolutions[0][1];
@@ -502,7 +502,7 @@ void CSequenceBatchRenderDialog::stashActiveViewportResolution()
if (activeViewport)
{
activeViewport->GetDimensions(&activeViewportWidth, &activeViewportHeight);
- }
+ }
}
void CSequenceBatchRenderDialog::OnGo()
@@ -640,7 +640,7 @@ void CSequenceBatchRenderDialog::OnResolutionSelected()
int defaultH;
const QString currentCustomResText = m_ui->m_resolutionCombo->currentText();
GetResolutionFromCustomResText(currentCustomResText.toStdString().c_str(), defaultW, defaultH);
-
+
CCustomResolutionDlg resDlg(defaultW, defaultH, this);
if (resDlg.exec() == QDialog::Accepted)
{
@@ -752,7 +752,7 @@ bool CSequenceBatchRenderDialog::LoadOutputOptions(const QString& pathname)
{
const QString customResText = resolutionNode->getContent();
m_ui->m_resolutionCombo->setItemText(curSel, customResText);
-
+
GetResolutionFromCustomResText(customResText.toStdString().c_str(), m_customResW, m_customResH);
}
m_ui->m_resolutionCombo->setCurrentIndex(curSel);
@@ -907,12 +907,12 @@ void CSequenceBatchRenderDialog::CaptureItemStart()
folder += "/";
folder += itemText;
- // If this is a relative path, prepend the @assets@ folder to match where the Renderer is going
+ // If this is a relative path, prepend the @products@ folder to match where the Renderer is going
// to dump the frame buffer image captures.
if (AzFramework::StringFunc::Path::IsRelative(folder.toUtf8().data()))
{
AZStd::string absolutePath;
- AZStd::string assetsRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@");
+ AZStd::string assetsRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@");
AzFramework::StringFunc::Path::Join(assetsRoot.c_str(), folder.toUtf8().data(), absolutePath);
folder = absolutePath.c_str();
}
@@ -962,7 +962,7 @@ void CSequenceBatchRenderDialog::CaptureItemStart()
m_renderContext.cvarDisplayInfoBU = cvarDebugInfo->GetIVal();
if (renderItem.disableDebugInfo && cvarDebugInfo->GetIVal())
{
- const int DISPLAY_INFO_OFF = 0;
+ const int DISPLAY_INFO_OFF = 0;
cvarDebugInfo->Set(DISPLAY_INFO_OFF);
}
}
@@ -1100,13 +1100,13 @@ void CSequenceBatchRenderDialog::OnUpdateEnd(IAnimSequence* sequence)
sequence->SetActiveDirector(m_renderContext.pActiveDirectorBU);
const auto imageFormat = m_ui->m_imageFormatCombo->currentText();
-
+
SRenderItem renderItem = m_renderItems[m_renderContext.currentItemIndex];
if (m_bFFMPEGCommandAvailable && renderItem.bCreateVideo)
{
// Create a video using the ffmpeg plug-in from captured images.
m_renderContext.processingFFMPEG = true;
-
+
AZStd::string outputFolder = m_renderContext.captureOptions.folder;
auto future = QtConcurrent::run(
[renderItem, outputFolder, imageFormat]
@@ -1238,7 +1238,7 @@ void CSequenceBatchRenderDialog::OnKickIdleTimout()
}
void CSequenceBatchRenderDialog::OnKickIdle()
-{
+{
if (m_renderContext.captureState == CaptureState::WarmingUpAfterResChange)
{
OnUpdateWarmingUpAfterResChange();
@@ -1254,7 +1254,7 @@ void CSequenceBatchRenderDialog::OnKickIdle()
else if (m_renderContext.captureState == CaptureState::Capturing)
{
OnUpdateCapturing();
- }
+ }
else if (m_renderContext.captureState == CaptureState::End)
{
OnUpdateEnd(m_renderContext.endingSequence);
diff --git a/Code/Editor/UndoDropDown.cpp b/Code/Editor/UndoDropDown.cpp
index 6bf807ebf4..4f346b57dd 100644
--- a/Code/Editor/UndoDropDown.cpp
+++ b/Code/Editor/UndoDropDown.cpp
@@ -101,13 +101,13 @@ public:
if (fresh.size() < m_stackNames.size())
{
- beginRemoveRows(createIndex(-1, -1), static_cast(fresh.size()), static_cast(m_stackNames.size() - 1));
+ beginRemoveRows(QModelIndex(), static_cast(fresh.size()), static_cast(m_stackNames.size() - 1));
m_stackNames = fresh;
endRemoveRows();
}
else
{
- beginInsertRows(createIndex(-1, -1), static_cast(m_stackNames.size()), static_cast(fresh.size() - 1));
+ beginInsertRows(QModelIndex(), static_cast(m_stackNames.size()), static_cast(fresh.size() - 1));
m_stackNames = fresh;
endInsertRows();
}
diff --git a/Code/Editor/Util/PathUtil.cpp b/Code/Editor/Util/PathUtil.cpp
index c9e6823824..ca3481ddae 100644
--- a/Code/Editor/Util/PathUtil.cpp
+++ b/Code/Editor/Util/PathUtil.cpp
@@ -11,9 +11,9 @@
#include "PathUtil.h"
-#include // for AZ_MAX_PATH_LEN
+#include
+#include
#include // for ebus events
-#include
#include
#include
#include
@@ -179,7 +179,7 @@ namespace Path
EBUS_EVENT_RESULT(engineRoot, AzFramework::ApplicationRequests::Bus, GetEngineRoot);
return QString(engineRoot);
}
-
+
//////////////////////////////////////////////////////////////////////////
QString& ReplaceFilename(const QString& strFilepath, const QString& strFilename, QString& strOutputFilename, bool bCallCaselessPath)
{
@@ -216,30 +216,21 @@ namespace Path
//////////////////////////////////////////////////////////////////////////
QString GetResolvedUserSandboxFolder()
{
- char resolvedPath[AZ_MAX_PATH_LEN] = { 0 };
- gEnv->pFileIO->ResolvePath(GetUserSandboxFolder().toUtf8().data(), resolvedPath, AZ_MAX_PATH_LEN);
- return QString::fromLatin1(resolvedPath);
+ AZ::IO::FixedMaxPath userSandboxFolderPath;
+ gEnv->pFileIO->ResolvePath(userSandboxFolderPath, GetUserSandboxFolder().toUtf8().constData());
+ return QString::fromUtf8(userSandboxFolderPath.c_str(), static_cast(userSandboxFolderPath.Native().size()));
}
// internal function, you should use GetEditingGameDataFolder instead.
AZStd::string GetGameAssetsFolder()
{
- const char* resultValue = nullptr;
- EBUS_EVENT_RESULT(resultValue, AzToolsFramework::AssetSystemRequestBus, GetAbsoluteDevGameFolderPath);
- if (!resultValue)
+ AZ::IO::Path projectPath;
+ if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
- if ((gEnv) && (gEnv->pFileIO))
- {
- resultValue = gEnv->pFileIO->GetAlias("@devassets@");
- }
+ settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath);
}
- if (!resultValue)
- {
- resultValue = ".";
- }
-
- return resultValue;
+ return projectPath.Native();
}
/// Get the data folder
@@ -258,26 +249,6 @@ namespace Path
return str;
}
- //! Get the root folder (in source control or other writable assets) where you should save root data.
- AZStd::string GetEditingRootFolder()
- {
- const char* resultValue = nullptr;
- EBUS_EVENT_RESULT(resultValue, AzToolsFramework::AssetSystemRequestBus, GetAbsoluteDevRootFolderPath);
-
- if (!resultValue)
- {
- if ((gEnv) && (gEnv->pFileIO))
- {
- resultValue = gEnv->pFileIO->GetAlias("@devassets@");
- }
- }
- if (!resultValue)
- {
- resultValue = ".";
- }
- return resultValue;
- }
-
AZStd::string MakeModPathFromGamePath(const char* relGamePath)
{
@@ -335,165 +306,60 @@ namespace Path
return "";
}
- bool relPathfound = false;
+ bool relPathFound = false;
AZStd::string relativePath;
AZStd::string fullAssetPath(fullPath.toUtf8().data());
- EBUS_EVENT_RESULT(relPathfound, AzToolsFramework::AssetSystemRequestBus, GetRelativeProductPathFromFullSourceOrProductPath, fullAssetPath, relativePath);
+ EBUS_EVENT_RESULT(relPathFound, AzToolsFramework::AssetSystemRequestBus, GetRelativeProductPathFromFullSourceOrProductPath, fullAssetPath, relativePath);
- if (relPathfound)
+ if (relPathFound)
{
// do not normalize this path, it will already be an appropriate asset ID.
return CaselessPaths(relativePath.c_str());
}
- char rootpath[_MAX_PATH] = { 0 };
- azstrcpy(rootpath, _MAX_PATH, Path::GetEditingRootFolder().c_str());
-
- if (bRelativeToGameFolder)
- {
- azstrcpy(rootpath, _MAX_PATH, Path::GetEditingGameDataFolder().c_str());
- }
-
- QString rootPathNormalized(rootpath);
- QString srcPathNormalized(fullPath);
-
-#if defined(AZ_PLATFORM_WINDOWS)
- // avoid confusing PathRelativePathTo
- rootPathNormalized.replace('/', '\\');
- srcPathNormalized.replace('/', '\\');
-#endif
+ AZ::IO::FixedMaxPath rootPath = bRelativeToGameFolder ? AZ::Utils::GetProjectPath() : AZ::Utils::GetEnginePath();
+ AZ::IO::FixedMaxPath resolvedFullPath;
+ AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(resolvedFullPath, fullPath.toUtf8().constData());
// Create relative path
- char resolvedSrcPath[AZ_MAX_PATH_LEN] = { 0 };
- AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(srcPathNormalized.toUtf8().data(), resolvedSrcPath, AZ_MAX_PATH_LEN);
- QByteArray path = QDir(rootPathNormalized).relativeFilePath(resolvedSrcPath).toUtf8();
- if (path.isEmpty())
- {
- return fullPath;
- }
- // The following code is required because the windows PathRelativePathTo function will always return "./SomePath" instead of just "SomePath"
- // Only remove single dot (.) and slash parts of a path, never the double dot (..)
- const char* pBuffer = path.data();
- bool bHasDot = false;
- while (*pBuffer && pBuffer != path.end())
- {
- switch (*pBuffer)
- {
- case '.':
- if (bHasDot)
- {
- // Found a double dot, rewind and stop removing
- pBuffer--;
- break;
- }
- // Fall through intended
- case '/':
- case '\\':
- bHasDot = (*pBuffer == '.');
- pBuffer++;
- continue;
- }
- break;
- }
-
- QString relPath = pBuffer;
- return CaselessPaths(relPath);
+ return CaselessPaths(resolvedFullPath.LexicallyProximate(rootPath).MakePreferred().c_str());
}
QString GamePathToFullPath(const QString& path)
{
using namespace AzToolsFramework;
- AZ_Warning("GamePathToFullPath", path.size() <= AZ_MAX_PATH_LEN, "Path exceeds maximum path length of %d", AZ_MAX_PATH_LEN);
- if ((gEnv) && (gEnv->pFileIO) && gEnv->pCryPak && path.size() <= AZ_MAX_PATH_LEN)
+ AZ_Warning("GamePathToFullPath", path.size() <= AZ::IO::MaxPathLength, "Path exceeds maximum path length of %zu", AZ::IO::MaxPathLength);
+ if (path.size() <= AZ::IO::MaxPathLength)
{
// first, adjust the file name for mods:
- bool fullPathfound = false;
- AZStd::string assetFullPath;
- AZStd::string adjustedFilePath = path.toUtf8().data();
- AssetSystemRequestBus::BroadcastResult(fullPathfound, &AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, adjustedFilePath, assetFullPath);
- if (fullPathfound)
+ bool fullPathFound = false;
+ AZ::IO::Path assetFullPath;
+ AZ::IO::Path adjustedFilePath = path.toUtf8().constData();
+ AssetSystemRequestBus::BroadcastResult(fullPathFound, &AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath,
+ adjustedFilePath.Native(), assetFullPath.Native());
+ if (fullPathFound)
{
- //if the bus message succeeds than normalize and lowercase the path
- AzFramework::StringFunc::Path::Normalize(assetFullPath);
- return assetFullPath.c_str();
+ //if the bus message succeeds than normalize
+ return assetFullPath.LexicallyNormal().c_str();
}
- // if the bus message didn't succeed, 'guess' the source assets:
+ // if the bus message didn't succeed, check if he path exist as a resolved path
else
{
// Not all systems have been converted to use local paths. Some editor files save XML files directly, and a full or correctly aliased path is already passed in.
// If the path passed in exists already, then return the resolved filepath
if (AZ::IO::FileIOBase::GetDirectInstance()->Exists(adjustedFilePath.c_str()))
{
- char resolvedPath[AZ_MAX_PATH_LEN + PathUtil::maxAliasLength] = { 0 };
- AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(adjustedFilePath.c_str(), resolvedPath, AZ_MAX_PATH_LEN + PathUtil::maxAliasLength);
- return QString::fromUtf8(resolvedPath);
+ AZ::IO::FixedMaxPath resolvedPath;
+ AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(resolvedPath, adjustedFilePath);
+ return QString::fromUtf8(resolvedPath.c_str(), static_cast(resolvedPath.Native().size()));
}
- // if we get here it means that the Asset Processor does not know about this file. most of the time we should never get here
- // the rest of this code just does a bunch of heuristic guesses in case of missing files or if the user has hand-edited
- // the asset cache by moving files in via some other means or external process.
- if (adjustedFilePath[0] != '@')
- {
- const char* prefix = (adjustedFilePath[0] == '/' || adjustedFilePath[0] == '\\') ? "@devassets@" : "@devassets@/";
- adjustedFilePath = prefix + adjustedFilePath;
- }
-
- char szAdjustedFile[AZ_MAX_PATH_LEN + PathUtil::maxAliasLength] = { 0 };
- gEnv->pFileIO->ResolvePath(adjustedFilePath.c_str(), szAdjustedFile, AZ_ARRAY_SIZE(szAdjustedFile));
-
- if ((azstrnicmp(szAdjustedFile, "@devassets@", 11) == 0) && ((szAdjustedFile[11] == '/') || (szAdjustedFile[11] == '\\')))
- {
- if (!gEnv->pCryPak->IsFileExist(szAdjustedFile))
- {
- AZStd::string newName(szAdjustedFile);
- AzFramework::StringFunc::Replace(newName, "@devassets@", "@devroot@/engine", false);
-
- if (gEnv->pCryPak->IsFileExist(newName.c_str()))
- {
- azstrcpy(szAdjustedFile, AZ_ARRAY_SIZE(szAdjustedFile), newName.c_str());
- }
- else
- {
- // getting tricky here, try @devroot@ alone, in case its 'editor'
- AzFramework::StringFunc::Replace(newName, "@devassets@", "@devroot@", false);
- if (gEnv->pCryPak->IsFileExist(szAdjustedFile))
- {
- azstrcpy(szAdjustedFile, AZ_ARRAY_SIZE(szAdjustedFile), newName.c_str());
- }
- // give up, best guess is just @devassets@
- }
- }
- }
-
- // we should very rarely actually get to this point in the code.
-
- // szAdjustedFile may contain an alias at this point. (@assets@/blah.whatever)
- // there is a case in which the loose asset exists only within a pak file for some reason
- // this is not recommended but it is possible.in that case, we want to return the original szAdjustedFile
- // without touching it or resolving it so that crypak can open it successfully.
- char adjustedPath[AZ_MAX_PATH_LEN + PathUtil::maxAliasLength] = { 0 };
- if (gEnv->pFileIO->ResolvePath(szAdjustedFile, adjustedPath, AZ_MAX_PATH_LEN + PathUtil::maxAliasLength)) // resolve to full path
- {
- if ((gEnv->pCryPak->IsFileExist(adjustedPath)) || (!gEnv->pCryPak->IsFileExist(szAdjustedFile)))
- {
- // note that if we get here, then EITHER
- // the file exists as a loose asset in the actual adjusted path
- // OR the file does not exist in the original passed-in aliased name (like '@assets@/whatever')
- // in which case we may as well just resolve the path to a full path and return it.
- assetFullPath = adjustedPath;
- AzFramework::StringFunc::Path::Normalize(assetFullPath);
- azstrcpy(szAdjustedFile, AZ_MAX_PATH_LEN + PathUtil::maxAliasLength, assetFullPath.c_str());
- }
- // if the above case succeeded then it means that the file does NOT exist loose
- // but DOES exist in a pak, in which case we leave szAdjustedFile with the alias on the front of it, meaning
- // fopens via crypak will actually succeed.
- }
- return szAdjustedFile;
+ return path;
}
}
else
{
- return "";
+ return QString{};
}
}
diff --git a/Code/Editor/Util/PathUtil.h b/Code/Editor/Util/PathUtil.h
index 2c49031155..163d74e358 100644
--- a/Code/Editor/Util/PathUtil.h
+++ b/Code/Editor/Util/PathUtil.h
@@ -44,9 +44,6 @@ namespace Path
//! always returns a full path
EDITOR_CORE_API AZStd::string GetEditingGameDataFolder();
- //! Get the root folder (in source control or other writable assets) where you should save root data.
- EDITOR_CORE_API AZStd::string GetEditingRootFolder();
-
//! Set the current mod NAME for editing purposes. After doing this the above functions will take this into account
//! name only, please!
EDITOR_CORE_API void SetModName(const char* input);
@@ -69,93 +66,6 @@ namespace Path
return strPath;
}
- //! Split full file name to path and filename
- //! @param filepath [IN] Full file name inclusing path.
- //! @param path [OUT] Extracted file path.
- //! @param file [OUT] Extracted file (with extension).
- inline void Split(const QString& filepath, QString& path, QString& file)
- {
- char path_buffer[_MAX_PATH];
- char drive[_MAX_DRIVE];
- char dir[_MAX_DIR];
- char fname[_MAX_FNAME];
- char ext[_MAX_EXT];
-#ifdef AZ_COMPILER_MSVC
- _splitpath_s(filepath.toUtf8().data(), drive, AZ_ARRAY_SIZE(drive), dir, AZ_ARRAY_SIZE(dir), fname, AZ_ARRAY_SIZE(fname), ext, AZ_ARRAY_SIZE(ext));
- _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), drive, dir, 0, 0);
- path = path_buffer;
- _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), 0, 0, fname, ext);
-#else
- _splitpath(filepath.toUtf8().data(), drive, dir, fname, ext);
- _makepath(path_buffer, drive, dir, 0, 0);
- path = path_buffer;
- _makepath(path_buffer, 0, 0, fname, ext);
-#endif
- file = path_buffer;
- }
- inline void Split(const AZStd::string& filepath, AZStd::string& path, AZStd::string& file)
- {
- char path_buffer[_MAX_PATH];
- char drive[_MAX_DRIVE];
- char dir[_MAX_DIR];
- char fname[_MAX_FNAME];
- char ext[_MAX_EXT];
-#ifdef AZ_COMPILER_MSVC
- _splitpath_s(filepath.c_str(), drive, AZ_ARRAY_SIZE(drive), dir, AZ_ARRAY_SIZE(dir), 0, 0, 0, 0);
- _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), drive, dir, 0, 0);
- path = path_buffer;
- _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), 0, 0, fname, ext);
-#else
- _splitpath(filepath.c_str(), drive, dir, fname, ext);
- _makepath(path_buffer, drive, dir, 0, 0);
- path = path_buffer;
- _makepath(path_buffer, 0, 0, fname, ext);
-#endif
- file = path_buffer;
- }
-
- //! Split full file name to path and filename
- //! @param filepath [IN] Full file name inclusing path.
- //! @param path [OUT] Extracted file path.
- //! @param filename [OUT] Extracted file (without extension).
- //! @param ext [OUT] Extracted files extension.
- inline void Split(const QString& filepath, QString& path, QString& filename, QString& fext)
- {
- char path_buffer[_MAX_PATH];
- char drive[_MAX_DRIVE];
- char dir[_MAX_DIR];
- char fname[_MAX_FNAME];
- char ext[_MAX_EXT];
-#ifdef AZ_COMPILER_MSVC
- _splitpath_s(filepath.toUtf8().data(), drive, AZ_ARRAY_SIZE(drive), dir, AZ_ARRAY_SIZE(dir), fname, AZ_ARRAY_SIZE(fname), ext, AZ_ARRAY_SIZE(ext));
- _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), drive, dir, 0, 0);
-#else
- _splitpath(filepath.toUtf8().data(), drive, dir, fname, ext);
- _makepath(path_buffer, drive, dir, 0, 0);
-#endif
- path = path_buffer;
- filename = fname;
- fext = ext;
- }
- inline void Split(const AZStd::string& filepath, AZStd::string& path, AZStd::string& filename, AZStd::string& fext)
- {
- char path_buffer[_MAX_PATH];
- char drive[_MAX_DRIVE];
- char dir[_MAX_DIR];
- char fname[_MAX_FNAME];
- char ext[_MAX_EXT];
-#ifdef AZ_COMPILER_MSVC
- _splitpath_s(filepath.c_str(), drive, AZ_ARRAY_SIZE(drive), dir, AZ_ARRAY_SIZE(dir), fname, AZ_ARRAY_SIZE(fname), ext, AZ_ARRAY_SIZE(ext));
- _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), drive, dir, 0, 0);
-#else
- _splitpath(filepath.c_str(), drive, dir, fname, ext);
- _makepath(path_buffer, drive, dir, 0, 0);
-#endif
- path = path_buffer;
- filename = fname;
- fext = ext;
- }
-
//! Split path into segments
//! @param filepath [IN] path
inline QStringList SplitIntoSegments(const QString& path)
diff --git a/Code/Editor/Viewport.cpp b/Code/Editor/Viewport.cpp
index 5b4f4a4df3..c8f2e268d9 100644
--- a/Code/Editor/Viewport.cpp
+++ b/Code/Editor/Viewport.cpp
@@ -19,6 +19,7 @@
#include
#include
+#include
// Editor
#include "ViewManager.h"
diff --git a/Code/Editor/Viewport.h b/Code/Editor/Viewport.h
index fdb8332479..7f8ccc4c4f 100644
--- a/Code/Editor/Viewport.h
+++ b/Code/Editor/Viewport.h
@@ -157,7 +157,7 @@ public:
virtual Vec3 SnapToGrid(const Vec3& vec) = 0;
- //! Get selection procision tolerance.
+ //! Get selection precision tolerance.
virtual float GetSelectionTolerance() const = 0;
//////////////////////////////////////////////////////////////////////////
diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.cpp b/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.cpp
index 3a83aa5d1c..3fa3b39ca5 100644
--- a/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.cpp
+++ b/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.cpp
@@ -173,6 +173,7 @@ namespace AZ
if (assetTracker)
{
+ assetTracker->FixUpAsset(*instance);
assetTracker->AddAsset(*instance);
}
@@ -185,7 +186,20 @@ namespace AZ
return context.Report(result, message);
}
- void SerializedAssetTracker::AddAsset(Asset& asset)
+ void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback)
+ {
+ m_assetFixUpCallback = AZStd::move(assetFixUpCallback);
+ }
+
+ void SerializedAssetTracker::FixUpAsset(Asset& asset)
+ {
+ if (m_assetFixUpCallback)
+ {
+ m_assetFixUpCallback(asset);
+ }
+ }
+
+ void SerializedAssetTracker::AddAsset(Asset asset)
{
m_serializedAssets.emplace_back(asset);
}
@@ -199,5 +213,6 @@ namespace AZ
{
return m_serializedAssets;
}
+
} // namespace Data
} // namespace AZ
diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.h b/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.h
index 879952c82d..e1b8cda00d 100644
--- a/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.h
+++ b/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.h
@@ -39,13 +39,18 @@ namespace AZ
{
public:
AZ_RTTI(SerializedAssetTracker, "{1E067091-8C0A-44B1-A455-6E97663F6963}");
+ using AssetFixUp = AZStd::function& asset)>;
- void AddAsset(Asset& asset);
+ void SetAssetFixUp(AssetFixUp assetFixUpCallback);
+ void FixUpAsset(Asset& asset);
+
+ void AddAsset(Asset asset);
AZStd::vector>& GetTrackedAssets();
const AZStd::vector>& GetTrackedAssets() const;
private:
AZStd::vector> m_serializedAssets;
+ AssetFixUp m_assetFixUpCallback;
};
} // namespace Data
} // namespace AZ
diff --git a/Code/Framework/AzCore/AzCore/Console/Console.cpp b/Code/Framework/AzCore/AzCore/Console/Console.cpp
index a1b8a1759d..9f207d9afd 100644
--- a/Code/Framework/AzCore/AzCore/Console/Console.cpp
+++ b/Code/Framework/AzCore/AzCore/Console/Console.cpp
@@ -476,15 +476,16 @@ namespace AZ
// Responsible for using the Json Serialization Issue Callback system
// to determine when a JSON Patch or JSON Merge Patch modifies a value
- // at a path underneath the IConsole::ConsoleRootCommandKey JSON pointer
+ // at a path underneath the IConsole::ConsoleRuntimeCommandKey JSON pointer
JsonSerializationResult::ResultCode operator()(AZStd::string_view message,
JsonSerializationResult::ResultCode result, AZStd::string_view path)
{
- AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
+ constexpr AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator };
+ constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator };
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
if (result.GetTask() == JsonSerializationResult::Tasks::Merge
&& result.GetProcessing() == JsonSerializationResult::Processing::Completed
- && inputKey.IsRelativeTo(consoleRootCommandKey))
+ && (inputKey.IsRelativeTo(consoleRootCommandKey) || inputKey.IsRelativeTo(consoleAutoexecCommandKey)))
{
if (auto type = m_settingsRegistry.GetType(path); type != SettingsRegistryInterface::Type::NoType)
{
@@ -510,12 +511,24 @@ namespace AZ
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
- AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
+ constexpr AZ::IO::PathView consoleRuntimeCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator };
+ constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator };
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
- // The ConsoleRootComamndKey is not a command itself so strictly children keys are being examined
- if (inputKey.IsRelativeTo(consoleRootCommandKey) && inputKey != consoleRootCommandKey)
+
+ // Abuses the IsRelativeToFuncton function of the path class to extract the console
+ // command from the settings registry objects
+ FixedValueString command;
+ if (inputKey != consoleRuntimeCommandKey && inputKey.IsRelativeTo(consoleRuntimeCommandKey))
+ {
+ command = inputKey.LexicallyRelative(consoleRuntimeCommandKey).Native();
+ }
+ else if (inputKey != consoleAutoexecCommandKey && inputKey.IsRelativeTo(consoleAutoexecCommandKey))
+ {
+ command = inputKey.LexicallyRelative(consoleAutoexecCommandKey).Native();
+ }
+
+ if (!command.empty())
{
- FixedValueString command = inputKey.LexicallyRelative(consoleRootCommandKey).Native();
ConsoleCommandContainer commandArgs;
// Argument string which stores the value from the Settings Registry long enough
// to pass into the PerformCommand. The ConsoleCommandContainer stores string_views
@@ -603,9 +616,10 @@ namespace AZ
void Console::RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry)
{
- // Make sure the there is a JSON object at the path of AZ::IConsole::ConsoleRootCommandKey
+ // Make sure the there is a JSON object at the ConsoleRuntimeCommandKey or ConsoleAutoexecKey
// So that JSON Patch is able to add values underneath that object (JSON Patch doesn't create intermediate objects)
- settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } }}})",
+ settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } } })"
+ R"(,"O3DE": { "Autoexec": { "ConsoleCommands": {} } } })",
SettingsRegistryInterface::Format::JsonMergePatch);
m_consoleCommandKeyHandler = settingsRegistry.RegisterNotifier(ConsoleCommandKeyNotificationHandler{ settingsRegistry, *this });
diff --git a/Code/Framework/AzCore/AzCore/Console/IConsole.h b/Code/Framework/AzCore/AzCore/Console/IConsole.h
index dafc284ce3..73d17ac65a 100644
--- a/Code/Framework/AzCore/AzCore/Console/IConsole.h
+++ b/Code/Framework/AzCore/AzCore/Console/IConsole.h
@@ -31,7 +31,8 @@ namespace AZ
using FunctorVisitor = AZStd::function;
- inline static constexpr AZStd::string_view ConsoleRootCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
+ inline static constexpr AZStd::string_view ConsoleRuntimeCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
+ inline static constexpr AZStd::string_view ConsoleAutoexecCommandKey = "/O3DE/Autoexec/ConsoleCommands";
IConsole() = default;
virtual ~IConsole() = default;
diff --git a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp
index 14504cc282..3edcbaa273 100644
--- a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp
+++ b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp
@@ -224,6 +224,8 @@ namespace AZ
void Debug::Trace::Terminate(int exitCode)
{
+ AZ_TracePrintf("Exit", "Called Terminate() with exit code: 0x%x", exitCode);
+ AZ::Debug::Trace::PrintCallstack("Exit");
Platform::Terminate(exitCode);
}
diff --git a/Code/Framework/AzCore/AzCore/EBus/BusImpl.h b/Code/Framework/AzCore/AzCore/EBus/BusImpl.h
index 8e655c0525..e2b1c2e92b 100644
--- a/Code/Framework/AzCore/AzCore/EBus/BusImpl.h
+++ b/Code/Framework/AzCore/AzCore/EBus/BusImpl.h
@@ -160,8 +160,8 @@ namespace AZ
/**
* Locking primitive that is used when executing events in the event queue.
*/
- using EventQueueMutexType = typename AZStd::Utils::if_c::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
- MutexType, typename Traits::EventQueueMutexType>::type;
+ using EventQueueMutexType = AZStd::conditional_t::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
+ MutexType, typename Traits::EventQueueMutexType>;
/**
* Pointer to an address on the bus.
@@ -180,14 +180,22 @@ namespace AZ
* `::ExecuteQueuedEvents()`.
* By default, the event queue is disabled.
*/
- static const bool EnableEventQueue = Traits::EnableEventQueue;
- static const bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
- static const bool EnableQueuedReferences = Traits::EnableQueuedReferences;
+ static constexpr bool EnableEventQueue = Traits::EnableEventQueue;
+ static constexpr bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
+ static constexpr bool EnableQueuedReferences = Traits::EnableQueuedReferences;
/**
* True if the EBus supports more than one address. Otherwise, false.
*/
- static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
+ static constexpr bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
+
+ /**
+ * Template Lock Guard class that wraps around the Mutex
+ * The EBus uses for Dispatching Events.
+ * This is not the EBus Context Mutex if LocklessDispatch is true
+ */
+ template
+ using DispatchLockGuard = typename Traits::template DispatchLockGuard;
};
/**
@@ -460,7 +468,7 @@ namespace AZ
using BusPtr = typename Traits::BusPtr;
/**
- * Helper to queue an event by BusIdType only when function queueing is enabled
+ * Helper to queue an event by BusIdType only when function queueing is enabled
* @param id Address ID. Handlers that are connected to this ID will receive the event.
* @param func Function pointer of the event to dispatch.
* @param args Function arguments that are passed to each handler.
@@ -581,7 +589,7 @@ namespace AZ
, public EBusBroadcaster
, public EBusEventer
, public EBusEventEnumerator
- , public AZStd::Utils::if_c, EBusNullQueue>::type
+ , public AZStd::conditional_t, EBusNullQueue>
{
};
@@ -599,7 +607,7 @@ namespace AZ
: public EventDispatcher
, public EBusBroadcaster
, public EBusBroadcastEnumerator
- , public AZStd::Utils::if_c, EBusNullQueue>::type
+ , public AZStd::conditional_t, EBusNullQueue>
{
};
diff --git a/Code/Framework/AzCore/AzCore/EBus/EBus.h b/Code/Framework/AzCore/AzCore/EBus/EBus.h
index da8c880963..a3dc10b103 100644
--- a/Code/Framework/AzCore/AzCore/EBus/EBus.h
+++ b/Code/Framework/AzCore/AzCore/EBus/EBus.h
@@ -236,6 +236,17 @@ namespace AZ
* code before or after an event.
*/
using EventProcessingPolicy = EBusEventProcessingPolicy;
+
+ /**
+ * Template Lock Guard class that wraps around the Mutex
+ * The EBus Context uses the LockGuard when dispatching
+ * (either AZStd::scoped_lock or NullLockGuard)
+ * The IsLocklessDispatch bool is there to defer evaluation of the LocklessDispatch constant
+ * Otherwise the value above in EBusTraits.h is always used and not the value
+ * that the derived trait class sets.
+ */
+ template
+ using DispatchLockGuard = AZStd::conditional_t, AZStd::scoped_lock>;
};
namespace Internal
@@ -496,6 +507,14 @@ namespace AZ
*/
static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
+ /**
+ * Template Lock Guard class that wraps around the Mutex
+ * The EBus uses for Dispatching Events.
+ * This is not EBus Context Mutex when LocklessDispatch is set
+ */
+ template
+ using DispatchLockGuard = typename ImplTraits::template DispatchLockGuard;
+
//////////////////////////////////////////////////////////////////////////
// Check to help identify common mistakes
/// @cond EXCLUDE_DOCS
@@ -620,11 +639,11 @@ namespace AZ
using ContextMutexType = AZStd::conditional_t, AZStd::shared_mutex, MutexType>;
/**
- * The scoped lock guard to use (either AZStd::scoped_lock or NullLockGuard
+ * The scoped lock guard to use
* during broadcast/event dispatch.
* @see EBusTraits::LocklessDispatch
*/
- using DispatchLockGuard = AZStd::conditional_t, AZStd::scoped_lock>;
+ using DispatchLockGuard = DispatchLockGuard;
/**
* The scoped lock guard to use during connection. Some specialized policies execute handler methods which
@@ -704,6 +723,11 @@ namespace AZ
static Context& GetOrCreateContext(bool trackCallstack=true);
static bool IsInDispatch(Context* context = GetContext(false));
+
+ /**
+ * Returns whether the EBus context is in the middle of a dispatch on the current thread
+ */
+ static bool IsInDispatchThisThread(Context* context = GetContext(false));
/// @cond EXCLUDE_DOCS
struct RouterCallstackEntry
: public CallstackEntry
@@ -1208,6 +1232,13 @@ AZ_POP_DISABLE_WARNING
return context != nullptr && context->m_dispatches > 0;
}
+ template
+ bool EBus::IsInDispatchThisThread(Context* context)
+ {
+ return context != nullptr && context->s_callstack != nullptr
+ && context->s_callstack->m_prev != nullptr;
+ }
+
//=========================================================================
template
EBus::RouterCallstackEntry::RouterCallstackEntry(Iterator it, const BusIdType* busId, bool isQueued, bool isReverse)
diff --git a/Code/Framework/AzCore/AzCore/IO/FileIO.h b/Code/Framework/AzCore/AzCore/IO/FileIO.h
index 6406025417..25dd422a47 100644
--- a/Code/Framework/AzCore/AzCore/IO/FileIO.h
+++ b/Code/Framework/AzCore/AzCore/IO/FileIO.h
@@ -148,7 +148,7 @@ namespace AZ
virtual AZ::u64 ModificationTime(HandleType fileHandle) = 0;
virtual AZ::u64 ModificationTime(const char* filePath) = 0;
- /// Get the size of the file. Returns Success if we report size.
+ /// Get the size of the file. Returns Success if we report size.
virtual Result Size(const char* filePath, AZ::u64& size) = 0;
virtual Result Size(HandleType fileHandle, AZ::u64& size) = 0;
@@ -198,7 +198,7 @@ namespace AZ
/// note: the callback will contain the full concatenated path (filePath + slash + fileName)
/// not just the individual file name found.
/// note: if the file path of the found file corresponds to a registered ALIAS, the longest matching alias will be returned
- /// so expect return values like @assets@/textures/mytexture.dds instead of a full path. This is so that fileIO works over remote connections.
+ /// so expect return values like @products@/textures/mytexture.dds instead of a full path. This is so that fileIO works over remote connections.
/// note: if rootPath is specified the implementation has the option of substituting it for the current directory
/// as would be the case on a file server.
typedef AZStd::function FindFilesCallbackType;
@@ -206,13 +206,18 @@ namespace AZ
// Alias system
- /// SetAlias - Adds an alias to the path resolution system, e.g. @user@, @root@, etc.
+ /// SetAlias - Adds an alias to the path resolution system, e.g. @user@, @products@, etc.
virtual void SetAlias(const char* alias, const char* path) = 0;
/// ClearAlias - Removes an alias from the path resolution system
virtual void ClearAlias(const char* alias) = 0;
/// GetAlias - Returns the destination path for a given alias, or nullptr if the alias does not exist
virtual const char* GetAlias(const char* alias) const = 0;
+ /// SetDeprecateAlias - Adds a deprecated alias with path resolution which points to a new alias
+ /// When the DeprecatedAlias is used an Error is logged and the alias is resolved to the path
+ /// specified by the new alais
+ virtual void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) = 0;
+
/// Shorten the given path if it contains an alias. it will always pick the longest alias match.
/// note that it re-uses the buffer, since the data can only get smaller and we don't want to internally allocate memory if we
/// can avoid it.
@@ -230,8 +235,8 @@ namespace AZ
//! ResolvePath - Replaces any aliases in path with their values and stores the result in resolvedPath,
//! also ensures that the path is absolute
- //! NOTE: If the path does not start with an alias then the resolved value of the @assets@ is used
- //! which has the effect of making the path relative to the @assets@/ folder
+ //! NOTE: If the path does not start with an alias then the resolved value of the @products@ is used
+ //! which has the effect of making the path relative to the @products@/ folder
//! returns true if path was resolved, false otherwise
//! note that all of the above file-finding and opening functions automatically resolve the path before operating
//! so you should not need to call this except in very exceptional circumstances where you absolutely need to
diff --git a/Code/Framework/AzCore/AzCore/IO/IStreamer.h b/Code/Framework/AzCore/AzCore/IO/IStreamer.h
index d959fa716c..438384e687 100644
--- a/Code/Framework/AzCore/AzCore/IO/IStreamer.h
+++ b/Code/Framework/AzCore/AzCore/IO/IStreamer.h
@@ -42,8 +42,8 @@ namespace AZ::IO
// These functions can't be called after a request has been queued.
//
- //! Creates a request to read a file.
- //! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
+ //! Creates a request to read a file.
+ //! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
//! @param outputBuffer The buffer that will hold the loaded data. This must be able to at least hold "size" number of bytes.
//! @param outputBufferSize The size of the buffer that will hold the loaded data. This must be equal or larger than "size" number of bytes.
//! @param readSize The number of bytes to read from the file at the relative path.
@@ -62,9 +62,9 @@ namespace AZ::IO
IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium,
size_t offset = 0) = 0;
- //! Sets a request to the read command.
+ //! Sets a request to the read command.
//! @param request The request that will store the read command.
- //! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
+ //! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
//! @param outputBuffer The buffer that will hold the loaded data. This must be able to at least hold "size" number of bytes.
//! @param outputBufferSize The size of the buffer that will hold the loaded data. This must be equal or larger than "size" number of bytes.
//! @param readSize The number of bytes to read from the file at the relative path.
@@ -84,8 +84,8 @@ namespace AZ::IO
IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium,
size_t offset = 0) = 0;
- //! Creates a request to the read command.
- //! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
+ //! Creates a request to the read command.
+ //! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
//! @param allocator The allocator used to reserve and release memory for the read request. Memory allocated this way will
//! be automatically freed when there are no more references to the FileRequestPtr. To avoid this, use GetReadRequestResult
//! to claim the pointer and use the provided allocator to release the memory at a later point.
@@ -106,9 +106,9 @@ namespace AZ::IO
IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium,
size_t offset = 0) = 0;
- //! Sets a request to the read command.
+ //! Sets a request to the read command.
//! @param request The request that will store the read command.
- //! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
+ //! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
//! @param allocator The allocator used to reserve and release memory for the read request. Memory allocated this way will
//! be automatically freed when there are no more references to the FileRequestPtr. To avoid this, use GetReadRequestResult
//! to claim the pointer and use the provided allocator to release the memory at a later point.
@@ -138,7 +138,7 @@ namespace AZ::IO
//! @result A smart pointer to the newly created request with the cancel command.
virtual FileRequestPtr Cancel(FileRequestPtr target) = 0;
- //! Sets a request to the cancel command.
+ //! Sets a request to the cancel command.
//! When this request completes it's not guaranteed to have canceled the target request. Not all requests can be canceled and requests
//! that already processing may complete. It's recommended to let the target request handle the completion of the request as normal
//! and handle cancellation by checking the status on the target request is set to IStreamerTypes::RequestStatus::Canceled.
@@ -177,7 +177,7 @@ namespace AZ::IO
//! DestroyDedicatedCache is called. Typical use of a dedicated cache is for files that have their own compression
//! and are periodically visited to read a section, e.g. streaming video play or streaming audio banks. This
//! request will fail if there are no nodes in Streamer's stack that deal with dedicated caches.
- //! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @assets@.
+ //! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @products@.
//! @return A smart pointer to the newly created request with the command to create a dedicated cache.
virtual FileRequestPtr CreateDedicatedCache(AZStd::string_view relativePath) = 0;
@@ -186,25 +186,25 @@ namespace AZ::IO
//! and are periodically visited to read a section, e.g. streaming video play or streaming audio banks. This
//! request will fail if there are no nodes in Streamer's stack that deal with dedicated caches.
//! @param request The request that will store the command to create a dedicated cache.
- //! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @assets@.
+ //! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @products@.
//! @return A reference to the provided request.
virtual FileRequestPtr& CreateDedicatedCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0;
//! Destroy a dedicated cache created by CreateDedicatedCache. See CreateDedicatedCache for more details.
- //! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @assets@.
+ //! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @products@.
//! @return A smart pointer to the newly created request with the command to destroy a dedicated cache.
virtual FileRequestPtr DestroyDedicatedCache(AZStd::string_view relativePath) = 0;
//! Destroy a dedicated cache created by CreateDedicatedCache. See CreateDedicatedCache for more details.
//! @param request The request that will store the command to destroy a dedicated cache.
- //! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @assets@.
+ //! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @products@.
//! @return A reference to the provided request.
virtual FileRequestPtr& DestroyDedicatedCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0;
//! Clears a file from all caches in use by Streamer.
//! Flushing the cache will cause the streaming stack to pause processing until it's idle before issuing the flush and resuming
//! processing. This can result in a noticeable interruption.
- //! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @assets@.
+ //! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @products@.
//! @return A smart pointer to the newly created request with the command to flush a file from all caches.
virtual FileRequestPtr FlushCache(AZStd::string_view relativePath) = 0;
@@ -212,7 +212,7 @@ namespace AZ::IO
//! Flushing the cache will cause the streaming stack to pause processing until it's idle before issuing the flush and resuming
//! processing. This can result in a noticeable interruption.
//! @param request The request that will store the command to flush a file from all caches.
- //! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @assets@.
+ //! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @products@.
//! @return A reference to the provided request.
virtual FileRequestPtr& FlushCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0;
@@ -334,7 +334,7 @@ namespace AZ::IO
//
//! Collect statistics from all the components that make up Streamer.
- //! This is thread safe in the sense that it won't crash.
+ //! This is thread safe in the sense that it won't crash.
//! Data is collected lockless from involved threads and might be slightly
//! out of date in some cases.
//! @param statistics The container where statistics will be added to.
diff --git a/Code/Framework/AzCore/AzCore/IO/Path/Path.h b/Code/Framework/AzCore/AzCore/IO/Path/Path.h
index 24f26daa51..3b5c224957 100644
--- a/Code/Framework/AzCore/AzCore/IO/Path/Path.h
+++ b/Code/Framework/AzCore/AzCore/IO/Path/Path.h
@@ -98,6 +98,11 @@ namespace AZ::IO
//! made from the internal string
constexpr AZStd::fixed_string FixedMaxPathString() const noexcept;
+ // as_posix
+ //! Replicates the behavior of the Python pathlib as_posix method
+ //! by replacing the Windows Path Separator with the Posix Path Seperator
+ constexpr AZStd::fixed_string FixedMaxPathStringAsPosix() const noexcept;
+
// decomposition
//! Given a windows path of "C:\O3DE\foo\bar\name.txt" and a posix path of
//! "/O3DE/foo/bar/name.txt"
@@ -178,7 +183,7 @@ namespace AZ::IO
//! Normalizes a path in a purely lexical manner.
//! # Path separators are converted to their preferred path separator
//! # Path parts of "." are collapsed to nothing empty
- //! # Paths parts of ".." are removed if there is a preceding directory
+ //! # Paths parts of ".." are removed if there is a preceding directory
//! The preceding directory is also removed
//! # Runs of Two or more path separators are collapsed into one path separator
//! unless the path begins with two path separators
@@ -238,7 +243,7 @@ namespace AZ::IO
// iterators
//! Returns an iterator to the beginning of the path that can be used to traverse the path
- //! according to the following
+ //! according to the following
//! 1. Root name - (0 or 1)
//! 2. Root directory - (0 or 1)
//! 3. Filename - (0 or more)
@@ -253,24 +258,23 @@ namespace AZ::IO
template
friend class BasicPath;
friend struct AZStd::hash;
-
- template
- static constexpr void MakeRelativeTo(PathResultType& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base);
-
struct PathIterable;
+
+ static constexpr void MakeRelativeTo(PathIterable& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base) noexcept;
+
//! Returns a structure that provides a view of the path parts which can be used for iteration
//! Only the path parts that correspond to creating an normalized path is returned
//! This function is useful for returning a "view" into a normalized path without the need
//! to allocate memory for the heap
static constexpr PathIterable GetNormalPathParts(const AZ::IO::PathView& path) noexcept;
- // joins the input path to the Path Iterable structure using similiar logic to Path::Append
- // If the input path is absolute it will replace the current PathIterable otherwise
- // the input path will be appended to the Path Iterable structure
- // For example a PathIterable with parts = ['C:', '/', 'foo']
- // If the path input = 'bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar']
- // If the path input = 'C:/bar', then the new PathIterable parts = [C:', '/', 'bar']
- // If the path input = 'C:bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar' ]
- // If the path input = 'D:bar', then the new PathIterable parts = [D:, 'bar' ]
+ //! joins the input path to the Path Iterable structure using similiar logic to Path::Append
+ //! If the input path is absolute it will replace the current PathIterable otherwise
+ //! the input path will be appended to the Path Iterable structure
+ //! For example a PathIterable with parts = ['C:', '/', 'foo']
+ //! If the path input = 'bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar']
+ //! If the path input = 'C:/bar', then the new PathIterable parts = [C:', '/', 'bar']
+ //! If the path input = 'C:bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar' ]
+ //! If the path input = 'D:bar', then the new PathIterable parts = [D:, 'bar' ]
static constexpr void AppendNormalPathParts(PathIterable& pathIterableResult, const AZ::IO::PathView& path) noexcept;
constexpr int ComparePathView(const PathView& other) const;
@@ -325,32 +329,32 @@ namespace AZ::IO
constexpr BasicPath(BasicPath&& other) = default;
// Conversion constructor for other types of BasicPath instantiations
- constexpr BasicPath(const PathView& other);
+ constexpr BasicPath(const PathView& other) noexcept;
// String constructors
//! Constructs a Path by copying the pathString to its internal string
//! The preferred separator is to the OS default path separator
constexpr BasicPath(const string_type& pathString) noexcept;
//! Constructs a Path by copying the pathString to its internal string
- //! The preferred separator it set to the parameter
+ //! The preferred separator is set to the parameter
constexpr BasicPath(const string_type& pathString, const char preferredSeparator) noexcept;
//! Constructs a Path by moving the pathString to its internal string
//! The preferred separator is to the OS default path separator
constexpr BasicPath(string_type&& pathString) noexcept;
//! Constructs a Path by copying the pathString to its internal string
- //! The preferred separator it set to the parameter
+ //! The preferred separator is set to the parameter
constexpr BasicPath(string_type&& pathString, const char preferredSeparator) noexcept;
//! Constructs a Path by constructing it's internal out of a string_view
//! The preferred separator is to the OS default path separator
constexpr BasicPath(AZStd::string_view src) noexcept;
//! Constructs a Path by constructing it's internal out of a string_view
- //! The preferred separators it set to the parameter
+ //! The preferred separator is set to the parameter
constexpr BasicPath(AZStd::string_view src, const char preferredSeparator) noexcept;
//! Constructs a Path by constructing it's internal out of a value_type*
//! The preferred separator is to the OS default path separator
constexpr BasicPath(const value_type* pathString) noexcept;
//! Constructs a Path by constructing it's internal out of a value_type*
- //! The preferred separator it set to the parameter
+ //! The preferred separator is set to the parameter
constexpr BasicPath(const value_type* pathString, const char preferredSeparator) noexcept;
//! Constructs a empty Path with the preferred separator set to the parameter
explicit constexpr BasicPath(const char preferredSeparator) noexcept;
@@ -371,7 +375,7 @@ namespace AZ::IO
constexpr BasicPath& operator=(BasicPath&& other) = default;
// conversion assignment operator
- constexpr BasicPath& operator=(const PathView& pathView);
+ constexpr BasicPath& operator=(const PathView& pathView) noexcept;
constexpr BasicPath& operator=(const string_type& str) noexcept;
constexpr BasicPath& operator=(string_type&& str) noexcept;
constexpr BasicPath& operator=(AZStd::string_view str) noexcept;
@@ -477,6 +481,12 @@ namespace AZ::IO
//! made from the internal string
constexpr AZStd::fixed_string FixedMaxPathString() const;
+ // as_posix
+ //! Replicates the behavior of the Python pathlib as_posix method
+ //! by replacing the Windows Path Separator with the Posix Path Seperator
+ AZStd::string StringAsPosix() const;
+ constexpr AZStd::fixed_string FixedMaxPathStringAsPosix() const noexcept;
+
// compare
//! Performs a compare of each of the path parts for equivalence
//! Each part of the path is compare using string comparison
@@ -574,7 +584,7 @@ namespace AZ::IO
//! Normalizes a path in a purely lexical manner.
//! # Path separators are converted to their preferred path separator
//! # Path parts of "." are collapsed to nothing empty
- //! # Paths parts of ".." are removed if there is a preceding directory
+ //! # Paths parts of ".." are removed if there is a preceding directory
//! The preceding directory is also removed
//! # Runs of Two or more path separators are collapsed into one path separator
//! unless the path begins with two path separators
@@ -616,7 +626,7 @@ namespace AZ::IO
// iterators
//! Returns an iterator to the beginning of the path that can be used to traverse the path
- //! according to the following
+ //! according to the following
//! 1. Root name - (0 or 1)
//! 2. Root directory - (0 or 1)
//! 3. Filename - (0 or more)
diff --git a/Code/Framework/AzCore/AzCore/IO/Path/Path.inl b/Code/Framework/AzCore/AzCore/IO/Path/Path.inl
index 0147ad3356..1d654c1502 100644
--- a/Code/Framework/AzCore/AzCore/IO/Path/Path.inl
+++ b/Code/Framework/AzCore/AzCore/IO/Path/Path.inl
@@ -240,6 +240,14 @@ namespace AZ::IO
return AZStd::fixed_string(m_path.begin(), m_path.end());
}
+ // as_posix
+ constexpr AZStd::fixed_string PathView::FixedMaxPathStringAsPosix() const noexcept
+ {
+ AZStd::fixed_string resultPath(m_path.begin(), m_path.end());
+ AZStd::replace(resultPath.begin(), resultPath.end(), AZ::IO::WindowsPathSeparator, AZ::IO::PosixPathSeparator);
+ return resultPath;
+ }
+
// decomposition
constexpr auto PathView::RootName() const -> PathView
{
@@ -473,8 +481,7 @@ namespace AZ::IO
return lhs.Compare(rhs) >= 0;
}
- template
- constexpr void PathView::MakeRelativeTo(PathResultType& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base)
+ constexpr void PathView::MakeRelativeTo(PathIterable& pathIterable, const AZ::IO::PathView& path, const AZ::IO::PathView& base) noexcept
{
const bool exactCaseCompare = path.m_preferred_separator == PosixPathSeparator
|| base.m_preferred_separator == PosixPathSeparator;
@@ -492,13 +499,11 @@ namespace AZ::IO
if (int res = Internal::ComparePathSegment(*pathParser, *pathParserBase, exactCaseCompare);
res != 0)
{
- pathResult.m_path = AZStd::string_view{};
return;
}
}
else if (CheckIterMismatchAtBase())
{
- pathResult.m_path = AZStd::string_view{};
return;
}
@@ -512,7 +517,6 @@ namespace AZ::IO
}
if (CheckIterMismatchAtBase())
{
- pathResult.m_path = AZStd::string_view{};
return;
}
}
@@ -530,7 +534,7 @@ namespace AZ::IO
// If there is no mismatch, return ".".
if (!pathParser && !pathParserBase)
{
- pathResult.m_path = AZStd::string_view{ "." };
+ pathIterable.emplace_back(".", parser::PathPartKind::PK_Dot);
return;
}
@@ -539,27 +543,25 @@ namespace AZ::IO
int elemCount = parser::DetermineLexicalElementCount(pathParserBase);
if (elemCount < 0)
{
- pathResult.m_path = AZStd::string_view{};
return;
}
// if elemCount == 0 and (pathParser == end() || pathParser->empty()), returns path("."); otherwise
if (elemCount == 0 && (pathParser.AtEnd() || *pathParser == ""))
{
- pathResult.m_path = AZStd::string_view{ "." };
+ pathIterable.emplace_back(".", parser::PathPartKind::PK_Dot);
return;
}
// return a path constructed with 'n' dot-dot elements, followed by the
// elements of '*this' after the mismatch.
- pathResult = PathResultType(path.m_preferred_separator);
while (elemCount--)
{
- pathResult /= "..";
+ pathIterable.emplace_back("..", parser::PathPartKind::PK_DotDot);
}
for (; pathParser; ++pathParser)
{
- pathResult /= *pathParser;
+ pathIterable.emplace_back(*pathParser, parser::ClassifyPathPart(pathParser));
}
}
@@ -673,7 +675,7 @@ namespace AZ::IO
// Basic Path implementation
template
- constexpr BasicPath::BasicPath(const PathView& other)
+ constexpr BasicPath::BasicPath(const PathView& other) noexcept
: m_path(other.m_path)
, m_preferred_separator(other.m_preferred_separator) {}
@@ -726,6 +728,7 @@ namespace AZ::IO
: m_path(first, last)
, m_preferred_separator(preferredSeparator) {}
+
template