merging latest dev + resolving 1 conflict

Signed-off-by: Gene Walters <genewalt@amazon.com>
monroegm-disable-blank-issue-2
Gene Walters 4 years ago
commit d2da35aec3

File diff suppressed because it is too large Load Diff

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

@ -1,5 +1,4 @@
<EngineDependencies versionnumber="1.0.0">
<Dependency path="libs/particles/preloadlibs.txt" optional="true" />
<Dependency path="libs/gameaudio/wwise/*.xml" optional="false" />
<Dependency path=":libs/gameaudio/wwise/levels/default_controls.xml" optional="false" />
</EngineDependencies>

@ -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,10 +52,7 @@ set(ENABLED_GEMS
AWSCore
AWSClientAuth
AWSMetrics
PrefabBuilder
AudioSystem
Multiplayer
)

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

@ -226,9 +226,9 @@ class TestMaterialEditor(object):
self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args):
"""
Tests each valid RHI option (Null RHI excluded) can be launched with the MaterialEditor.
Checks for the "Finished loading viewport configurtions." success message post lounch.
Checks for the "Finished loading viewport configurations." success message post launch.
"""
expected_lines = ["Finished loading viewport configurtions."]
expected_lines = ["Finished loading viewport configurations."]
unexpected_lines = [
# "Trace::Assert",
# "Trace::Error",
@ -241,7 +241,7 @@ class TestMaterialEditor(object):
generic_launcher,
editor_script="",
run_python="--runpython",
timeout=30,
timeout=60,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=False,

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

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

@ -25,28 +25,39 @@ import prefab.Prefab_Test_Utils as prefab_test_utils
# This is a helper class which contains some of the useful information about a prefab instance.
class PrefabInstance:
def __init__(self, name: str=None, prefab_file_name: str=None, container_entity: EditorEntity=EntityId()):
self.name = name
def __init__(self, prefab_file_name: str=None, container_entity: EditorEntity=EntityId()):
self.prefab_file_name: str = prefab_file_name
self.container_entity: EditorEntity = container_entity
def __eq__(self, other):
return other and self.container_entity.id == other.container_entity.id
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash(self.container_entity.id)
"""
See if this instance is valid to be used with other prefab operations.
:return: Whether the target instance is valid or not.
"""
def is_valid() -> bool:
return self.container_entity.id.IsValid() and self.name is not None and self.prefab_file_name in Prefab.existing_prefabs
return self.container_entity.id.IsValid() and self.prefab_file_name in Prefab.existing_prefabs
"""
Reparent this instance to target parent entity.
The function will also check pop up dialog ui in editor to see if there's prefab cyclical dependency error while reparenting prefabs.
:param parent_entity_id: The id of the entity this instance should be a child of in the transform hierarchy next.
"""
async def ui_reparent_prefab_instance(self, parent_entity_id: EntityId):
container_entity_name = self.container_entity.get_name()
current_children_entity_ids_having_prefab_name = prefab_test_utils.get_children_ids_by_name(parent_entity_id, container_entity_name)
Report.info(f'current_children_entity_ids_having_prefab_name: {current_children_entity_ids_having_prefab_name}')
container_entity_id_before_reparent = self.container_entity.id
original_parent = EditorEntity(self.container_entity.get_parent_id())
original_parent_before_reparent_children_ids = set(original_parent.get_children_ids())
new_parent = EditorEntity(parent_entity_id)
new_parent_before_reparent_children_ids = set(new_parent.get_children_ids())
pyside_utils.run_soon(lambda: self.container_entity.set_parent_entity(parent_entity_id))
pyside_utils.run_soon(lambda: prefab_test_utils.wait_for_propagation())
@ -60,18 +71,23 @@ class PrefabInstance:
except pyside_utils.EventLoopTimeoutException:
pass
updated_children_entity_ids_having_prefab_name = prefab_test_utils.get_children_ids_by_name(parent_entity_id, container_entity_name)
Report.info(f'updated_children_entity_ids_having_prefab_name: {updated_children_entity_ids_having_prefab_name}')
new_child_with_reparented_prefab_name_added = len(updated_children_entity_ids_having_prefab_name) == len(current_children_entity_ids_having_prefab_name) + 1
assert new_child_with_reparented_prefab_name_added, "No entity with reparented prefab name become a child of target parent entity"
original_parent_after_reparent_children_ids = set(original_parent.get_children_ids())
assert len(original_parent_after_reparent_children_ids) == len(original_parent_before_reparent_children_ids) - 1, \
"The children count of the Prefab Instance's original parent should be decreased by 1."
assert not container_entity_id_before_reparent in original_parent_after_reparent_children_ids, \
"This Prefab Instance is still a child entity of its original parent entity."
new_parent_after_reparent_children_ids = set(new_parent.get_children_ids())
assert len(new_parent_after_reparent_children_ids) == len(new_parent_before_reparent_children_ids) + 1, \
"The children count of the Prefab Instance's new parent should be increased by 1."
updated_container_entity_id = set(updated_children_entity_ids_having_prefab_name).difference(current_children_entity_ids_having_prefab_name).pop()
updated_container_entity = EditorEntity(updated_container_entity_id)
updated_container_entity_parent_id = updated_container_entity.get_parent_id()
has_correct_parent = updated_container_entity_parent_id.ToString() == parent_entity_id.ToString()
assert has_correct_parent, "Prefab reparented is *not* under the expected parent entity"
container_entity_id_after_reparent = set(new_parent_after_reparent_children_ids).difference(new_parent_before_reparent_children_ids).pop()
reparented_container_entity = EditorEntity(container_entity_id_after_reparent)
reparented_container_entity_parent_id = reparented_container_entity.get_parent_id()
has_correct_parent = reparented_container_entity_parent_id.ToString() == parent_entity_id.ToString()
assert has_correct_parent, "Prefab Instance reparented is *not* under the expected parent entity"
self.container_entity = EditorEntity(updated_container_entity_id)
self.container_entity = reparented_container_entity
# This is a helper class which contains some of the useful information about a prefab template.
class Prefab:
@ -81,7 +97,7 @@ class Prefab:
def __init__(self, file_name: str):
self.file_name:str = file_name
self.file_path: str = prefab_test_utils.get_prefab_file_path(file_name)
self.instances: dict = {}
self.instances: set[PrefabInstance] = set()
"""
Check if a prefab is ready to be used to generate its instances.
@ -122,10 +138,10 @@ class Prefab:
:param entities: The entities that should form the new prefab (along with their descendants).
:param file_name: A unique file name of new prefab.
:param prefab_instance_name: A name for the very first instance generated while prefab creation. The default instance name is the same as file_name.
:return: An outcome object with an entityId of the new prefab's container entity; on failure, it comes with an error message detailing the cause of the error.
:return: Created Prefab object and the very first PrefabInstance object owned by the prefab.
"""
@classmethod
def create_prefab(cls, entities: list[EditorEntity], file_name: str, prefab_instance_name: str=None) -> Prefab:
def create_prefab(cls, entities: list[EditorEntity], file_name: str, prefab_instance_name: str=None) -> (Prefab, PrefabInstance):
assert not Prefab.is_prefab_loaded(file_name), f"Can't create Prefab '{file_name}' since the prefab already exists"
new_prefab = Prefab(file_name)
@ -133,18 +149,18 @@ class Prefab:
create_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'CreatePrefabInMemory', entity_ids, new_prefab.file_path)
assert create_prefab_result.IsSuccess(), f"Prefab operation 'CreatePrefab' failed. Error: {create_prefab_result.GetError()}"
container_entity = EditorEntity(create_prefab_result.GetValue())
container_entity_id = create_prefab_result.GetValue()
container_entity = EditorEntity(container_entity_id)
if prefab_instance_name:
container_entity.set_name(prefab_instance_name)
else:
prefab_instance_name = file_name
prefab_test_utils.wait_for_propagation()
container_entity_id = prefab_test_utils.find_entity_by_unique_name(prefab_instance_name)
new_prefab.instances[prefab_instance_name] = PrefabInstance(prefab_instance_name, file_name, EditorEntity(container_entity_id))
new_prefab_instance = PrefabInstance(file_name, EditorEntity(container_entity_id))
new_prefab.instances.add(new_prefab_instance)
Prefab.existing_prefabs[file_name] = new_prefab
return new_prefab
return new_prefab, new_prefab_instance
"""
Remove target prefab instances.
@ -152,22 +168,15 @@ class Prefab:
"""
@classmethod
def remove_prefabs(cls, prefab_instances: list[PrefabInstance]):
instances_to_remove_name_counts = Counter()
instances_removed_expected_name_counts = Counter()
entities_to_remove = [prefab_instance.container_entity for prefab_instance in prefab_instances]
while entities_to_remove:
entity = entities_to_remove.pop(-1)
entity_name = entity.get_name()
instances_to_remove_name_counts[entity_name] += 1
entity_ids_to_remove = []
entity_id_queue = [prefab_instance.container_entity for prefab_instance in prefab_instances]
while entity_id_queue:
entity = entity_id_queue.pop(0)
children_entity_ids = entity.get_children_ids()
for child_entity_id in children_entity_ids:
entities_to_remove.append(EditorEntity(child_entity_id))
entity_id_queue.append(EditorEntity(child_entity_id))
for entity_name, entity_count in instances_to_remove_name_counts.items():
entities = prefab_test_utils.find_entities_by_name(entity_name)
instances_removed_expected_name_counts[entity_name] = len(entities) - entity_count
entity_ids_to_remove.append(entity.id)
container_entity_ids = [prefab_instance.container_entity.id for prefab_instance in prefab_instances]
delete_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'DeleteEntitiesAndAllDescendantsInInstance', container_entity_ids)
@ -175,28 +184,24 @@ class Prefab:
prefab_test_utils.wait_for_propagation()
prefab_entities_deleted = True
for entity_name, expected_entity_count in instances_removed_expected_name_counts.items():
actual_entity_count = len(prefab_test_utils.find_entities_by_name(entity_name))
if actual_entity_count is not expected_entity_count:
prefab_entities_deleted = False
break
assert prefab_entities_deleted, "Not all entities and descendants in target prefabs are deleted."
entity_ids_after_delete = set(prefab_test_utils.get_all_entities())
for entity_id_removed in entity_ids_to_remove:
if entity_id_removed in entity_ids_after_delete:
assert prefab_entities_deleted, "Not all entities and descendants in target prefabs are deleted."
for instance in prefab_instances:
instance_deleted_prefab = Prefab.get_prefab(instance.prefab_file_name)
instance_deleted_prefab.instances.pop(instance.name)
instance_deleted_prefab.instances.remove(instance)
instance = PrefabInstance()
"""
Instantiate an instance of this prefab.
:param name: A name for newly instantiated prefab instance. The default instance name is the same as this prefab's file name.
:param parent_entity: The entity the prefab should be a child of in the transform hierarchy.
:param name: A name for newly instantiated prefab instance. The default instance name is the same as this prefab's file name.
:param prefab_position: The position in world space the prefab should be instantiated in.
:return: An outcome object with an entityId of the new prefab's container entity; on failure, it comes with an error message detailing the cause of the error.
:return: Instantiated PrefabInstance object owned by this prefab.
"""
def instantiate(self, name: str=None, parent_entity: EditorEntity=None, prefab_position: Vector3=Vector3()) -> PrefabInstance:
def instantiate(self, parent_entity: EditorEntity=None, name: str=None, prefab_position: Vector3=Vector3()) -> PrefabInstance:
parent_entity_id = parent_entity.id if parent_entity is not None else EntityId()
instantiate_prefab_result = prefab.PrefabPublicRequestBus(
@ -209,13 +214,13 @@ class Prefab:
if name:
container_entity.set_name(name)
else:
name = self.file_name
prefab_test_utils.wait_for_propagation()
container_entity_id = prefab_test_utils.find_entity_by_unique_name(name)
self.instances[name] = PrefabInstance(name, self.file_name, EditorEntity(container_entity_id))
new_prefab_instance = PrefabInstance(self.file_name, EditorEntity(container_entity_id))
assert not new_prefab_instance in self.instances, "This prefab instance is already existed before this instantiation."
self.instances.add(new_prefab_instance)
prefab_test_utils.check_entity_at_position(container_entity_id, prefab_position)
return container_entity_id
return new_prefab_instance

@ -22,11 +22,10 @@ def Prefab_BasicWorkflow_CreateAndDeletePrefab():
car_prefab_entities = [car_entity]
# Checks for prefab creation passed or not
car_prefab = Prefab.create_prefab(
_, car = Prefab.create_prefab(
car_prefab_entities, CAR_PREFAB_FILE_NAME)
# Checks for prefab deletion passed or not
car = car_prefab.instances[CAR_PREFAB_FILE_NAME]
Prefab.remove_prefabs([car])
if __name__ == "__main__":

@ -28,7 +28,7 @@ def Prefab_BasicWorkflow_CreateAndReparentPrefab():
car_prefab_entities = [car_entity]
# Checks for prefab creation passed or not
car_prefab = Prefab.create_prefab(
_, car = Prefab.create_prefab(
car_prefab_entities, CAR_PREFAB_FILE_NAME)
# Creates another new Entity at the root level
@ -36,12 +36,10 @@ def Prefab_BasicWorkflow_CreateAndReparentPrefab():
wheel_prefab_entities = [wheel_entity]
# Checks for wheel prefab creation passed or not
wheel_prefab = Prefab.create_prefab(
_, wheel = Prefab.create_prefab(
wheel_prefab_entities, WHEEL_PREFAB_FILE_NAME)
# Checks for prefab reparenting passed or not
car = car_prefab.instances[CAR_PREFAB_FILE_NAME]
wheel = wheel_prefab.instances[WHEEL_PREFAB_FILE_NAME]
await wheel.ui_reparent_prefab_instance(car.container_entity.id)
run_test()

@ -22,11 +22,11 @@ def Prefab_BasicWorkflow_InstantiatePrefab():
# Checks for prefab instantiation passed or not
test_prefab = Prefab.get_prefab(EXISTING_TEST_PREFAB_FILE_NAME)
instantiated_test_container_entity_id = test_prefab.instantiate(
test_instance = test_prefab.instantiate(
prefab_position=INSTANTIATED_TEST_PREFAB_POSITION)
prefab_test_utils.check_entity_children_count(
instantiated_test_container_entity_id,
test_instance.container_entity.id,
EXPECTED_TEST_PREFAB_CHILDREN_COUNT)
if __name__ == "__main__":

@ -29,20 +29,8 @@ def find_entities_by_name(entity_name):
searchFilter.names = [entity_name]
return entity.SearchBus(bus.Broadcast, 'SearchEntities', searchFilter)
def find_entity_by_unique_name(entity_name):
unique_name_entity_found_result = (
"Entity with a unique name found",
"Entity with a unique name *not* found")
entities = find_entities_by_name(entity_name)
unique_name_entity_found = len(entities) == 1
Report.result(unique_name_entity_found_result, unique_name_entity_found)
if unique_name_entity_found:
return entities[0]
else:
Report.info(f"{len(entities)} entities with name '{entity_name}' found")
return EntityId()
def get_all_entities():
return entity.SearchBus(bus.Broadcast, 'SearchEntities', entity.SearchFilter())
def check_entity_at_position(entity_id, expected_entity_position):
entity_at_expected_position_result = (

@ -122,6 +122,7 @@ class TestAutomation(TestAutomationBase):
from . import Debugger_HappyPath_TargetMultipleEntities as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.xfail(reason="Test fails to find expected lines, it needs to be fixed.")
def test_EditMenu_Default_UndoRedo(self, request, workspace, editor, launcher_platform, project):
from . import EditMenu_Default_UndoRedo as test_module
self._run_test(request, workspace, editor, test_module)
@ -181,6 +182,7 @@ class TestAutomation(TestAutomationBase):
from . import NodePalette_SearchText_Deletion as test_module
self._run_test(request, workspace, editor, test_module)
@pytest.mark.xfail(reason="Test fails to find expected lines, it needs to be fixed.")
def test_VariableManager_UnpinVariableType_Works(self, request, workspace, editor, launcher_platform):
from . import VariableManager_UnpinVariableType_Works as test_module
self._run_test(request, workspace, editor, test_module)

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

@ -7,7 +7,6 @@
*/
#pragma once
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/AssetEditor/AssetEditorBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>

@ -9,7 +9,6 @@
#if !defined(Q_MOC_RUN)
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/UserSettings/UserSettings.h>
#include <AzToolsFramework/AssetEditor/AssetEditorBus.h>
#include <QWidget>

@ -141,6 +141,10 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent)
m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget,
&AzAssetBrowser::SearchWidget::ClearTypeFilter);
connect(
this, &AzAssetBrowserWindow::SizeChangedSignal, m_ui->m_assetBrowserTableViewWidget,
&AzAssetBrowser::AssetBrowserTableView::UpdateSizeSlot);
m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main");
}
@ -164,6 +168,25 @@ QObject* AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(QObject* pa
return listener;
}
void AzAssetBrowserWindow::resizeEvent(QResizeEvent* resizeEvent)
{
// leftLayout is the parent of the tableView
// rightLayout is the parent of the preview window.
// Workaround: When docking windows this event keeps holding the old size of the widgets instead of the new one
// but the resizeEvent holds the new size of the whole widget
// So we have to save the proportions somehow
const QWidget* leftLayout = m_ui->m_leftLayout;
const QVBoxLayout* rightLayout = m_ui->m_rightLayout;
const float oldLeftLayoutWidth = aznumeric_cast<float>(leftLayout->geometry().width());
const float oldWidth = aznumeric_cast<float>(leftLayout->geometry().width() + rightLayout->geometry().width());
const float newWidth = oldLeftLayoutWidth * aznumeric_cast<float>(resizeEvent->size().width()) / oldWidth;
emit SizeChangedSignal(aznumeric_cast<int>(newWidth));
QWidget::resizeEvent(resizeEvent);
}
void AzAssetBrowserWindow::OnInitViewToggleButton()
{
CreateSwitchViewMenu();

@ -53,9 +53,17 @@ public:
static QObject* createListenerForShowAssetEditorEvent(QObject* parent);
Q_SIGNALS:
void SizeChangedSignal(int newWidth);
protected:
void resizeEvent(QResizeEvent* resizeEvent) override;
private:
void OnInitViewToggleButton();
void UpdateDisplayInfo();
protected slots:
void CreateSwitchViewMenu();
void SetExpandedAssetBrowserMode();

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

@ -26,6 +26,9 @@
#include <AzToolsFramework/UI/SearchWidget/SearchCriteriaWidget.hxx>
#include <AzToolsFramework/Editor/EditorSettingsAPIBus.h>
//AzCore
#include <AzCore/Component/ComponentApplicationBus.h>
// Editor
#include "Clipboard.h"

@ -12,7 +12,6 @@
#if !defined(Q_MOC_RUN)
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include "Include/EditorCoreAPI.h"
#include "ReflectedPropertyItem.h"

@ -10,10 +10,10 @@
#define CRYINCLUDE_EDITOR_UTILS_REFLECTEDVAR_H
#pragma once
#include <AzCore/Serialization/SerializeContext.h>
#include <algorithm>
#include <limits>
#include "Util/VariablePropertyType.h"
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>

@ -44,7 +44,7 @@ enum
{
// in milliseconds
GameModeIdleFrequency = 0,
EditorModeIdleFrequency = 0,
EditorModeIdleFrequency = 1,
InactiveModeFrequency = 10,
UninitializedFrequency = 9999,
};

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

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

@ -13,6 +13,7 @@
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/Render/IntersectorInterface.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
#include <EditorViewportSettings.h>
namespace SandboxEditor
@ -94,7 +95,8 @@ namespace SandboxEditor
cameras.AddCamera(m_firstPersonPanCamera);
cameras.AddCamera(m_firstPersonTranslateCamera);
cameras.AddCamera(m_firstPersonScrollCamera);
cameras.AddCamera(m_orbitCamera);
cameras.AddCamera(m_firstPersonFocusCamera);
cameras.AddCamera(m_pivotCamera);
});
return controller;
@ -110,6 +112,7 @@ namespace SandboxEditor
viewportId, &AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Events::BeginCursorCapture);
}
};
const auto showCursor = [viewportId = m_viewportId]
{
if (SandboxEditor::CameraCaptureCursorForLook())
@ -131,8 +134,8 @@ namespace SandboxEditor
m_firstPersonRotateCamera->SetActivationBeganFn(hideCursor);
m_firstPersonRotateCamera->SetActivationEndedFn(showCursor);
m_firstPersonPanCamera =
AZStd::make_shared<AzFramework::PanCameraInput>(SandboxEditor::CameraFreePanChannelId(), AzFramework::LookPan);
m_firstPersonPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(
SandboxEditor::CameraFreePanChannelId(), AzFramework::LookPan, AzFramework::TranslatePivot);
m_firstPersonPanCamera->m_panSpeedFn = []
{
@ -151,8 +154,8 @@ namespace SandboxEditor
const auto translateCameraInputChannelIds = BuildTranslateCameraInputChannelIds();
m_firstPersonTranslateCamera =
AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::LookTranslation, translateCameraInputChannelIds);
m_firstPersonTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivot);
m_firstPersonTranslateCamera->m_translateSpeedFn = []
{
@ -171,120 +174,120 @@ namespace SandboxEditor
return SandboxEditor::CameraScrollSpeed();
};
m_orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>(SandboxEditor::CameraOrbitChannelId());
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_orbitCamera->SetLookAtFn(
[viewportId = m_viewportId](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional<AZ::Vector3>
if (entityPivot.has_value())
{
AZStd::optional<AZ::Vector3> lookAtAfterInterpolation;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
lookAtAfterInterpolation, viewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::LookAtAfterInterpolation);
return entityPivot->GetTranslation();
}
// initially attempt to use the last set look at point after an interpolation has finished
if (lookAtAfterInterpolation.has_value())
{
return *lookAtAfterInterpolation;
}
const float RayDistance = 1000.0f;
AzFramework::RenderGeometry::RayRequest ray;
ray.m_startWorldPosition = position;
ray.m_endWorldPosition = position + direction * RayDistance;
ray.m_onlyVisible = true;
AzFramework::RenderGeometry::RayResult renderGeometryIntersectionResult;
AzFramework::RenderGeometry::IntersectorBus::EventResult(
renderGeometryIntersectionResult, AzToolsFramework::GetEntityContextId(),
&AzFramework::RenderGeometry::IntersectorBus::Events::RayIntersect, ray);
// attempt a ray intersection with any visible mesh and return the intersection position if successful
if (renderGeometryIntersectionResult)
{
return renderGeometryIntersectionResult.m_worldPosition;
}
// otherwise just use the identity
return AZ::Vector3::CreateZero();
};
m_firstPersonFocusCamera =
AZStd::make_shared<AzFramework::FocusCameraInput>(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusLook);
// if there is no selection or no intersection, fallback to default camera orbit behavior (ground plane
// intersection)
return {};
m_firstPersonFocusCamera->SetPivotFn(pivotFn);
m_pivotCamera = AZStd::make_shared<AzFramework::PivotCameraInput>(SandboxEditor::CameraPivotChannelId());
m_pivotCamera->SetPivotFn(
[pivotFn]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
{
return pivotFn();
});
m_orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(SandboxEditor::CameraOrbitLookChannelId());
m_pivotRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(SandboxEditor::CameraPivotLookChannelId());
m_orbitRotateCamera->m_rotateSpeedFn = []
m_pivotRotateCamera->m_rotateSpeedFn = []
{
return SandboxEditor::CameraRotateSpeed();
};
m_orbitRotateCamera->m_invertYawFn = []
m_pivotRotateCamera->m_invertYawFn = []
{
return SandboxEditor::CameraOrbitYawRotationInverted();
return SandboxEditor::CameraPivotYawRotationInverted();
};
m_orbitTranslateCamera =
AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::OrbitTranslation, translateCameraInputChannelIds);
m_pivotTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslateOffset);
m_orbitTranslateCamera->m_translateSpeedFn = []
m_pivotTranslateCamera->m_translateSpeedFn = []
{
return SandboxEditor::CameraTranslateSpeed();
};
m_orbitTranslateCamera->m_boostMultiplierFn = []
m_pivotTranslateCamera->m_boostMultiplierFn = []
{
return SandboxEditor::CameraBoostMultiplier();
};
m_orbitDollyScrollCamera = AZStd::make_shared<AzFramework::OrbitDollyScrollCameraInput>();
m_pivotDollyScrollCamera = AZStd::make_shared<AzFramework::PivotDollyScrollCameraInput>();
m_orbitDollyScrollCamera->m_scrollSpeedFn = []
m_pivotDollyScrollCamera->m_scrollSpeedFn = []
{
return SandboxEditor::CameraScrollSpeed();
};
m_orbitDollyMoveCamera =
AZStd::make_shared<AzFramework::OrbitDollyCursorMoveCameraInput>(SandboxEditor::CameraOrbitDollyChannelId());
m_pivotDollyMoveCamera = AZStd::make_shared<AzFramework::PivotDollyMotionCameraInput>(SandboxEditor::CameraPivotDollyChannelId());
m_orbitDollyMoveCamera->m_cursorSpeedFn = []
m_pivotDollyMoveCamera->m_motionSpeedFn = []
{
return SandboxEditor::CameraDollyMotionSpeed();
};
m_orbitPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(SandboxEditor::CameraOrbitPanChannelId(), AzFramework::OrbitPan);
m_pivotPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(
SandboxEditor::CameraPivotPanChannelId(), AzFramework::LookPan, AzFramework::TranslateOffset);
m_orbitPanCamera->m_panSpeedFn = []
m_pivotPanCamera->m_panSpeedFn = []
{
return SandboxEditor::CameraPanSpeed();
};
m_orbitPanCamera->m_invertPanXFn = []
m_pivotPanCamera->m_invertPanXFn = []
{
return SandboxEditor::CameraPanInvertedX();
};
m_orbitPanCamera->m_invertPanYFn = []
m_pivotPanCamera->m_invertPanYFn = []
{
return SandboxEditor::CameraPanInvertedY();
};
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_pivotFocusCamera =
AZStd::make_shared<AzFramework::FocusCameraInput>(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusPivot);
m_pivotFocusCamera->SetPivotFn(pivotFn);
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_pivotCamera->m_pivotCameras.AddCamera(m_pivotFocusCamera);
}
void EditorModularViewportCameraComposer::OnEditorModularViewportCameraComposerSettingsChanged()
{
const auto translateCameraInputChannelIds = BuildTranslateCameraInputChannelIds();
m_firstPersonTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
m_orbitTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
m_firstPersonPanCamera->SetPanInputChannelId(SandboxEditor::CameraFreePanChannelId());
m_orbitPanCamera->SetPanInputChannelId(SandboxEditor::CameraOrbitPanChannelId());
m_firstPersonRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraFreeLookChannelId());
m_orbitRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraOrbitLookChannelId());
m_orbitCamera->SetOrbitInputChannelId(SandboxEditor::CameraOrbitChannelId());
m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId());
m_firstPersonFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId());
m_pivotCamera->SetPivotInputChannelId(SandboxEditor::CameraPivotChannelId());
m_pivotTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
m_pivotPanCamera->SetPanInputChannelId(SandboxEditor::CameraPivotPanChannelId());
m_pivotRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraPivotLookChannelId());
m_pivotDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraPivotDollyChannelId());
m_pivotFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId());
}
void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId)
@ -295,8 +298,7 @@ namespace SandboxEditor
AZ::TransformBus::EventResult(worldFromLocal, viewEntityId, &AZ::TransformBus::Events::GetWorldTM);
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame,
worldFromLocal);
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, worldFromLocal);
}
else
{

@ -42,12 +42,14 @@ namespace SandboxEditor
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::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::OrbitDollyCursorMoveCameraInput> m_orbitDollyMoveCamera;
AZStd::shared_ptr<AzFramework::PanCameraInput> m_orbitPanCamera;
AZStd::shared_ptr<AzFramework::FocusCameraInput> m_firstPersonFocusCamera;
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::FocusCameraInput> m_pivotFocusCamera;
AzFramework::ViewportId m_viewportId;
};

