Merge branch 'development' of https://github.com/o3de/o3de into Network/olexl/hierarchy_optimizations_cr

monroegm-disable-blank-issue-2
AMZN-Olex 4 years ago
commit 065e0635d4

@ -125,17 +125,6 @@
<Class name="AZStd::string" field="Comment" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
</Class>
<Class name="AZStd::pair" field="element" type="{941B5626-118F-55BC-925E-6E416A7520E4}">
<Class name="AZStd::string" field="value1" value="@devroot@/*.waf_files" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="FileTagData" field="value2" version="2" type="{5F66E43B-548B-4AA8-8CD8-F6924F6031E6}">
<Class name="unsigned char" field="FilePatternType" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="AZStd::set" field="FileTags" type="{166F208E-DE97-53FE-B349-BDD9FE9B8693}">
<Class name="AZStd::string" field="element" value="ignore" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="element" value="productdependency" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
<Class name="AZStd::string" field="Comment" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
</Class>
<Class name="AZStd::pair" field="element" type="{941B5626-118F-55BC-925E-6E416A7520E4}">
<Class name="AZStd::string" field="value1" value="*/editor/leveltemplates.xml" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="FileTagData" field="value2" version="2" type="{5F66E43B-548B-4AA8-8CD8-F6924F6031E6}">
@ -207,27 +196,6 @@
<Class name="AZStd::string" field="Comment" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
</Class>
<Class name="AZStd::pair" field="element" type="{941B5626-118F-55BC-925E-6E416A7520E4}">
<Class name="AZStd::string" field="value1" value="@devroot@/*wscript" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="FileTagData" field="value2" version="2" type="{5F66E43B-548B-4AA8-8CD8-F6924F6031E6}">
<Class name="unsigned char" field="FilePatternType" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="AZStd::set" field="FileTags" type="{166F208E-DE97-53FE-B349-BDD9FE9B8693}">
<Class name="AZStd::string" field="element" value="ignore" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="element" value="productdependency" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
<Class name="AZStd::string" field="Comment" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
</Class>
<Class name="AZStd::pair" field="element" type="{941B5626-118F-55BC-925E-6E416A7520E4}">
<Class name="AZStd::string" field="value1" value=".*/assetprocessorplatformconfig.ini" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="FileTagData" field="value2" version="2" type="{5F66E43B-548B-4AA8-8CD8-F6924F6031E6}">
<Class name="unsigned char" field="FilePatternType" value="2" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="AZStd::set" field="FileTags" type="{166F208E-DE97-53FE-B349-BDD9FE9B8693}">
<Class name="AZStd::string" field="element" value="editoronly" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
<Class name="AZStd::string" field="Comment" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
</Class>
<Class name="AZStd::pair" field="element" type="{941B5626-118F-55BC-925E-6E416A7520E4}">
<Class name="AZStd::string" field="value1" value=".*/gems?/?.*/gem.json" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="FileTagData" field="value2" version="2" type="{5F66E43B-548B-4AA8-8CD8-F6924F6031E6}">

@ -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"
]
}
}
}

@ -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

@ -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
)

@ -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()

@ -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.

@ -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)

@ -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

@ -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

@ -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:

@ -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

@ -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

@ -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)

@ -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)

@ -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)

@ -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

@ -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

@ -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

@ -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"
]

@ -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):

@ -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

@ -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
pyRunFile @engroot@/Tests/hydra/LevelComponentCommands_test_case.py exit_when_done

@ -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
pyRunFile @engroot@/Tests/hydra/ViewportTitleDlgCommands_test_case.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)

@ -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))

@ -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

@ -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))

@ -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))

@ -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))

@ -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"])

@ -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():

@ -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"

@ -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"

@ -51,6 +51,14 @@
[
"Gems/UiBasics"
]
},
"Hair":
{
"SourcePaths":
[
"Gems/AtomTressFX/Assets",
"Gems/AtomTressFX/Assets/Passes"
]
}
}
}

@ -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"

@ -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"

@ -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"

@ -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"

@ -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"

@ -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"

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:827a63985273229050bf4f63030bcc666f045091fe81cf8157a9ca23b40074b6
size 3214

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d8d24963e6e8765205bc79cbe2304fc39f1245ee75249e2834a71c96c3cab824
size 22700

