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..0d6fe4c8fa 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt
+++ b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt
@@ -65,4 +65,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_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..f5ac411a01 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py
@@ -42,5 +42,8 @@ class TestAutomation(EditorTestSuite):
class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module
+ class AtomEditorComponents_ReflectionProbeAdded(EditorSharedTest):
+ from Atom.tests import hydra_AtomEditorComponents_ReflectionProbeAdded as test_module
+
class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest):
from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/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_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/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/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/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..b53076361e 100644
--- a/Code/Editor/Core/LevelEditorMenuHandler.cpp
+++ b/Code/Editor/Core/LevelEditorMenuHandler.cpp
@@ -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);
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
+ {
+ };
+
+ 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");
- // 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);
+ // 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);
+ 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 sure there is no leading slash
- if (!filename.isEmpty() && (filename[0] == '\\' || filename[0] == '/'))
- {
- filename = filename.mid(1);
- }
+ // 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);
- if (!filename.isEmpty())
+ if (!projectRelativeFilePath.empty())
{
- //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)
- {
- sCallback.pListener->OnFileChange(qPrintable(filenameRelGame), IFileChangeListener::EChangeType(rChange.changeType));
- }
- else if (filename.compare(sCallback.item, Qt::CaseInsensitive) == 0)
+ if (AZ::IO::PathView(sCallback.item.toUtf8().constData()) == projectRelativeFilePath)
{
- 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);
-
- // otherwise just use the identity
- return entityPivot.value_or(AZ::Transform::CreateIdentity()).GetTranslation();
+ return entityPivot->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_pivotCamera->SetPivotInputChannelId(SandboxEditor::CameraPivotChannelId());
- m_pivotTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
- m_pivotPanCamera->SetPanInputChannelId(SandboxEditor::CameraPivotPanChannelId());
- m_pivotRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraPivotLookChannelId());
- m_pivotDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraPivotDollyChannelId());
+ m_firstPersonFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId());
+
+ 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/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
index b9e4878879..41e950a058 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
@@ -1895,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/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/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/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)
- {
- if ((gEnv) && (gEnv->pFileIO))
- {
- resultValue = gEnv->pFileIO->GetAlias("@devassets@");
- }
- }
-
- if (!resultValue)
+ AZ::IO::Path projectPath;
+ if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
- resultValue = ".";
+ settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath);
}
- 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/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/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/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;
+ struct PathIterable;
- template
- static constexpr void MakeRelativeTo(PathResultType& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base);
+ static constexpr void MakeRelativeTo(PathIterable& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base) noexcept;
- struct PathIterable;
//! 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
constexpr BasicPath::operator PathView() const noexcept
{
@@ -733,7 +736,7 @@ namespace AZ::IO
}
template
- constexpr auto BasicPath::operator=(const PathView& other) -> BasicPath&
+ constexpr auto BasicPath::operator=(const PathView& other) noexcept -> BasicPath&
{
m_path = other.m_path;
m_preferred_separator = other.m_preferred_separator;
@@ -974,13 +977,13 @@ namespace AZ::IO
template
constexpr auto BasicPath::MakePreferred() -> BasicPath&
{
- if (m_preferred_separator != '/')
+ if (m_preferred_separator != PosixPathSeparator)
{
- AZStd::replace(m_path.begin(), m_path.end(), '/', m_preferred_separator);
+ AZStd::replace(m_path.begin(), m_path.end(), PosixPathSeparator, m_preferred_separator);
}
else
{
- AZStd::replace(m_path.begin(), m_path.end(), '\\', m_preferred_separator);
+ AZStd::replace(m_path.begin(), m_path.end(), WindowsPathSeparator, m_preferred_separator);
}
return *this;
}
@@ -1033,6 +1036,24 @@ namespace AZ::IO
return AZStd::fixed_string(m_path.begin(), m_path.end());
}
+ // as_posix
+ // Returns a copy of the path with the path separators converted to PosixPathSeparator
+ template
+ AZStd::string BasicPath::StringAsPosix() const
+ {
+ AZStd::string resultPath(m_path.begin(), m_path.end());
+ AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
+ return resultPath;
+ }
+
+ template
+ constexpr AZStd::fixed_string BasicPath::FixedMaxPathStringAsPosix() const noexcept
+ {
+ AZStd::fixed_string resultPath(m_path.begin(), m_path.end());
+ AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
+ return resultPath;
+ }
+
template
constexpr void BasicPath::swap(BasicPath& rhs) noexcept
{
@@ -1234,6 +1255,7 @@ namespace AZ::IO
{
pathResult /= pathPartView;
}
+
return pathResult;
}
@@ -1241,7 +1263,13 @@ namespace AZ::IO
constexpr auto BasicPath::LexicallyRelative(const PathView& base) const -> BasicPath
{
BasicPath pathResult(m_preferred_separator);
- static_cast(*this).MakeRelativeTo(pathResult, *this, base);
+ PathView::PathIterable pathIterable;
+ PathView::MakeRelativeTo(pathIterable, *this, base);
+ for ([[maybe_unused]] auto [pathPartView, pathPartKind] : pathIterable)
+ {
+ pathResult /= pathPartView;
+ }
+
return pathResult;
}
@@ -1355,7 +1383,7 @@ namespace AZ::IO
return !basePathParts.empty() || !thisPathParts.IsAbsolute();
}
- constexpr FixedMaxPath PathView::LexicallyNormal() const
+ constexpr auto PathView::LexicallyNormal() const -> FixedMaxPath
{
FixedMaxPath pathResult(m_preferred_separator);
PathIterable pathIterable = GetNormalPathParts(*this);
@@ -1367,21 +1395,28 @@ namespace AZ::IO
return pathResult;
}
- constexpr FixedMaxPath PathView::LexicallyRelative(const PathView& base) const
+ constexpr auto PathView::LexicallyRelative(const PathView& base) const -> FixedMaxPath
{
FixedMaxPath pathResult(m_preferred_separator);
- MakeRelativeTo(pathResult, *this, base);
+ PathIterable pathIterable;
+ MakeRelativeTo(pathIterable, *this, base);
+ for ([[maybe_unused]] auto [pathPartView, pathPartKind] : pathIterable)
+ {
+ pathResult /= pathPartView;
+ }
+
return pathResult;
}
- constexpr FixedMaxPath PathView::LexicallyProximate(const PathView& base) const
+ constexpr auto PathView::LexicallyProximate(const PathView& base) const -> FixedMaxPath
{
- FixedMaxPath result = LexicallyRelative(base);
- if (result.empty())
+ FixedMaxPath pathResult = LexicallyRelative(base);
+ if (pathResult.empty())
{
return FixedMaxPath(*this);
}
- return result;
+
+ return pathResult;
}
}
diff --git a/Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl b/Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl
index a1faa29b31..e700ab0196 100644
--- a/Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl
+++ b/Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl
@@ -49,8 +49,8 @@ namespace AZ::IO
constexpr void clear() noexcept;
- friend constexpr auto PathView::GetNormalPathParts(const AZ::IO::PathView&) noexcept -> PathIterable;
friend constexpr auto PathView::AppendNormalPathParts(PathIterable& pathIterable, const AZ::IO::PathView&) noexcept -> void;
+ friend constexpr auto PathView::MakeRelativeTo(PathIterable& pathIterable, const AZ::IO::PathView&, const AZ::IO::PathView&) noexcept -> void;
PartKindArray m_parts{};
size_t m_size{};
};
diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobManagerComponent.cpp b/Code/Framework/AzCore/AzCore/Jobs/JobManagerComponent.cpp
index 25dfc7a493..6f5ccc93e4 100644
--- a/Code/Framework/AzCore/AzCore/Jobs/JobManagerComponent.cpp
+++ b/Code/Framework/AzCore/AzCore/Jobs/JobManagerComponent.cpp
@@ -18,6 +18,14 @@
#include
#include
+#include
+
+#include
+
+AZ_CVAR(float, cl_jobThreadsConcurrencyRatio, 0.6f, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system multiplier on the number of hw threads the machine creates at initialization");
+AZ_CVAR(uint32_t, cl_jobThreadsNumReserved, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system number of hardware threads that are reserved for O3DE system threads");
+AZ_CVAR(uint32_t, cl_jobThreadsMinNumber, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system minimum number of worker threads to create after scaling the number of hw threads");
+
namespace AZ
{
//=========================================================================
@@ -46,9 +54,10 @@ namespace AZ
JobManagerThreadDesc threadDesc;
int numberOfWorkerThreads = m_numberOfWorkerThreads;
- if (numberOfWorkerThreads <= 0)
+ if (numberOfWorkerThreads <= 0) // spawn default number of threads
{
- numberOfWorkerThreads = AZ::GetMin(static_cast(desc.m_workerThreads.capacity()), AZStd::thread::hardware_concurrency());
+ uint32_t scaledHardwareThreads = Threading::CalcNumWorkerThreads(cl_jobThreadsConcurrencyRatio, cl_jobThreadsMinNumber, cl_jobThreadsNumReserved);
+ numberOfWorkerThreads = AZ::GetMin(static_cast(desc.m_workerThreads.capacity()), scaledHardwareThreads);
#if (AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS)
numberOfWorkerThreads = AZ::GetMin(numberOfWorkerThreads, AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS);
#endif // (AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS)
diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobManagerDesc.h b/Code/Framework/AzCore/AzCore/Jobs/JobManagerDesc.h
index 5594d7aa15..94f84f27b3 100644
--- a/Code/Framework/AzCore/AzCore/Jobs/JobManagerDesc.h
+++ b/Code/Framework/AzCore/AzCore/Jobs/JobManagerDesc.h
@@ -36,7 +36,7 @@ namespace AZ
*/
int m_stackSize;
- JobManagerThreadDesc(int cpuId = -1, int priority = -100000, int stackSize = -1)
+ JobManagerThreadDesc(int cpuId = -1, int priority = 0, int stackSize = -1)
: m_cpuId(cpuId)
, m_priority(priority)
, m_stackSize(stackSize)
diff --git a/Code/Framework/AzCore/AzCore/Math/Uuid.cpp b/Code/Framework/AzCore/AzCore/Math/Uuid.cpp
index d875e28a1e..410d235a37 100644
--- a/Code/Framework/AzCore/AzCore/Math/Uuid.cpp
+++ b/Code/Framework/AzCore/AzCore/Math/Uuid.cpp
@@ -135,18 +135,12 @@ namespace AZ
{
stringLength = strlen(uuidString);
}
- if (stringLength > MaxPermissiveStringSize)
- {
- if (!skipWarnings)
- {
- AZ_Warning("Math", false, "Can't create UUID from string length %zu over maximum %zu", stringLength, MaxPermissiveStringSize);
- }
- return Uuid::CreateNull();
- }
+
size_t newLength{ 0 };
char createString[MaxPermissiveStringSize];
- for (size_t curPos = 0; curPos < stringLength; ++curPos)
+ // Loop until we get to the end of the string OR stop once we've accumulated a full UUID string worth of data
+ for (size_t curPos = 0; curPos < stringLength && newLength < ValidUuidStringLength; ++curPos)
{
char curChar = uuidString[curPos];
switch (curChar)
diff --git a/Code/Framework/AzCore/AzCore/Math/Uuid.h b/Code/Framework/AzCore/AzCore/Math/Uuid.h
index ea920e45c8..6b77e7ec3e 100644
--- a/Code/Framework/AzCore/AzCore/Math/Uuid.h
+++ b/Code/Framework/AzCore/AzCore/Math/Uuid.h
@@ -42,8 +42,9 @@ namespace AZ
//VER_AZ_RANDOM_CRC32 = 6, // 0 1 1 0
};
+ static constexpr int ValidUuidStringLength = 32; /// Number of characters (data only, no extra formatting) in a valid UUID string
static const size_t MaxStringBuffer = 39; /// 32 Uuid + 4 dashes + 2 brackets + 1 terminate
-
+
Uuid() {}
Uuid(const char* string, size_t stringLength = 0) { *this = CreateString(string, stringLength); }
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
index 113fdd433e..36f66312d8 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
@@ -276,7 +276,9 @@ namespace AZ::SettingsRegistryMergeUtils
return engineRoot;
}
- return {};
+ // Fall back to using the project root as the engine root if the engine path could not be reconciled
+ // by checking the project.json "engine" string within o3de_manifest.json "engine_paths" object
+ return projectRoot;
}
AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry)
@@ -309,7 +311,13 @@ namespace AZ::SettingsRegistryMergeUtils
return projectRoot;
}
- return {};
+ // Step 3 Check for a "Cache" directory by scanning upwards from the executable directory
+ if (auto candidateRoot = Internal::ScanUpRootLocator("Cache");
+ !candidateRoot.empty() && AZ::IO::SystemFile::IsDirectory(candidateRoot.c_str()))
+ {
+ projectRoot = AZStd::move(candidateRoot);
+ }
+ return projectRoot;
}
AZStd::string_view ConfigParserSettings::DefaultCommentPrefixFilter(AZStd::string_view line)
@@ -538,7 +546,7 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath path = AZ::Utils::GetExecutableDirectory();
registry.Set(FilePathKey_BinaryFolder, path.LexicallyNormal().Native());
- // Engine root folder - corresponds to the @engroot@ and @devroot@ aliases
+ // Engine root folder - corresponds to the @engroot@ and @engroot@ aliases
AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry);
registry.Set(FilePathKey_EngineRootFolder, engineRoot.LexicallyNormal().Native());
@@ -562,7 +570,7 @@ namespace AZ::SettingsRegistryMergeUtils
assetPlatform = AZ::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
}
- // Project path - corresponds to the @devassets@ alias
+ // Project path - corresponds to the @projectroot@ alias
// NOTE: Here we append to engineRoot, but if projectPathValue is absolute then engineRoot is discarded.
path = engineRoot / projectPathValue;
@@ -654,7 +662,7 @@ namespace AZ::SettingsRegistryMergeUtils
}
else
{
- // Cache: root - same as the @root@ alias, this is the starting path for cache files.
+ // Cache: root - same as the @products@ alias, this is the starting path for cache files.
path = normalizedProjectPath / "Cache";
registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
path /= assetPlatform;
diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp
index 7da04d7301..4097348798 100644
--- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp
+++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp
@@ -308,11 +308,11 @@ namespace AZ
void TaskExecutor::SetInstance(TaskExecutor* executor)
{
- if (!executor)
+ if (!executor) // allow unsetting the executor
{
s_executor.Reset();
}
- else if (!s_executor) // ignore any calls to set after the first (this happens in unit tests that create new system entities)
+ else if (!s_executor) // ignore any extra executors after the first (this happens during unit tests)
{
s_executor = AZ::Environment::CreateVariable(s_executorName, executor);
}
diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp
index eed461ecb4..56b56e96a1 100644
--- a/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp
+++ b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp
@@ -11,9 +11,15 @@
#include
#include
#include
+#include
+#include
// Create a cvar as a central location for experimentation with switching from the Job system to TaskGraph system.
AZ_CVAR(bool, cl_activateTaskGraph, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Flag clients of TaskGraph to switch between jobs/taskgraph (Note does not disable task graph system)");
+AZ_CVAR(float, cl_taskGraphThreadsConcurrencyRatio, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph calculate the number of worker threads to spawn by scaling the number of hw threads, value is clamped between 0.0f and 1.0f");
+AZ_CVAR(uint32_t, cl_taskGraphThreadsNumReserved, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph number of hardware threads that are reserved for O3DE system threads. Value is clamped between 0 and the number of logical cores in the system");
+AZ_CVAR(uint32_t, cl_taskGraphThreadsMinNumber, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph minimum number of worker threads to create after scaling the number of hw threads");
+
static constexpr uint32_t TaskExecutorServiceCrc = AZ_CRC_CE("TaskExecutorService");
namespace AZ
@@ -24,8 +30,8 @@ namespace AZ
if (Interface::Get() == nullptr)
{
- Interface::Register(this);
- m_taskExecutor = aznew TaskExecutor();
+ Interface::Register(this); // small window that another thread can try to use taskgraph between this line and the set instance.
+ m_taskExecutor = aznew TaskExecutor(Threading::CalcNumWorkerThreads(cl_taskGraphThreadsConcurrencyRatio, cl_taskGraphThreadsMinNumber, cl_taskGraphThreadsNumReserved));
TaskExecutor::SetInstance(m_taskExecutor);
}
}
diff --git a/Code/Framework/AzCore/AzCore/Threading/ThreadUtils.cpp b/Code/Framework/AzCore/AzCore/Threading/ThreadUtils.cpp
new file mode 100644
index 0000000000..93eba9cc3f
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Threading/ThreadUtils.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+#include
+#include
+
+namespace AZ::Threading
+{
+ uint32_t CalcNumWorkerThreads(float workerThreadsRatio, uint32_t minNumWorkerThreads, uint32_t reservedNumThreads)
+ {
+ const uint32_t maxHardwareThreads = AZStd::thread::hardware_concurrency();
+ const uint32_t numReservedThreads = AZ::GetMin(reservedNumThreads, maxHardwareThreads); // protect against num reserved being bigger than the number of hw threads
+ const uint32_t maxWorkerThreads = maxHardwareThreads - numReservedThreads;
+ const float requestedWorkerThreads = AZ::GetClamp(workerThreadsRatio, 0.0f, 1.0f) * static_cast(maxWorkerThreads);
+ const uint32_t requestedWorkerThreadsRounded = AZStd::lround(requestedWorkerThreads);
+ const uint32_t numWorkerThreads = AZ::GetMax(minNumWorkerThreads, requestedWorkerThreadsRounded);
+ return numWorkerThreads;
+ }
+};
diff --git a/Code/Framework/AzCore/AzCore/Threading/ThreadUtils.h b/Code/Framework/AzCore/AzCore/Threading/ThreadUtils.h
new file mode 100644
index 0000000000..505d900289
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Threading/ThreadUtils.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+
+namespace AZ::Threading
+{
+ //! Calculates the number of worker threads a system should use based on the number of hardware threads a device has.
+ //! result = max (minNumWorkerThreads, workerThreadsRatio * (num_hardware_threads - reservedNumThreads))
+ //! @param workerThreadsRatio scale applied to the calculated maximum number of threads available after reserved threads have been accounted for. Clamped between 0 and 1.
+ //! @param minNumWorkerThreads minimum value that will be returned. Value is unclamped and can be more than num_hardware_threads.
+ //! @param reservedNumThreads number of hardware threads to reserve for O3DE system threads. Value clamped to num_hardware_threads.
+ //! @return number of worker threads for the calling system to allocate
+ uint32_t CalcNumWorkerThreads(float workerThreadsRatio, uint32_t minNumWorkerThreads, uint32_t reservedNumThreads);
+};
diff --git a/Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockFileIOBase.h b/Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockFileIOBase.h
index c9b7d44e1f..d7d0789c1a 100644
--- a/Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockFileIOBase.h
+++ b/Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockFileIOBase.h
@@ -52,6 +52,7 @@ namespace AZ
MOCK_METHOD2(SetAlias, void(const char* alias, const char* path));
MOCK_METHOD1(ClearAlias, void(const char* alias));
MOCK_CONST_METHOD1(GetAlias, const char*(const char* alias));
+ MOCK_METHOD2(SetDeprecatedAlias, void(AZStd::string_view, AZStd::string_view));
MOCK_CONST_METHOD2(ConvertToAlias, AZStd::optional(char* inOutBuffer, AZ::u64 bufferLength));
MOCK_CONST_METHOD2(ConvertToAlias, bool(AZ::IO::FixedMaxPath& aliasPath, const AZ::IO::PathView& path));
MOCK_CONST_METHOD3(ResolvePath, bool(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize));
diff --git a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp
index c4dd24be35..2031d14d08 100644
--- a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp
+++ b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp
@@ -51,6 +51,20 @@ namespace AZ::Utils
return executableDirectory;
}
+ AZStd::optional ConvertToAbsolutePath(AZStd::string_view path)
+ {
+ AZ::IO::FixedMaxPathString absolutePath;
+ AZ::IO::FixedMaxPathString srcPath{ path };
+ if (ConvertToAbsolutePath(srcPath.c_str(), absolutePath.data(), absolutePath.capacity()))
+ {
+ // Fix the size value of the fixed string by calculating the c-string length using char traits
+ absolutePath.resize_no_construct(AZStd::char_traits::length(absolutePath.data()));
+ return srcPath;
+ }
+
+ return AZStd::nullopt;
+ }
+
AZ::IO::FixedMaxPathString GetEngineManifestPath()
{
AZ::IO::FixedMaxPath o3deManifestPath = GetO3deManifestDirectory();
diff --git a/Code/Framework/AzCore/AzCore/Utils/Utils.h b/Code/Framework/AzCore/AzCore/Utils/Utils.h
index d8d4290f7f..c147e38bfb 100644
--- a/Code/Framework/AzCore/AzCore/Utils/Utils.h
+++ b/Code/Framework/AzCore/AzCore/Utils/Utils.h
@@ -104,6 +104,7 @@ namespace AZ
// Attempts the supplied path to an absolute path.
//! Returns nullopt if path cannot be converted to an absolute path
AZStd::optional ConvertToAbsolutePath(AZStd::string_view path);
+ bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 absolutePathMaxSize);
//! Save a string to a file. Otherwise returns a failure with error message.
AZ::Outcome WriteFile(AZStd::string_view content, AZStd::string_view filePath);
diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake
index 14579cbf33..010c748402 100644
--- a/Code/Framework/AzCore/AzCore/azcore_files.cmake
+++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake
@@ -639,6 +639,8 @@ set(FILES
Threading/ThreadSafeDeque.inl
Threading/ThreadSafeObject.h
Threading/ThreadSafeObject.inl
+ Threading/ThreadUtils.h
+ Threading/ThreadUtils.cpp
Time/ITime.h
Time/TimeSystemComponent.cpp
Time/TimeSystemComponent.h
diff --git a/Code/Framework/AzCore/CMakeLists.txt b/Code/Framework/AzCore/CMakeLists.txt
index 60c5f50594..96ed838ccc 100644
--- a/Code/Framework/AzCore/CMakeLists.txt
+++ b/Code/Framework/AzCore/CMakeLists.txt
@@ -39,7 +39,7 @@ ly_add_target(
3rdParty::Lua
3rdParty::RapidJSON
3rdParty::RapidXML
- 3rdParty::zlib
+ 3rdParty::ZLIB
3rdParty::zstd
3rdParty::cityhash
${AZ_CORE_PIX_BUILD_DEPENDENCIES}
diff --git a/Code/Framework/AzCore/Platform/Android/AzCore/Utils/Utils_Android.cpp b/Code/Framework/AzCore/Platform/Android/AzCore/Utils/Utils_Android.cpp
index 1333006c4c..9a3f825bfc 100644
--- a/Code/Framework/AzCore/Platform/Android/AzCore/Utils/Utils_Android.cpp
+++ b/Code/Framework/AzCore/Platform/Android/AzCore/Utils/Utils_Android.cpp
@@ -60,23 +60,34 @@ namespace AZ
return writeStorage ? AZStd::make_optional(writeStorage) : AZStd::nullopt;
}
- AZStd::optional ConvertToAbsolutePath(AZStd::string_view path)
+ bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength)
{
- AZ::IO::FixedMaxPathString absolutePath;
- AZ::IO::FixedMaxPathString srcPath{ path };
- if (AZ::Android::Utils::IsApkPath(srcPath.c_str()))
+ if (AZ::Android::Utils::IsApkPath(path))
{
- return srcPath;
+ azstrcpy(absolutePath, maxLength, path);
+ return true;
}
- if(char* result = realpath(srcPath.c_str(), absolutePath.data()); result)
+#ifdef PATH_MAX
+ static constexpr size_t UnixMaxPathLength = PATH_MAX;
+#else
+ // Fallback to 4096 if the PATH_MAX macro isn't defined on the Unix System
+ static constexpr size_t UnixMaxPathLength = 4096;
+#endif
+ if (!AZ::IO::PathView(path).IsAbsolute())
{
- // Fix the size value of the fixed string by calculating the c-string length using char traits
- absolutePath.resize_no_construct(AZStd::char_traits::length(absolutePath.data()));
- return absolutePath;
+ // note that realpath fails if the path does not exist and actually changes the return value
+ // to be the actual place that FAILED, which we don't want.
+ // if we fail, we'd prefer to fall through and at least use the original path.
+ char absolutePathBuffer[UnixMaxPathLength];
+ if (const char* result = realpath(path, absolutePathBuffer); result != nullptr)
+ {
+ azstrcpy(absolutePath, maxLength, absolutePathBuffer);
+ return true;
+ }
}
-
- return AZStd::nullopt;
+ azstrcpy(absolutePath, maxLength, path);
+ return AZ::IO::PathView(absolutePath).IsAbsolute();
}
}
}
diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp
index 2e31936057..7327c8f152 100644
--- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp
+++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp
@@ -47,23 +47,32 @@ namespace AZ
AZ::IO::FixedMaxPath path{pass->pw_dir};
return path.Native();
}
-
+
return {};
}
- AZStd::optional ConvertToAbsolutePath(AZStd::string_view path)
+ bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength)
{
- AZ::IO::FixedMaxPathString absolutePath;
- AZ::IO::FixedMaxPathString srcPath{ path };
-
- if (char* result = realpath(srcPath.c_str(), absolutePath.data()); result)
+#ifdef PATH_MAX
+ static constexpr size_t UnixMaxPathLength = PATH_MAX;
+#else
+ // Fallback to 4096 if the PATH_MAX macro isn't defined on the Unix System
+ static constexpr size_t UnixMaxPathLength = 4096;
+#endif
+ if (!AZ::IO::PathView(path).IsAbsolute())
{
- // Fix the size value of the fixed string by calculating the c-string length using char traits
- absolutePath.resize_no_construct(AZStd::char_traits::length(absolutePath.data()));
- return absolutePath;
+ // note that realpath fails if the path does not exist and actually changes the return value
+ // to be the actual place that FAILED, which we don't want.
+ // if we fail, we'd prefer to fall through and at least use the original path.
+ char absolutePathBuffer[UnixMaxPathLength];
+ if (const char* result = realpath(path, absolutePathBuffer); result != nullptr)
+ {
+ azstrcpy(absolutePath, maxLength, absolutePathBuffer);
+ return true;
+ }
}
-
- return AZStd::nullopt;
+ azstrcpy(absolutePath, maxLength, path);
+ return AZ::IO::PathView(absolutePath).IsAbsolute();
}
} // namespace Utils
} // namespace AZ
diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/std/parallel/internal/thread_UnixLike.cpp b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/std/parallel/internal/thread_UnixLike.cpp
index 079f167408..b615f677f1 100644
--- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/std/parallel/internal/thread_UnixLike.cpp
+++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/std/parallel/internal/thread_UnixLike.cpp
@@ -59,6 +59,10 @@ namespace AZStd
{
priority = desc->m_priority;
}
+ else
+ {
+ priority = SCHED_OTHER;
+ }
if (desc->m_name)
{
name = desc->m_name;
diff --git a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Utils/Utils_WinAPI.cpp b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Utils/Utils_WinAPI.cpp
index bcba24b768..24786e2bc9 100644
--- a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Utils/Utils_WinAPI.cpp
+++ b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Utils/Utils_WinAPI.cpp
@@ -67,19 +67,12 @@ namespace AZ
return AZStd::nullopt;
}
- AZStd::optional ConvertToAbsolutePath(AZStd::string_view path)
+ bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength)
{
- AZ::IO::FixedMaxPathString absolutePath;
- AZ::IO::FixedMaxPathString srcPath{ path };
- char* result = _fullpath(absolutePath.data(), srcPath.c_str(), absolutePath.capacity());
- // Force update of the fixed_string size() value
- absolutePath.resize_no_construct(AZStd::char_traits::length(absolutePath.data()));
- if (result)
- {
- return absolutePath;
- }
-
- return AZStd::nullopt;
+ char* result = _fullpath(absolutePath, path, maxLength);
+ return result != nullptr;
}
+
+
}
}
diff --git a/Code/Framework/AzCore/Tests/FileIOBaseTestTypes.h b/Code/Framework/AzCore/Tests/FileIOBaseTestTypes.h
index 26b3df55bb..5897b3aaee 100644
--- a/Code/Framework/AzCore/Tests/FileIOBaseTestTypes.h
+++ b/Code/Framework/AzCore/Tests/FileIOBaseTestTypes.h
@@ -426,6 +426,10 @@ public:
return nullptr;
}
+ void SetDeprecatedAlias(AZStd::string_view, AZStd::string_view) override
+ {
+ }
+
void ClearAlias(const char* ) override { }
AZStd::optional ConvertToAlias(char* inOutBuffer, AZ::u64) const override
diff --git a/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp b/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp
index 34076a10f6..cf239a0821 100644
--- a/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp
+++ b/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp
@@ -698,7 +698,7 @@ AZ_POP_DISABLE_WARNING
using PathViewLexicallyProximateFixture = PathLexicallyFixture;
- TEST_P(PathViewLexicallyProximateFixture, LexicallyProximate_ReturnsRelativePathIfNotEmptyOrTestPathIfNot)
+ TEST_P(PathViewLexicallyProximateFixture, LexicallyProximate_ReturnsRelativePathIfNotEmptyOrTestPath)
{
const auto& testParams = GetParam();
AZ::IO::PathView testPath(testParams.m_testPathString, testParams.m_preferredSeparator);
diff --git a/Code/Framework/AzCore/Tests/UUIDTests.cpp b/Code/Framework/AzCore/Tests/UUIDTests.cpp
index a18dc33c4f..64b9048a62 100644
--- a/Code/Framework/AzCore/Tests/UUIDTests.cpp
+++ b/Code/Framework/AzCore/Tests/UUIDTests.cpp
@@ -247,4 +247,27 @@ namespace UnitTest
Uuid right = Uuid::CreateStringPermissive(permissiveStr1);
EXPECT_EQ(left, right);
}
+
+ TEST_F(UuidTests, CreateStringPermissive_StringWithExtraData_Succeeds)
+ {
+ const char uuidStr[] = "{34D44249-E599-4B30-811F-4215C2DEA269}";
+ Uuid left = Uuid::CreateString(uuidStr);
+
+ const char permissiveStr[] = "0x34D44249-0xE5994B30-0x811F4215-0xC2DEA269 Hello World";
+ Uuid right = Uuid::CreateStringPermissive(permissiveStr);
+ EXPECT_EQ(left, right);
+
+ }
+
+ TEST_F(UuidTests, CreateStringPermissive_StringWithLotsOfExtraData_Succeeds)
+ {
+ const char uuidStr[] = "{34D44249-E599-4B30-811F-4215C2DEA269}";
+ Uuid left = Uuid::CreateString(uuidStr);
+
+ const char permissiveStr[] = "0x34D44249-0xE5994B30-0x811F4215-0xC2DEA269 Hello World this is a really long string "
+ "with lots of extra data to make sure we can parse a long string without failing as long as the uuid is in "
+ "the beginning of the string then we should succeed";
+ Uuid right = Uuid::CreateStringPermissive(permissiveStr);
+ EXPECT_EQ(left, right);
+ }
}
diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
index e72e2de472..1f99a594fa 100644
--- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
@@ -77,11 +77,15 @@
namespace AzFramework
{
+
namespace ApplicationInternal
{
static constexpr const char s_prefabSystemKey[] = "/Amazon/Preferences/EnablePrefabSystem";
static constexpr const char s_prefabWipSystemKey[] = "/Amazon/Preferences/EnablePrefabSystemWipFeatures";
static constexpr const char s_legacySlicesAssertKey[] = "/Amazon/Preferences/ShouldAssertForLegacySlicesUsage";
+ static constexpr const char* DeprecatedFileIOAliasesRoot = "/O3DE/AzCore/FileIO/DeprecatedAliases";
+ static constexpr const char* DeprecatedFileIOAliasesOldAliasKey = "OldAlias";
+ static constexpr const char* DeprecatedFileIOAliasesNewAliasKey = "NewAlias";
}
Application::Application()
@@ -563,6 +567,68 @@ namespace AzFramework
}
}
+ struct DeprecatedAliasesKeyVisitor
+ : AZ::SettingsRegistryInterface::Visitor
+ {
+ using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse;
+ using VisitAction = AZ::SettingsRegistryInterface::VisitAction;
+ using Type = AZ::SettingsRegistryInterface::Type;
+
+ using AZ::SettingsRegistryInterface::Visitor::Visit;
+
+ VisitResponse Traverse(AZStd::string_view path, AZStd::string_view,
+ VisitAction action, Type type) override
+ {
+ if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
+ {
+ if (type == AZ::SettingsRegistryInterface::Type::Array)
+ {
+ m_parentArrayPath = path;
+ }
+
+ // Strip off last path segment from json path and check if is a child element of the array
+ if (AZ::StringFunc::TokenizeLast(path, '/');
+ m_parentArrayPath == path)
+ {
+ m_aliases.emplace_back();
+ }
+ }
+ else if (action == AZ::SettingsRegistryInterface::VisitAction::End)
+ {
+ if (type == AZ::SettingsRegistryInterface::Type::Array)
+ {
+ m_parentArrayPath = AZStd::string{};
+ }
+ }
+
+ return AZ::SettingsRegistryInterface::VisitResponse::Continue;
+ }
+
+ void Visit(AZStd::string_view, AZStd::string_view valueName, Type, AZStd::string_view value) override
+ {
+ if (!m_aliases.empty())
+ {
+ if (valueName == ApplicationInternal::DeprecatedFileIOAliasesOldAliasKey)
+ {
+ m_aliases.back().m_oldAlias = value;
+ }
+ else if (valueName == ApplicationInternal::DeprecatedFileIOAliasesNewAliasKey)
+ {
+ m_aliases.back().m_newAlias = value;
+ }
+ }
+ }
+
+ struct AliasPair
+ {
+ AZStd::string m_oldAlias;
+ AZStd::string m_newAlias;
+ };
+ AZStd::vector m_aliases;
+
+ private:
+ AZStd::string m_parentArrayPath;
+ };
static void CreateUserCache(const AZ::IO::FixedMaxPath& cacheUserPath, AZ::IO::FileIOBase& fileIoBase)
{
@@ -610,9 +676,8 @@ namespace AzFramework
void Application::SetFileIOAliases()
{
- if (m_archiveFileIO)
+ if (auto fileIoBase = m_archiveFileIO.get(); fileIoBase)
{
- auto fileIoBase = m_archiveFileIO.get();
// Set up the default file aliases based on the settings registry
fileIoBase->SetAlias("@engroot@", GetEngineRoot());
fileIoBase->SetAlias("@projectroot@", GetEngineRoot());
@@ -620,29 +685,20 @@ namespace AzFramework
{
AZ::IO::FixedMaxPath pathAliases;
- if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder))
- {
- fileIoBase->SetAlias("@projectcache@", pathAliases.c_str());
- }
pathAliases.clear();
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
{
- fileIoBase->SetAlias("@assets@", pathAliases.c_str());
- fileIoBase->SetAlias("@projectplatformcache@", pathAliases.c_str());
- fileIoBase->SetAlias("@root@", pathAliases.c_str()); // Deprecated Use @projectplatformcache@
+ fileIoBase->SetAlias("@products@", pathAliases.c_str());
}
pathAliases.clear();
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
{
fileIoBase->SetAlias("@engroot@", pathAliases.c_str());
- fileIoBase->SetAlias("@devroot@", pathAliases.c_str()); // Deprecated - Use @engroot@
}
pathAliases.clear();
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
{
- fileIoBase->SetAlias("@devassets@", pathAliases.c_str()); // Deprecated - Use @projectsourceassets@
fileIoBase->SetAlias("@projectroot@", pathAliases.c_str());
- fileIoBase->SetAlias("@projectsourceassets@", (pathAliases / "Assets").c_str());
}
}
@@ -663,6 +719,15 @@ namespace AzFramework
}
fileIoBase->SetAlias("@log@", projectLogPath.c_str());
fileIoBase->CreatePath(projectLogPath.c_str());
+
+ DeprecatedAliasesKeyVisitor visitor;
+ if (m_settingsRegistry->Visit(visitor, ApplicationInternal::DeprecatedFileIOAliasesRoot))
+ {
+ for (const auto& [oldAlias, newAlias] : visitor.m_aliases)
+ {
+ fileIoBase->SetDeprecatedAlias(oldAlias, newAlias);
+ }
+ }
}
}
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
index 8064ba6669..e5a42aabbc 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
@@ -1121,7 +1121,7 @@ namespace AZ::IO
if (AZ::IO::FixedMaxPath pathBindRoot; !AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, szBindRoot))
{
- AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, "@assets@");
+ AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, "@products@");
desc.m_pathBindRoot = pathBindRoot.LexicallyNormal().String();
}
else
@@ -1807,9 +1807,9 @@ namespace AZ::IO
if (m_eRecordFileOpenList != IArchive::RFOM_Disabled)
{
// we only want to record ASSET access
- // assets are identified as files that are relative to the resolved @assets@ alias path
+ // assets are identified as files that are relative to the resolved @products@ alias path
auto fileIoBase = AZ::IO::FileIOBase::GetInstance();
- const char* aliasValue = fileIoBase->GetAlias("@assets@");
+ const char* aliasValue = fileIoBase->GetAlias("@products@");
if (AZ::IO::FixedMaxPath resolvedFilePath;
fileIoBase->ResolvePath(resolvedFilePath, szFilename)
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp
index 85ce0b6f9a..9e6e1034ea 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp
@@ -546,6 +546,16 @@ namespace AZ::IO
realUnderlyingFileIO->GetAlias(alias);
}
+ void ArchiveFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias)
+ {
+ FileIOBase* realUnderlyingFileIO = FileIOBase::GetDirectInstance();
+ if (!realUnderlyingFileIO)
+ {
+ return;
+ }
+ realUnderlyingFileIO->SetDeprecatedAlias(oldAlias, newAlias);
+ }
+
AZStd::optional ArchiveFileIO::ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const
{
if ((!inOutBuffer) || (bufferLength == 0))
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h
index 21cef18a7a..7fd4e15e3a 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h
+++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h
@@ -63,6 +63,7 @@ namespace AZ::IO
IO::Result FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback) override;
void SetAlias(const char* alias, const char* path) override;
void ClearAlias(const char* alias) override;
+ void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
AZStd::optional ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp
index d7a92efbf6..7b483dc5de 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp
@@ -186,8 +186,8 @@ namespace AZ::IO
{
// filter out the stuff which does not match.
- // the problem here is that szDir might be something like "@assets@/levels/*"
- // but our archive might be mounted at the root, or at some other folder at like "@assets@" or "@assets@/levels/mylevel"
+ // the problem here is that szDir might be something like "@products@/levels/*"
+ // but our archive might be mounted at the root, or at some other folder at like "@products@" or "@products@/levels/mylevel"
// so there's really no way to filter out opening the pack and looking at the files inside.
// however, the bind root is not part of the inner zip entry name either
// and the ZipDir::FindFile actually expects just the chopped off piece.
@@ -202,22 +202,22 @@ namespace AZ::IO
// Example:
- // "@assets@\\levels\\*" <--- szDir
- // "@assets@\\" <--- mount point
+ // "@products@\\levels\\*" <--- szDir
+ // "@products@\\" <--- mount point
// ~~~~~~~~~~~ Common part
// "levels\\*" <---- remainder that is not in common
// "" <--- mount point remainder. In this case, we should scan the contents of the pak for the remainder
// Example:
- // "@assets@\\levels\\*" <--- szDir
- // "@assets@\\levels\\mylevel\\" <--- mount point (its level.pak)
+ // "@products@\\levels\\*" <--- szDir
+ // "@products@\\levels\\mylevel\\" <--- mount point (its level.pak)
// ~~~~~~~~~~~~~~~~~~ common part
// "*" <---- remainder that is not in common
// "mylevel\\" <--- mount point remainder.
// example:
- // "@assets@\\levels\\otherlevel\\*" <--- szDir
- // "@assets@\\levels\\mylevel\\" <--- mount point (its level.pak)
+ // "@products@\\levels\\otherlevel\\*" <--- szDir
+ // "@products@\\levels\\mylevel\\" <--- mount point (its level.pak)
// "otherlevel\\*" <---- remainder
// "mylevel\\" <--- mount point remainder.
@@ -249,7 +249,7 @@ namespace AZ::IO
// which means we may search inside the pack.
ScanInZip(it->pZip.get(), sourcePathRemainder.Native());
}
-
+
}
}
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/MissingFileReport.cpp b/Code/Framework/AzFramework/AzFramework/Archive/MissingFileReport.cpp
index 0a6116d313..4c6ed0363e 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/MissingFileReport.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Archive/MissingFileReport.cpp
@@ -94,7 +94,7 @@ namespace AZ::IO::Internal
}
AZStd::smatch matches;
- const AZStd::regex lodRegex("@assets@\\\\(.*)_lod[0-9]+(\\.cgfm?)");
+ const AZStd::regex lodRegex("@products@\\\\(.*)_lod[0-9]+(\\.cgfm?)");
if (!AZStd::regex_match(szPath, matches, lodRegex) || matches.size() != 3)
{
// The current file is not a valid LOD file
diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp
index 9b1946701c..e6b8211c28 100644
--- a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp
@@ -725,7 +725,7 @@ namespace AzFramework
if (!info.m_relativePath.empty())
{
- const char* devAssetRoot = fileIO->GetAlias("@devassets@");
+ const char* devAssetRoot = fileIO->GetAlias("@projectroot@");
if (devAssetRoot)
{
AZ::Data::AssetStreamInfo streamInfo;
diff --git a/Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp b/Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp
index 725f71a3c9..5d82cf488a 100644
--- a/Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp
@@ -133,6 +133,8 @@ namespace AzFramework
behaviorContext->Class("Entity")
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Attribute(AZ::Script::Attributes::ConstructorOverride, &Internal::BehaviorEntityScriptConstructor)
+ ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
+ ->Attribute(AZ::Script::Attributes::Module, "entity")
->Constructor()
->Constructor()
->Constructor()
diff --git a/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp b/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp
index a7c6b18061..cc31df3dbc 100644
--- a/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp
@@ -61,7 +61,7 @@ namespace AzFramework
AZ::IO::Path& gemAbsPath = gemInfo.m_absoluteSourcePaths.emplace_back(value);
// 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/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp
index 49e16dcb90..c1b9c941bc 100644
--- a/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp
+++ b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp
@@ -12,10 +12,12 @@
#include
#include
#include
+#include
#include
#include
#include
#include
+#include
#include
namespace AZ
@@ -292,7 +294,7 @@ namespace AZ
void LocalFileIO::CheckInvalidWrite([[maybe_unused]] const char* path)
{
#if defined(AZ_ENABLE_TRACING)
- const char* assetAliasPath = GetAlias("@assets@");
+ const char* assetAliasPath = GetAlias("@products@");
if (path && assetAliasPath)
{
const AZ::IO::PathView pathView(path);
@@ -478,17 +480,15 @@ namespace AZ
return false;
}
- if (IsAbsolutePath(path))
+ if (AZ::IO::PathView(path).HasRootPath())
{
size_t pathLen = strlen(path);
if (pathLen + 1 < resolvedPathSize)
{
azstrncpy(resolvedPath, resolvedPathSize, path, pathLen + 1);
- //see if the absolute path uses @assets@ or @root@, if it does lowercase the relative part
- [[maybe_unused]] bool lowercasePath = LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@assets@"))
- || LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@root@"))
- || LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@projectplatformcache@"));
+ //see if the absolute path matches the resolved value of @products@, if it does lowercase the relative part
+ LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@products@"));
ToUnixSlashes(resolvedPath, resolvedPathSize);
return true;
@@ -499,34 +499,39 @@ namespace AZ
}
}
- char rootedPathBuffer[AZ_MAX_PATH_LEN] = {0};
+ constexpr AZStd::string_view productAssetAlias = "@products@";
+ // Add plus one for the path separator: /
+ constexpr size_t MaxPathSizeWithProductAssetAlias = AZ::IO::MaxPathLength + productAssetAlias.size() + 1;
+ using RootedPathString = AZStd::fixed_string;
+ RootedPathString rootedPathBuffer;
const char* rootedPath = path;
- // if the path does not begin with an alias, then it is assumed to begin with @assets@
+ // if the path does not begin with an alias, then it is assumed to begin with @products@
if (path[0] != '@')
{
- if (GetAlias("@assets@"))
+ if (GetAlias("@products@"))
{
- const int rootLength = 9;// strlen("@assets@/")
- azstrncpy(rootedPathBuffer, AZ_MAX_PATH_LEN, "@assets@/", rootLength);
- size_t pathLen = strlen(path);
- size_t rootedPathBufferlength = rootLength + pathLen + 1;// +1 for null terminator
- if (rootedPathBufferlength > resolvedPathSize)
+
+ if (const size_t requiredSize = productAssetAlias.size() + strlen(path) + 1;
+ requiredSize > rootedPathBuffer.capacity())
{
- AZ_Assert(rootedPathBufferlength < resolvedPathSize, "Constructed path length is wrong:%s", rootedPathBuffer);//path constructed is wrong
- size_t remainingSize = resolvedPathSize - rootLength - 1;// - 1 for null terminator
- azstrncpy(rootedPathBuffer + rootLength, AZ_MAX_PATH_LEN, path, remainingSize);
- rootedPathBuffer[resolvedPathSize - 1] = '\0';
+ AZ_Error("FileIO", false, "Prepending the %.*s alias to the input path results in a path longer than the"
+ " AZ::IO::MaxPathLength + the alias size of %zu. The size of the potential failed path is %zu",
+ AZ_STRING_ARG(productAssetAlias), rootedPathBuffer.capacity(), requiredSize)
}
else
{
- azstrncpy(rootedPathBuffer + rootLength, AZ_MAX_PATH_LEN - rootLength, path, pathLen + 1);
+ rootedPathBuffer = RootedPathString::format("%.*s/%s", AZ_STRING_ARG(productAssetAlias), path);
}
}
else
{
- ConvertToAbsolutePath(path, rootedPathBuffer, AZ_MAX_PATH_LEN);
+ if (ConvertToAbsolutePath(path, rootedPathBuffer.data(), rootedPathBuffer.capacity()))
+ {
+ // Recalculate the internal string length
+ rootedPathBuffer.resize_no_construct(AZStd::char_traits::length(rootedPathBuffer.data()));
+ }
}
- rootedPath = rootedPathBuffer;
+ rootedPath = rootedPathBuffer.c_str();
}
if (ResolveAliases(rootedPath, resolvedPath, resolvedPathSize))
@@ -561,11 +566,57 @@ namespace AZ
const char* LocalFileIO::GetAlias(const char* key) const
{
- const auto it = m_aliases.find(key);
- if (it != m_aliases.end())
+ if (const auto it = m_aliases.find(key); it != m_aliases.end())
{
return it->second.c_str();
}
+ else if (const auto deprecatedIt = m_deprecatedAliases.find(key);
+ deprecatedIt != m_deprecatedAliases.end())
+ {
+ AZ_Error("FileIO", false, R"(Alias "%s" is deprecated. Please use alias "%s" instead)",
+ key, deprecatedIt->second.c_str());
+ AZStd::string_view aliasValue = deprecatedIt->second;
+ // Contains the list of aliases resolved so far
+ // If max_size is hit, than an error is logged and nullptr is returned
+ using VisitedAliasSet = AZStd::fixed_unordered_set;
+ VisitedAliasSet visitedAliasSet;
+ while (aliasValue.starts_with("@"))
+ {
+ if (visitedAliasSet.contains(aliasValue))
+ {
+ AZ_Error("FileIO", false, "Cycle found with for alias %.*s when trying to resolve deprecated alias %s",
+ AZ_STRING_ARG(aliasValue), key);
+ return nullptr;
+ }
+
+ if(visitedAliasSet.size() == visitedAliasSet.max_size())
+ {
+ AZ_Error("FileIO", false, "Unable to resolve path to deprecated alias %s within %zu steps",
+ key, visitedAliasSet.max_size());
+ return nullptr;
+ }
+
+ // Add the current alias value to the visited set
+ visitedAliasSet.emplace(aliasValue);
+
+ // Check if the alias value corresponds to another alias
+ if (auto resolvedIter = m_aliases.find(aliasValue); resolvedIter != m_aliases.end())
+ {
+ aliasValue = resolvedIter->second;
+ }
+ else if (resolvedIter = m_deprecatedAliases.find(aliasValue);
+ resolvedIter != m_deprecatedAliases.end())
+ {
+ aliasValue = resolvedIter->second;
+ }
+ else
+ {
+ return nullptr;
+ }
+ }
+
+ return aliasValue.data();
+ }
return nullptr;
}
@@ -574,6 +625,11 @@ namespace AZ
m_aliases.erase(key);
}
+ void LocalFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias)
+ {
+ m_deprecatedAliases[oldAlias] = newAlias;
+ }
+
AZStd::optional LocalFileIO::ConvertToAliasBuffer(char* outBuffer, AZ::u64 outBufferLength, AZStd::string_view inBuffer) const
{
size_t longestMatch = 0;
@@ -675,7 +731,9 @@ namespace AZ
: string_view_pair{};
size_t requiredResolvedPathSize = pathView.size() - aliasKey.size() + aliasValue.size() + 1;
- AZ_Assert(path != resolvedPath && resolvedPathSize >= requiredResolvedPathSize, "Resolved path is incorrect");
+ AZ_Assert(path != resolvedPath, "ResolveAliases does not support inplace update of the path");
+ AZ_Assert(resolvedPathSize >= requiredResolvedPathSize, "Resolved path size %llu not large enough. It needs to be %zu",
+ resolvedPathSize, requiredResolvedPathSize);
// we assert above, but we also need to properly handle the case when the resolvedPath buffer size
// is too small to copy the source into.
if (path == resolvedPath || (resolvedPathSize < requiredResolvedPathSize))
@@ -699,13 +757,9 @@ namespace AZ
resolvedPath[resolvedPathLen] = '\0';
// If the path started with one of the "asset cache" path aliases, lowercase the path
- const char* assetAliasPath = GetAlias("@assets@");
- const char* rootAliasPath = GetAlias("@root@");
- const char* projectPlatformCacheAliasPath = GetAlias("@projectplatformcache@");
+ const char* projectPlatformCacheAliasPath = GetAlias("@products@");
- const bool lowercasePath = (assetAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, assetAliasPath)) ||
- (rootAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, rootAliasPath)) ||
- (projectPlatformCacheAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, projectPlatformCacheAliasPath));
+ const bool lowercasePath = projectPlatformCacheAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, projectPlatformCacheAliasPath);
if (lowercasePath)
{
@@ -822,5 +876,10 @@ namespace AZ
return pathStr + "/";
}
+
+ bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
+ {
+ return AZ::Utils::ConvertToAbsolutePath(path, absolutePath, maxLength);
+ }
} // namespace IO
} // namespace AZ
diff --git a/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.h b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.h
index a9db55b320..a5ee1519a2 100644
--- a/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.h
+++ b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.h
@@ -61,6 +61,8 @@ namespace AZ
void SetAlias(const char* alias, const char* path) override;
void ClearAlias(const char* alias) override;
const char* GetAlias(const char* alias) const override;
+ void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
+
AZStd::optional ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
@@ -71,7 +73,7 @@ namespace AZ
bool GetFilename(HandleType fileHandle, char* filename, AZ::u64 filenameSize) const override;
bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const;
-
+
private:
SystemFile* GetFilePointerFromHandle(HandleType fileHandle);
@@ -79,7 +81,6 @@ namespace AZ
AZStd::optional ConvertToAliasBuffer(char* outBuffer, AZ::u64 outBufferLength, AZStd::string_view inBuffer) const;
bool ResolveAliases(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize) const;
- bool IsAbsolutePath(const char* path) const;
bool LowerIfBeginsWith(char* inOutBuffer, AZ::u64 bufferLen, const char* alias) const;
@@ -91,6 +92,7 @@ namespace AZ
AZStd::atomic m_nextHandle;
AZStd::unordered_map m_openFiles;
AZStd::unordered_map m_aliases;
+ AZStd::unordered_map m_deprecatedAliases;
void CheckInvalidWrite(const char* path);
};
diff --git a/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.cpp b/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.cpp
index 041e5baf4a..9e05cd5cb9 100644
--- a/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.cpp
+++ b/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.cpp
@@ -49,14 +49,14 @@ namespace AZ
s_IOLog.append(m_name);
s_IOLog.append("\r\n");
}
-
+
void Append(const char* line)
{
s_IOLog.append(AZStd::string::format("%u ", m_fileOperation));
s_IOLog.append(line);
s_IOLog.append("\r\n");
}
-
+
~LogCall()
{
s_IOLog.append(AZStd::string::format("%u End ", m_fileOperation));
@@ -251,7 +251,7 @@ namespace AZ
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) size request failed. return Error", filePath).c_str());
return ResultCode::Error;
}
-
+
size = response.m_size;
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) size=%u. return Success", filePath, size).c_str());
return ResultCode::Success;
@@ -793,6 +793,12 @@ namespace AZ
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ClearAlias(alias=%s)", alias?alias:"nullptr").c_str());
}
+ void NetworkFileIO::SetDeprecatedAlias([[maybe_unused]] AZStd::string_view oldAlias, [[maybe_unused]] AZStd::string_view newAlias)
+ {
+ REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::SetDeprecatedAlias(oldAlias=%.*s, newAlias=%.*s)",
+ AZ_STRING_ARG(oldAlias), AZ_STRING_ARG(newAlias)).c_str());
+ }
+
AZStd::optional NetworkFileIO::ConvertToAlias(char* inOutBuffer, [[maybe_unused]] AZ::u64 bufferLength) const
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ConvertToAlias(inOutBuffer=%s, bufferLength=%u)", inOutBuffer?inOutBuffer:"nullptr", bufferLength).c_str());
@@ -927,7 +933,7 @@ namespace AZ
{
m_cacheLookaheadPos = filePosition - CacheStartFilePosition();
}
-
+
void RemoteFileCache::SyncCheck()
{
#ifdef REMOTEFILEIO_SYNC_CHECK
@@ -955,7 +961,7 @@ namespace AZ
AZ_TracePrintf(RemoteFileCacheChannel, "RemoteFileCache::SyncCheck(m_fileHandle=%u) tell request failed.", m_fileHandle);
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileCache::SyncCheck(m_fileHandle=%u) tell request failed.", m_fileHandle).c_str());
}
-
+
if (responce.m_offset != m_filePosition)
{
AZ_TracePrintf(RemoteFileCacheChannel, "RemoteFileCache::SyncCheck(m_fileHandle=%u) failed!!! m_filePosition=%u tell=%u", m_fileHandle, m_filePosition, responce.m_offset);
@@ -1028,7 +1034,7 @@ namespace AZ
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Close(fileHandle=%u)", fileHandle).c_str());
Result returnValue = NetworkFileIO::Close(fileHandle);
-
+
if (returnValue == ResultCode::Success)
{
AZStd::lock_guard lock(m_remoteFileCacheGuard);
@@ -1160,7 +1166,7 @@ namespace AZ
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Read(fileHandle=%u, buffer=OUT, size=%u, failOnFewerThanSizeBytesRead=%s, bytesRead=OUT)", fileHandle, size, failOnFewerThanSizeBytesRead ? "True" : "False").c_str());
AZStd::lock_guard lock(m_remoteFileCacheGuard);
RemoteFileCache& cache = GetCache(fileHandle);
-
+
AZ::u64 remainingBytesToRead = size;
AZ::u64 bytesReadFromCache = 0;
AZ::u64 remainingBytesInCache = cache.RemainingBytes();
@@ -1263,7 +1269,7 @@ namespace AZ
RemoteFileCache& cache = GetCache(fileHandle);
if (cache.m_cacheLookaheadBuffer.size() && cache.RemainingBytes())
{
- // find out where we are
+ // find out where we are
AZ::u64 seekPosition = cache.CacheFilePosition();
// note, seeks are predicted, and do not ask for a response.
@@ -1361,6 +1367,14 @@ namespace AZ
}
}
+ void RemoteFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias)
+ {
+ if (m_excludedFileIO)
+ {
+ m_excludedFileIO->SetDeprecatedAlias(oldAlias, newAlias);
+ }
+ }
+
AZStd::optional RemoteFileIO::ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const
{
return m_excludedFileIO ? m_excludedFileIO->ConvertToAlias(inOutBuffer, bufferLength) : strlen(inOutBuffer);
diff --git a/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.h b/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.h
index d91e59bebc..77e91e1978 100644
--- a/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.h
+++ b/Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.h
@@ -102,6 +102,7 @@ namespace AZ
Result FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback) override;
void SetAlias(const char* alias, const char* path) override;
void ClearAlias(const char* alias) override;
+ void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
AZStd::optional ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
@@ -194,6 +195,7 @@ namespace AZ
void SetAlias(const char* alias, const char* path) override;
const char* GetAlias(const char* alias) const override;
void ClearAlias(const char* alias) override;
+ void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
AZStd::optional ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.cpp b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.cpp
index 6837807f4e..a24860fd34 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingAnd.cpp
@@ -35,6 +35,7 @@ namespace AzFramework
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputMappingAnd::Config::GetNameLabelOverride)
->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_sourceInputChannelNames, "Source Input Channel Names",
"The source input channel names that will be mapped to the output input channel name.")
+ ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.cpp b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.cpp
index bc47065c05..7c983766c8 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Input/Mappings/InputMappingOr.cpp
@@ -35,6 +35,7 @@ namespace AzFramework
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputMappingOr::Config::GetNameLabelOverride)
->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_sourceInputChannelNames, "Source Input Channel Names",
"The source input channel names that will be mapped to the output input channel name.")
+ ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h b/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h
new file mode 100644
index 0000000000..22b65f8340
--- /dev/null
+++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/IMatchmakingRequests.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+#include
+#include
+
+namespace AzFramework
+{
+ //! IMatchmakingRequests
+ //! Pure virtual session interface class to abstract the details of session handling from application code.
+ class IMatchmakingRequests
+ {
+ public:
+ AZ_RTTI(IMatchmakingRequests, "{BC0B74DA-A448-4F40-9B50-9D73142829D5}");
+
+ IMatchmakingRequests() = default;
+ virtual ~IMatchmakingRequests() = default;
+
+ // Registers a player's acceptance or rejection of a proposed matchmaking.
+ // @param acceptMatchRequest The request of AcceptMatch operation
+ virtual void AcceptMatch(const AcceptMatchRequest& acceptMatchRequest) = 0;
+
+ // Create a game match for a group of players.
+ // @param startMatchmakingRequest The request of StartMatchmaking operation
+ // @return A unique identifier for a matchmaking ticket
+ virtual AZStd::string StartMatchmaking(const StartMatchmakingRequest& startMatchmakingRequest) = 0;
+
+ // Cancels a matchmaking ticket that is currently being processed.
+ // @param stopMatchmakingRequest The request of StopMatchmaking operation
+ virtual void StopMatchmaking(const StopMatchmakingRequest& stopMatchmakingRequest) = 0;
+ };
+
+ //! IMatchmakingAsyncRequests
+ //! Async version of IMatchmakingRequests
+ class IMatchmakingAsyncRequests
+ {
+ public:
+ AZ_RTTI(ISessionAsyncRequests, "{53513480-2D02-493C-B44E-96AA27F42429}");
+
+ IMatchmakingAsyncRequests() = default;
+ virtual ~IMatchmakingAsyncRequests() = default;
+
+ // AcceptMatch Async
+ // @param acceptMatchRequest The request of AcceptMatch operation
+ virtual void AcceptMatchAsync(const AcceptMatchRequest& acceptMatchRequest) = 0;
+
+ // StartMatchmaking Async
+ // @param startMatchmakingRequest The request of StartMatchmaking operation
+ virtual void StartMatchmakingAsync(const StartMatchmakingRequest& startMatchmakingRequest) = 0;
+
+ // StopMatchmaking Async
+ // @param stopMatchmakingRequest The request of StopMatchmaking operation
+ virtual void StopMatchmakingAsync(const StopMatchmakingRequest& stopMatchmakingRequest) = 0;
+ };
+} // namespace AzFramework
diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h
new file mode 100644
index 0000000000..ad61971a11
--- /dev/null
+++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingNotifications.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace AzFramework
+{
+ //! MatchmakingAsyncRequestNotifications
+ //! The notifications correspond to matchmaking async requests
+ class MatchmakingAsyncRequestNotifications
+ : public AZ::EBusTraits
+ {
+ public:
+ // Safeguard handler for multi-threaded use case
+ using MutexType = AZStd::recursive_mutex;
+
+ //////////////////////////////////////////////////////////////////////////
+ // EBusTraits overrides
+ static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
+ static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+ //////////////////////////////////////////////////////////////////////////
+
+ // OnAcceptMatchAsyncComplete is fired once AcceptMatchAsync completes
+ virtual void OnAcceptMatchAsyncComplete() = 0;
+
+ // OnStartMatchmakingAsyncComplete is fired once StartMatchmakingAsync completes
+ // @param matchmakingTicketId The unique identifier for the matchmaking ticket
+ virtual void OnStartMatchmakingAsyncComplete(const AZStd::string& matchmakingTicketId) = 0;
+
+ // OnStopMatchmakingAsyncComplete is fired once StopMatchmakingAsync completes
+ virtual void OnStopMatchmakingAsyncComplete() = 0;
+ };
+ using MatchmakingAsyncRequestNotificationBus = AZ::EBus;
+
+ //! MatchmakingNotifications
+ //! The matchmaking notifications to listen for performing required operations
+ class MatchAcceptanceNotifications
+ : public AZ::EBusTraits
+ {
+ public:
+ // Safeguard handler for multi-threaded use case
+ using MutexType = AZStd::recursive_mutex;
+
+ //////////////////////////////////////////////////////////////////////////
+ // EBusTraits overrides
+ static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
+ static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+ //////////////////////////////////////////////////////////////////////////
+
+ // OnMatchAcceptance is fired when DescribeMatchmaking ticket status is REQUIRES_ACCEPTANCE
+ virtual void OnMatchAcceptance() = 0;
+ };
+ using MatchAcceptanceNotificationBus = AZ::EBus;
+} // namespace AzFramework
diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.cpp b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.cpp
new file mode 100644
index 0000000000..b6c8c55fa4
--- /dev/null
+++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+namespace AzFramework
+{
+ void AcceptMatchRequest::Reflect(AZ::ReflectContext* context)
+ {
+ if (auto serializeContext = azrtti_cast(context))
+ {
+ serializeContext->Class()
+ ->Version(0)
+ ->Field("acceptMatch", &AcceptMatchRequest::m_acceptMatch)
+ ->Field("playerIds", &AcceptMatchRequest::m_playerIds)
+ ->Field("ticketId", &AcceptMatchRequest::m_ticketId);
+
+ if (AZ::EditContext* editContext = serializeContext->GetEditContext())
+ {
+ editContext->Class("AcceptMatchRequest", "The container for AcceptMatch request parameters")
+ ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+ ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
+ ->DataElement(AZ::Edit::UIHandlers::Default, &AcceptMatchRequest::m_acceptMatch, "AcceptMatch",
+ "Player response to accept or reject match")
+ ->DataElement(AZ::Edit::UIHandlers::Default, &AcceptMatchRequest::m_playerIds, "PlayerIds",
+ "A list of unique identifiers for players delivering the response")
+ ->DataElement(AZ::Edit::UIHandlers::Default, &AcceptMatchRequest::m_ticketId, "TicketId",
+ "A unique identifier for a matchmaking ticket");
+ }
+ }
+ }
+
+ void StartMatchmakingRequest::Reflect(AZ::ReflectContext* context)
+ {
+ if (auto serializeContext = azrtti_cast(context))
+ {
+ serializeContext->Class()
+ ->Version(0)
+ ->Field("ticketId", &StartMatchmakingRequest::m_ticketId);
+
+ if (AZ::EditContext* editContext = serializeContext->GetEditContext())
+ {
+ editContext->Class("StartMatchmakingRequest", "The container for StartMatchmaking request parameters")
+ ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+ ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
+ ->DataElement(AZ::Edit::UIHandlers::Default, &StartMatchmakingRequest::m_ticketId, "TicketId",
+ "A unique identifier for a matchmaking ticket");
+ }
+ }
+ }
+
+ void StopMatchmakingRequest::Reflect(AZ::ReflectContext* context)
+ {
+ if (auto serializeContext = azrtti_cast(context))
+ {
+ serializeContext->Class()
+ ->Version(0)
+ ->Field("ticketId", &StopMatchmakingRequest::m_ticketId);
+
+ if (AZ::EditContext* editContext = serializeContext->GetEditContext())
+ {
+ editContext->Class("StopMatchmakingRequest", "The container for StopMatchmaking request parameters")
+ ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+ ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
+ ->DataElement(AZ::Edit::UIHandlers::Default, &StopMatchmakingRequest::m_ticketId, "TicketId",
+ "A unique identifier for a matchmaking ticket");
+ }
+ }
+ }
+} // namespace AzFramework
diff --git a/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h
new file mode 100644
index 0000000000..58e335696f
--- /dev/null
+++ b/Code/Framework/AzFramework/AzFramework/Matchmaking/MatchmakingRequests.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace AZ
+{
+ class ReflectContext;
+}
+
+namespace AzFramework
+{
+ //! AcceptMatchRequest
+ //! The container for AcceptMatch request parameters.
+ struct AcceptMatchRequest
+ {
+ AZ_RTTI(AcceptMatchRequest, "{AD289D76-CEE2-424F-847E-E62AA83B7D79}");
+ static void Reflect(AZ::ReflectContext* context);
+
+ AcceptMatchRequest() = default;
+ virtual ~AcceptMatchRequest() = default;
+
+ // Player response to accept or reject match
+ bool m_acceptMatch;
+ // A list of unique identifiers for players delivering the response
+ AZStd::vector m_playerIds;
+ // A unique identifier for a matchmaking ticket
+ AZStd::string m_ticketId;
+ };
+
+ //! StartMatchmakingRequest
+ //! The container for StartMatchmaking request parameters.
+ struct StartMatchmakingRequest
+ {
+ AZ_RTTI(StartMatchmakingRequest, "{70B47776-E8E7-4993-BEC3-5CAEC3D48E47}");
+ static void Reflect(AZ::ReflectContext* context);
+
+ StartMatchmakingRequest() = default;
+ virtual ~StartMatchmakingRequest() = default;
+
+ // A unique identifier for a matchmaking ticket
+ AZStd::string m_ticketId;
+ };
+
+ //! StopMatchmakingRequest
+ //! The container for StopMatchmaking request parameters.
+ struct StopMatchmakingRequest
+ {
+ AZ_RTTI(StopMatchmakingRequest, "{6132E293-65EF-4DC2-A8A0-00269697229D}");
+ static void Reflect(AZ::ReflectContext* context);
+
+ StopMatchmakingRequest() = default;
+ virtual ~StopMatchmakingRequest() = default;
+
+ // A unique identifier for a matchmaking ticket
+ AZStd::string m_ticketId;
+ };
+} // namespace AzFramework
diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h
index 0323686442..bdc3fe1444 100644
--- a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h
+++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h
@@ -10,98 +10,11 @@
#include
#include
-#include
#include
-#include
+#include
namespace AzFramework
{
- struct SessionConfig;
-
- //! CreateSessionRequest
- //! The container for CreateSession request parameters.
- struct CreateSessionRequest
- {
- AZ_RTTI(CreateSessionRequest, "{E39C2A45-89C9-4CFB-B337-9734DC798930}");
- static void Reflect(AZ::ReflectContext* context);
-
- CreateSessionRequest() = default;
- virtual ~CreateSessionRequest() = default;
-
- // A unique identifier for a player or entity creating the session.
- AZStd::string m_creatorId;
-
- // A collection of custom properties for a session.
- AZStd::unordered_map m_sessionProperties;
-
- // A descriptive label that is associated with a session.
- AZStd::string m_sessionName;
-
- // The maximum number of players that can be connected simultaneously to the session.
- uint64_t m_maxPlayer = 0;
- };
-
- //! SearchSessionsRequest
- //! The container for SearchSessions request parameters.
- struct SearchSessionsRequest
- {
- AZ_RTTI(SearchSessionsRequest, "{B49207A8-8549-4ADB-B7D9-D7A4932F9B4B}");
- static void Reflect(AZ::ReflectContext* context);
-
- SearchSessionsRequest() = default;
- virtual ~SearchSessionsRequest() = default;
-
- // String containing the search criteria for the session search. If no filter expression is included, the request returns results
- // for all active sessions.
- AZStd::string m_filterExpression;
-
- // Instructions on how to sort the search results. If no sort expression is included, the request returns results in random order.
- AZStd::string m_sortExpression;
-
- // The maximum number of results to return.
- uint8_t m_maxResult = 0;
-
- // A token that indicates the start of the next sequential page of results.
- AZStd::string m_nextToken;
- };
-
- //! SearchSessionsResponse
- //! The container for SearchSession request results.
- struct SearchSessionsResponse
- {
- AZ_RTTI(SearchSessionsResponse, "{F93DE7DC-D381-4E08-8A3B-0B08F7C38714}");
- static void Reflect(AZ::ReflectContext* context);
-
- SearchSessionsResponse() = default;
- virtual ~SearchSessionsResponse() = default;
-
- // A collection of sessions that match the search criteria and sorted in specific order.
- AZStd::vector m_sessionConfigs;
-
- // A token that indicates the start of the next sequential page of results.
- AZStd::string m_nextToken;
- };
-
- //! JoinSessionRequest
- //! The container for JoinSession request parameters.
- struct JoinSessionRequest
- {
- AZ_RTTI(JoinSessionRequest, "{519769E8-3CDE-4385-A0D7-24DBB3685657}");
- static void Reflect(AZ::ReflectContext* context);
-
- JoinSessionRequest() = default;
- virtual ~JoinSessionRequest() = default;
-
- // A unique identifier for the session.
- AZStd::string m_sessionId;
-
- // A unique identifier for a player. Player IDs are developer-defined.
- AZStd::string m_playerId;
-
- // Developer-defined information related to a player.
- AZStd::string m_playerData;
- };
-
//! ISessionRequests
//! Pure virtual session interface class to abstract the details of session handling from application code.
class ISessionRequests
diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.cpp b/Code/Framework/AzFramework/AzFramework/Session/SessionRequests.cpp
similarity index 98%
rename from Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.cpp
rename to Code/Framework/AzFramework/AzFramework/Session/SessionRequests.cpp
index 5536d9a47d..a7ffaa2491 100644
--- a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Session/SessionRequests.cpp
@@ -6,9 +6,10 @@
*
*/
+#include
#include
#include
-#include
+#include
#include
namespace AzFramework
diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionRequests.h b/Code/Framework/AzFramework/AzFramework/Session/SessionRequests.h
new file mode 100644
index 0000000000..1ae018e1ef
--- /dev/null
+++ b/Code/Framework/AzFramework/AzFramework/Session/SessionRequests.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+#include
+#include
+
+namespace AZ
+{
+ class ReflectContext;
+}
+
+namespace AzFramework
+{
+ struct SessionConfig;
+
+ //! CreateSessionRequest
+ //! The container for CreateSession request parameters.
+ struct CreateSessionRequest
+ {
+ AZ_RTTI(CreateSessionRequest, "{E39C2A45-89C9-4CFB-B337-9734DC798930}");
+ static void Reflect(AZ::ReflectContext* context);
+
+ CreateSessionRequest() = default;
+ virtual ~CreateSessionRequest() = default;
+
+ // A unique identifier for a player or entity creating the session.
+ AZStd::string m_creatorId;
+
+ // A collection of custom properties for a session.
+ AZStd::unordered_map m_sessionProperties;
+
+ // A descriptive label that is associated with a session.
+ AZStd::string m_sessionName;
+
+ // The maximum number of players that can be connected simultaneously to the session.
+ uint64_t m_maxPlayer = 0;
+ };
+
+ //! SearchSessionsRequest
+ //! The container for SearchSessions request parameters.
+ struct SearchSessionsRequest
+ {
+ AZ_RTTI(SearchSessionsRequest, "{B49207A8-8549-4ADB-B7D9-D7A4932F9B4B}");
+ static void Reflect(AZ::ReflectContext* context);
+
+ SearchSessionsRequest() = default;
+ virtual ~SearchSessionsRequest() = default;
+
+ // String containing the search criteria for the session search. If no filter expression is included, the request returns results
+ // for all active sessions.
+ AZStd::string m_filterExpression;
+
+ // Instructions on how to sort the search results. If no sort expression is included, the request returns results in random order.
+ AZStd::string m_sortExpression;
+
+ // The maximum number of results to return.
+ uint8_t m_maxResult = 0;
+
+ // A token that indicates the start of the next sequential page of results.
+ AZStd::string m_nextToken;
+ };
+
+ //! SearchSessionsResponse
+ //! The container for SearchSession request results.
+ struct SearchSessionsResponse
+ {
+ AZ_RTTI(SearchSessionsResponse, "{F93DE7DC-D381-4E08-8A3B-0B08F7C38714}");
+ static void Reflect(AZ::ReflectContext* context);
+
+ SearchSessionsResponse() = default;
+ virtual ~SearchSessionsResponse() = default;
+
+ // A collection of sessions that match the search criteria and sorted in specific order.
+ AZStd::vector m_sessionConfigs;
+
+ // A token that indicates the start of the next sequential page of results.
+ AZStd::string m_nextToken;
+ };
+
+ //! JoinSessionRequest
+ //! The container for JoinSession request parameters.
+ struct JoinSessionRequest
+ {
+ AZ_RTTI(JoinSessionRequest, "{519769E8-3CDE-4385-A0D7-24DBB3685657}");
+ static void Reflect(AZ::ReflectContext* context);
+
+ JoinSessionRequest() = default;
+ virtual ~JoinSessionRequest() = default;
+
+ // A unique identifier for the session.
+ AZStd::string m_sessionId;
+
+ // A unique identifier for a player. Player IDs are developer-defined.
+ AZStd::string m_playerId;
+
+ // Developer-defined information related to a player.
+ AZStd::string m_playerData;
+ };
+} // namespace AzFramework
diff --git a/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h b/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h
index 92eb28a110..3f6a8f0960 100644
--- a/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h
+++ b/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h
@@ -8,6 +8,7 @@
#pragma once
#include