@ -61,7 +61,7 @@ static AZStd::vector<AZStd::string> GetEditorInputNames()
void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serialize)
{
serialize.Class<CameraMovementSettings>()
->Version(2)
->Version(3)
->Field("TranslateSpeed", &CameraMovementSettings::m_translateSpeed)
->Field("RotateSpeed", &CameraMovementSettings::m_rotateSpeed)
->Field("BoostMultiplier", &CameraMovementSettings::m_boostMultiplier)
@ -73,12 +73,12 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
->Field("TranslateSmoothing", &CameraMovementSettings::m_translateSmoothing)
->Field("TranslateSmoothness", &CameraMovementSettings::m_translateSmoothness)
->Field("CaptureCursorLook", &CameraMovementSettings::m_captureCursorLook)
->Field("OrbitYawRotationInverted", &CameraMovementSettings::m_orbitYawRotationInverted)
->Field("PivotYawRotationInverted", &CameraMovementSettings::m_pivotYawRotationInverted)
->Field("PanInvertedX", &CameraMovementSettings::m_panInvertedX)
->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY);
serialize.Class<CameraInputSettings>()
->Version(1)
->Version(2)
->Field("TranslateForward", &CameraInputSettings::m_translateForwardChannelId)
->Field("TranslateBackward", &CameraInputSettings::m_translateBackwardChannelId)
->Field("TranslateLeft", &CameraInputSettings::m_translateLeftChannelId)
@ -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("Orbit", &CameraInputSettings::m_orbitChannelId)
->Field("Pivot", &CameraInputSettings::m_pivotChannelId)
->Field("FreeLook", &CameraInputSettings::m_freeLookChannelId)
->Field("FreePan", &CameraInputSettings::m_freePanChannelId)
->Field("OrbitLook", &CameraInputSettings::m_orbitLookChannelId)
->Field("OrbitDolly", &CameraInputSettings::m_orbitDollyChannelId)
->Field("OrbitPan", &CameraInputSettings::m_orbitPanChannelId);
->Field("PivotLook", &CameraInputSettings::m_pivotLookChannelId)
->Field("PivotDolly", &CameraInputSettings::m_pivotDollyChannelId)
->Field("PivotPan", &CameraInputSettings::m_pivotPanChannelId)
->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_orbitYawRotationInverted, "Camera Orbit Yaw Inverted",
"Inverted yaw rotation while orbiting")
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_pivotYawRotationInverted, "Camera Pivot Yaw Inverted",
"Inverted yaw rotation while pivoting")
->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_orbitChannelId, "Orbit",
"Key/button to begin the camera orbit behavior")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotChannelId, "Pivot",
"Key/button to begin the camera pivot 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_orbitLookChannelId, "Orbit Look",
"Key/button to begin camera orbit look")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotLookChannelId, "Pivot Look",
"Key/button to begin camera pivot look")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotDollyChannelId, "Pivot Dolly",
"Key/button to begin camera pivot dolly")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitDollyChannelId, "Orbit Dolly",
"Key/button to begin camera orbit dolly")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotPanChannelId, "Pivot Pan",
"Key/button to begin camera pivot pan")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitPanChannelId, "Orbit Pan",
"Key/button to begin camera orbit pan")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_focusChannelId, "Focus", "Key/button to focus camera pivot")
->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::SetCameraOrbitYawRotationInverted(m_cameraMovementSettings.m_orbitYawRotationInverted);
SandboxEditor::SetCameraPivotYawRotationInverted(m_cameraMovementSettings.m_pivotYawRotationInverted);
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::SetCameraOrbitChannelId(m_cameraInputSettings.m_orbitChannelId);
SandboxEditor::SetCameraPivotChannelId(m_cameraInputSettings.m_pivotChannelId);
SandboxEditor::SetCameraFreeLookChannelId(m_cameraInputSettings.m_freeLookChannelId);
SandboxEditor::SetCameraFreePanChannelId(m_cameraInputSettings.m_freePanChannelId);
SandboxEditor::SetCameraOrbitLookChannelId(m_cameraInputSettings.m_orbitLookChannelId);
SandboxEditor::SetCameraOrbitDollyChannelId(m_cameraInputSettings.m_orbitDollyChannelId);
SandboxEditor::SetCameraOrbitPanChannelId(m_cameraInputSettings.m_orbitPanChannelId);
SandboxEditor::SetCameraPivotLookChannelId(m_cameraInputSettings.m_pivotLookChannelId);
SandboxEditor::SetCameraPivotDollyChannelId(m_cameraInputSettings.m_pivotDollyChannelId);
SandboxEditor::SetCameraPivotPanChannelId(m_cameraInputSettings.m_pivotPanChannelId);
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_orbitYawRotationInverted = SandboxEditor::CameraOrbitYawRotationInverted();
m_cameraMovementSettings.m_pivotYawRotationInverted = SandboxEditor::CameraPivotYawRotationInverted();
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_orbitChannelId = SandboxEditor::CameraOrbitChannelId().GetName();
m_cameraInputSettings.m_pivotChannelId = SandboxEditor::CameraPivotChannelId().GetName();
m_cameraInputSettings.m_freeLookChannelId = SandboxEditor::CameraFreeLookChannelId().GetName();
m_cameraInputSettings.m_freePanChannelId = SandboxEditor::CameraFreePanChannelId().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_pivotLookChannelId = SandboxEditor::CameraPivotLookChannelId().GetName();
m_cameraInputSettings.m_pivotDollyChannelId = SandboxEditor::CameraPivotDollyChannelId().GetName();
m_cameraInputSettings.m_pivotPanChannelId = SandboxEditor::CameraPivotPanChannelId().GetName();
m_cameraInputSettings.m_focusChannelId = SandboxEditor::CameraFocusChannelId().GetName();
}