@ -0,0 +1,8 @@
{
"values": [
{
"$type": "ScriptProcessorRule",
"scriptFilename": "Editor/Scripts/scene_mesh_to_prefab.py"
}
]
}

@ -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

@ -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<int>(folder.Native().size())));
folderTreeItem->AddChild(QString::fromUtf8(fileNameWithoutExtension.c_str(),
static_cast<int>(fileNameWithoutExtension.Native().size())), path, eTreeImage_File);
}
}

@ -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);

@ -33,6 +33,7 @@ public:
protected:
void SetupEnvironment() override
{
AttachEditorCoreAZEnvironment(AZ::Environment::GetInstance());
m_allocatorScope.ActivateAllocators();
m_cryPak = new NiceMock<CryPakMock>();
@ -49,6 +50,7 @@ protected:
{
delete m_cryPak;
m_allocatorScope.DeactivateAllocators();
DetachEditorCoreAZEnvironment();
}
private:

@ -5,22 +5,29 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include <AzTest/AzTest.h>
#include "Util/PathUtil.h"
#include <CrySystemBus.h>
TEST(PathUtil, GamePathToFullPath_DoesNotBufferOverflow)
#include <AzCore/UnitTest/TestTypes.h>
#include <Util/PathUtil.h>
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);
}
}

@ -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<AZ::SliceAsset>() && info.m_relativePath[0] != '@')
{
sliceAssetInfos.push_back(info);

@ -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)
{

@ -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;

@ -14,6 +14,8 @@
// Editor
#include "CryEdit.h"
#include <AzCore/Utils/Utils.h>
//////////////////////////////////////////////////////////////////////////
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<SFileChangeCallback>::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));
}
}
}

@ -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<AzFramework::PanCameraInput>(
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<AzFramework::TranslateCameraInput>(
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<AzFramework::ScrollTranslationCameraInput>();
m_firstPersonScrollCamera = AZStd::make_shared<AzFramework::LookScrollTranslationCameraInput>();
m_firstPersonScrollCamera->m_scrollSpeedFn = []
{
return SandboxEditor::CameraScrollSpeed();
};
m_pivotCamera = AZStd::make_shared<AzFramework::PivotCameraInput>(SandboxEditor::CameraPivotChannelId());
const auto pivotFn = []
{
// use the manipulator transform as the pivot point
AZStd::optional<AZ::Transform> 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<AZ::Transform> 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<AzFramework::FocusCameraInput>(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusLook);
m_firstPersonFocusCamera->SetPivotFn(pivotFn);
m_orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>(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<AzFramework::RotateCameraInput>(SandboxEditor::CameraPivotLookChannelId());
m_orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(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<AzFramework::TranslateCameraInput>(
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslateOffset);
m_orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
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<AzFramework::PivotDollyScrollCameraInput>();
m_orbitDollyScrollCamera = AZStd::make_shared<AzFramework::OrbitDollyScrollCameraInput>();
m_pivotDollyScrollCamera->m_scrollSpeedFn = []
m_orbitDollyScrollCamera->m_scrollSpeedFn = []
{
return SandboxEditor::CameraScrollSpeed();
};
m_pivotDollyMoveCamera = AZStd::make_shared<AzFramework::PivotDollyMotionCameraInput>(SandboxEditor::CameraPivotDollyChannelId());
m_orbitDollyMoveCamera = AZStd::make_shared<AzFramework::OrbitDollyMotionCameraInput>(SandboxEditor::CameraOrbitDollyChannelId());
m_pivotDollyMoveCamera->m_motionSpeedFn = []
m_orbitDollyMoveCamera->m_motionSpeedFn = []
{
return SandboxEditor::CameraDollyMotionSpeed();
};
m_pivotPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(
SandboxEditor::CameraPivotPanChannelId(), AzFramework::LookPan, AzFramework::TranslateOffset);
m_orbitPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(
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<AzFramework::FocusCameraInput>(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)

@ -41,13 +41,15 @@ namespace SandboxEditor
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_firstPersonRotateCamera;
AZStd::shared_ptr<AzFramework::PanCameraInput> m_firstPersonPanCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_firstPersonTranslateCamera;
AZStd::shared_ptr<AzFramework::ScrollTranslationCameraInput> m_firstPersonScrollCamera;
AZStd::shared_ptr<AzFramework::PivotCameraInput> m_pivotCamera;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_pivotRotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_pivotTranslateCamera;
AZStd::shared_ptr<AzFramework::PivotDollyScrollCameraInput> m_pivotDollyScrollCamera;
AZStd::shared_ptr<AzFramework::PivotDollyMotionCameraInput> m_pivotDollyMoveCamera;
AZStd::shared_ptr<AzFramework::PanCameraInput> m_pivotPanCamera;
AZStd::shared_ptr<AzFramework::LookScrollTranslationCameraInput> m_firstPersonScrollCamera;
AZStd::shared_ptr<AzFramework::FocusCameraInput> m_firstPersonFocusCamera;
AZStd::shared_ptr<AzFramework::OrbitCameraInput> m_orbitCamera;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_orbitRotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_orbitTranslateCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyScrollCameraInput> m_orbitDollyScrollCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyMotionCameraInput> m_orbitDollyMoveCamera;
AZStd::shared_ptr<AzFramework::PanCameraInput> m_orbitPanCamera;
AZStd::shared_ptr<AzFramework::FocusCameraInput> m_orbitFocusCamera;
AzFramework::ViewportId m_viewportId;
};

@ -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<CEditorPreferencesPage_ViewportCamera>()
->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<CEditorPreferencesPage_ViewportCamera>("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();
}

@ -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;

@ -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<typename T>
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

@ -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

@ -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()))
{

@ -30,6 +30,7 @@ class CXTPDockingPaneLayout; // Needed for settings.h
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/functional.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzCore/std/string/conversions.h>
#include <Util/PathUtil.h>
@ -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<int>(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. <a href=\"%s\">Learn more...</a>",
@ -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. <a href=\"%s\">Learn more...</a>", s_documentationWebAddress);
@ -282,12 +250,12 @@ void AssetImporterWindow::OpenFileInternal(const AZStd::string& filePath)
auto asyncLoadHandler = AZStd::make_shared<AZ::SceneAPI::SceneUI::AsyncOperationProcessingHandler>(
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<int>(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

@ -7,9 +7,11 @@
*/
#include <AzCore/Debug/Profiler.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Debug/TraceContext.h>
@ -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<SceneAPI::Containers::Scene> scene = sceneIt->second.lock();
@ -98,14 +93,14 @@ namespace AZ
}
AZStd::shared_ptr<SceneAPI::Containers::Scene> 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;
}

@ -39,7 +39,6 @@ ly_add_target(
EDITOR_COMMON_IMPORTS
BUILD_DEPENDENCIES
PRIVATE
3rdParty::zlib
3rdParty::Qt::Core
3rdParty::Qt::Widgets
Legacy::CryCommon

@ -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(

@ -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;

@ -27,37 +27,31 @@ namespace
}
template<typename StringType>
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<typename StringType>
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<void*>(func);
}
AZStd::string GetDevRoot()
AZStd::string GetEngineRoot()
{
return GetAbsoluteDevRoot<AZStd::string>();
return GetAbsoluteEngineRoot<AZStd::string>();
}
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>();
QString defaultPath = GetAbsoluteEngineRoot<QString>();
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<QString>(), ::GetProjectName<QString>());
QString defaultPath = QStringLiteral("%1Code%2/Resources/").arg(GetAbsoluteEngineRoot<QString>(), ::GetProjectName<QString>());
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

@ -17,7 +17,7 @@
namespace ProjectSettingsTool
{
void* ConvertFunctorToVoid(AZStd::pair<QValidator::State, const QString>(*func)(const QString&));
AZStd::string GetDevRoot();
AZStd::string GetEngineRoot();
AZStd::string GetProjectRoot();
AZStd::string GetProjectName();

@ -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());
}

@ -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<int>(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);

@ -11,9 +11,9 @@
#include "PathUtil.h"
#include <AzCore/IO/SystemFile.h> // for AZ_MAX_PATH_LEN
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/Utils/Utils.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h> // for ebus events
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzCore/std/string/conversions.h>
#include <AzFramework/IO/LocalFileIO.h>
@ -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<int>(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<int>(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{};
}
}

@ -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)

@ -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<AssetData>& asset)
void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback)
{
m_assetFixUpCallback = AZStd::move(assetFixUpCallback);
}
void SerializedAssetTracker::FixUpAsset(Asset<AssetData>& asset)
{
if (m_assetFixUpCallback)
{
m_assetFixUpCallback(asset);
}
}
void SerializedAssetTracker::AddAsset(Asset<AssetData> asset)
{
m_serializedAssets.emplace_back(asset);
}
@ -199,5 +213,6 @@ namespace AZ
{
return m_serializedAssets;
}
} // namespace Data
} // namespace AZ