@ -54,7 +54,7 @@ private:
float m_translateSmoothness;
bool m_translateSmoothing;
bool m_captureCursorLook;
bool m_orbitYawRotationInverted;
bool m_pivotYawRotationInverted;
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_orbitChannelId;
AZStd::string m_pivotChannelId;
AZStd::string m_freeLookChannelId;
AZStd::string m_freePanChannelId;
AZStd::string m_orbitLookChannelId;
AZStd::string m_orbitDollyChannelId;
AZStd::string m_orbitPanChannelId;
AZStd::string m_pivotLookChannelId;
AZStd::string m_pivotDollyChannelId;
AZStd::string m_pivotPanChannelId;
AZStd::string m_focusChannelId;
};
CameraMovementSettings m_cameraMovementSettings;

@ -9,6 +9,9 @@
#include "EditorPreferencesTreeWidgetItem.h"
// AzCore
#include <AzCore/Component/ComponentApplicationBus.h>
// AzToolsFramework
#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>

@ -48,7 +48,7 @@ namespace SandboxEditor
{
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
viewportContext->GetId(), &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
AZ::Transform::CreateFromQuaternionAndTranslation(CameraRotation(pitch, yaw), position), 0.0f);
AZ::Transform::CreateFromQuaternionAndTranslation(CameraRotation(pitch, yaw), position));
}
}

@ -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 CameraOrbitYawRotationInvertedSetting = "/Amazon/Preferences/Editor/Camera/YawRotationInverted";
constexpr AZStd::string_view CameraPivotYawRotationInvertedSetting = "/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 CameraOrbitIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitId";
constexpr AZStd::string_view CameraPivotIdSetting = "/Amazon/Preferences/Editor/Camera/PivotId";
constexpr AZStd::string_view CameraFreeLookIdSetting = "/Amazon/Preferences/Editor/Camera/FreeLookId";
constexpr AZStd::string_view CameraFreePanIdSetting = "/Amazon/Preferences/Editor/Camera/FreePanId";
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 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 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 CameraOrbitYawRotationInverted()
bool CameraPivotYawRotationInverted()
{
return GetRegistry(CameraOrbitYawRotationInvertedSetting, false);
return GetRegistry(CameraPivotYawRotationInvertedSetting, false);
}
void SetCameraOrbitYawRotationInverted(const bool inverted)
void SetCameraPivotYawRotationInverted(const bool inverted)
{
SetRegistry(CameraOrbitYawRotationInvertedSetting, inverted);
SetRegistry(CameraPivotYawRotationInvertedSetting, inverted);
}
bool CameraPanInvertedX()
@ -403,14 +404,14 @@ namespace SandboxEditor
SetRegistry(CameraTranslateBoostIdSetting, cameraTranslateBoostId);
}
AzFramework::InputChannelId CameraOrbitChannelId()
AzFramework::InputChannelId CameraPivotChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str());
return AzFramework::InputChannelId(GetRegistry(CameraPivotIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str());
}
void SetCameraOrbitChannelId(AZStd::string_view cameraOrbitId)
void SetCameraPivotChannelId(AZStd::string_view cameraPivotId)
{
SetRegistry(CameraOrbitIdSetting, cameraOrbitId);
SetRegistry(CameraPivotIdSetting, cameraPivotId);
}
AzFramework::InputChannelId CameraFreeLookChannelId()
@ -433,33 +434,43 @@ namespace SandboxEditor
SetRegistry(CameraFreePanIdSetting, cameraFreePanId);
}
AzFramework::InputChannelId CameraOrbitLookChannelId()
AzFramework::InputChannelId CameraPivotLookChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitLookIdSetting, AZStd::string("mouse_button_left")).c_str());
return AzFramework::InputChannelId(GetRegistry(CameraPivotLookIdSetting, AZStd::string("mouse_button_left")).c_str());
}
void SetCameraOrbitLookChannelId(AZStd::string_view cameraOrbitLookId)
void SetCameraPivotLookChannelId(AZStd::string_view cameraPivotLookId)
{
SetRegistry(CameraOrbitLookIdSetting, cameraOrbitLookId);
SetRegistry(CameraPivotLookIdSetting, cameraPivotLookId);
}
AzFramework::InputChannelId CameraOrbitDollyChannelId()
AzFramework::InputChannelId CameraPivotDollyChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitDollyIdSetting, AZStd::string("mouse_button_right")).c_str());
return AzFramework::InputChannelId(GetRegistry(CameraPivotDollyIdSetting, AZStd::string("mouse_button_right")).c_str());
}
void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId)
void SetCameraPivotDollyChannelId(AZStd::string_view cameraPivotDollyId)
{
SetRegistry(CameraOrbitDollyIdSetting, cameraOrbitDollyId);
SetRegistry(CameraPivotDollyIdSetting, cameraPivotDollyId);
}
AzFramework::InputChannelId CameraOrbitPanChannelId()
AzFramework::InputChannelId CameraPivotPanChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitPanIdSetting, AZStd::string("mouse_button_middle")).c_str());
return AzFramework::InputChannelId(GetRegistry(CameraPivotPanIdSetting, AZStd::string("mouse_button_middle")).c_str());
}
void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId)
void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId)
{
SetRegistry(CameraOrbitPanIdSetting, cameraOrbitPanId);
SetRegistry(CameraPivotPanIdSetting, cameraPivotPanId);
}
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 CameraOrbitYawRotationInverted();
SANDBOX_API void SetCameraOrbitYawRotationInverted(bool inverted);
SANDBOX_API bool CameraPivotYawRotationInverted();
SANDBOX_API void SetCameraPivotYawRotationInverted(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 CameraOrbitChannelId();
SANDBOX_API void SetCameraOrbitChannelId(AZStd::string_view cameraOrbitId);
SANDBOX_API AzFramework::InputChannelId CameraPivotChannelId();
SANDBOX_API void SetCameraPivotChannelId(AZStd::string_view cameraPivotId);
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 CameraOrbitLookChannelId();
SANDBOX_API void SetCameraOrbitLookChannelId(AZStd::string_view cameraOrbitLookId);
SANDBOX_API AzFramework::InputChannelId CameraPivotLookChannelId();
SANDBOX_API void SetCameraPivotLookChannelId(AZStd::string_view cameraPivotLookId);
SANDBOX_API AzFramework::InputChannelId CameraOrbitDollyChannelId();
SANDBOX_API void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId);
SANDBOX_API AzFramework::InputChannelId CameraPivotDollyChannelId();
SANDBOX_API void SetCameraPivotDollyChannelId(AZStd::string_view cameraPivotDollyId);
SANDBOX_API AzFramework::InputChannelId CameraOrbitPanChannelId();
SANDBOX_API void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId);
SANDBOX_API AzFramework::InputChannelId CameraPivotPanChannelId();
SANDBOX_API void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId);
SANDBOX_API AzFramework::InputChannelId CameraFocusChannelId();
SANDBOX_API void SetCameraFocusChannelId(AZStd::string_view cameraFocusId);
} // namespace SandboxEditor