@ -39,13 +39,18 @@ namespace AZ
{
public:
AZ_RTTI(SerializedAssetTracker, "{1E067091-8C0A-44B1-A455-6E97663F6963}");
using AssetFixUp = AZStd::function<void(Asset<AssetData>& asset)>;
void AddAsset(Asset<AssetData>& asset);
void SetAssetFixUp(AssetFixUp assetFixUpCallback);
void FixUpAsset(Asset<AssetData>& asset);
void AddAsset(Asset<AssetData> asset);
AZStd::vector<Asset<AssetData>>& GetTrackedAssets();
const AZStd::vector<Asset<AssetData>>& GetTrackedAssets() const;
private:
AZStd::vector<Asset<AssetData>> m_serializedAssets;
AssetFixUp m_assetFixUpCallback;
};
} // namespace Data
} // namespace AZ

@ -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);
}

@ -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<bool(const char*)> 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

@ -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.

@ -98,6 +98,11 @@ namespace AZ::IO
//! made from the internal string
constexpr AZStd::fixed_string<MaxPathLength> 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<MaxPathLength> 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 <typename StringType>
friend class BasicPath;
friend struct AZStd::hash<PathView>;
struct PathIterable;
template <typename PathResultType>
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<MaxPathLength> 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<MaxPathLength> 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)

@ -240,6 +240,14 @@ namespace AZ::IO
return AZStd::fixed_string<MaxPathLength>(m_path.begin(), m_path.end());
}
// as_posix
constexpr AZStd::fixed_string<MaxPathLength> PathView::FixedMaxPathStringAsPosix() const noexcept
{
AZStd::fixed_string<MaxPathLength> 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 <typename PathResultType>
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 <typename StringType>
constexpr BasicPath<StringType>::BasicPath(const PathView& other)
constexpr BasicPath<StringType>::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 <typename StringType>
constexpr BasicPath<StringType>::operator PathView() const noexcept
{
@ -733,7 +736,7 @@ namespace AZ::IO
}
template <typename StringType>
constexpr auto BasicPath<StringType>::operator=(const PathView& other) -> BasicPath&
constexpr auto BasicPath<StringType>::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 <typename StringType>
constexpr auto BasicPath<StringType>::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<MaxPathLength>(m_path.begin(), m_path.end());
}
// as_posix
// Returns a copy of the path with the path separators converted to PosixPathSeparator
template <typename StringType>
AZStd::string BasicPath<StringType>::StringAsPosix() const
{
AZStd::string resultPath(m_path.begin(), m_path.end());
AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
return resultPath;
}
template <typename StringType>
constexpr AZStd::fixed_string<MaxPathLength> BasicPath<StringType>::FixedMaxPathStringAsPosix() const noexcept
{
AZStd::fixed_string<MaxPathLength> resultPath(m_path.begin(), m_path.end());
AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
return resultPath;
}
template <typename StringType>
constexpr void BasicPath<StringType>::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<StringType>::LexicallyRelative(const PathView& base) const -> BasicPath
{
BasicPath pathResult(m_preferred_separator);
static_cast<PathView>(*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;
}
}

@ -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{};
};