@ -2428,7 +2428,7 @@ void EditorViewportWidget::RestoreViewportAfterGameMode()
}
else
{
AZ_Error("CryLegacy", false, "Not restoring the editor viewport camera is currently unsupported");
AZ_Warning("CryLegacy", false, "Not restoring the editor viewport camera is currently unsupported");
SetViewTM(preGameModeViewTM);
}
}

@ -35,8 +35,6 @@ struct IDisplayViewport
*/
virtual float GetDistanceToLine(const Vec3& lineP1, const Vec3& lineP2, const QPoint& point) const = 0;
virtual CBaseObjectsCache* GetVisibleObjectsCache() = 0;
enum EAxis
{
AXIS_NONE,

@ -167,7 +167,7 @@ namespace UnitTest
AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
transformToInterpolateTo, 0.0f);
transformToInterpolateTo);
// simulate interpolation
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
@ -193,7 +193,7 @@ namespace UnitTest
// When
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
transformToInterpolateTo, 0.0f);
transformToInterpolateTo);
// simulate interpolation
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });

@ -27,10 +27,11 @@ AZ_POP_DISABLE_WARNING
#endif
// AzCore
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Utils/Utils.h>
// AzFramework

@ -33,10 +33,6 @@
AZ_CVAR_EXTERNED(bool, ed_visibility_logTiming);
AZ_CVAR(
bool, ed_visibility_use, true, nullptr, AZ::ConsoleFunctorFlags::Null,
"Enable/disable using the new IVisibilitySystem for Entity visibility determination");
/*!
* Class Description used for object templates.
* This description filled from Xml template files.
@ -76,17 +72,6 @@ public:
int GameCreationOrder() override { return superType->GameCreationOrder(); };
};
void CBaseObjectsCache::AddObject(CBaseObject* object)
{
m_objects.push_back(object);
if (object->GetType() == OBJTYPE_AZENTITY)
{
auto componentEntityObject = static_cast<CComponentEntityObject*>(object);
m_entityIds.push_back(componentEntityObject->GetAssociatedEntityId());
}
}
//////////////////////////////////////////////////////////////////////////
// CObjectManager implementation.
//////////////////////////////////////////////////////////////////////////
@ -1267,25 +1252,8 @@ void CObjectManager::Display(DisplayContext& dc)
UpdateVisibilityList();
}
bool viewIsDirty = dc.settings->IsDisplayHelpers(); // displaying helpers require computing all the bound boxes and things anyway.
if (!viewIsDirty)
if (dc.settings->IsDisplayHelpers())
{
if (CBaseObjectsCache* cache = dc.view->GetVisibleObjectsCache())
{
// if the current rendering viewport has an out-of-date cache serial number, it needs to be refreshed too.
// views set their cache empty when they indicate they need to force a refresh.
if ((cache->GetObjectCount() == 0) || (cache->GetSerialNumber() != m_visibilitySerialNumber))
{
viewIsDirty = true;
}
}
}
if (viewIsDirty)
{
FindDisplayableObjects(dc, true); // this also actually draws the helpers.
// Also broadcast for anyone else that needs to draw global debug to do so now
AzFramework::DebugDisplayEventBus::Broadcast(&AzFramework::DebugDisplayEvents::DrawGlobalDebugInfo);
}
@ -1296,94 +1264,14 @@ void CObjectManager::Display(DisplayContext& dc)
}
}
void CObjectManager::ForceUpdateVisibleObjectCache(DisplayContext& dc)
void CObjectManager::ForceUpdateVisibleObjectCache([[maybe_unused]] DisplayContext& dc)
{
FindDisplayableObjects(dc, false);
AZ_Assert(false, "CObjectManager::ForceUpdateVisibleObjectCache is legacy/deprecated and should not be used.");
}
void CObjectManager::FindDisplayableObjects(DisplayContext& dc, [[maybe_unused]] bool bDisplay)
void CObjectManager::FindDisplayableObjects([[maybe_unused]] DisplayContext& dc, [[maybe_unused]] bool bDisplay)
{
// if the new IVisibilitySystem is being used, do not run this logic
if (ed_visibility_use)
{
return;
}
AZ_PROFILE_FUNCTION(Editor);
auto start = std::chrono::steady_clock::now();
CBaseObjectsCache* pDispayedViewObjects = dc.view->GetVisibleObjectsCache();
if (!pDispayedViewObjects)
{
return;
}
pDispayedViewObjects->SetSerialNumber(m_visibilitySerialNumber); // update viewport to be latest serial number
AABB bbox;
bbox.min.zero();
bbox.max.zero();
pDispayedViewObjects->ClearObjects();
pDispayedViewObjects->Reserve(static_cast<int>(m_visibleObjects.size()));
if (dc.flags & DISPLAY_2D)
{
int numVis = static_cast<int>(m_visibleObjects.size());
for (int i = 0; i < numVis; i++)
{
CBaseObject* obj = m_visibleObjects[i];
obj->GetBoundBox(bbox);
if (dc.box.IsIntersectBox(bbox))
{
pDispayedViewObjects->AddObject(obj);
}
}
}
else
{
CSelectionGroup* pSelection = GetSelection();
if (pSelection && pSelection->GetCount() > 1)
{
AABB mergedAABB;
mergedAABB.Reset();
for (int i = 0, iCount(pSelection->GetCount()); i < iCount; ++i)
{
CBaseObject* pObj(pSelection->GetObject(i));
if (pObj == nullptr)
{
continue;
}
AABB aabb;
pObj->GetBoundBox(aabb);
mergedAABB.Add(aabb);
}
pSelection->GetObject(0)->CBaseObject::DrawDimensions(dc, &mergedAABB);
}
int numVis = static_cast<int>(m_visibleObjects.size());
for (int i = 0; i < numVis; i++)
{
CBaseObject* obj = m_visibleObjects[i];
if (obj)
{
if ((dc.flags & DISPLAY_SELECTION_HELPERS) || obj->IsSelected())
{
pDispayedViewObjects->AddObject(obj);
}
}
}
}
if (ed_visibility_logTiming && !ed_visibility_use)
{
auto stop = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = stop - start;
AZ_Printf("Visibility", "FindDisplayableObjects (old) - Duration: %f", diff);
}
AZ_Assert(false, "CObjectManager::FindDisplayableObjects is legacy/deprecated and should not be used.");
}
void CObjectManager::BeginEditParams(CBaseObject* obj, int flags)
@ -1630,214 +1518,24 @@ bool CObjectManager::HitTestObject(CBaseObject* obj, HitContext& hc)
return (bSelectionHelperHit || obj->HitTest(hc));
}
//////////////////////////////////////////////////////////////////////////
bool CObjectManager::HitTest(HitContext& hitInfo)
bool CObjectManager::HitTest([[maybe_unused]] HitContext& hitInfo)
{
AZ_PROFILE_FUNCTION(Editor);
hitInfo.object = nullptr;
hitInfo.dist = FLT_MAX;
hitInfo.axis = 0;
hitInfo.manipulatorMode = 0;
HitContext hcOrg = hitInfo;
if (hcOrg.view)
{
hcOrg.view->GetPerpendicularAxis(nullptr, &hcOrg.b2DViewport);
}
hcOrg.rayDir = hcOrg.rayDir.GetNormalized();
HitContext hc = hcOrg;
float mindist = FLT_MAX;
if (!hitInfo.bIgnoreAxis && !hc.bUseSelectionHelpers)
{
// Test gizmos.
if (m_gizmoManager->HitTest(hc))
{
if (hc.axis != 0)
{
hitInfo.object = hc.object;
hitInfo.gizmo = hc.gizmo;
hitInfo.axis = hc.axis;
hitInfo.manipulatorMode = hc.manipulatorMode;
hitInfo.dist = hc.dist;
return true;
}
}
}
if (hitInfo.bOnlyGizmo)
{
return false;
}
// Only HitTest objects, that where previously Displayed.
CBaseObjectsCache* pDispayedViewObjects = hitInfo.view->GetVisibleObjectsCache();
const bool iconsPrioritized = true; // Force icons to always be prioritized over other things you hit. Can change to be a configurable option in the future.
CBaseObject* selected = nullptr;
const char* name = nullptr;
bool iconHit = false;
int numVis = pDispayedViewObjects->GetObjectCount();
for (int i = 0; i < numVis; i++)
{
CBaseObject* obj = pDispayedViewObjects->GetObject(i);
if (obj == hitInfo.pExcludedObject)
{
continue;
}
if (HitTestObject(obj, hc))
{
if (m_selectCallback && !m_selectCallback->CanSelectObject(obj))
{
continue;
}
// Check if this object is nearest.
if (hc.axis != 0)
{
hitInfo.object = obj;
hitInfo.axis = hc.axis;
hitInfo.dist = hc.dist;
return true;
}
// When prioritizing icons, we don't allow non-icon hits to beat icon hits
if (iconsPrioritized && iconHit && !hc.iconHit)
{
continue;
}
if (hc.dist < mindist || (!iconHit && hc.iconHit))
{
if (hc.iconHit)
{
iconHit = true;
}
mindist = hc.dist;
name = hc.name;
selected = obj;
}
// Clear the object pointer if an object was hit, not just if the collision
// was closer than any previous. Not all paths from HitTestObject set the object pointer and so you could get
// an object from a previous (rejected) result but with collision information about a closer hit.
hc.object = nullptr;
hc.iconHit = false;
// If use deep selection
if (hitInfo.pDeepSelection)
{
hitInfo.pDeepSelection->AddObject(hc.dist, obj);
}
}
}
if (selected)
{
hitInfo.object = selected;
hitInfo.dist = mindist;
hitInfo.name = name;
hitInfo.iconHit = iconHit;
return true;
}
AZ_Assert(false, "CObjectManager::HitTest is legacy/deprecated and should not be used.");
return false;
}
void CObjectManager::FindObjectsInRect(CViewport* view, const QRect& rect, std::vector<GUID>& guids)
{
AZ_PROFILE_FUNCTION(Editor);
if (rect.width() < 1 || rect.height() < 1)
{
return;
}
HitContext hc;
hc.view = view;
hc.b2DViewport = view->GetType() != ET_ViewportCamera;
hc.rect = rect;
hc.bUseSelectionHelpers = view->GetAdvancedSelectModeFlag();
guids.clear();
CBaseObjectsCache* pDispayedViewObjects = view->GetVisibleObjectsCache();
int numVis = pDispayedViewObjects->GetObjectCount();
for (int i = 0; i < numVis; ++i)
{
CBaseObject* pObj = pDispayedViewObjects->GetObject(i);
HitTestObjectAgainstRect(pObj, view, hc, guids);
}
void CObjectManager::FindObjectsInRect(
[[maybe_unused]] CViewport* view, [[maybe_unused]] const QRect& rect, [[maybe_unused]] std::vector<GUID>& guids)
{
AZ_Assert(false, "CObjectManager::FindObjectsInRect is legacy/deprecated and should not be used.");
}
//////////////////////////////////////////////////////////////////////////
void CObjectManager::SelectObjectsInRect(CViewport* view, const QRect& rect, bool bSelect)
void CObjectManager::SelectObjectsInRect(
[[maybe_unused]] CViewport* view, [[maybe_unused]] const QRect& rect, [[maybe_unused]] bool bSelect)
{
AZ_PROFILE_FUNCTION(Editor);
// Ignore too small rectangles.
if (rect.width() < 1 || rect.height() < 1)
{
return;
}
CUndo undo("Select Object(s)");
HitContext hc;
hc.view = view;
hc.b2DViewport = view->GetType() != ET_ViewportCamera;
hc.rect = rect;
hc.bUseSelectionHelpers = view->GetAdvancedSelectModeFlag();
bool isUndoRecording = GetIEditor()->IsUndoRecording();
if (isUndoRecording)
{
m_processingBulkSelect = true;
}
CBaseObjectsCache* displayedViewObjects = view->GetVisibleObjectsCache();
int numVis = displayedViewObjects->GetObjectCount();
// Tracking the previous selection allows proper undo/redo functionality of additional
// selections (CTRL + drag select)
AZStd::unordered_set<const CBaseObject*> previousSelection;
for (int i = 0; i < numVis; ++i)
{
CBaseObject* object = displayedViewObjects->GetObject(i);
if (object->IsSelected())
{
previousSelection.insert(object);
}
else
{
// This will update m_currSelection
SelectObjectInRect(object, view, hc, bSelect);
// Legacy undo/redo does not go through the Ebus system and must be done individually
if (isUndoRecording && object->GetType() != OBJTYPE_AZENTITY)
{
GetIEditor()->RecordUndo(new CUndoBaseObjectSelect(object, true));
}
}
}
if (isUndoRecording && m_currSelection)
{
// Component Entities can handle undo/redo in bulk due to Ebuses
GetIEditor()->RecordUndo(new CUndoBaseObjectBulkSelect(previousSelection, *m_currSelection));
}
m_processingBulkSelect = false;
AZ_Assert(false, "CObjectManager::SelectObjectsInRect is legacy/deprecated and should not be used.");
}
//////////////////////////////////////////////////////////////////////////
@ -3011,6 +2709,4 @@ namespace AzToolsFramework
}
}
}
} // namespace AzToolsFramework

@ -52,40 +52,6 @@ public:
}
};
//////////////////////////////////////////////////////////////////////////
// Array of editor objects.
//////////////////////////////////////////////////////////////////////////
class CBaseObjectsCache
{
public:
int GetObjectCount() const { return static_cast<int>(m_objects.size()); }
CBaseObject* GetObject(int nIndex) const { return m_objects[nIndex]; }
void AddObject(CBaseObject* object);
void ClearObjects()
{
m_objects.clear();
m_entityIds.clear();
}
void Reserve(int nCount)
{
m_objects.reserve(nCount);
m_entityIds.reserve(nCount);
}
const AZStd::vector<AZ::EntityId>& GetEntityIdCache() const { return m_entityIds; }
/// Checksum is used as a dirty flag.
unsigned int GetSerialNumber() { return m_serialNumber; }
void SetSerialNumber(unsigned int serialNumber) { m_serialNumber = serialNumber; }
private:
//! List of objects that was displayed at last frame.
std::vector<_smart_ptr<CBaseObject> > m_objects;
AZStd::vector<AZ::EntityId> m_entityIds;
unsigned int m_serialNumber = 0;
};
/*!
* CObjectManager is a singleton object that
* manages global set of objects in level.

@ -142,8 +142,7 @@ void GetSelectedEntitiesSetWithFlattenedHierarchy(AzToolsFramework::EntityIdSet&
}
SandboxIntegrationManager::SandboxIntegrationManager()
: m_inObjectPickMode(false)
, m_startedUndoRecordingNestingLevel(0)
: m_startedUndoRecordingNestingLevel(0)
, m_dc(nullptr)
, m_notificationWindowManager(new AzToolsFramework::SliceOverridesNotificationWindowManager())
{
@ -1000,62 +999,6 @@ void SandboxIntegrationManager::SetupSliceContextMenu_Modify(QMenu* menu, const
revertAction->setEnabled(canRevert);
}
void SandboxIntegrationManager::HandleObjectModeSelection(const AZ::Vector2& point, [[maybe_unused]] int flags, bool& handled)
{
// Todo - Use a custom "edit tool". This will eliminate the need for this bus message entirely, which technically
// makes this feature less intrusive on Sandbox.
// UPDATE: This is now provided by EditorPickEntitySelection when the new Viewport Interaction Model changes are enabled.
if (m_inObjectPickMode)
{
CViewport* view = GetIEditor()->GetViewManager()->GetGameViewport();
const QPoint viewPoint(static_cast<int>(point.GetX()), static_cast<int>(point.GetY()));
HitContext hitInfo;
hitInfo.view = view;
if (view->HitTest(viewPoint, hitInfo))
{
if (hitInfo.object && (hitInfo.object->GetType() == OBJTYPE_AZENTITY))
{
CComponentEntityObject* entityObject = static_cast<CComponentEntityObject*>(hitInfo.object);
AzToolsFramework::EditorPickModeRequestBus::Broadcast(
&AzToolsFramework::EditorPickModeRequests::PickModeSelectEntity, entityObject->GetAssociatedEntityId());
}
}
AzToolsFramework::EditorPickModeRequestBus::Broadcast(
&AzToolsFramework::EditorPickModeRequests::StopEntityPickMode);
handled = true;
}
}
void SandboxIntegrationManager::UpdateObjectModeCursor(AZ::u32& cursorId, AZStd::string& cursorStr)
{
if (m_inObjectPickMode)
{
cursorId = static_cast<AZ::u64>(STD_CURSOR_HAND);
cursorStr = "Pick an entity...";
}
}
void SandboxIntegrationManager::OnEntityPickModeStarted()
{
m_inObjectPickMode = true;
// Currently this object pick mode is activated only via PropertyEntityIdCtrl picker.
// When the picker button is clicked, we transfer focus to the viewport so the
// spacebar can still be used to activate selection helpers.
if (CViewport* view = GetIEditor()->GetViewManager()->GetGameViewport())
{
view->SetFocus();
}
}
void SandboxIntegrationManager::OnEntityPickModeStopped()
{
m_inObjectPickMode = false;
}
void SandboxIntegrationManager::CreateEditorRepresentation(AZ::Entity* entity)
{
IEditor* editor = GetIEditor();
@ -1739,8 +1682,7 @@ void SandboxIntegrationManager::GoToEntitiesInViewports(const AzToolsFramework::
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
viewportContext->GetId(),
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform,
distanceToLookAt);
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform);
}
}
}
@ -1953,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()))
{

@ -93,7 +93,6 @@ namespace AzToolsFramework
class SandboxIntegrationManager
: private AzToolsFramework::ToolsApplicationEvents::Bus::Handler
, private AzToolsFramework::EditorRequests::Bus::Handler
, private AzToolsFramework::EditorPickModeNotificationBus::Handler
, private AzToolsFramework::EditorContextMenuBus::Handler
, private AzToolsFramework::EditorWindowRequests::Bus::Handler
, private AzFramework::AssetCatalogEventBus::Handler
@ -140,8 +139,6 @@ private:
QDockWidget* InstanceViewPane(const char* paneName) override;
void CloseViewPane(const char* paneName) override;
void BrowseForAssets(AzToolsFramework::AssetBrowser::AssetSelectionModel& selection) override;
void HandleObjectModeSelection(const AZ::Vector2& point, int flags, bool& handled) override;
void UpdateObjectModeCursor(AZ::u32& cursorId, AZStd::string& cursorStr) override;
void CreateEditorRepresentation(AZ::Entity* entity) override;
bool DestroyEditorRepresentation(AZ::EntityId entityId, bool deleteAZEntity) override;
void CloneSelection(bool& handled) override;
@ -175,10 +172,6 @@ private:
QWidget* GetAppMainWindow() override;
//////////////////////////////////////////////////////////////////////////
// EditorPickModeNotificationBus
void OnEntityPickModeStarted() override;
void OnEntityPickModeStopped() override;
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::EditorContextMenu::Bus::Handler overrides
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override;
@ -281,7 +274,6 @@ private:
private:
AZ::Vector2 m_contextMenuViewPoint;
int m_inObjectPickMode;
short m_startedUndoRecordingNestingLevel; // used in OnBegin/EndUndo to ensure we only accept undo's we started recording
AzToolsFramework::SliceOverridesNotificationWindowManager* m_notificationWindowManager;

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

@ -18,6 +18,7 @@
#include <AzCore/Utils/Utils.h>
// AzToolsFramework
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>

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

@ -36,9 +36,6 @@
#include "CryEdit.h"
#include "Viewport.h"
// Atom Renderer
#include <Atom/RPI.Public/RPISystemInterface.h>
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <TrackView/ui_SequenceBatchRenderDialog.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
@ -272,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)
@ -363,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()
@ -497,7 +494,7 @@ void CSequenceBatchRenderDialog::OnSavePreset()
}
void CSequenceBatchRenderDialog::stashActiveViewportResolution()
{
{
// stash active resolution in global vars
activeViewportWidth = resolutions[0][0];
activeViewportHeight = resolutions[0][1];
@ -505,7 +502,7 @@ void CSequenceBatchRenderDialog::stashActiveViewportResolution()
if (activeViewport)
{
activeViewport->GetDimensions(&activeViewportWidth, &activeViewportHeight);
}
}
}
void CSequenceBatchRenderDialog::OnGo()
@ -643,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)
{
@ -755,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);
@ -910,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();
}
@ -965,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);
}
}
@ -1103,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]
@ -1237,18 +1234,11 @@ void CSequenceBatchRenderDialog::OnKickIdleTimout()
{
componentApplication->TickSystem();
}
// Directly tick the renderer, as it's no longer part of the system tick
if (auto rpiSystem = AZ::RPI::RPISystemInterface::Get())
{
rpiSystem->SimulationTick();
rpiSystem->RenderTick();
}
}
}
void CSequenceBatchRenderDialog::OnKickIdle()
{
{
if (m_renderContext.captureState == CaptureState::WarmingUpAfterResChange)
{
OnUpdateWarmingUpAfterResChange();
@ -1264,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);

@ -19,6 +19,7 @@
// Editor
#include "AnimationContext.h"
#include <AzCore/Asset/AssetSerializer.h>
namespace
{

@ -101,13 +101,13 @@ public:
if (fresh.size() < m_stackNames.size())
{
beginRemoveRows(createIndex(-1, -1), static_cast<int>(fresh.size()), static_cast<int>(m_stackNames.size() - 1));
beginRemoveRows(QModelIndex(), static_cast<int>(fresh.size()), static_cast<int>(m_stackNames.size() - 1));
m_stackNames = fresh;
endRemoveRows();
}
else
{
beginInsertRows(createIndex(-1, -1), static_cast<int>(m_stackNames.size()), static_cast<int>(fresh.size() - 1));
beginInsertRows(QModelIndex(), static_cast<int>(m_stackNames.size()), static_cast<int>(fresh.size() - 1));
m_stackNames = fresh;
endInsertRows();
}

@ -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,8 +173,6 @@ QtViewport::QtViewport(QWidget* parent)
m_bAdvancedSelectMode = false;
m_pVisibleObjectsCache = new CBaseObjectsCache;
m_constructionPlane.SetPlane(Vec3_OneZ, Vec3_Zero);
m_constructionPlaneAxisX = Vec3_Zero;
m_constructionPlaneAxisY = Vec3_Zero;
@ -204,8 +202,6 @@ QtViewport::QtViewport(QWidget* parent)
//////////////////////////////////////////////////////////////////////////
QtViewport::~QtViewport()
{
delete m_pVisibleObjectsCache;
GetIEditor()->GetViewManager()->UnregisterViewport(this);
}
@ -376,11 +372,6 @@ void QtViewport::OnDeactivate()
void QtViewport::ResetContent()
{
m_pMouseOverObject = nullptr;
// Need to clear visual object cache.
// Right after loading new level, some code(e.g. OnMouseMove) access invalid
// previous level object before cache updated.
GetVisibleObjectsCache()->ClearObjects();
}
//////////////////////////////////////////////////////////////////////////
@ -398,11 +389,8 @@ void QtViewport::Update()
m_viewportUi.Update();
m_bAdvancedSelectMode = false;
bool bSpaceClick = false;
{
bSpaceClick = CheckVirtualKey(Qt::Key_Space) & !CheckVirtualKey(Qt::Key_Shift) /*& !CheckVirtualKey(Qt::Key_Control)*/;
}
if (bSpaceClick && hasFocus())
if (CheckVirtualKey(Qt::Key_Space) && !CheckVirtualKey(Qt::Key_Shift) && hasFocus())
{
m_bAdvancedSelectMode = true;
}