@ -18,6 +18,14 @@
#include <AzCore/std/parallel/thread.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Threading/ThreadUtils.h>
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<unsigned int>(desc.m_workerThreads.capacity()), AZStd::thread::hardware_concurrency());
uint32_t scaledHardwareThreads = Threading::CalcNumWorkerThreads(cl_jobThreadsConcurrencyRatio, cl_jobThreadsMinNumber, cl_jobThreadsNumReserved);
numberOfWorkerThreads = AZ::GetMin(static_cast<unsigned int>(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)

@ -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)

@ -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)

@ -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); }

@ -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;

@ -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<TaskExecutor*>(s_executorName, executor);
}

@ -11,9 +11,15 @@
#include <AzCore/Task/TaskGraphSystemComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Threading/ThreadUtils.h>
// 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<TaskGraphActiveInterface>::Get() == nullptr)
{
Interface<TaskGraphActiveInterface>::Register(this);
m_taskExecutor = aznew TaskExecutor();
Interface<TaskGraphActiveInterface>::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);
}
}

@ -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 <AzCore/Threading/ThreadUtils.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/Math/MathUtils.h>
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<uint32_t>(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<float>(workerThreadsRatio, 0.0f, 1.0f) * static_cast<float>(maxWorkerThreads);
const uint32_t requestedWorkerThreadsRounded = AZStd::lround(requestedWorkerThreads);
const uint32_t numWorkerThreads = AZ::GetMax<uint32_t>(minNumWorkerThreads, requestedWorkerThreadsRounded);
return numWorkerThreads;
}
};

@ -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 <AzCore/base.h>
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);
};

@ -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<AZ::u64>(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));

@ -51,6 +51,20 @@ namespace AZ::Utils
return executableDirectory;
}
AZStd::optional<AZ::IO::FixedMaxPathString> 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<char>::length(absolutePath.data()));
return srcPath;
}
return AZStd::nullopt;
}
AZ::IO::FixedMaxPathString GetEngineManifestPath()
{
AZ::IO::FixedMaxPath o3deManifestPath = GetO3deManifestDirectory();

@ -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<AZ::IO::FixedMaxPathString> 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<void, AZStd::string> WriteFile(AZStd::string_view content, AZStd::string_view filePath);

@ -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

@ -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}

@ -60,23 +60,34 @@ namespace AZ
return writeStorage ? AZStd::make_optional<AZ::IO::FixedMaxPathString>(writeStorage) : AZStd::nullopt;
}
AZStd::optional<AZ::IO::FixedMaxPathString> 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<char>::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();
}
}
}

@ -47,23 +47,32 @@ namespace AZ
AZ::IO::FixedMaxPath path{pass->pw_dir};
return path.Native();
}
return {};
}
AZStd::optional<AZ::IO::FixedMaxPathString> 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<char>::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

@ -59,6 +59,10 @@ namespace AZStd
{
priority = desc->m_priority;
}
else
{
priority = SCHED_OTHER;
}
if (desc->m_name)
{
name = desc->m_name;

@ -67,19 +67,12 @@ namespace AZ
return AZStd::nullopt;
}
AZStd::optional<AZ::IO::FixedMaxPathString> 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<char>::length(absolutePath.data()));
if (result)
{
return absolutePath;
}
return AZStd::nullopt;
char* result = _fullpath(absolutePath, path, maxLength);
return result != nullptr;
}
}
}

@ -426,6 +426,10 @@ public:
return nullptr;
}
void SetDeprecatedAlias(AZStd::string_view, AZStd::string_view) override
{
}
void ClearAlias(const char* ) override { }
AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64) const override

@ -698,7 +698,7 @@ AZ_POP_DISABLE_WARNING
using PathViewLexicallyProximateFixture = PathLexicallyFixture<PathViewLexicallyProximateParams>;
TEST_P(PathViewLexicallyProximateFixture, LexicallyProximate_ReturnsRelativePathIfNotEmptyOrTestPathIfNot)
TEST_P(PathViewLexicallyProximateFixture, LexicallyProximate_ReturnsRelativePathIfNotEmptyOrTestPath)
{
const auto& testParams = GetParam();
AZ::IO::PathView testPath(testParams.m_testPathString, testParams.m_preferredSeparator);

@ -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);
}
}

@ -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<AliasPair> 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);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save