@ -157,7 +157,7 @@ public:
virtual Vec3 SnapToGrid(const Vec3& vec) = 0;
//! Get selection procision tolerance.
//! Get selection precision tolerance.
virtual float GetSelectionTolerance() const = 0;
//////////////////////////////////////////////////////////////////////////
@ -491,10 +491,6 @@ public:
void ResetCursor() override;
void SetSupplementaryCursorStr(const QString& str) override;
//////////////////////////////////////////////////////////////////////////
// Return visble objects cache.
CBaseObjectsCache* GetVisibleObjectsCache() override { return m_pVisibleObjectsCache; };
void RegisterRenderListener(IRenderListener* piListener) override;
bool UnregisterRenderListener(IRenderListener* piListener) override;
bool IsRenderListenerRegistered(IRenderListener* piListener) override;
@ -612,8 +608,6 @@ protected:
int m_nLastUpdateFrame;
int m_nLastMouseMoveFrame;
CBaseObjectsCache* m_pVisibleObjectsCache;
QRect m_rcClient;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING

@ -15,6 +15,7 @@
#include "ViewportTitleDlg.h"
// Qt
#include <QLabel>
#include <QInputDialog>
#include <AtomLyIntegration/AtomViewportDisplayInfo/AtomViewportInfoDisplayBus.h>
@ -36,8 +37,10 @@
#include "MathConversion.h"
#include "EditorViewportSettings.h"
#include <AzCore/std/algorithm.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/std/algorithm.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <LmbrCentral/Audio/AudioSystemComponentBus.h>
@ -145,6 +148,11 @@ CViewportTitleDlg::~CViewportTitleDlg()
AZ::VR::VREventBus::Handler::BusDisconnect();
GetISystem()->GetISystemEventDispatcher()->RemoveListener(this);
GetIEditor()->UnregisterNotifyListener(this);
if (m_prefabViewportFocusPathHandler)
{
delete m_prefabViewportFocusPathHandler;
}
}
void CViewportTitleDlg::SetupCameraDropdownMenu()
@ -292,8 +300,6 @@ void CViewportTitleDlg::SetViewPane(CLayoutViewPane* pViewPane)
//////////////////////////////////////////////////////////////////////////
void CViewportTitleDlg::OnInitDialog()
{
m_ui->m_titleBtn->setText(m_title);
// Add a child parented to us that listens for r_displayInfo changes.
auto displayInfoHelper = new CViewportTitleDlgDisplayInfoHelper(this);
connect(displayInfoHelper, &CViewportTitleDlgDisplayInfoHelper::ViewportInfoStatusUpdated, this, &CViewportTitleDlg::UpdateDisplayInfo);
@ -314,13 +320,28 @@ void CViewportTitleDlg::OnInitDialog()
m_cameraSpeed->setFixedWidth(width);
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (isPrefabSystemEnabled)
{
m_prefabViewportFocusPathHandler = new AzToolsFramework::Prefab::PrefabViewportFocusPathHandler();
m_prefabViewportFocusPathHandler->Initialize(m_ui->m_prefabFocusPath, m_ui->m_prefabFocusBackButton);
}
else
{
m_ui->m_prefabFocusPath->setEnabled(false);
m_ui->m_prefabFocusBackButton->setEnabled(false);
m_ui->m_prefabFocusPath->hide();
m_ui->m_prefabFocusBackButton->hide();
}
}
//////////////////////////////////////////////////////////////////////////
void CViewportTitleDlg::SetTitle(const QString& title)
{
m_title = title;
m_ui->m_titleBtn->setText(m_title);
}
//////////////////////////////////////////////////////////////////////////

@ -19,6 +19,7 @@
#include <QWidgetAction>
#include <QComboBox>
#include <AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h>
#include <AzQtComponents/Components/Widgets/SpinBox.h>
#include <HMDBus.h>
@ -176,6 +177,8 @@ protected:
QWidgetAction* m_gridSizeActionWidget = nullptr;
QWidgetAction* m_angleSizeActionWidget = nullptr;
AzToolsFramework::Prefab::PrefabViewportFocusPathHandler* m_prefabViewportFocusPathHandler = nullptr;
QScopedPointer<Ui::ViewportTitleDlg> m_ui;
};

@ -28,7 +28,7 @@
<height>29</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0,0,0,0,0,0,0,0">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0,0,0,0,0,0,0">
<property name="leftMargin">
<number>10</number>
</property>
@ -42,102 +42,112 @@
<number>0</number>
</property>
<item>
<widget class="QLabel" name="m_titleBtn">
<widget class="QToolButton" name="m_prefabFocusBackButton">
<property name="toolTip">
<string>Up one level</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Breadcrumb/img/UI20/Breadcrumb/arrow_left-default.svg</normaloff>:/Breadcrumb/img/UI20/Breadcrumb/arrow_left-default.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="AzQtComponents::BreadCrumbs" name="m_prefabFocusPath" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
<property name="text">
<string>Static</string>
</spacer>
</item>
<item>
<widget class="QToolButton" name="m_cameraMenu">
<property name="toolTip">
<string>Camera settings</string>
</property>
<property name="indent">
<number>11</number>
<property name="icon">
<iconset>
<normaloff>:/Menu/camera.svg</normaloff>:/Menu/camera.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="m_cameraMenu">
<property name="icon">
<iconset>
<normaloff>:/Menu/camera.svg</normaloff>:/Menu/camera.svg
</iconset>
</property>
<property name="toolTip">
<string>Camera settings</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="m_debugInformationMenu">
<property name="toolTip">
<string>Debug information</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/debug.svg</normaloff>:/Menu/debug.svg
</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<widget class="QToolButton" name="m_debugInformationMenu">
<property name="toolTip">
<string>Debug information</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/debug.svg</normaloff>:/Menu/debug.svg</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="m_helpers">
<property name="toolTip">
<string>Toggle viewport helpers</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/helpers.svg</normaloff>:/Menu/helpers.svg
</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<widget class="QToolButton" name="m_helpers">
<property name="toolTip">
<string>Toggle viewport helpers</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/helpers.svg</normaloff>:/Menu/helpers.svg</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="m_resolutionMenu">
<property name="toolTip">
<string>Viewport resolution</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/resolution.svg</normaloff>:/Menu/resolution.svg
</iconset>
</property>
</widget>
<widget class="QToolButton" name="m_resolutionMenu">
<property name="toolTip">
<string>Viewport resolution</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/resolution.svg</normaloff>:/Menu/resolution.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="m_overflowBtn">
<property name="toolTip">
<string>Other settings</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/menu.svg</normaloff>:/Menu/menu.svg
</iconset>
</property>
</widget>
<widget class="QToolButton" name="m_overflowBtn">
<property name="toolTip">
<string>Other settings</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/Menu/menu.svg</normaloff>:/Menu/menu.svg</iconset>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::ButtonDivider</class>
<class>AzQtComponents::BreadCrumbs</class>
<extends>QWidget</extends>
<header>AzQtComponents/Components/ButtonDivider.h</header>
<header>AzQtComponents/Components/Widgets/BreadCrumbs.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../Framework/AzQtComponents/AzQtComponents/Images/resources.qrc"/>
</resources>
<connections/>
<resources>
<include location="../../Framework/AzQtComponents/AzQtComponents/Images/resources.qrc"/>
</resources>
<connections/>
</ui>

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

Loading…
Cancel
Save