merging latest dev

Signed-off-by: Gene Walters <genewalt@amazon.com>
This commit is contained in:
Gene Walters
2021-11-23 12:16:54 -08:00
2108 changed files with 51776 additions and 33755 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
---
name: ar_bug_report.md
name: Automated Review bug report
about: Create a bug for a an issue found in the Automated Review
title: 'AR Bug Report'
labels: 'needs-triage,kind/bug,kind/automation'
+2 -1
View File
@@ -8,11 +8,12 @@
if(NOT PROJECT_NAME)
cmake_minimum_required(VERSION 3.20)
include(cmake/CompilerSettings.cmake)
project(AutomatedTesting
LANGUAGES C CXX
VERSION 1.0.0.0
)
include(EngineFinder.cmake OPTIONAL)
include(cmake/EngineFinder.cmake OPTIONAL)
find_package(o3de REQUIRED)
o3de_initialize()
else()
@@ -12,6 +12,7 @@ import pytest
import ly_test_tools.log.log_monitor
from AWS.common import constants
from AWS.common.resource_mappings import AWS_RESOURCE_MAPPINGS_ACCOUNT_ID_KEY
# fixture imports
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor
@@ -70,6 +71,41 @@ class TestAWSClientAuthWindows(object):
halt_on_unexpected=True,
)
assert result, 'Anonymous credentials fetched successfully.'
@pytest.mark.parametrize('level', ['AWS/ClientAuth'])
def test_anonymous_credentials_no_global_accountid(self,
level: str,
launcher: pytest.fixture,
resource_mappings: pytest.fixture,
workspace: pytest.fixture,
asset_processor: pytest.fixture
):
"""
Test to verify AWS Cognito Identity pool anonymous authorization.
Setup: Updates resource mapping file using existing CloudFormation stacks.
Tests: Getting credentials when no credentials are configured
Verification: Log monitor looks for success credentials log.
"""
# Remove top-level account ID from resource mappings
resource_mappings.clear_select_keys([AWS_RESOURCE_MAPPINGS_ACCOUNT_ID_KEY])
asset_processor.start()
asset_processor.wait_for_idle()
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), constants.GAME_LOG_NAME)
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor)
launcher.args = ['+LoadLevel', level]
launcher.args.extend(['-rhi=null'])
with launcher.start(launch_ap=False):
result = log_monitor.monitor_log_for_lines(
expected_lines=['(Script) - Success anonymous credentials'],
unexpected_lines=['(Script) - Fail anonymous credentials'],
halt_on_unexpected=True,
)
assert result, 'Anonymous credentials fetched successfully.'
def test_password_signin_credentials(self,
launcher: pytest.fixture,
@@ -13,6 +13,8 @@ from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAutomation(EditorTestSuite):
enable_prefab_system = False
@pytest.mark.test_case_id("C36525657")
class AtomEditorComponents_BloomAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_BloomAdded as test_module
@@ -104,6 +104,7 @@ class TestAllComponentsIndepthTests(object):
halt_on_unexpected=True,
cfg_args=[level],
null_renderer=False,
enable_prefab_system=False,
)
similarity_threshold = 0.99
@@ -158,6 +159,7 @@ class TestAllComponentsIndepthTests(object):
halt_on_unexpected=True,
cfg_args=[level],
null_renderer=False,
enable_prefab_system=False,
)
similarity_threshold = 0.99
@@ -205,6 +207,7 @@ class TestPerformanceBenchmarkSuite(object):
halt_on_unexpected=True,
cfg_args=[level],
null_renderer=False,
enable_prefab_system=False,
)
aggregator = BenchmarkDataAggregator(workspace, logger, 'periodic')
@@ -242,5 +245,6 @@ class TestMaterialEditor(object):
halt_on_unexpected=False,
null_renderer=False,
cfg_args=[cfg_args],
log_file_name="MaterialEditor.log"
log_file_name="MaterialEditor.log",
enable_prefab_system=False,
)
@@ -23,6 +23,8 @@ 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"]
enable_prefab_system = False
@pytest.mark.test_case_id("C34603773")
class AtomGPU_BasicLevelSetup_SetsUpLevel(EditorSharedTest):
use_null_renderer = False # Default is True
@@ -85,6 +85,7 @@ class TestAtomEditorComponentsMain(object):
halt_on_unexpected=True,
null_renderer=True,
cfg_args=cfg_args,
enable_prefab_system=False,
)
@@ -155,5 +156,6 @@ class TestMaterialEditorBasicTests(object):
halt_on_unexpected=True,
null_renderer=True,
log_file_name="MaterialEditor.log",
enable_prefab_system=False,
)
@@ -23,28 +23,28 @@ from base import TestAutomationBase
class TestAutomation(TestAutomationBase):
def test_ActorSplitsAfterCollision(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterCollision as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ActorSplitsAfterRadialDamage(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterRadialDamage as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ActorSplitsAfterCapsuleDamage(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterCapsuleDamage as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ActorSplitsAfterImpactSpreadDamage(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterImpactSpreadDamage as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ActorSplitsAfterShearDamage(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterShearDamage as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ActorSplitsAfterTriangleDamage(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterTriangleDamage as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ActorSplitsAfterStressDamage(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterStressDamage as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@@ -51,5 +51,6 @@ class TestComponentAssetListAutomation(object):
editor,
"ComponentUpdateListProperty_test_case.py",
expected_lines=expected_lines,
cfg_args=[level]
cfg_args=[level],
enable_prefab_system=False,
)
@@ -28,7 +28,7 @@ Assuming CMake is already setup on your operating system, below are some sample
cd /path/to/od3e/
mkdir windows_vs2019
cd windows_vs2019
cmake .. -G "Visual Studio 16 2019" -A x64 -T host=x64 -DLY_3RDPARTY_PATH="%3RDPARTYPATH%" -DLY_PROJECTS=AutomatedTesting
cmake .. -G "Visual Studio 16 2019" -DLY_PROJECTS=AutomatedTesting
To manually install the project in development mode using your own installed Python interpreter:
cd /path/to/od3e/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools
@@ -57,6 +57,10 @@ def add_level_component(component_name):
level_component_list, entity.EntityType().Level)
level_component_outcome = editor.EditorLevelComponentAPIBus(bus.Broadcast, 'AddComponentsOfType',
[level_component_type_ids_list[0]])
if not level_component_outcome.IsSuccess():
print('Failed to add {} level component'.format(component_name))
return None
level_component = level_component_outcome.GetValue()[0]
return level_component
@@ -29,7 +29,7 @@ def teardown_editor(editor):
def launch_and_validate_results(request, test_directory, editor, editor_script, expected_lines, unexpected_lines=[],
halt_on_unexpected=False, run_python="--runpythontest", auto_test_mode=True, null_renderer=True, cfg_args=[],
timeout=300, log_file_name="Editor.log"):
timeout=300, log_file_name="Editor.log", enable_prefab_system=True):
"""
Runs the Editor with the specified script, and monitors for expected log lines.
:param request: Special fixture providing information of the requesting test function.
@@ -45,17 +45,22 @@ def launch_and_validate_results(request, test_directory, editor, editor_script,
:param cfg_args: Additional arguments for CFG, such as LevelName.
:param timeout: Length of time for test to run. Default is 60.
:param log_file_name: Name of the log file created by the editor. Defaults to 'Editor.log'
:param enable_prefab_system: Flag to determine whether to use new prefab system or use deprecated slice system. Defaults to True.
"""
test_case = os.path.join(test_directory, editor_script)
request.addfinalizer(lambda: teardown_editor(editor))
logger.debug("Running automated test: {}".format(editor_script))
editor.args.extend(["--skipWelcomeScreenDialog", "--regset=/Amazon/Settings/EnableSourceControl=false",
"--regset=/Amazon/Preferences/EnablePrefabSystem=false", run_python, test_case,
run_python, test_case,
f"--pythontestcase={request.node.name}", "--runpythonargs", " ".join(cfg_args)])
if auto_test_mode:
editor.args.extend(["--autotest_mode"])
if null_renderer:
editor.args.extend(["-rhi=Null"])
if enable_prefab_system:
editor.args.extend(["--regset=/Amazon/Preferences/EnablePrefabSystem=true"])
else:
editor.args.extend(["--regset=/Amazon/Preferences/EnablePrefabSystem=false"])
with editor.start():
@@ -23,7 +23,6 @@ from base import TestAutomationBase
class TestAutomation(TestAutomationBase):
def _run_prefab_test(self, request, workspace, editor, test_module, batch_mode=True, autotest_mode=True):
self._run_test(request, workspace, editor, test_module,
extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"],
batch_mode=batch_mode,
autotest_mode=autotest_mode)
@@ -24,7 +24,6 @@ from base import TestAutomationBase
class TestAutomation(TestAutomationBase):
def _run_prefab_test(self, request, workspace, editor, test_module, batch_mode=True, autotest_mode=True):
self._run_test(request, workspace, editor, test_module,
extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"],
batch_mode=batch_mode,
autotest_mode=autotest_mode)
@@ -25,8 +25,8 @@ class TestAutomation(TestAutomationBase):
def test_NvCloth_AddClothSimulationToMesh(self, request, workspace, editor, launcher_platform):
from .tests import NvCloth_AddClothSimulationToMesh as test_module
self._run_test(request, workspace, editor, test_module, use_null_renderer = self.use_null_renderer)
self._run_test(request, workspace, editor, test_module, use_null_renderer = self.use_null_renderer, enable_prefab_system=False)
def test_NvCloth_AddClothSimulationToActor(self, request, workspace, editor, launcher_platform):
from .tests import NvCloth_AddClothSimulationToActor as test_module
self._run_test(request, workspace, editor, test_module, use_null_renderer = self.use_null_renderer)
self._run_test(request, workspace, editor, test_module, use_null_renderer = self.use_null_renderer, enable_prefab_system=False)
@@ -26,158 +26,158 @@ class TestAutomation(TestAutomationBase):
r"AutomatedTesting\Levels\Physics\Material_LibraryCrudOperationsReflectOnRagdollBones")
def test_Material_LibraryCrudOperationsReflectOnRagdollBones(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_LibraryCrudOperationsReflectOnRagdollBones as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Material_RagdollBones(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_RagdollBones as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@fm.file_revert("c15308221_material_componentsinsyncwithlibrary.physmaterial",
r"AutomatedTesting\Levels\Physics\Material_ComponentsInSyncWithLibrary")
def test_Material_ComponentsInSyncWithLibrary(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_ComponentsInSyncWithLibrary as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
# BUG: LY-107723")
def test_ScriptCanvas_SetKinematicTargetTransform(self, request, workspace, editor, launcher_platform):
from .tests.script_canvas import ScriptCanvas_SetKinematicTargetTransform as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
# Failing, PhysXTerrain
@fm.file_revert("c4925579_material_addmodifydeleteonterrain.physmaterial",
r"AutomatedTesting\Levels\Physics\Material_LibraryCrudOperationsReflectOnTerrain")
def test_Material_LibraryCrudOperationsReflectOnTerrain(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_LibraryCrudOperationsReflectOnTerrain as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
# Failing, PhysXTerrain
def test_Terrain_TerrainTexturePainterWorks(self, request, workspace, editor, launcher_platform):
from .tests.terrain import Terrain_TerrainTexturePainterWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
# Failing, PhysXTerrain
def test_Material_CanBeAssignedToTerrain(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_CanBeAssignedToTerrain as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
# Failing, PhysXTerrain
def test_Material_DefaultLibraryConsistentOnAllFeatures(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_DefaultLibraryConsistentOnAllFeatures as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
# Failing, PhysXTerrain
@fm.file_revert("all_ones_1.physmaterial", r"AutomatedTesting\Levels\Physics\Material_DefaultMaterialLibraryChangesWork")
@fm.file_override("default.physxconfiguration", "Material_DefaultMaterialLibraryChangesWork.physxconfiguration", "AutomatedTesting")
def test_Material_DefaultMaterialLibraryChangesWork(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_DefaultMaterialLibraryChangesWork as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Collider_SameCollisionGroupSameLayerCollide(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_SameCollisionGroupSameLayerCollide as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Ragdoll_OldRagdollSerializationNoErrors(self, request, workspace, editor, launcher_platform):
from .tests.ragdoll import Ragdoll_OldRagdollSerializationNoErrors as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@fm.file_override("default.physxconfiguration", "ScriptCanvas_OverlapNode.physxconfiguration")
def test_ScriptCanvas_OverlapNode(self, request, workspace, editor, launcher_platform):
from .tests.script_canvas import ScriptCanvas_OverlapNode as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Material_StaticFriction(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_StaticFriction as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@fm.file_revert("c4888315_material_addmodifydeleteoncollider.physmaterial",
r"AutomatedTesting\Levels\Physics\Material_LibraryCrudOperationsReflectOnCollider")
def test_Material_LibraryCrudOperationsReflectOnCollider(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_LibraryCrudOperationsReflectOnCollider as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@fm.file_revert("c15563573_material_addmodifydeleteoncharactercontroller.physmaterial",
r"AutomatedTesting\Levels\Physics\Material_LibraryCrudOperationsReflectOnCharacterController")
def test_Material_LibraryCrudOperationsReflectOnCharacterController(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_LibraryCrudOperationsReflectOnCharacterController as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@fm.file_revert("c4888315_material_addmodifydeleteoncollider.physmaterial",
r"AutomatedTesting\Levels\Physics\Material_LibraryCrudOperationsReflectOnCollider")
def test_Material_LibraryCrudOperationsReflectOnCollider(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_LibraryCrudOperationsReflectOnCollider as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@fm.file_revert("c15563573_material_addmodifydeleteoncharactercontroller.physmaterial",
r"AutomatedTesting\Levels\Physics\Material_LibraryCrudOperationsReflectOnCharacterController")
def test_Material_LibraryCrudOperationsReflectOnCharacterController(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_LibraryCrudOperationsReflectOnCharacterController as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@fm.file_revert("c4044455_material_librarychangesinstantly.physmaterial",
r"AutomatedTesting\Levels\Physics\C4044455_Material_LibraryChangesInstantly")
def test_Material_LibraryChangesReflectInstantly(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_LibraryChangesReflectInstantly as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@fm.file_revert("Material_LibraryUpdatedAcrossLevels.physmaterial",
r"AutomatedTesting\Levels\Physics\Material_LibraryUpdatedAcrossLevels")
def test_Material_LibraryUpdatedAcrossLevels(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_LibraryUpdatedAcrossLevels as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_RigidBody_LinearDampingAffectsMotion(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_LinearDampingAffectsMotion as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Terrain_CollisionAgainstRigidBody(self, request, workspace, editor, launcher_platform):
from .tests.terrain import Terrain_CollisionAgainstRigidBody as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ShapeCollider_CylinderShapeCollides(self, request, workspace, editor, launcher_platform):
from .tests.collider import ShapeCollider_CylinderShapeCollides as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Physics_WorldBodyBusWorksOnEditorComponents(self, request, workspace, editor, launcher_platform):
from .tests import Physics_WorldBodyBusWorksOnEditorComponents as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Collider_PxMeshErrorIfNoMesh(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_PxMeshErrorIfNoMesh as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ForceRegion_ImpulsesBoxShapedRigidBody(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_ImpulsesBoxShapedRigidBody as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Terrain_SpawnSecondTerrainComponentWarning(self, request, workspace, editor, launcher_platform):
from .tests.terrain import Terrain_SpawnSecondTerrainComponentWarning as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Terrain_AddPhysTerrainComponent(self, request, workspace, editor, launcher_platform):
from .tests.terrain import Terrain_AddPhysTerrainComponent as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Terrain_CanAddMultipleTerrainComponents(self, request, workspace, editor, launcher_platform):
from .tests.terrain import Terrain_CanAddMultipleTerrainComponents as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Terrain_MultipleTerrainComponentsWarning(self, request, workspace, editor, launcher_platform):
from .tests.terrain import Terrain_MultipleTerrainComponentsWarning as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Terrain_MultipleTerrainComponentsWarning(self, request, workspace, editor, launcher_platform):
from .tests.terrain import Terrain_MultipleTerrainComponentsWarning as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ForceRegion_HighValuesDirectionAxesWorkWithNoError(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_HighValuesDirectionAxesWorkWithNoError as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Terrain_MultipleResolutionsValid(self, request, workspace, editor, launcher_platform):
from .tests.terrain import Terrain_MultipleResolutionsValid as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ForceRegion_SmallMagnitudeDeviationOnLargeForces(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_SmallMagnitudeDeviationOnLargeForces as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@@ -30,33 +30,33 @@ class TestAutomation(TestAutomationBase):
def test_RigidBody_EnablingGravityWorksUsingNotificationsPoC(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_EnablingGravityWorksUsingNotificationsPoC as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_LocalSpaceForceOnRigidBodies(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_LocalSpaceForceOnRigidBodies as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','Material_DynamicFriction.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_Material_DynamicFriction(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_DynamicFriction as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Collider_SameCollisionGroupDiffLayersCollide(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_SameCollisionGroupDiffLayersCollide as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_CharacterController_SwitchLevels(self, request, workspace, editor, launcher_platform):
from .tests.character_controller import CharacterController_SwitchLevels as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Ragdoll_AddPhysxRagdollComponentWorks(self, request, workspace, editor, launcher_platform):
from .tests.ragdoll import Ragdoll_AddPhysxRagdollComponentWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ScriptCanvas_MultipleRaycastNode(self, request, workspace, editor, launcher_platform):
@@ -64,43 +64,43 @@ class TestAutomation(TestAutomationBase):
# Fixme: This test previously relied on unexpected lines log reading with is now not supported.
# Now the log reading must be done inside the test, preferably with the Tracer() utility
# unexpected_lines = ["Assert"] + test_module.Lines.unexpected
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','Collider_DiffCollisionGroupDiffCollidingLayersNotCollide.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_Collider_DiffCollisionGroupDiffCollidingLayersNotCollide(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_DiffCollisionGroupDiffCollidingLayersNotCollide as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Joints_HingeLeadFollowerCollide(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_HingeLeadFollowerCollide as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Collider_PxMeshConvexMeshCollides(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_PxMeshConvexMeshCollides as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ShapeCollider_CylinderShapeCollides(self, request, workspace, editor, launcher_platform):
from .tests.shape_collider import ShapeCollider_CylinderShapeCollides as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_C15425929_Undo_Redo(self, request, workspace, editor, launcher_platform):
from .tests import Physics_UndoRedoWorksOnEntityWithPhysComponents as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.GROUP_tick
@pytest.mark.xfail(reason="Test still under development.")
def test_Tick_InterpolatedRigidBodyMotionIsSmooth(self, request, workspace, editor, launcher_platform):
from .tests.tick import Tick_InterpolatedRigidBodyMotionIsSmooth as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.GROUP_tick
@pytest.mark.xfail(reason="Test still under development.")
def test_Tick_CharacterGameplayComponentMotionIsSmooth(self, request, workspace, editor, launcher_platform):
from .tests.tick import Tick_CharacterGameplayComponentMotionIsSmooth as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@@ -59,9 +59,6 @@ class EditorSingleTest_WithFileOverrides(EditorSingleTest):
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomationWithPrefabSystemEnabled(EditorTestSuite):
global_extra_cmdline_args = ['-BatchMode', '-autotest_mode',
'--regset=/Amazon/Preferences/EnablePrefabSystem=true']
@staticmethod
def get_number_parallel_editors():
return 16
@@ -81,6 +78,8 @@ class TestAutomationWithPrefabSystemEnabled(EditorTestSuite):
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
enable_prefab_system = False
@staticmethod
def get_number_parallel_editors():
return 16
@@ -31,223 +31,223 @@ class TestAutomation(TestAutomationBase):
@revert_physics_config
def test_Terrain_NoPhysTerrainComponentNoCollision(self, request, workspace, editor, launcher_platform):
from .tests.terrain import Terrain_NoPhysTerrainComponentNoCollision as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_InitialLinearVelocity(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_InitialLinearVelocity as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_StartGravityEnabledWorks(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_StartGravityEnabledWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_KinematicModeWorks(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_KinematicModeWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_LinearDampingForceOnRigidBodies(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_LinearDampingForceOnRigidBodies as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_SimpleDragForceOnRigidBodies(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_SimpleDragForceOnRigidBodies as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_CapsuleShapedForce(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_CapsuleShapedForce as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_ImpulsesCapsuleShapedRigidBody(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_ImpulsesCapsuleShapedRigidBody as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_MomentOfInertiaManualSetting(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_MomentOfInertiaManualSetting as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_COM_ManualSettingWorks(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_COM_ManualSettingWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_AddRigidBodyComponent(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_AddRigidBodyComponent as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_SplineForceOnRigidBodies(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_SplineForceOnRigidBodies as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','Material_RestitutionCombine.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_Material_RestitutionCombine(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_RestitutionCombine as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','Material_FrictionCombine.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_Material_FrictionCombine(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_FrictionCombine as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Collider_ColliderPositionOffset(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_ColliderPositionOffset as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_AngularDampingAffectsRotation(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_AngularDampingAffectsRotation as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Physics_VerifyColliderRigidBodyMeshAndTerrainWorkTogether(self, request, workspace, editor, launcher_platform):
from .tests import Physics_VerifyColliderRigidBodyMeshAndTerrainWorkTogether as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_MultipleForcesInSameComponentCombineForces(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_MultipleForcesInSameComponentCombineForces as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_ImpulsesPxMeshShapedRigidBody(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_ImpulsesPxMeshShapedRigidBody as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ScriptCanvas_TriggerEvents(self, request, workspace, editor, launcher_platform):
from .tests.script_canvas import ScriptCanvas_TriggerEvents as test_module
# FIXME: expected_lines = test_module.LogLines.expected_lines
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_ZeroPointForceDoesNothing(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_ZeroPointForceDoesNothing as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_ZeroWorldSpaceForceDoesNothing(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_ZeroWorldSpaceForceDoesNothing as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_ZeroLinearDampingDoesNothing(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_ZeroLinearDampingDoesNothing as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_MovingForceRegionChangesNetForce(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_MovingForceRegionChangesNetForce as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ScriptCanvas_CollisionEvents(self, request, workspace, editor, launcher_platform):
from .tests.script_canvas import ScriptCanvas_CollisionEvents as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_DirectionHasNoAffectOnTotalForce(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_DirectionHasNoAffectOnTotalForce as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_StartAsleepWorks(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_StartAsleepWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_SliceFileInstantiates(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_SliceFileInstantiates as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_ZeroLocalSpaceForceDoesNothing(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_ZeroLocalSpaceForceDoesNothing as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_ZeroSimpleDragForceDoesNothing(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_ZeroSimpleDragForceDoesNothing as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_COM_ComputingWorks(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_COM_ComputingWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_MassDifferentValuesWorks(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_MassDifferentValuesWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','Material_RestitutionCombinePriorityOrder.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_Material_RestitutionCombinePriorityOrder(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_RestitutionCombinePriorityOrder as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_SplineRegionWithModifiedTransform(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_SplineRegionWithModifiedTransform as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ScriptCanvas_ShapeCast(self, request, workspace, editor, launcher_platform):
from .tests.script_canvas import ScriptCanvas_ShapeCast as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_InitialAngularVelocity(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_InitialAngularVelocity as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_ZeroSplineForceDoesNothing(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_ZeroSplineForceDoesNothing as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Physics_DynamicSliceWithPhysNotSpawnsStaticSlice(self, request, workspace, editor, launcher_platform):
from .tests import Physics_DynamicSliceWithPhysNotSpawnsStaticSlice as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_PositionOffset(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_PositionOffset as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','Material_FrictionCombinePriorityOrder.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_Material_FrictionCombinePriorityOrder(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_FrictionCombinePriorityOrder as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(
reason="Something with the CryRenderer disabling is causing this test to fail now.")
@revert_physics_config
def test_Ragdoll_LevelSwitchDoesNotCrash(self, request, workspace, editor, launcher_platform):
from .tests.ragdoll import Ragdoll_LevelSwitchDoesNotCrash as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_MultipleComponentsCombineForces(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_MultipleComponentsCombineForces as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
# Marking the test as an expected failure due to sporadic failure on Automated Review: LYN-2580
# The test still runs, but a failure of the test doesn't result in the test run failing
@@ -258,102 +258,102 @@ class TestAutomation(TestAutomationBase):
'AutomatedTesting/Registry', search_subdirs=True)
def test_Material_PerFaceMaterialGetsCorrectMaterial(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_PerFaceMaterialGetsCorrectMaterial as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(
reason="This test will sometimes fail as the ball will continue to roll before the timeout is reached.")
@revert_physics_config
def test_RigidBody_SleepWhenBelowKineticThreshold(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_SleepWhenBelowKineticThreshold as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_COM_NotIncludesTriggerShapes(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_COM_NotIncludesTriggerShapes as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Material_NoEffectIfNoColliderShape(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_NoEffectIfNoColliderShape as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Collider_TriggerPassThrough(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_TriggerPassThrough as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_SetGravityWorks(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_SetGravityWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','Material_CharacterController.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_Material_CharacterController(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_CharacterController as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Material_EmptyLibraryUsesDefault(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_EmptyLibraryUsesDefault as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_NoQuiverOnHighLinearDampingForce(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_NoQuiverOnHighLinearDampingForce as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_RigidBody_ComputeInertiaWorks(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_ComputeInertiaWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ScriptCanvas_PostPhysicsUpdate(self, request, workspace, editor, launcher_platform):
from .tests.script_canvas import ScriptCanvas_PostPhysicsUpdate as test_module
# Fixme: unexpected_lines = ["Assert"] + test_module.Lines.unexpected
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','Collider_NoneCollisionGroupSameLayerNotCollide.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_Collider_NoneCollisionGroupSameLayerNotCollide(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_NoneCollisionGroupSameLayerNotCollide as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','Collider_SameCollisionGroupSameCustomLayerCollide.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_Collider_SameCollisionGroupSameCustomLayerCollide(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_SameCollisionGroupSameCustomLayerCollide as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxdefaultsceneconfiguration.setreg','ScriptCanvas_PostUpdateEvent.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_ScriptCanvas_PostUpdateEvent(self, request, workspace, editor, launcher_platform):
from .tests.script_canvas import ScriptCanvas_PostUpdateEvent as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','Material_Restitution.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_Material_Restitution(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_Restitution as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxdefaultsceneconfiguration.setreg', 'ScriptCanvas_PreUpdateEvent.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_ScriptCanvas_PreUpdateEvent(self, request, workspace, editor, launcher_platform):
from .tests.script_canvas import ScriptCanvas_PreUpdateEvent as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_PxMeshShapedForce(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_PxMeshShapedForce as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
# Marking the Test as expected to fail using the xfail decorator due to sporadic failure on Automated Review: SPEC-3146
# The test still runs, but a failure of the test doesn't result in the test run failing
@@ -361,176 +361,173 @@ class TestAutomation(TestAutomationBase):
@revert_physics_config
def test_RigidBody_MaxAngularVelocityWorks(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_MaxAngularVelocityWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Joints_HingeSoftLimitsConstrained(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_HingeSoftLimitsConstrained as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Joints_BallSoftLimitsConstrained(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_BallSoftLimitsConstrained as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Joints_BallLeadFollowerCollide(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_BallLeadFollowerCollide as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','Collider_AddingNewGroupWorks.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_Collider_AddingNewGroupWorks(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_AddingNewGroupWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ShapeCollider_InactiveWhenNoShapeComponent(self, request, workspace, editor, launcher_platform):
from .tests.shape_collider import ShapeCollider_InactiveWhenNoShapeComponent as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Collider_CheckDefaultShapeSettingIsPxMesh(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_CheckDefaultShapeSettingIsPxMesh as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ShapeCollider_LargeNumberOfShapeCollidersWontCrashEditor(self, request, workspace, editor, launcher_platform):
from .tests.shape_collider import ShapeCollider_LargeNumberOfShapeCollidersWontCrashEditor as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Collider_SphereShapeEditing(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_SphereShapeEditing as test_module
self._run_test(request, workspace, editor, test_module,
extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"])
self._run_test(request, workspace, editor, test_module)
@revert_physics_config
def test_Collider_BoxShapeEditing(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_BoxShapeEditing as test_module
self._run_test(request, workspace, editor, test_module,
extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"])
self._run_test(request, workspace, editor, test_module)
@revert_physics_config
def test_Collider_CapsuleShapeEditing(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_CapsuleShapeEditing as test_module
self._run_test(request, workspace, editor, test_module,
extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"])
self._run_test(request, workspace, editor, test_module)
def test_ForceRegion_WithNonTriggerColliderWarning(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_WithNonTriggerColliderWarning as test_module
# Fixme: expected_lines = ["[Warning] (PhysX Force Region) - Please ensure collider component marked as trigger exists in entity"]
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ForceRegion_WorldSpaceForceOnRigidBodies(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_WorldSpaceForceOnRigidBodies as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ForceRegion_PointForceOnRigidBodies(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_PointForceOnRigidBodies as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ForceRegion_SphereShapedForce(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_SphereShapedForce as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ForceRegion_RotationalOffset(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_RotationalOffset as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Material_LibraryClearingAssignsDefault(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_LibraryClearingAssignsDefault as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Collider_AddColliderComponent(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_AddColliderComponent as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(
reason="This will fail due to this issue ATOM-15487.")
def test_Collider_PxMeshAutoAssignedWhenModifyingRenderMeshComponent(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_PxMeshAutoAssignedWhenModifyingRenderMeshComponent as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Collider_PxMeshAutoAssignedWhenAddingRenderMeshComponent(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_PxMeshAutoAssignedWhenAddingRenderMeshComponent as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Collider_MultipleSurfaceSlots(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_MultipleSurfaceSlots as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Collider_PxMeshNotAutoAssignedWhenNoPhysicsFbx(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_PxMeshNotAutoAssignedWhenNoPhysicsFbx as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_RigidBody_EnablingGravityWorksPoC(self, request, workspace, editor, launcher_platform):
from .tests.rigid_body import RigidBody_EnablingGravityWorksPoC as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
@fm.file_override('physxsystemconfiguration.setreg','Collider_CollisionGroupsWorkflow.setreg_override',
'AutomatedTesting/Registry', search_subdirs=True)
def test_Collider_CollisionGroupsWorkflow(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_CollisionGroupsWorkflow as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Collider_ColliderRotationOffset(self, request, workspace, editor, launcher_platform):
from .tests.collider import Collider_ColliderRotationOffset as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ForceRegion_ParentChildForcesCombineForces(self, request, workspace, editor, launcher_platform):
from .tests.force_region import ForceRegion_ParentChildForcesCombineForces as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_ShapeCollider_CanBeAddedWitNoWarnings(self, request, workspace, editor, launcher_platform):
from .tests.shape_collider import ShapeCollider_CanBeAddedWitNoWarnings as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Physics_UndoRedoWorksOnEntityWithPhysComponents(self, request, workspace, editor, launcher_platform):
from .tests import Physics_UndoRedoWorksOnEntityWithPhysComponents as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Joints_Fixed2BodiesConstrained(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_Fixed2BodiesConstrained as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Joints_Hinge2BodiesConstrained(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_Hinge2BodiesConstrained as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Joints_Ball2BodiesConstrained(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_Ball2BodiesConstrained as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Joints_FixedBreakable(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_FixedBreakable as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Joints_HingeBreakable(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_HingeBreakable as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Joints_BallBreakable(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_BallBreakable as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Joints_HingeNoLimitsConstrained(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_HingeNoLimitsConstrained as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Joints_BallNoLimitsConstrained(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_BallNoLimitsConstrained as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Joints_GlobalFrameConstrained(self, request, workspace, editor, launcher_platform):
from .tests.joints import Joints_GlobalFrameConstrained as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@revert_physics_config
def test_Material_DefaultLibraryUpdatedAcrossLevels(self, request, workspace, editor, launcher_platform):
@@ -540,7 +537,7 @@ class TestAutomation(TestAutomationBase):
search_subdirs=True)
def levels_before(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_DefaultLibraryUpdatedAcrossLevels_before as test_module_0
self._run_test(request, workspace, editor, test_module_0)
self._run_test(request, workspace, editor, test_module_0, enable_prefab_system=False)
# File override replaces the previous physxconfiguration file with another where the only difference is the default material library
@fm.file_override("physxsystemconfiguration.setreg",
@@ -549,7 +546,7 @@ class TestAutomation(TestAutomationBase):
search_subdirs=True)
def levels_after(self, request, workspace, editor, launcher_platform):
from .tests.material import Material_DefaultLibraryUpdatedAcrossLevels_after as test_module_1
self._run_test(request, workspace, editor, test_module_1)
self._run_test(request, workspace, editor, test_module_1, enable_prefab_system=False)
levels_before(self, request, workspace, editor, launcher_platform)
levels_after(self, request, workspace, editor, launcher_platform)
@@ -32,7 +32,7 @@ class TestAutomation(TestAutomationBase):
def test_ScriptCanvas_GetCollisionNameReturnsName(self, request, workspace, editor, launcher_platform):
from .tests.script_canvas import ScriptCanvas_GetCollisionNameReturnsName as test_module
# Fixme: expected_lines=["Layer Name: Right"]
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
## Seems to be flaky, need to investigate
def test_ScriptCanvas_GetCollisionNameReturnsNothingWhenHasToggledLayer(self, request, workspace, editor, launcher_platform):
@@ -42,4 +42,4 @@ class TestAutomation(TestAutomationBase):
# Fixme: for group in collision_groups:
# Fixme: unexpected_lines.append(f"GroupName: {group}")
# Fixme: expected_lines=["GroupName: "]
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@@ -30,11 +30,11 @@ class TestUtils(TestAutomationBase):
expected_lines = []
unexpected_lines = ["Assert"]
self._run_test(request, workspace, editor, physmaterial_editor_test_module, expected_lines, unexpected_lines)
self._run_test(request, workspace, editor, physmaterial_editor_test_module, expected_lines, unexpected_lines, enable_prefab_system=False)
def test_UtilTest_Tracer_PicksErrorsAndWarnings(self, request, workspace, launcher_platform, editor):
from .utils import UtilTest_Tracer_PicksErrorsAndWarnings as testcase_module
self._run_test(request, workspace, editor, testcase_module, [], [])
self._run_test(request, workspace, editor, testcase_module, [], [], enable_prefab_system=False)
def test_FileManagement_FindingFiles(self, workspace, launcher_platform):
"""
@@ -263,4 +263,4 @@ class TestUtils(TestAutomationBase):
expected_lines = []
unexpected_lines = ["Assert"]
self._run_test(request, workspace, editor, test_module, expected_lines, unexpected_lines)
self._run_test(request, workspace, editor, test_module, expected_lines, unexpected_lines, enable_prefab_system=False)
@@ -12,7 +12,6 @@ import pytest
import os
import sys
from ly_test_tools import LAUNCHERS
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@@ -24,42 +23,41 @@ class TestAutomation(TestAutomationBase):
def _run_prefab_test(self, request, workspace, editor, test_module, batch_mode=True, autotest_mode=True):
self._run_test(request, workspace, editor, test_module,
extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"],
batch_mode=batch_mode,
autotest_mode=autotest_mode)
def test_PrefabLevel_OpensLevelWithEntities(self, request, workspace, editor, launcher_platform):
from .tests import PrefabLevel_OpensLevelWithEntities as test_module
def test_OpenLevel_ContainingTwoEntities(self, request, workspace, editor, launcher_platform):
from Prefab.tests.open_level import OpenLevel_ContainingTwoEntities as test_module
self._run_prefab_test(request, workspace, editor, test_module)
def test_PrefabBasicWorkflow_CreatePrefab(self, request, workspace, editor, launcher_platform):
from .tests import PrefabBasicWorkflow_CreatePrefab as test_module
def test_CreatePrefab_WithSingleEntity(self, request, workspace, editor, launcher_platform):
from Prefab.tests.create_prefab import CreatePrefab_WithSingleEntity as test_module
self._run_prefab_test(request, workspace, editor, test_module)
def test_PrefabBasicWorkflow_InstantiatePrefab(self, request, workspace, editor, launcher_platform):
from .tests import PrefabBasicWorkflow_InstantiatePrefab as test_module
def test_InstantiatePrefab_ContainingASingleEntity(self, request, workspace, editor, launcher_platform):
from Prefab.tests.instantiate_prefab import InstantiatePrefab_ContainingASingleEntity as test_module
self._run_prefab_test(request, workspace, editor, test_module)
def test_PrefabBasicWorkflow_CreateAndDeletePrefab(self, request, workspace, editor, launcher_platform):
from .tests import PrefabBasicWorkflow_CreateAndDeletePrefab as test_module
def test_DeletePrefab_ContainingASingleEntity(self, request, workspace, editor, launcher_platform):
from Prefab.tests.delete_prefab import DeletePrefab_ContainingASingleEntity as test_module
self._run_prefab_test(request, workspace, editor, test_module)
def test_PrefabBasicWorkflow_CreateAndReparentPrefab(self, request, workspace, editor, launcher_platform):
from .tests import PrefabBasicWorkflow_CreateAndReparentPrefab as test_module
def test_ReparentPrefab_UnderAnotherPrefab(self, request, workspace, editor, launcher_platform):
from Prefab.tests.reparent_prefab import ReparentPrefab_UnderAnotherPrefab as test_module
self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False)
def test_PrefabBasicWorkflow_CreateReparentAndDetachPrefab(self, request, workspace, editor, launcher_platform):
from .tests import PrefabBasicWorkflow_CreateReparentAndDetachPrefab as test_module
def test_DetachPrefab_UnderAnotherPrefab(self, request, workspace, editor, launcher_platform):
from Prefab.tests.detach_prefab import DetachPrefab_UnderAnotherPrefab as test_module
self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False)
def test_PrefabBasicWorkflow_CreateAndDuplicatePrefab(self, request, workspace, editor, launcher_platform):
from .tests import PrefabBasicWorkflow_CreateAndDuplicatePrefab as test_module
def test_DuplicatePrefab_ContainingASingleEntity(self, request, workspace, editor, launcher_platform):
from Prefab.tests.duplicate_prefab import DuplicatePrefab_ContainingASingleEntity as test_module
self._run_prefab_test(request, workspace, editor, test_module)
def test_PrefabComplexWorflow_CreatePrefabOfChildEntity(self, request, workspace, editor, launcher_platform):
from .tests import PrefabComplexWorflow_CreatePrefabOfChildEntity as test_module
def test_CreatePrefab_UnderAnEntity(self, request, workspace, editor, launcher_platform):
from Prefab.tests.create_prefab import CreatePrefab_UnderAnEntity as test_module
self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False)
def test_PrefabComplexWorflow_CreatePrefabInsidePrefab(self, request, workspace, editor, launcher_platform):
from .tests import PrefabComplexWorflow_CreatePrefabInsidePrefab as test_module
def test_CreatePrefab_UnderAnotherPrefab(self, request, workspace, editor, launcher_platform):
from Prefab.tests.create_prefab import CreatePrefab_UnderAnotherPrefab as test_module
self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False)
@@ -5,7 +5,7 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
def PrefabComplexWorflow_CreatePrefabOfChildEntity():
def CreatePrefab_UnderAnEntity():
"""
Test description:
- Creates two entities, parent and child. Child entity has Parent entity as its parent.
@@ -18,7 +18,7 @@ def PrefabComplexWorflow_CreatePrefabOfChildEntity():
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.prefab_utils import Prefab
import PrefabTestUtils as prefab_test_utils
import Prefab.tests.PrefabTestUtils as prefab_test_utils
prefab_test_utils.open_base_tests_level()
@@ -49,4 +49,4 @@ def PrefabComplexWorflow_CreatePrefabOfChildEntity():
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(PrefabComplexWorflow_CreatePrefabOfChildEntity)
Report.start_test(CreatePrefab_UnderAnEntity)
@@ -5,7 +5,7 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
def PrefabComplexWorflow_CreatePrefabInsidePrefab():
def CreatePrefab_UnderAnotherPrefab():
"""
Test description:
- Creates an entity with a physx collider
@@ -17,7 +17,7 @@ def PrefabComplexWorflow_CreatePrefabInsidePrefab():
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.prefab_utils import Prefab
import PrefabTestUtils as prefab_test_utils
import Prefab.tests.PrefabTestUtils as prefab_test_utils
prefab_test_utils.open_base_tests_level()
@@ -54,4 +54,4 @@ def PrefabComplexWorflow_CreatePrefabInsidePrefab():
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(PrefabComplexWorflow_CreatePrefabInsidePrefab)
Report.start_test(CreatePrefab_UnderAnotherPrefab)
@@ -5,7 +5,7 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
def PrefabBasicWorkflow_CreatePrefab():
def CreatePrefab_WithSingleEntity():
CAR_PREFAB_FILE_NAME = 'car_prefab'
@@ -13,7 +13,7 @@ def PrefabBasicWorkflow_CreatePrefab():
from editor_python_test_tools.utils import Report
from editor_python_test_tools.prefab_utils import Prefab
import PrefabTestUtils as prefab_test_utils
import Prefab.tests.PrefabTestUtils as prefab_test_utils
prefab_test_utils.open_base_tests_level()
@@ -26,4 +26,4 @@ def PrefabBasicWorkflow_CreatePrefab():
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(PrefabBasicWorkflow_CreatePrefab)
Report.start_test(CreatePrefab_WithSingleEntity)
@@ -5,14 +5,14 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
def PrefabBasicWorkflow_CreateAndDeletePrefab():
def DeletePrefab_ContainingASingleEntity():
CAR_PREFAB_FILE_NAME = 'car_prefab'
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.prefab_utils import Prefab
import PrefabTestUtils as prefab_test_utils
import Prefab.tests.PrefabTestUtils as prefab_test_utils
prefab_test_utils.open_base_tests_level()
@@ -29,4 +29,4 @@ def PrefabBasicWorkflow_CreateAndDeletePrefab():
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(PrefabBasicWorkflow_CreateAndDeletePrefab)
Report.start_test(DeletePrefab_ContainingASingleEntity)
@@ -5,7 +5,7 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
def PrefabBasicWorkflow_CreateReparentAndDetachPrefab():
def DetachPrefab_UnderAnotherPrefab():
CAR_PREFAB_FILE_NAME = 'car_prefab'
WHEEL_PREFAB_FILE_NAME = 'wheel_prefab'
@@ -18,7 +18,7 @@ def PrefabBasicWorkflow_CreateReparentAndDetachPrefab():
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.prefab_utils import Prefab
import PrefabTestUtils as prefab_test_utils
import Prefab.tests.PrefabTestUtils as prefab_test_utils
prefab_test_utils.open_base_tests_level()
@@ -48,4 +48,4 @@ def PrefabBasicWorkflow_CreateReparentAndDetachPrefab():
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(PrefabBasicWorkflow_CreateReparentAndDetachPrefab)
Report.start_test(DetachPrefab_UnderAnotherPrefab)
@@ -5,14 +5,14 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
def PrefabBasicWorkflow_CreateAndDuplicatePrefab():
def DuplicatePrefab_ContainingASingleEntity():
CAR_PREFAB_FILE_NAME = 'car_prefab'
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.prefab_utils import Prefab
import PrefabTestUtils as prefab_test_utils
import Prefab.tests.PrefabTestUtils as prefab_test_utils
prefab_test_utils.open_base_tests_level()
@@ -29,4 +29,4 @@ def PrefabBasicWorkflow_CreateAndDuplicatePrefab():
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(PrefabBasicWorkflow_CreateAndDuplicatePrefab)
Report.start_test(DuplicatePrefab_ContainingASingleEntity)
@@ -5,7 +5,7 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
def PrefabBasicWorkflow_InstantiatePrefab():
def InstantiatePrefab_ContainingASingleEntity():
from azlmbr.math import Vector3
@@ -15,7 +15,7 @@ def PrefabBasicWorkflow_InstantiatePrefab():
from editor_python_test_tools.prefab_utils import Prefab
import PrefabTestUtils as prefab_test_utils
import Prefab.tests.PrefabTestUtils as prefab_test_utils
prefab_test_utils.open_base_tests_level()
@@ -30,4 +30,4 @@ def PrefabBasicWorkflow_InstantiatePrefab():
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(PrefabBasicWorkflow_InstantiatePrefab)
Report.start_test(InstantiatePrefab_ContainingASingleEntity)
@@ -14,7 +14,7 @@ class Tests():
# fmt:on
def PrefabLevel_OpensLevelWithEntities():
def OpenLevel_ContainingTwoEntities():
"""
Opens the level that contains 2 entities, "EmptyEntity" and "EntityWithPxCollider".
This test makes sure that both entities exist after opening the level and that:
@@ -70,4 +70,4 @@ def PrefabLevel_OpensLevelWithEntities():
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(PrefabLevel_OpensLevelWithEntities)
Report.start_test(OpenLevel_ContainingTwoEntities)
@@ -5,7 +5,7 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
def PrefabBasicWorkflow_CreateAndReparentPrefab():
def ReparentPrefab_UnderAnotherPrefab():
CAR_PREFAB_FILE_NAME = 'car_prefab'
WHEEL_PREFAB_FILE_NAME = 'wheel_prefab'
@@ -18,7 +18,7 @@ def PrefabBasicWorkflow_CreateAndReparentPrefab():
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.prefab_utils import Prefab
import PrefabTestUtils as prefab_test_utils
import Prefab.tests.PrefabTestUtils as prefab_test_utils
prefab_test_utils.open_base_tests_level()
@@ -45,4 +45,4 @@ def PrefabBasicWorkflow_CreateAndReparentPrefab():
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(PrefabBasicWorkflow_CreateAndReparentPrefab)
Report.start_test(ReparentPrefab_UnderAnotherPrefab)
@@ -8,42 +8,54 @@ import azlmbr.bus
import azlmbr.asset
import azlmbr.editor
import azlmbr.math
import azlmbr.legacy.general
def raise_and_stop(msg):
print (msg)
print('Starting mock asset tests')
handler = azlmbr.editor.EditorEventBusHandler()
def on_notify_editor_initialized(args):
# These tests are meant to check that the test_asset.mock source asset turned into
# a test_asset.mock_asset product asset via the Python asset builder system
mockAssetType = azlmbr.math.Uuid_CreateString('{9274AD17-3212-4651-9F3B-7DCCB080E467}', 0)
mockAssetPath = 'gem/pythontests/pythonassetbuilder/test_asset.mock_asset'
assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', mockAssetPath, mockAssetType, False)
if (assetId.is_valid() is False):
print(f'Mock AssetId is not valid! Got {assetId.to_string()} instead')
else:
print(f'Mock AssetId is valid!')
assetIdString = assetId.to_string()
if (assetIdString.endswith(':528cca58') is False):
print(f'Mock AssetId {assetIdString} has unexpected sub-id for {mockAssetPath}!')
else:
print(f'Mock AssetId has expected sub-id for {mockAssetPath}!')
print ('Mock asset exists')
# These tests detect if the geom_group.fbx file turns into a number of azmodel product assets
def test_azmodel_product(generatedModelAssetPath):
azModelAssetType = azlmbr.math.Uuid_CreateString('{2C7477B6-69C5-45BE-8163-BCD6A275B6D8}', 0)
assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', generatedModelAssetPath, azModelAssetType, False)
assetIdString = assetId.to_string()
if (assetId.is_valid()):
print(f'AssetId found for asset ({generatedModelAssetPath}) found')
else:
print(f'Asset at path {generatedModelAssetPath} has unexpected asset ID ({assetIdString})!')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center_1.azmodel')
# clear up notification handler
global handler
handler.disconnect()
handler = None
print('Finished mock asset tests')
azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt')
# These tests are meant to check that the test_asset.mock source asset turned into
# a test_asset.mock_asset product asset via the Python asset builder system
mockAssetType = azlmbr.math.Uuid_CreateString('{9274AD17-3212-4651-9F3B-7DCCB080E467}', 0)
mockAssetPath = 'gem/pythontests/pythonassetbuilder/test_asset.mock_asset'
assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', mockAssetPath, mockAssetType, False)
if (assetId.is_valid() is False):
raise_and_stop(f'Mock AssetId is not valid! Got {assetId.to_string()} instead')
assetIdString = assetId.to_string()
if (assetIdString.endswith(':528cca58') is False):
raise_and_stop(f'Mock AssetId {assetIdString} has unexpected sub-id for {mockAssetPath}!')
print ('Mock asset exists')
# These tests detect if the geom_group.fbx file turns into a number of azmodel product assets
def test_azmodel_product(generatedModelAssetPath):
azModelAssetType = azlmbr.math.Uuid_CreateString('{2C7477B6-69C5-45BE-8163-BCD6A275B6D8}', 0)
assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', generatedModelAssetPath, azModelAssetType, False)
assetIdString = assetId.to_string()
if (assetId.is_valid()):
print(f'AssetId found for asset ({generatedModelAssetPath}) found')
else:
raise_and_stop(f'Asset at path {generatedModelAssetPath} has unexpected asset ID ({assetIdString})!')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center_1.azmodel')
azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt')
handler.connect()
handler.add_callback('NotifyEditorInitialized', on_notify_editor_initialized)
@@ -12,13 +12,12 @@ class Tests():
add_terrain_collider = ("Terrain Physics Heightfield Collider component added", "Failed to add a Terrain Physics Heightfield Collider component")
box_dimensions_changed = ("Aabb dimensions changed successfully", "Failed change Aabb dimensions")
configuration_changed = ("Terrain size changed successfully", "Failed terrain size change")
no_errors_and_warnings_found = ("No errors and warnings found", "Found errors and warnings")
#fmt: on
def TerrainPhysicsCollider_ChangesSizeWithAxisAlignedBoxShapeChanges():
"""
Summary:
Test aspects of the TerrainHeightGradientList through the BehaviorContext and the Property Tree.
Test aspects of the Terrain Physics Heightfield Collider through the BehaviorContext and the Property Tree.
Test Steps:
Expected Behavior:
@@ -0,0 +1,164 @@
"""
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():
create_terrain_spawner_entity = ("Terrain_spawner_entity created successfully", "Failed to create terrain_spawner_entity")
create_height_provider_entity = ("Height_provider_entity created successfully", "Failed to create height_provider_entity")
create_test_ball = ("Ball created successfully", "Failed to create Ball")
box_dimensions_changed = ("Aabb dimensions changed successfully", "Failed change Aabb dimensions")
shape_changed = ("Shape changed successfully", "Failed Shape change")
entity_added = ("Entity added successfully", "Failed Entity add")
frequency_changed = ("Frequency changed successfully", "Failed Frequency change")
shape_set = ("Shape set to Sphere successfully", "Failed to set Sphere shape")
test_collision = ("Ball collided with terrain", "Ball failed to collide with terrain")
no_errors_and_warnings_found = ("No errors and warnings found", "Found errors and warnings")
#fmt: on
def Terrain_SupportsPhysics():
"""
Summary:
Test aspects of the TerrainHeightGradientList through the BehaviorContext and the Property Tree.
Test Steps:
Expected Behavior:
The Editor is stable there are no warnings or errors.
Test Steps:
1) Load the base level
2) Create 2 test entities, one parent at 512.0, 512.0, 50.0 and one child at the default position and add the required components
2a) Create a ball at 600.0, 600.0, 46.0 - This position is not too high over the heightfield so will collide in a reasonable time
3) Start the Tracer to catch any errors and warnings
4) Change the Axis Aligned Box Shape dimensions
5) Set the Vegetation Shape reference to TestEntity1
6) Set the FastNoise gradient frequency to 0.01
7) Set the Gradient List to TestEntity2
8) Set the PhysX Collider to Sphere mode
9) Disable and Enable the Terrain Gradient List so that it is recognised
10) Enter game mode and test if the ball hits the heightfield within 3 seconds
11) Verify there are no errors and warnings in the logs
:return: None
"""
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import TestHelper as helper, Report
from editor_python_test_tools.utils import Report, Tracer
import editor_python_test_tools.hydra_editor_utils as hydra
import azlmbr.math as azmath
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.editor as editor
import math
SET_BOX_X_SIZE = 1024.0
SET_BOX_Y_SIZE = 1024.0
SET_BOX_Z_SIZE = 100.0
helper.init_idle()
# 1) Load the level
helper.open_level("", "Base")
helper.wait_for_condition(lambda: general.get_current_level_name() == "Base", 2.0)
#1a) Load the level components
hydra.add_level_component("Terrain World")
hydra.add_level_component("Terrain World Renderer")
# 2) Create 2 test entities, one parent at 512.0, 512.0, 50.0 and one child at the default position and add the required components
entity1_components_to_add = ["Axis Aligned Box Shape", "Terrain Layer Spawner", "Terrain Height Gradient List", "Terrain Physics Heightfield Collider", "PhysX Heightfield Collider"]
entity2_components_to_add = ["Vegetation Reference Shape", "Gradient Transform Modifier", "FastNoise Gradient"]
ball_components_to_add = ["Sphere Shape", "PhysX Collider", "PhysX Rigid Body"]
terrain_spawner_entity = hydra.Entity("TestEntity1")
terrain_spawner_entity.create_entity(azmath.Vector3(512.0, 512.0, 50.0), entity1_components_to_add)
Report.result(Tests.create_terrain_spawner_entity, terrain_spawner_entity.id.IsValid())
height_provider_entity = hydra.Entity("TestEntity2")
height_provider_entity.create_entity(azmath.Vector3(0.0, 0.0, 0.0), entity2_components_to_add,terrain_spawner_entity.id)
Report.result(Tests.create_height_provider_entity, height_provider_entity.id.IsValid())
# 2a) Create a ball at 600.0, 600.0, 46.0 - This position is not too high over the heightfield so will collide in a reasonable time
ball = hydra.Entity("Ball")
ball.create_entity(azmath.Vector3(600.0, 600.0, 46.0), ball_components_to_add)
Report.result(Tests.create_test_ball, ball.id.IsValid())
# Give everything a chance to finish initializing.
general.idle_wait_frames(1)
# 3) Start the Tracer to catch any errors and warnings
with Tracer() as section_tracer:
# 4) Change the Axis Aligned Box Shape dimensions
box_dimensions = azmath.Vector3(SET_BOX_X_SIZE, SET_BOX_Y_SIZE, SET_BOX_Z_SIZE)
terrain_spawner_entity.get_set_test(0, "Axis Aligned Box Shape|Box Configuration|Dimensions", box_dimensions)
box_shape_dimensions = hydra.get_component_property_value(terrain_spawner_entity.components[0], "Axis Aligned Box Shape|Box Configuration|Dimensions")
Report.result(Tests.box_dimensions_changed, box_dimensions == box_shape_dimensions)
# 5) Set the Vegetaion Shape reference to TestEntity1
height_provider_entity.get_set_test(0, "Configuration|Shape Entity Id", terrain_spawner_entity.id)
entityId = hydra.get_component_property_value(height_provider_entity.components[0], "Configuration|Shape Entity Id")
Report.result(Tests.shape_changed, entityId == terrain_spawner_entity.id)
# 6) Set the FastNoise Gradient frequency to 0.01
Frequency = 0.01
height_provider_entity.get_set_test(2, "Configuration|Frequency", Frequency)
FrequencyVal = hydra.get_component_property_value(height_provider_entity.components[2], "Configuration|Frequency")
Report.result(Tests.frequency_changed, math.isclose(Frequency, FrequencyVal, abs_tol = 0.00001))
# 7) Set the Gradient List to TestEntity2
propertyTree = hydra.get_property_tree(terrain_spawner_entity.components[2])
propertyTree.add_container_item("Configuration|Gradient Entities", 0, height_provider_entity.id)
checkID = propertyTree.get_container_item("Configuration|Gradient Entities", 0)
Report.result(Tests.entity_added, checkID.GetValue() == height_provider_entity.id)
# 8) Set the PhysX Collider to Sphere mode
shape = 0
hydra.get_set_test(ball, 1, "Shape Configuration|Shape", shape)
setShape = hydra.get_component_property_value(ball.components[1], "Shape Configuration|Shape")
Report.result(Tests.shape_set, shape == setShape)
# 9) Disable and Enable the Terrain Gradient List so that it is recognised
editor.EditorComponentAPIBus(bus.Broadcast, 'EnableComponents', [terrain_spawner_entity.components[2]])
general.enter_game_mode()
general.idle_wait_frames(1)
# 10) Enter game mode and test if the ball hits the heightfield within 3 seconds
TIMEOUT = 3.0
class Collider:
id = general.find_game_entity("Ball")
touched_ground = False
terrain_id = general.find_game_entity("TestEntity1")
def on_collision_begin(args):
other_id = args[0]
if other_id.Equal(terrain_id):
Report.info("Touched ground")
Collider.touched_ground = True
handler = azlmbr.physics.CollisionNotificationBusHandler()
handler.connect(Collider.id)
handler.add_callback("OnCollisionBegin", on_collision_begin)
helper.wait_for_condition(lambda: Collider.touched_ground, TIMEOUT)
Report.result(Tests.test_collision, Collider.touched_ground)
general.exit_game_mode()
# 11) Verify there are no errors and warnings in the logs
helper.wait_for_condition(lambda: section_tracer.has_errors or section_tracer.has_asserts, 1.0)
for error_info in section_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in section_tracer.asserts:
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Terrain_SupportsPhysics)
@@ -13,13 +13,17 @@ import os
import sys
from ly_test_tools import LAUNCHERS
from ly_test_tools.o3de.editor_test import EditorTestSuite, EditorSingleTest
from ly_test_tools.o3de.editor_test import EditorTestSuite, EditorSharedTest
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
#global_extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"]
class test_AxisAlignedBoxShape_ConfigurationWorks(EditorSingleTest):
enable_prefab_system = False
class test_AxisAlignedBoxShape_ConfigurationWorks(EditorSharedTest):
from .EditorScripts import TerrainPhysicsCollider_ChangesSizeWithAxisAlignedBoxShapeChanges as test_module
class test_Terrain_SupportsPhysics(EditorSharedTest):
from .EditorScripts import Terrain_SupportsPhysics as test_module
@@ -24,12 +24,12 @@ from base import TestAutomationBase
class TestAutomation(TestAutomationBase):
def test_WhiteBox_AddComponentToEntity(self, request, workspace, editor, launcher_platform):
from .tests import WhiteBox_AddComponentToEntity as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_WhiteBox_SetDefaultShape(self, request, workspace, editor, launcher_platform):
from .tests import WhiteBox_SetDefaultShape as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_WhiteBox_SetInvisible(self, request, workspace, editor, launcher_platform):
from .tests import WhiteBox_SetInvisible as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@@ -52,7 +52,7 @@ class TestAutomationBase:
cls._kill_ly_processes()
def _run_test(self, request, workspace, editor, testcase_module, extra_cmdline_args=[], batch_mode=True,
autotest_mode=True, use_null_renderer=True):
autotest_mode=True, use_null_renderer=True, enable_prefab_system=True):
test_starttime = time.time()
self.logger = logging.getLogger(__name__)
errors = []
@@ -97,6 +97,11 @@ class TestAutomationBase:
pycmd += ["-BatchMode"]
if autotest_mode:
pycmd += ["-autotest_mode"]
if enable_prefab_system:
pycmd += ["--regset=/Amazon/Preferences/EnablePrefabSystem=true"]
else:
pycmd += ["--regset=/Amazon/Preferences/EnablePrefabSystem=false"]
pycmd += extra_cmdline_args
editor.args.extend(pycmd) # args are added to the WinLauncher start command
editor.start(backupFiles = False, launch_ap = False)
@@ -62,7 +62,7 @@ def AssetBrowser_SearchFiltering():
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def verify_files_appeared(model, allowed_asset_extentions, parent_index=QtCore.QModelIndex()):
def verify_files_appeared(model, allowed_asset_extensions, parent_index=QtCore.QModelIndex()):
indexes = [parent_index]
while len(indexes) > 0:
parent_index = indexes.pop(0)
@@ -71,7 +71,7 @@ def AssetBrowser_SearchFiltering():
cur_data = cur_index.data(Qt.DisplayRole)
if (
"." in cur_data
and (cur_data.lower().split(".")[-1] not in allowed_asset_extentions)
and (cur_data.lower().split(".")[-1] not in allowed_asset_extensions)
and not cur_data[-1] == ")"
):
Report.info(f"Incorrect file found: {cur_data}")
@@ -94,16 +94,21 @@ def AssetBrowser_SearchFiltering():
Report.info("Asset Browser is already open")
editor_window = pyside_utils.get_editor_main_window()
app = QtWidgets.QApplication.instance()
# 3) Type the name of an asset in the search bar and make sure only one asset is filtered in Asset browser
# 3) Type the name of an asset in the search bar and make sure it is filtered to and selectable
asset_browser = editor_window.findChild(QtWidgets.QDockWidget, "Asset Browser")
search_bar = asset_browser.findChild(QtWidgets.QLineEdit, "textSearch")
search_bar.setText("cedar.fbx")
asset_browser_tree = asset_browser.findChild(QtWidgets.QTreeView, "m_assetBrowserTreeViewWidget")
model_index = pyside_utils.find_child_by_pattern(asset_browser_tree, "cedar.fbx")
pyside_utils.item_view_index_mouse_click(asset_browser_tree, model_index)
asset_browser_table = asset_browser.findChild(QtWidgets.QTreeView, "m_assetBrowserTableViewWidget")
found = await pyside_utils.wait_for_condition(lambda: pyside_utils.find_child_by_pattern(asset_browser_table, "cedar.fbx"), 5.0)
if found:
model_index = pyside_utils.find_child_by_pattern(asset_browser_table, "cedar.fbx")
else:
Report.result(Tests.asset_filtered, found)
pyside_utils.item_view_index_mouse_click(asset_browser_table, model_index)
is_filtered = await pyside_utils.wait_for_condition(
lambda: asset_browser_tree.indexBelow(asset_browser_tree.currentIndex()) == QtCore.QModelIndex(), 5.0)
lambda: asset_browser_table.currentIndex() == model_index, 5.0)
Report.result(Tests.asset_filtered, is_filtered)
# 4) Click the "X" in the search bar.
@@ -84,8 +84,8 @@ def AssetBrowser_TreeNavigation():
# 3) Collapse all files initially
main_window = editor_window.findChild(QtWidgets.QMainWindow)
asset_browser = pyside_utils.find_child_by_hierarchy(main_window, ..., "Asset Browser")
tree = pyside_utils.find_child_by_hierarchy(asset_browser, ..., "m_assetBrowserTreeViewWidget")
asset_browser = pyside_utils.find_child_by_pattern(main_window, text="Asset Browser", type=QtWidgets.QDockWidget)
tree = pyside_utils.find_child_by_pattern(asset_browser, "m_assetBrowserTreeViewWidget")
scroll_area = tree.findChild(QtWidgets.QWidget, "qt_scrollarea_vcontainer")
scroll_bar = scroll_area.findChild(QtWidgets.QScrollBar)
tree.collapseAll()
@@ -33,14 +33,14 @@ class TestAutomation(TestAutomationBase):
def test_BasicEditorWorkflows_LevelEntityComponentCRUD(self, request, workspace, editor, launcher_platform,
remove_test_level):
from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False)
self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False, enable_prefab_system=False)
@pytest.mark.REQUIRES_gpu
def test_BasicEditorWorkflows_GPU_LevelEntityComponentCRUD(self, request, workspace, editor, launcher_platform,
remove_test_level):
from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False,
use_null_renderer=False)
use_null_renderer=False, enable_prefab_system=False)
def test_EntityOutlienr_EntityOrdering(self, request, workspace, editor, launcher_platform):
from .EditorScripts import EntityOutliner_EntityOrdering as test_module
@@ -51,5 +51,4 @@ class TestAutomation(TestAutomationBase):
test_module,
batch_mode=False,
autotest_mode=True,
extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"]
)
@@ -21,6 +21,8 @@ class TestAutomationNoAutoTestMode(EditorTestSuite):
# Disable -autotest_mode and -BatchMode. Tests cannot run in -BatchMode due to UI interactions, and these tests
# interact with modal dialogs
global_extra_cmdline_args = []
enable_prefab_system = False
class test_BasicEditorWorkflows_LevelEntityComponentCRUD(EditorSingleTest):
# Custom teardown to remove slice asset created during test
@@ -43,7 +45,6 @@ class TestAutomationNoAutoTestMode(EditorTestSuite):
class test_InputBindings_Add_Remove_Input_Events(EditorSharedTest):
from .EditorScripts import InputBindings_Add_Remove_Input_Events as test_module
@pytest.mark.skip(reason="Crashes Editor: ATOM-15493")
class test_AssetPicker_UI_UX(EditorSharedTest):
from .EditorScripts import AssetPicker_UI_UX as test_module
@@ -57,10 +58,11 @@ class TestAutomationAutoTestMode(EditorTestSuite):
# Enable only -autotest_mode for these tests. Tests cannot run in -BatchMode due to UI interactions
global_extra_cmdline_args = ["-autotest_mode"]
enable_prefab_system = False
class test_AssetBrowser_TreeNavigation(EditorSharedTest):
from .EditorScripts import AssetBrowser_TreeNavigation as test_module
@pytest.mark.skip(reason="Crashes Editor: ATOM-15493")
class test_AssetBrowser_SearchFiltering(EditorSharedTest):
from .EditorScripts import AssetBrowser_SearchFiltering as test_module
@@ -74,6 +76,5 @@ class TestAutomationAutoTestMode(EditorTestSuite):
class test_Menus_FileMenuOptions_Work(EditorSharedTest):
from .EditorScripts import Menus_FileMenuOptions as test_module
class test_BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(EditorSharedTest):
from .EditorScripts import BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD as test_module
@@ -32,31 +32,29 @@ class TestAutomation(TestAutomationBase):
def test_AssetBrowser_TreeNavigation(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AssetBrowser_TreeNavigation as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)
self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False)
@pytest.mark.skip(reason="Crashes Editor: ATOM-15493")
def test_AssetBrowser_SearchFiltering(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AssetBrowser_SearchFiltering as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)
self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False)
@pytest.mark.skip(reason="Crashes Editor: ATOM-15493")
def test_AssetPicker_UI_UX(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AssetPicker_UI_UX as test_module
self._run_test(request, workspace, editor, test_module, autotest_mode=False, batch_mode=False)
self._run_test(request, workspace, editor, test_module, autotest_mode=False, batch_mode=False, enable_prefab_system=False)
def test_ComponentCRUD_Add_Delete_Components(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ComponentCRUD_Add_Delete_Components as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)
self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False)
def test_InputBindings_Add_Remove_Input_Events(self, request, workspace, editor, launcher_platform):
from .EditorScripts import InputBindings_Add_Remove_Input_Events as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False)
self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False, enable_prefab_system=False)
def test_Menus_ViewMenuOptions_Work(self, request, workspace, editor, launcher_platform):
from .EditorScripts import Menus_ViewMenuOptions as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)
self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False)
@pytest.mark.skip(reason="Times out due to dialogs failing to dismiss: LYN-4208")
def test_Menus_FileMenuOptions_Work(self, request, workspace, editor, launcher_platform):
from .EditorScripts import Menus_FileMenuOptions as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)
self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False)
@@ -20,8 +20,8 @@ class TestAutomation(TestAutomationBase):
def test_Menus_EditMenuOptions_Work(self, request, workspace, editor, launcher_platform):
from .EditorScripts import Menus_EditMenuOptions as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)
self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False)
def test_Docking_BasicDockedTools(self, request, workspace, editor, launcher_platform):
from .EditorScripts import Docking_BasicDockedTools as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)
self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False)
@@ -19,6 +19,8 @@ class TestAutomationAutoTestMode(EditorTestSuite):
# Enable only -autotest_mode for these tests. Tests cannot run in -BatchMode due to UI interactions
global_extra_cmdline_args = ["-autotest_mode"]
enable_prefab_system = False
class test_Docking_BasicDockedTools(EditorSharedTest):
from .EditorScripts import Docking_BasicDockedTools as test_module
@@ -20,8 +20,8 @@ class TestAutomation(TestAutomationBase):
def test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(self, request, workspace, editor, launcher_platform):
from .EditorScripts import DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_EmptyInstanceSpawner_EmptySpawnerWorks(self, request, workspace, editor, launcher_platform):
from .EditorScripts import EmptyInstanceSpawner_EmptySpawnerWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@@ -18,6 +18,8 @@ from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, E
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
enable_prefab_system = False
class test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(EditorParallelTest):
from .EditorScripts import DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks as test_module
@@ -52,158 +52,158 @@ class TestAutomation(TestAutomationBase):
def test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_AltitudeFilter_FilterStageToggle(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(self, request, workspace, editor, remove_test_slice, launcher_platform):
from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_AssetWeightSelector_InstancesExpressBasedOnWeight(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AssetWeightSelector_InstancesExpressBasedOnWeight as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155")
def test_DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius(self, request, workspace, editor, launcher_platform):
from .EditorScripts import DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4155")
def test_DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius(self, request, workspace, editor, launcher_platform):
from .EditorScripts import DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_SurfaceDataRefreshes_RemainsStable(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SurfaceDataRefreshes_RemainsStable as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_VegetationInstances_DespawnWhenOutOfRange(self, request, workspace, editor, launcher_platform):
from .EditorScripts import VegetationInstances_DespawnWhenOutOfRange as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_InstanceSpawnerPriority_LayerAndSubPriority_HigherValuesPlantOverLower(self, request, workspace, editor, launcher_platform):
from .EditorScripts import InstanceSpawnerPriority_LayerAndSubPriority as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LayerBlocker_InstancesBlockedInConfiguredArea(self, request, workspace, editor, launcher_platform):
from .EditorScripts import LayerBlocker_InstancesBlockedInConfiguredArea as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LayerSpawner_InheritBehaviorFlag(self, request, workspace, editor, launcher_platform):
from .EditorScripts import LayerSpawner_InheritBehaviorFlag as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LayerSpawner_InstancesPlantInAllSupportedShapes(self, request, workspace, editor, launcher_platform):
from .EditorScripts import LayerSpawner_InstancesPlantInAllSupportedShapes as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LayerSpawner_FilterStageToggle(self, request, workspace, editor, launcher_platform):
from .EditorScripts import LayerSpawner_FilterStageToggle as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2038")
def test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(self, request, workspace, editor, launcher_platform):
from .EditorScripts import LayerSpawner_InstancesRefreshUsingCorrectViewportCamera as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_MeshBlocker_InstancesBlockedByMesh(self, request, workspace, editor, launcher_platform):
from .EditorScripts import MeshBlocker_InstancesBlockedByMesh as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_MeshBlocker_InstancesBlockedByMeshHeightTuning(self, request, workspace, editor, launcher_platform):
from .EditorScripts import MeshBlocker_InstancesBlockedByMeshHeightTuning as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_MeshSurfaceTagEmitter_DependentOnMeshComponent(self, request, workspace, editor, launcher_platform):
from .EditorScripts import MeshSurfaceTagEmitter_DependentOnMeshComponent as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform):
from .EditorScripts import MeshSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_PhysXColliderSurfaceTagEmitter_E2E_Editor(self, request, workspace, editor, launcher_platform):
from .EditorScripts import PhysXColliderSurfaceTagEmitter_E2E_Editor as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets(self, request, workspace, editor, launcher_platform):
from .EditorScripts import PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_PositionModifier_AutoSnapToSurfaceWorks(self, request, workspace, editor, launcher_platform):
from .EditorScripts import PositionModifier_AutoSnapToSurfaceWorks as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_RotationModifier_InstancesRotateWithinRange(self, request, workspace, editor, launcher_platform):
from .EditorScripts import RotationModifier_InstancesRotateWithinRange as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_RotationModifierOverrides_InstancesRotateWithinRange(self, request, workspace, editor, launcher_platform):
from .EditorScripts import RotationModifierOverrides_InstancesRotateWithinRange as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ScaleModifier_InstancesProperlyScale(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ScaleModifier_InstancesProperlyScale as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ScaleModifierOverrides_InstancesProperlyScale(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ScaleModifierOverrides_InstancesProperlyScale as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ShapeIntersectionFilter_InstancesPlantInAssignedShape(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ShapeIntersectionFilter_InstancesPlantInAssignedShape as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ShapeIntersectionFilter_FilterStageToggle(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ShapeIntersectionFilter_FilterStageToggle as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_SlopeAlignmentModifier_InstanceSurfaceAlignment(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SlopeAlignmentModifier_InstanceSurfaceAlignment as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_SurfaceMaskFilter_BasicSurfaceTagCreation(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SurfaceMaskFilter_BasicSurfaceTagCreation as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_SurfaceMaskFilter_ExclusiveSurfaceTags_Function(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SurfaceMaskFilter_ExclusionList as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_SurfaceMaskFilter_InclusiveSurfaceTags_Function(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SurfaceMaskFilter_InclusionList as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_SystemSettings_SectorPointDensity(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SystemSettings_SectorPointDensity as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_SystemSettings_SectorSize(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SystemSettings_SectorSize as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.SUITE_periodic
@@ -219,7 +219,7 @@ class TestAutomationE2E(TestAutomationBase):
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
from .EditorScripts import DynamicSliceInstanceSpawner_Embedded_E2E as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.parametrize("launcher_platform", ['windows'])
def test_DynamicSliceInstanceSpawner_Embedded_E2E_Launcher(self, workspace, launcher, level,
@@ -240,7 +240,7 @@ class TestAutomationE2E(TestAutomationBase):
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.parametrize("launcher_platform", ['windows'])
def test_DynamicSliceInstanceSpawner_External_E2E_Launcher(self, workspace, launcher, level,
@@ -261,7 +261,7 @@ class TestAutomationE2E(TestAutomationBase):
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
from .EditorScripts import LayerBlender_E2E_Editor as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.parametrize("launcher_platform", ['windows'])
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4170")
@@ -17,7 +17,7 @@ from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, E
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
global_extra_cmdline_args = ["-BatchMode", "-autotest_mode", "--regset=/Amazon/Preferences/EnablePrefabSystem=true"]
global_extra_cmdline_args = ["-BatchMode", "-autotest_mode"]
class test_DynVegUtils_TempPrefabCreationWorks(EditorSharedTest):
from .EditorScripts import DynVegUtils_TempPrefabCreationWorks as test_module
@@ -20,52 +20,52 @@ class TestAutomation(TestAutomationBase):
def test_GradientGenerators_Incompatibilities(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientGenerators_Incompatibilities as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_GradientModifiers_Incompatibilities(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientModifiers_Incompatibilities as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_GradientPreviewSettings_DefaultPinnedEntityIsSelf(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientPreviewSettings_DefaultPinnedEntityIsSelf as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_GradientSampling_GradientReferencesAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientSampling_GradientReferencesAddRemoveSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_GradientSurfaceTagEmitter_ComponentDependencies(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientSurfaceTagEmitter_ComponentDependencies as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_GradientTransform_RequiresShape(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientTransform_RequiresShape as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_GradientTransform_ComponentIncompatibleWithSpawners(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientTransform_ComponentIncompatibleWithSpawners as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_GradientTransform_ComponentIncompatibleWithExpectedGradients(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientTransform_ComponentIncompatibleWithExpectedGradients as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ImageGradient_RequiresShape(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ImageGradient_RequiresShape as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ImageGradient_ProcessedImageAssignedSuccessfully(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ImageGradient_ProcessedImageAssignedSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@@ -15,6 +15,8 @@ from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, E
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
enable_prefab_system = False
class test_GradientGenerators_Incompatibilities(EditorSharedTest):
from .EditorScripts import GradientGenerators_Incompatibilities as test_module
@@ -22,8 +22,8 @@ class TestAutomation(TestAutomationBase):
def test_LandscapeCanvas_SlotConnections_UpdateComponentReferences(self, request, workspace, editor, launcher_platform):
from .EditorScripts import SlotConnections_UpdateComponentReferences as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_GradientMixer_NodeConstruction(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientMixer_NodeConstruction as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@@ -18,6 +18,8 @@ from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, E
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
enable_prefab_system = False
class test_LandscapeCanvas_SlotConnections_UpdateComponentReferences(EditorSharedTest):
from .EditorScripts import SlotConnections_UpdateComponentReferences as test_module
@@ -33,89 +33,89 @@ class TestAutomation(TestAutomationBase):
def test_LandscapeCanvas_AreaNodes_DependentComponentsAdded(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AreaNodes_DependentComponentsAdded as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_AreaNodes_EntityCreatedOnNodeAdd(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AreaNodes_EntityCreatedOnNodeAdd as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_AreaNodes_EntityRemovedOnNodeDelete(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AreaNodes_EntityRemovedOnNodeDelete as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_LayerExtenderNodes_ComponentEntitySync(self, request, workspace, editor, launcher_platform):
from .EditorScripts import LayerExtenderNodes_ComponentEntitySync as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_Edit_DisabledNodeDuplication(self, request, workspace, editor, launcher_platform):
from .EditorScripts import Edit_DisabledNodeDuplication as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_Edit_UndoNodeDelete_SliceEntity(self, request, workspace, editor, launcher_platform):
from .EditorScripts import Edit_UndoNodeDelete_SliceEntity as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_NewGraph_CreatedSuccessfully(self, request, workspace, editor, launcher_platform):
from .EditorScripts import NewGraph_CreatedSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_Component_AddedRemoved(self, request, workspace, editor, launcher_platform):
from .EditorScripts import Component_AddedRemoved as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_GraphClosed_OnLevelChange(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GraphClosed_OnLevelChange as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/2201")
def test_LandscapeCanvas_GraphClosed_OnEntityDelete(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GraphClosed_OnEntityDelete as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_GraphClosed_TabbedGraphClosesIndependently(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GraphClosed_TabbedGraph as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_Slice_CreateInstantiate(self, request, workspace, editor, remove_test_slice, launcher_platform):
from .EditorScripts import Slice_CreateInstantiate as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_GradientModifierNodes_EntityCreatedOnNodeAdd(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientModifierNodes_EntityCreatedOnNodeAdd as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_GradientModifierNodes_EntityRemovedOnNodeDelete(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientModifierNodes_EntityRemovedOnNodeDelete as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_GradientNodes_DependentComponentsAdded(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientNodes_DependentComponentsAdded as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_GradientNodes_EntityCreatedOnNodeAdd(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientNodes_EntityCreatedOnNodeAdd as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_GradientNodes_EntityRemovedOnNodeDelete(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientNodes_EntityRemovedOnNodeDelete as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_GraphUpdates_UpdateComponents(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GraphUpdates_UpdateComponents as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_ComponentUpdates_UpdateGraph(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ComponentUpdates_UpdateGraph as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_LayerBlender_NodeConstruction(self, request, workspace, editor, launcher_platform):
from .EditorScripts import LayerBlender_NodeConstruction as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_ShapeNodes_EntityCreatedOnNodeAdd(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ShapeNodes_EntityCreatedOnNodeAdd as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_LandscapeCanvas_ShapeNodes_EntityRemovedOnNodeDelete(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ShapeNodes_EntityRemovedOnNodeDelete as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@@ -27,15 +27,15 @@ TEST_DIRECTORY = os.path.dirname(__file__)
class TestAutomation(TestAutomationBase):
def test_Pane_HappyPath_OpenCloseSuccessfully(self, request, workspace, editor, launcher_platform):
from . import Pane_HappyPath_OpenCloseSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Pane_HappyPath_DocksProperly(self, request, workspace, editor, launcher_platform):
from . import Pane_HappyPath_DocksProperly as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Pane_HappyPath_ResizesProperly(self, request, workspace, editor, launcher_platform):
from . import Pane_HappyPath_ResizesProperly as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(reason="Test fails to find expected lines, it needs to be fixed.")
@pytest.mark.parametrize("level", ["tmp_level"])
@@ -45,7 +45,7 @@ class TestAutomation(TestAutomationBase):
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import ScriptCanvas_TwoComponents_InteractSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(reason="Test fails to find expected lines, it needs to be fixed.")
@pytest.mark.parametrize("level", ["tmp_level"])
@@ -55,15 +55,15 @@ class TestAutomation(TestAutomationBase):
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import ScriptCanvas_ChangingAssets_ComponentStable as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Graph_HappyPath_ZoomInZoomOut(self, request, workspace, editor, launcher_platform):
from . import Graph_HappyPath_ZoomInZoomOut as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_NodePalette_HappyPath_CanSelectNode(self, request, workspace, editor, launcher_platform):
from . import NodePalette_HappyPath_CanSelectNode as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(reason="Test fails to find expected lines, it needs to be fixed.")
@pytest.mark.parametrize("level", ["tmp_level"])
@@ -73,11 +73,11 @@ class TestAutomation(TestAutomationBase):
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import ScriptCanvasComponent_OnEntityActivatedDeactivated_PrintMessage as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_NodePalette_HappyPath_ClearSelection(self, request, workspace, editor, launcher_platform, project):
from . import NodePalette_HappyPath_ClearSelection as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(reason="Test fails to find expected lines, it needs to be fixed.")
@pytest.mark.parametrize("level", ["tmp_level"])
@@ -87,7 +87,7 @@ class TestAutomation(TestAutomationBase):
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import ScriptCanvas_TwoEntities_UseSimultaneously as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ScriptEvent_HappyPath_CreatedWithoutError(self, request, workspace, editor, launcher_platform, project):
def teardown():
@@ -99,19 +99,19 @@ class TestAutomation(TestAutomationBase):
[os.path.join(workspace.paths.project(), "ScriptCanvas", "test_file.scriptevent")], True, True
)
from . import ScriptEvent_HappyPath_CreatedWithoutError as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ScriptCanvasTools_Toggle_OpenCloseSuccess(self, request, workspace, editor, launcher_platform):
from . import ScriptCanvasTools_Toggle_OpenCloseSuccess as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_NodeInspector_HappyPath_VariableRenames(self, request, workspace, editor, launcher_platform, project):
from . import NodeInspector_HappyPath_VariableRenames as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Debugger_HappyPath_TargetMultipleGraphs(self, request, workspace, editor, launcher_platform, project):
from . import Debugger_HappyPath_TargetMultipleGraphs as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.parametrize("level", ["tmp_level"])
def test_Debugger_HappyPath_TargetMultipleEntities(self, request, workspace, editor, launcher_platform, project, level):
@@ -120,16 +120,16 @@ class TestAutomation(TestAutomationBase):
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import Debugger_HappyPath_TargetMultipleEntities as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@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)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Pane_Undocked_ClosesSuccessfully(self, request, workspace, editor, launcher_platform):
from . import Pane_Undocked_ClosesSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.parametrize("level", ["tmp_level"])
def test_Entity_HappyPath_AddScriptCanvasComponent(self, request, workspace, editor, launcher_platform, project, level):
@@ -138,11 +138,11 @@ class TestAutomation(TestAutomationBase):
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import Entity_HappyPath_AddScriptCanvasComponent as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Pane_Default_RetainOnSCRestart(self, request, workspace, editor, launcher_platform):
from . import Pane_Default_RetainOnSCRestart as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(reason="Test fails to find expected lines, it needs to be fixed.")
@pytest.mark.parametrize("level", ["tmp_level"])
@@ -152,7 +152,7 @@ class TestAutomation(TestAutomationBase):
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import ScriptEvents_HappyPath_SendReceiveAcrossMultiple as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(reason="Test fails to find expected lines, it needs to be fixed.")
@pytest.mark.parametrize("level", ["tmp_level"])
@@ -162,7 +162,7 @@ class TestAutomation(TestAutomationBase):
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import ScriptEvents_Default_SendReceiveSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@pytest.mark.xfail(reason="Test fails to find expected lines, it needs to be fixed.")
@pytest.mark.parametrize("level", ["tmp_level"])
@@ -172,24 +172,24 @@ class TestAutomation(TestAutomationBase):
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True)
from . import ScriptEvents_ReturnSetType_Successfully as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_NodeCategory_ExpandOnClick(self, request, workspace, editor, launcher_platform):
from . import NodeCategory_ExpandOnClick as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_NodePalette_SearchText_Deletion(self, request, workspace, editor, launcher_platform):
from . import NodePalette_SearchText_Deletion as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@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)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_Node_HappyPath_DuplicateNode(self, request, workspace, editor, launcher_platform):
from . import Node_HappyPath_DuplicateNode as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
def test_ScriptEvent_AddRemoveParameter_ActionsSuccessful(self, request, workspace, editor, launcher_platform):
def teardown():
@@ -201,7 +201,7 @@ class TestAutomation(TestAutomationBase):
[os.path.join(workspace.paths.project(), "ScriptCanvas", "test_file.scriptevent")], True, True
)
from . import ScriptEvent_AddRemoveParameter_ActionsSuccessful as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
# NOTE: We had to use hydra_test_utils.py, as TestAutomationBase run_test method
# fails because of pyside_utils import
@@ -220,7 +220,14 @@ class TestScriptCanvasTests(object):
"File->Open action working as expected: True",
]
hydra.launch_and_validate_results(
request, TEST_DIRECTORY, editor, "FileMenu_Default_NewAndOpen.py", expected_lines, auto_test_mode=False, timeout=60,
request,
TEST_DIRECTORY,
editor,
"FileMenu_Default_NewAndOpen.py",
expected_lines,
auto_test_mode=False,
timeout=60,
enable_prefab_system=False,
)
@pytest.mark.xfail(reason="Test fails to find expected lines, it needs to be fixed.")
@@ -239,6 +246,7 @@ class TestScriptCanvasTests(object):
expected_lines,
auto_test_mode=False,
timeout=60,
enable_prefab_system=False,
)
def test_GraphClose_Default_SavePrompt(self, request, editor, launcher_platform):
@@ -255,6 +263,7 @@ class TestScriptCanvasTests(object):
expected_lines,
auto_test_mode=False,
timeout=60,
enable_prefab_system=False,
)
def test_VariableManager_Default_CreateDeleteVars(self, request, editor, launcher_platform):
@@ -269,6 +278,7 @@ class TestScriptCanvasTests(object):
expected_lines,
auto_test_mode=False,
timeout=60,
enable_prefab_system=False,
)
@pytest.mark.parametrize(
@@ -304,6 +314,7 @@ class TestScriptCanvasTests(object):
cfg_args=[config.get('cfg_args')],
auto_test_mode=False,
timeout=60,
enable_prefab_system=False,
)
@pytest.mark.xfail(reason="Test fails to find expected lines, it needs to be fixed.")
@@ -332,6 +343,7 @@ class TestScriptCanvasTests(object):
expected_lines,
auto_test_mode=False,
timeout=60,
enable_prefab_system=False,
)
@pytest.mark.xfail(reason="Test fails to find expected lines, it needs to be fixed.")
@@ -359,5 +371,6 @@ class TestScriptCanvasTests(object):
expected_lines,
auto_test_mode=False,
timeout=60,
enable_prefab_system=False,
)
@@ -23,4 +23,4 @@ class TestAutomation(TestAutomationBase):
def test_Opening_Closing_Pane(self, request, workspace, editor, launcher_platform):
from . import Opening_Closing_Pane as test_module
self._run_test(request, workspace, editor, test_module)
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
@@ -31,4 +31,4 @@ class TestAutomation(TestAutomationBase):
from . import Editor_NewExistingLevels_Works as test_module
self._run_test(request, workspace, editor, test_module, extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=false"])
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
+44 -35
View File
@@ -1,52 +1,61 @@
{
"ContainerEntity": {
"Id": "ContainerEntity",
"Name": "Base",
"Id": "Entity_[1146574390643]",
"Name": "Level",
"Components": {
"Component_[10182366347512475253]": {
"$type": "EditorPrefabComponent",
"Id": 10182366347512475253
"Component_[10641544592923449938]": {
"$type": "EditorInspectorComponent",
"Id": 10641544592923449938
},
"Component_[12917798267488243668]": {
"$type": "EditorPendingCompositionComponent",
"Id": 12917798267488243668
},
"Component_[3261249813163778338]": {
"Component_[12039882709170782873]": {
"$type": "EditorOnlyEntityComponent",
"Id": 3261249813163778338
"Id": 12039882709170782873
},
"Component_[3837204912784440039]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 3837204912784440039
"Component_[12265484671603697631]": {
"$type": "EditorPendingCompositionComponent",
"Id": 12265484671603697631
},
"Component_[4272963378099646759]": {
"Component_[14126657869720434043]": {
"$type": "EditorEntitySortComponent",
"Id": 14126657869720434043,
"ChildEntityOrderEntryArray": [
{
"EntityId": ""
},
{
"EntityId": "",
"SortIndex": 1
}
]
},
"Component_[15230859088967841193]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 4272963378099646759,
"Id": 15230859088967841193,
"Parent Entity": ""
},
"Component_[4848458548047175816]": {
"$type": "EditorVisibilityComponent",
"Id": 4848458548047175816
"Component_[16239496886950819870]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 16239496886950819870
},
"Component_[5787060997243919943]": {
"$type": "EditorInspectorComponent",
"Id": 5787060997243919943
},
"Component_[7804170251266531779]": {
"$type": "EditorLockComponent",
"Id": 7804170251266531779
},
"Component_[7874177159288365422]": {
"$type": "EditorEntitySortComponent",
"Id": 7874177159288365422
},
"Component_[8018146290632383969]": {
"Component_[5688118765544765547]": {
"$type": "EditorEntityIconComponent",
"Id": 8018146290632383969
"Id": 5688118765544765547
},
"Component_[8452360690590857075]": {
"Component_[6545738857812235305]": {
"$type": "SelectionComponent",
"Id": 8452360690590857075
"Id": 6545738857812235305
},
"Component_[7247035804068349658]": {
"$type": "EditorPrefabComponent",
"Id": 7247035804068349658
},
"Component_[9307224322037797205]": {
"$type": "EditorLockComponent",
"Id": 9307224322037797205
},
"Component_[9562516168917670048]": {
"$type": "EditorVisibilityComponent",
"Id": 9562516168917670048
}
}
}
@@ -1,7 +1,7 @@
{
"Amazon": {
"Preferences": {
"EnablePrefabSystem": false
"EnablePrefabSystem": true
}
}
}
@@ -0,0 +1,13 @@
#
# 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
#
#
# File to tweak compiler settings before compiler detection happens (before project() is called)
# We dont have PAL enabled at this point, so we can only use pure-CMake variables
if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux")
include(cmake/Platform/Linux/CompilerSettings_linux.cmake)
endif()
@@ -1,3 +1,4 @@
# {BEGIN_LICENSE}
#
# 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.
@@ -5,18 +6,34 @@
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
# {END_LICENSE}
# This file is copied during engine registration. Edits to this file will be lost next
# time a registration happens.
include_guard()
# Read the engine name from the project_json file
file(READ ${CMAKE_CURRENT_LIST_DIR}/project.json project_json)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/project.json)
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/project.json project_json)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/project.json)
string(JSON LY_ENGINE_NAME_TO_USE ERROR_VARIABLE json_error GET ${project_json} engine)
if(json_error)
message(FATAL_ERROR "Unable to read key 'engine' from 'project.json', error: ${json_error}")
message(FATAL_ERROR "Unable to read key 'engine' from 'project.json'\nError: ${json_error}")
endif()
if(CMAKE_MODULE_PATH)
foreach(module_path ${CMAKE_MODULE_PATH})
if(EXISTS ${module_path}/Findo3de.cmake)
file(READ ${module_path}/../engine.json engine_json)
string(JSON engine_name ERROR_VARIABLE json_error GET ${engine_json} engine_name)
if(json_error)
message(FATAL_ERROR "Unable to read key 'engine_name' from 'engine.json'\nError: ${json_error}")
endif()
if(LY_ENGINE_NAME_TO_USE STREQUAL engine_name)
return() # Engine being forced through CMAKE_MODULE_PATH
endif()
endif()
endforeach()
endif()
if(DEFINED ENV{USERPROFILE} AND EXISTS $ENV{USERPROFILE})
@@ -25,6 +42,11 @@ else()
set(manifest_path $ENV{HOME}/.o3de/o3de_manifest.json) # Unix
endif()
set(registration_error [=[
Engine registration is required before configuring a project.
Run 'scripts/o3de register --this-engine' from the engine root.
]=])
# Read the ~/.o3de/o3de_manifest.json file and look through the 'engines_path' object.
# Find a key that matches LY_ENGINE_NAME_TO_USE and use that as the engine path.
if(EXISTS ${manifest_path})
@@ -33,36 +55,38 @@ if(EXISTS ${manifest_path})
string(JSON engines_path_count ERROR_VARIABLE json_error LENGTH ${manifest_json} engines_path)
if(json_error)
message(FATAL_ERROR "Unable to read key 'engines_path' from '${manifest_path}', error: ${json_error}")
message(FATAL_ERROR "Unable to read key 'engines_path' from '${manifest_path}'\nError: ${json_error}\n${registration_error}")
endif()
string(JSON engines_path_type ERROR_VARIABLE json_error TYPE ${manifest_json} engines_path)
if(json_error OR NOT ${engines_path_type} STREQUAL "OBJECT")
message(FATAL_ERROR "Type of 'engines_path' in '${manifest_path}' is not a JSON Object, error: ${json_error}")
message(FATAL_ERROR "Type of 'engines_path' in '${manifest_path}' is not a JSON Object\nError: ${json_error}")
endif()
math(EXPR engines_path_count "${engines_path_count}-1")
foreach(engine_path_index RANGE ${engines_path_count})
string(JSON engine_name ERROR_VARIABLE json_error MEMBER ${manifest_json} engines_path ${engine_path_index})
if(json_error)
message(FATAL_ERROR "Unable to read 'engines_path/${engine_path_index}' from '${manifest_path}', error: ${json_error}")
message(FATAL_ERROR "Unable to read 'engines_path/${engine_path_index}' from '${manifest_path}'\nError: ${json_error}")
endif()
if(LY_ENGINE_NAME_TO_USE STREQUAL engine_name)
string(JSON engine_path ERROR_VARIABLE json_error GET ${manifest_json} engines_path ${engine_name})
if(json_error)
message(FATAL_ERROR "Unable to read value from 'engines_path/${engine_name}', error: ${json_error}")
message(FATAL_ERROR "Unable to read value from 'engines_path/${engine_name}'\nError: ${json_error}")
endif()
if(engine_path)
list(APPEND CMAKE_MODULE_PATH "${engine_path}/cmake")
break()
return()
endif()
endif()
endforeach()
message(FATAL_ERROR "The project.json uses engine name '${LY_ENGINE_NAME_TO_USE}' but no engine with that name has been registered.\n${registration_error}")
else()
# If the user is passing CMAKE_MODULE_PATH we assume thats where we will find the engine
if(NOT CMAKE_MODULE_PATH)
message(FATAL_ERROR "Engine registration is required before configuring a project. Please register an engine by running 'scripts/o3de register --this-engine'")
message(FATAL_ERROR "O3DE Manifest file not found.\n${registration_error}")
endif()
endif()
+3 -28
View File
@@ -234,7 +234,7 @@ void Q2DViewport::UpdateContent(int flags)
}
//////////////////////////////////////////////////////////////////////////
void Q2DViewport::OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
void Q2DViewport::OnRButtonDown([[maybe_unused]] Qt::KeyboardModifiers modifiers, const QPoint& point)
{
if (GetIEditor()->IsInGameMode())
{
@@ -246,9 +246,6 @@ void Q2DViewport::OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& p
setFocus();
}
// Check Edit Tool.
MouseCallback(eMouseRDown, point, modifiers);
SetCurrentCursor(STD_CURSOR_MOVE, QString());
// Save the mouse down position
@@ -273,17 +270,8 @@ void Q2DViewport::OnRButtonUp([[maybe_unused]] Qt::KeyboardModifiers modifiers,
}
//////////////////////////////////////////////////////////////////////////
void Q2DViewport::OnMButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
void Q2DViewport::OnMButtonDown([[maybe_unused]] Qt::KeyboardModifiers modifiers, const QPoint& point)
{
////////////////////////////////////////////////////////////////////////
// User pressed the middle mouse button
////////////////////////////////////////////////////////////////////////
// Check Edit Tool.
if (MouseCallback(eMouseMDown, point, modifiers))
{
return;
}
// Save the mouse down position
m_RMouseDownPos = point;
@@ -300,14 +288,8 @@ void Q2DViewport::OnMButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& p
}
//////////////////////////////////////////////////////////////////////////
void Q2DViewport::OnMButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point)
void Q2DViewport::OnMButtonUp([[maybe_unused]] Qt::KeyboardModifiers modifiers, [[maybe_unused]] const QPoint& point)
{
// Check Edit Tool.
if (MouseCallback(eMouseMUp, point, modifiers))
{
return;
}
SetViewMode(NothingMode);
ReleaseMouse();
@@ -547,13 +529,6 @@ QPoint Q2DViewport::WorldToView(const Vec3& wp) const
QPoint p = QPoint(static_cast<int>(sp.x), static_cast<int>(sp.y));
return p;
}
//////////////////////////////////////////////////////////////////////////
QPoint Q2DViewport::WorldToViewParticleEditor(const Vec3& wp, [[maybe_unused]] int width, [[maybe_unused]] int height) const //Eric@conffx implement for the children class of IDisplayViewport
{
Vec3 sp = m_screenTM.TransformPoint(wp);
QPoint p = QPoint(static_cast<int>(sp.x), static_cast<int>(sp.y));
return p;
}
//////////////////////////////////////////////////////////////////////////
Vec3 Q2DViewport::ViewToWorld(const QPoint& vp, [[maybe_unused]] bool* collideWithTerrain, [[maybe_unused]] bool onlyTerrain, [[maybe_unused]] bool bSkipVegetation, [[maybe_unused]] bool bTestRenderMesh, [[maybe_unused]] bool* collideWithObject) const
-2
View File
@@ -50,8 +50,6 @@ public:
//! Map world space position to viewport position.
QPoint WorldToView(const Vec3& wp) const override;
QPoint WorldToViewParticleEditor(const Vec3& wp, int width, int height) const override; //Eric@conffx
//! Map viewport position to world space position.
Vec3 ViewToWorld(const QPoint& vp, bool* collideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const override;
//! Map viewport position to world space ray from camera.
-9
View File
@@ -152,15 +152,6 @@ ActionManager::ActionWrapper& ActionManager::ActionWrapper::SetMenu(DynamicMenu*
return *this;
}
ActionManager::ActionWrapper& ActionManager::ActionWrapper::SetApplyHoverEffect()
{
// Our standard toolbar icons, when hovered on, get a white color effect.
// But for this to work we need .pngs that look good with this effect, so this only works with the standard toolbars
// and looks very ugly for other toolbars, including toolbars loaded from XML (which just show a white rectangle)
m_action->setProperty("IconHasHoverEffect", true);
return *this;
}
ActionManager::ActionWrapper& ActionManager::ActionWrapper::SetReserved()
{
m_action->setProperty("Reserved", true);
-1
View File
@@ -151,7 +151,6 @@ public:
}
ActionWrapper& SetMenu(DynamicMenu* menu);
ActionWrapper& SetApplyHoverEffect();
operator QAction*() const {
return m_action;
@@ -10,24 +10,21 @@
#include "AnimationBipedBoneNames.h"
namespace EditorAnimationBones
namespace EditorAnimationBones::Biped
{
namespace Biped
{
const char* Pelvis = "Bip01 Pelvis";
const char* Head = "Bip01 Head";
const char* Weapon = "weapon_bone";
const char* Pelvis = "Bip01 Pelvis";
const char* Head = "Bip01 Head";
const char* Weapon = "weapon_bone";
const char* LeftEye = "eye_bone_left";
const char* RightEye = "eye_bone_right";
const char* LeftEye = "eye_bone_left";
const char* RightEye = "eye_bone_right";
const char* Spine[5] = { "Bip01 Spine", "Bip01 Spine1", "Bip01 Spine2", "Bip01 Spine3", "Bip01 Spine4" };
const char* Neck[2] = { "Bip01 Neck", "Bip01 Neck1" };
const char* Spine[5] = { "Bip01 Spine", "Bip01 Spine1", "Bip01 Spine2", "Bip01 Spine3", "Bip01 Spine4" };
const char* Neck[2] = { "Bip01 Neck", "Bip01 Neck1" };
const char* LeftHeel = "Bip01 L Heel";
const char* LeftToe[2] = { "Bip01 L Toe0", "Bip01 L Toe1" };
const char* LeftHeel = "Bip01 L Heel";
const char* LeftToe[2] = { "Bip01 L Toe0", "Bip01 L Toe1" };
const char* RightHeel = "Bip01 R Heel";
const char* RightToe[2] = { "Bip01 R Toe0", "Bip01 R Toe1" };
}
}
const char* RightHeel = "Bip01 R Heel";
const char* RightToe[2] = { "Bip01 R Toe0", "Bip01 R Toe1" };
} // namespace EditorAnimationBones::Biped
@@ -140,7 +140,7 @@ bool AssetImporterManager::OnBrowseFiles()
bool encounteredCrate = false;
QStringList invalidFiles;
for (QString path : fileDialog.selectedFiles())
for (const QString& path : fileDialog.selectedFiles())
{
QString fileName = GetFileName(path);
QFileInfo info(path);
@@ -671,7 +671,7 @@ AzToolsFramework::PropertyRowWidget* ReflectedPropertyControl::FindPropertyRowWi
return nullptr;
}
const AzToolsFramework::ReflectedPropertyEditor::WidgetList& widgets = m_editor->GetWidgets();
for (auto instance : widgets)
for (const auto& instance : widgets)
{
if (instance.second->label() == item->GetPropertyName())
{
+1 -1
View File
@@ -1128,7 +1128,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
const QString oldLevelPattern = QDir(oldLevelFolder).absoluteFilePath("*.*");
const QString oldLevelName = Path::GetFile(GetLevelPathName());
const QString oldLevelXml = Path::ReplaceExtension(oldLevelName, "xml");
AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskAndInZips);
AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), AZ::IO::FileSearchLocation::Any);
if (findHandle)
{
do
+2 -2
View File
@@ -40,10 +40,10 @@ AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
namespace
{
// File name extension for python files
const QString s_kPythonFileNameSpec = "*.py";
const QString s_kPythonFileNameSpec("*.py");
// Tree root element name
const QString s_kRootElementName = "Python Scripts";
const QString s_kRootElementName("Python Scripts");
}
//////////////////////////////////////////////////////////////////////////
@@ -145,6 +145,15 @@ namespace SandboxEditor
}
};
const auto trackingTransform = [viewportId = m_viewportId]
{
bool tracking = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
tracking, viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsTrackingTransform);
return tracking;
};
m_firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(SandboxEditor::CameraFreeLookChannelId());
m_firstPersonRotateCamera->m_rotateSpeedFn = []
@@ -152,6 +161,11 @@ namespace SandboxEditor
return SandboxEditor::CameraRotateSpeed();
};
m_firstPersonRotateCamera->m_constrainPitch = [trackingTransform]
{
return !trackingTransform();
};
// default behavior is to hide the cursor but this can be disabled (useful for remote desktop)
// note: See CaptureCursorLook in the Settings Registry
m_firstPersonRotateCamera->SetActivationBeganFn(hideCursor);
@@ -255,6 +269,11 @@ namespace SandboxEditor
return SandboxEditor::CameraOrbitYawRotationInverted();
};
m_orbitRotateCamera->m_constrainPitch = [trackingTransform]
{
return !trackingTransform();
};
m_orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslateOffsetOrbit);
@@ -337,12 +356,12 @@ 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::StartTrackingTransform, worldFromLocal);
}
else
{
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StopTrackingTransform);
}
}
+27 -83
View File
@@ -298,13 +298,9 @@ AzToolsFramework::ViewportInteraction::MousePick EditorViewportWidget::BuildMous
{
AzToolsFramework::ViewportInteraction::MousePick mousePick;
mousePick.m_screenCoordinates = AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(point);
if (const auto& ray = m_renderViewport->ViewportScreenToWorldRay(mousePick.m_screenCoordinates);
ray.has_value())
{
mousePick.m_rayOrigin = ray.value().origin;
mousePick.m_rayDirection = ray.value().direction;
}
const auto[origin, direction] = m_renderViewport->ViewportScreenToWorldRay(mousePick.m_screenCoordinates);
mousePick.m_rayOrigin = origin;
mousePick.m_rayDirection = direction;
return mousePick;
}
@@ -895,23 +891,6 @@ AZ::Vector3 EditorViewportWidget::PickTerrain(const AzFramework::ScreenPoint& po
return LYVec3ToAZVec3(ViewToWorld(AzToolsFramework::ViewportInteraction::QPointFromScreenPoint(point), nullptr, true));
}
AZ::EntityId EditorViewportWidget::PickEntity(const AzFramework::ScreenPoint& point)
{
AZ::EntityId entityId;
HitContext hitInfo;
hitInfo.view = this;
if (HitTest(AzToolsFramework::ViewportInteraction::QPointFromScreenPoint(point), hitInfo))
{
if (hitInfo.object && (hitInfo.object->GetType() == OBJTYPE_AZENTITY))
{
auto entityObject = static_cast<CComponentEntityObject*>(hitInfo.object);
entityId = entityObject->GetAssociatedEntityId();
}
}
return entityId;
}
float EditorViewportWidget::TerrainHeight(const AZ::Vector2& position)
{
return GetIEditor()->GetTerrainElevation(position.GetX(), position.GetY());
@@ -1636,16 +1615,15 @@ void EditorViewportWidget::RenderSelectedRegion()
Vec3 EditorViewportWidget::WorldToView3D(const Vec3& wp, [[maybe_unused]] int nFlags) const
{
Vec3 out(0, 0, 0);
float x, y, z;
float x, y;
ProjectToScreen(wp.x, wp.y, wp.z, &x, &y, &z);
if (_finite(x) && _finite(y) && _finite(z))
ProjectToScreen(wp.x, wp.y, wp.z, &x, &y);
if (_finite(x) && _finite(y))
{
out.x = (x / 100) * m_rcClient.width();
out.y = (y / 100) * m_rcClient.height();
out.x /= static_cast<float>(QHighDpiScaling::factor(windowHandle()->screen()));
out.y /= static_cast<float>(QHighDpiScaling::factor(windowHandle()->screen()));
out.z = z;
}
return out;
}
@@ -1655,24 +1633,6 @@ QPoint EditorViewportWidget::WorldToView(const Vec3& wp) const
{
return AzToolsFramework::ViewportInteraction::QPointFromScreenPoint(m_renderViewport->ViewportWorldToScreen(LYVec3ToAZVec3(wp)));
}
//////////////////////////////////////////////////////////////////////////
QPoint EditorViewportWidget::WorldToViewParticleEditor(const Vec3& wp, int width, int height) const
{
QPoint p;
float x, y, z;
ProjectToScreen(wp.x, wp.y, wp.z, &x, &y, &z);
if (_finite(x) || _finite(y))
{
p.rx() = static_cast<int>((x / 100) * width);
p.ry() = static_cast<int>((y / 100) * height);
}
else
{
QPoint(0, 0);
}
return p;
}
//////////////////////////////////////////////////////////////////////////
Vec3 EditorViewportWidget::ViewToWorld(
@@ -1688,20 +1648,16 @@ Vec3 EditorViewportWidget::ViewToWorld(
AZ_UNUSED(collideWithObject);
auto ray = m_renderViewport->ViewportScreenToWorldRay(AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(vp));
if (!ray.has_value())
{
return Vec3(0, 0, 0);
}
const float maxDistance = 10000.f;
Vec3 v = AZVec3ToLYVec3(ray.value().direction) * maxDistance;
Vec3 v = AZVec3ToLYVec3(ray.direction) * maxDistance;
if (!_finite(v.x) || !_finite(v.y) || !_finite(v.z))
{
return Vec3(0, 0, 0);
}
Vec3 colp = AZVec3ToLYVec3(ray.value().origin) + 0.002f * v;
Vec3 colp = AZVec3ToLYVec3(ray.origin) + 0.002f * v;
return colp;
}
@@ -1740,21 +1696,19 @@ bool EditorViewportWidget::RayRenderMeshIntersection(IRenderMesh* pRenderMesh, c
return bRes;*/
}
void EditorViewportWidget::UnProjectFromScreen(float sx, float sy, float sz, float* px, float* py, float* pz) const
void EditorViewportWidget::UnProjectFromScreen(float sx, float sy, float* px, float* py, float* pz) const
{
AZ::Vector3 wp;
wp = m_renderViewport->ViewportScreenToWorld(AzFramework::ScreenPoint{(int)sx, m_rcClient.bottom() - ((int)sy)}, sz).value_or(wp);
const AZ::Vector3 wp = m_renderViewport->ViewportScreenToWorld(AzFramework::ScreenPoint{(int)sx, m_rcClient.bottom() - ((int)sy)});
*px = wp.GetX();
*py = wp.GetY();
*pz = wp.GetZ();
}
void EditorViewportWidget::ProjectToScreen(float ptx, float pty, float ptz, float* sx, float* sy, float* sz) const
void EditorViewportWidget::ProjectToScreen(float ptx, float pty, float ptz, float* sx, float* sy) const
{
AzFramework::ScreenPoint screenPosition = m_renderViewport->ViewportWorldToScreen(AZ::Vector3{ptx, pty, ptz});
*sx = static_cast<float>(screenPosition.m_x);
*sy = static_cast<float>(screenPosition.m_y);
*sz = 0.f;
}
//////////////////////////////////////////////////////////////////////////
@@ -1764,32 +1718,22 @@ void EditorViewportWidget::ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3&
Vec3 pos0, pos1;
float wx, wy, wz;
UnProjectFromScreen(static_cast<float>(vp.x()), static_cast<float>(rc.bottom() - vp.y()), 0.0f, &wx, &wy, &wz);
if (!_finite(wx) || !_finite(wy) || !_finite(wz))
{
return;
}
if (fabs(wx) > 1000000 || fabs(wy) > 1000000 || fabs(wz) > 1000000)
{
return;
}
pos0(wx, wy, wz);
UnProjectFromScreen(static_cast<float>(vp.x()), static_cast<float>(rc.bottom() - vp.y()), 1.0f, &wx, &wy, &wz);
if (!_finite(wx) || !_finite(wy) || !_finite(wz))
{
return;
}
if (fabs(wx) > 1000000 || fabs(wy) > 1000000 || fabs(wz) > 1000000)
{
return;
}
pos1(wx, wy, wz);
UnProjectFromScreen(static_cast<float>(vp.x()), static_cast<float>(rc.bottom() - vp.y()), &wx, &wy, &wz);
Vec3 v = (pos1 - pos0);
v = v.GetNormalized();
if (!_finite(wx) || !_finite(wy) || !_finite(wz))
{
return;
}
if (fabs(wx) > 1000000 || fabs(wy) > 1000000 || fabs(wz) > 1000000)
{
return;
}
pos0(wx, wy, wz);
raySrc = pos0;
rayDir = v;
rayDir = (pos0 - AZVec3ToLYVec3(m_renderViewport->GetCameraState().m_position)).GetNormalized();
}
//////////////////////////////////////////////////////////////////////////
@@ -2338,10 +2282,10 @@ void* EditorViewportWidget::GetSystemCursorConstraintWindow() const
return systemCursorConstrained ? renderOverlayHWND() : nullptr;
}
void EditorViewportWidget::BuildDragDropContext(AzQtComponents::ViewportDragContext& context, const QPoint& pt)
void EditorViewportWidget::BuildDragDropContext(
AzQtComponents::ViewportDragContext& context, const AzFramework::ViewportId viewportId, const QPoint& point)
{
const auto scaledPoint = WidgetToViewport(pt);
QtViewport::BuildDragDropContext(context, scaledPoint);
QtViewport::BuildDragDropContext(context, viewportId, point);
}
void EditorViewportWidget::RestoreViewportAfterGameMode()
+4 -5
View File
@@ -165,7 +165,6 @@ private:
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, const QPoint& point) override;
void SetViewportId(int id) override;
QPoint WorldToView(const Vec3& wp) const override;
QPoint WorldToViewParticleEditor(const Vec3& wp, int width, int height) const override;
Vec3 WorldToView3D(const Vec3& wp, int nFlags = 0) const override;
Vec3 ViewToWorld(const QPoint& vp, bool* collideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const override;
void ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3& rayDir) const override;
@@ -206,7 +205,6 @@ private:
void* GetSystemCursorConstraintWindow() const override;
// AzToolsFramework::MainEditorViewportInteractionRequestBus overrides ...
AZ::EntityId PickEntity(const AzFramework::ScreenPoint& point) override;
AZ::Vector3 PickTerrain(const AzFramework::ScreenPoint& point) override;
float TerrainHeight(const AZ::Vector2& position) override;
bool ShowingWorldSpace() override;
@@ -273,7 +271,8 @@ private:
bool CheckRespondToInput() const;
void BuildDragDropContext(AzQtComponents::ViewportDragContext& context, const QPoint& pt) override;
void BuildDragDropContext(
AzQtComponents::ViewportDragContext& context, AzFramework::ViewportId viewportId, const QPoint& point) override;
void SetAsActiveViewport();
void PushDisableRendering();
@@ -304,8 +303,8 @@ private:
const DisplayContext& GetDisplayContext() const { return m_displayContext; }
CBaseObject* GetCameraObject() const;
void UnProjectFromScreen(float sx, float sy, float sz, float* px, float* py, float* pz) const;
void ProjectToScreen(float ptx, float pty, float ptz, float* sx, float* sy, float* sz) const;
void UnProjectFromScreen(float sx, float sy, float* px, float* py, float* pz) const;
void ProjectToScreen(float ptx, float pty, float ptz, float* sx, float* sy) const;
AZ::RPI::ViewPtr GetCurrentAtomView() const;
-1
View File
@@ -45,7 +45,6 @@ struct IDisplayViewport
virtual const Matrix34& GetViewTM() const = 0;
virtual const Matrix34& GetScreenTM() const = 0;
virtual QPoint WorldToView(const Vec3& worldPoint) const = 0;
virtual QPoint WorldToViewParticleEditor(const Vec3& worldPoint, int width, int height) const = 0;
virtual Vec3 WorldToView3D(const Vec3& worldPoint, int flags = 0) const = 0;
virtual Vec3 ViewToWorld(const QPoint& vp, bool* collideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const = 0;
virtual void ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3& rayDir) const = 0;
@@ -27,7 +27,9 @@ namespace UnitTest
AZ::Entity* m_entity = nullptr;
AZ::ComponentDescriptor* m_transformComponent = nullptr;
static const AzFramework::ViewportId TestViewportId;
static inline constexpr AzFramework::ViewportId TestViewportId = 2345;
static inline constexpr float HalfInterpolateToTransformDuration =
AtomToolsFramework::ModularViewportCameraControllerRequests::InterpolateToTransformDuration * 0.5f;
void SetUp() override
{
@@ -76,8 +78,6 @@ namespace UnitTest
}
};
const AzFramework::ViewportId EditorCameraFixture::TestViewportId = AzFramework::ViewportId(1337);
TEST_F(EditorCameraFixture, ModularViewportCameraControllerReferenceFrameUpdatedWhenViewportEntityisChanged)
{
// Given
@@ -91,8 +91,8 @@ namespace UnitTest
&Camera::EditorCameraNotificationBus::Events::OnViewportViewEntityChanged, m_entity->GetId());
// ensure the viewport updates after the viewport view entity change
const float deltaTime = 1.0f / 60.0f;
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
// note: do a large step to ensure smoothing finishes (e.g. not 1.0f/60.0f)
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(2.0f), AZ::ScriptTimePoint() });
// retrieve updated camera transform
const AZ::Transform cameraTransform = m_cameraViewportContextView->GetCameraTransform();
@@ -102,61 +102,40 @@ namespace UnitTest
EXPECT_THAT(cameraTransform, IsClose(entityTransform));
}
TEST_F(EditorCameraFixture, ReferenceFrameRemainsIdentityAfterExternalCameraTransformChangeWhenNotSet)
TEST_F(EditorCameraFixture, TrackingTransformIsTrueAfterTransformIsTracked)
{
// Given
m_cameraViewportContextView->SetCameraTransform(AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f)));
// Given/When
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, referenceFrame);
// When
AZ::Transform referenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
bool trackingTransform = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
referenceFrame, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
trackingTransform, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsTrackingTransform);
// Then
// reference frame is still the identity
EXPECT_THAT(referenceFrame, IsClose(AZ::Transform::CreateIdentity()));
EXPECT_THAT(trackingTransform, ::testing::IsTrue());
}
TEST_F(EditorCameraFixture, ExternalCameraTransformChangeWhenReferenceFrameIsSetUpdatesReferenceFrame)
TEST_F(EditorCameraFixture, TrackingTransformIsFalseAfterTransformIsStoppedBeingTracked)
{
// Given
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
const AZ::Transform nextTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f));
m_cameraViewportContextView->SetCameraTransform(nextTransform);
// When
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
currentReferenceFrame, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
// Then
EXPECT_THAT(currentReferenceFrame, IsClose(nextTransform));
}
TEST_F(EditorCameraFixture, ReferenceFrameReturnedToIdentityAfterClear)
{
// Given
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, referenceFrame);
// When
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
currentReferenceFrame, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StopTrackingTransform);
// Then
EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity()));
bool trackingTransform = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
trackingTransform, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsTrackingTransform);
EXPECT_THAT(trackingTransform, ::testing::IsFalse());
}
TEST_F(EditorCameraFixture, InterpolateToTransform)
@@ -169,8 +148,10 @@ namespace UnitTest
transformToInterpolateTo);
// simulate interpolation
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
m_controllerList->UpdateViewport(
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
m_controllerList->UpdateViewport(
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
const auto finalTransform = m_cameraViewportContextView->GetCameraTransform();
@@ -184,7 +165,7 @@ namespace UnitTest
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, referenceFrame);
AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f));
@@ -195,18 +176,85 @@ namespace UnitTest
transformToInterpolateTo);
// simulate interpolation
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
currentReferenceFrame, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
m_controllerList->UpdateViewport(
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
m_controllerList->UpdateViewport(
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
const auto finalTransform = m_cameraViewportContextView->GetCameraTransform();
// Then
EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo));
EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity()));
}
TEST_F(EditorCameraFixture, BeginningCameraInterpolationReturnsTrue)
{
// Given/When
bool interpolationBegan = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
interpolationBegan, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
// Then
EXPECT_THAT(interpolationBegan, ::testing::IsTrue());
}
TEST_F(EditorCameraFixture, CameraInterpolationDoesNotBeginDuringAnExistingInterpolation)
{
// Given/When
bool initialInterpolationBegan = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
initialInterpolationBegan, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
m_controllerList->UpdateViewport(
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
bool nextInterpolationBegan = true;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
nextInterpolationBegan, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
bool interpolating = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
interpolating, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsInterpolating);
// Then
EXPECT_THAT(initialInterpolationBegan, ::testing::IsTrue());
EXPECT_THAT(nextInterpolationBegan, ::testing::IsFalse());
EXPECT_THAT(interpolating, ::testing::IsTrue());
}
TEST_F(EditorCameraFixture, CameraInterpolationCanBeginAfterAnInterpolationCompletes)
{
// Given/When
bool initialInterpolationBegan = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
initialInterpolationBegan, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
m_controllerList->UpdateViewport(
{ TestViewportId,
AzFramework::FloatSeconds(AtomToolsFramework::ModularViewportCameraControllerRequests::InterpolateToTransformDuration + 0.5f),
AZ::ScriptTimePoint() });
bool interpolating = true;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
interpolating, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsInterpolating);
bool nextInterpolationBegan = false;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
nextInterpolationBegan, TestViewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
// Then
EXPECT_THAT(initialInterpolationBegan, ::testing::IsTrue());
EXPECT_THAT(interpolating, ::testing::IsFalse());
EXPECT_THAT(nextInterpolationBegan, ::testing::IsTrue());
}
} // namespace UnitTest
@@ -74,7 +74,7 @@ namespace UnitTest
class ModularViewportCameraControllerFixture : public AllocatorsTestFixture
{
public:
static const AzFramework::ViewportId TestViewportId;
static inline constexpr AzFramework::ViewportId TestViewportId = 1234;
void SetUp() override
{
@@ -146,6 +146,17 @@ namespace UnitTest
controller->SetCameraPropsBuilderCallback(
[](AzFramework::CameraProps& cameraProps)
{
// note: rotateSmoothness is also used for roll (not related to camera input directly)
cameraProps.m_rotateSmoothnessFn = []
{
return 5.0f;
};
cameraProps.m_translateSmoothnessFn = []
{
return 5.0f;
};
cameraProps.m_rotateSmoothingEnabledFn = []
{
return false;
@@ -209,8 +220,6 @@ namespace UnitTest
AZStd::unique_ptr<SandboxEditor::EditorModularViewportCameraComposer> m_editorModularViewportCameraComposer;
};
const AzFramework::ViewportId ModularViewportCameraControllerFixture::TestViewportId = AzFramework::ViewportId(0);
TEST_F(ModularViewportCameraControllerFixture, MouseMovementDoesNotAccumulateExcessiveDriftInModularViewportCameraWithVaryingDeltaTime)
{
SandboxEditor::SetCameraCaptureCursorForLook(false);
@@ -380,6 +389,7 @@ namespace UnitTest
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::RightButton, Qt::NoModifier, start + mouseDelta);
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
// update the position of the widget
const auto offset = QPoint(500, 500);
@@ -12,6 +12,7 @@
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <Editor/ViewportManipulatorController.h>
#include <Mocks/MockWindowRequests.h>
namespace UnitTest
{
@@ -77,14 +78,15 @@ namespace UnitTest
class ViewportManipulatorControllerFixture : public AllocatorsTestFixture
{
public:
static const AzFramework::ViewportId TestViewportId;
static inline constexpr AzFramework::ViewportId TestViewportId = 1234;
static inline const QSize WidgetSize = QSize(1920, 1080);
void SetUp() override
{
AllocatorsTestFixture::SetUp();
m_rootWidget = AZStd::make_unique<QWidget>();
m_rootWidget->setFixedSize(QSize(100, 100));
m_rootWidget->setFixedSize(WidgetSize);
QApplication::setActiveWindow(m_rootWidget.get());
m_controllerList = AZStd::make_shared<AzFramework::ViewportControllerList>();
@@ -111,8 +113,6 @@ namespace UnitTest
AZStd::unique_ptr<AzToolsFramework::QtEventToAzInputMapper> m_inputChannelMapper;
};
const AzFramework::ViewportId ViewportManipulatorControllerFixture::TestViewportId = AzFramework::ViewportId(0);
TEST_F(ViewportManipulatorControllerFixture, AnEventIsNotPropagatedToTheViewportWhenAManipulatorHandlesItFirst)
{
// forward input events to our controller list
@@ -227,4 +227,74 @@ namespace UnitTest
// the key was released (cleared)
EXPECT_TRUE(endedEvent);
}
TEST_F(ViewportManipulatorControllerFixture, DoubleClickIsNotRegisteredIfMouseDeltaHasMovedMoreThanDeadzoneInClickInterval)
{
AzFramework::NativeWindowHandle nativeWindowHandle = nullptr;
// forward input events to our controller list
QObject::connect(
m_inputChannelMapper.get(), &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, m_rootWidget.get(),
[this, nativeWindowHandle](const AzFramework::InputChannel* inputChannel, [[maybe_unused]] QEvent* event)
{
m_controllerList->HandleInputChannelEvent(
AzFramework::ViewportControllerInputEvent{ TestViewportId, nativeWindowHandle, *inputChannel });
});
::testing::NiceMock<MockWindowRequests> mockWindowRequests;
mockWindowRequests.Connect(nativeWindowHandle);
using ::testing::Return;
// note: WindowRequests is used internally by ViewportManipulatorController
ON_CALL(mockWindowRequests, GetClientAreaSize())
.WillByDefault(Return(AzFramework::WindowSize(WidgetSize.width(), WidgetSize.height())));
EditorInteractionViewportSelectionFake editorInteractionViewportFake;
editorInteractionViewportFake.m_internalHandleMouseManipulatorInteraction = [](const MouseInteractionEvent&)
{
// report the event was not handled (manipulator was not interacted with)
return false;
};
bool doubleClickDetected = false;
editorInteractionViewportFake.m_internalHandleMouseViewportInteraction =
[&doubleClickDetected](const MouseInteractionEvent& mouseInteractionEvent)
{
// ensure no double click event is detected with the given inputs below
if (mouseInteractionEvent.m_mouseEvent == AzToolsFramework::ViewportInteraction::MouseEvent::DoubleClick)
{
doubleClickDetected = true;
}
return true;
};
editorInteractionViewportFake.Connect();
m_controllerList->Add(AZStd::make_shared<SandboxEditor::ViewportManipulatorController>());
// simulate a click, move, click
MouseMove(m_rootWidget.get(), QPoint(0, 0), QPoint(10, 10));
MousePressAndMove(m_rootWidget.get(), QPoint(10, 10), QPoint(0, 0), Qt::MouseButton::LeftButton);
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier, QPoint(10, 10));
MouseMove(m_rootWidget.get(), QPoint(10, 10), QPoint(20, 20));
MousePressAndMove(m_rootWidget.get(), QPoint(20, 20), QPoint(0, 0), Qt::MouseButton::LeftButton);
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier, QPoint(20, 20));
// ensure no double click was detected
EXPECT_FALSE(doubleClickDetected);
// simulate double click (sanity check it still is detected correctly with no movement)
MouseMove(m_rootWidget.get(), QPoint(0, 0), QPoint(10, 10));
MousePressAndMove(m_rootWidget.get(), QPoint(10, 10), QPoint(0, 0), Qt::MouseButton::LeftButton);
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier, QPoint(10, 10));
MousePressAndMove(m_rootWidget.get(), QPoint(10, 10), QPoint(0, 0), Qt::MouseButton::LeftButton);
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier, QPoint(10, 10));
// ensure a double click was detected
EXPECT_TRUE(doubleClickDetected);
mockWindowRequests.Disconnect();
editorInteractionViewportFake.Disconnect();
}
} // namespace UnitTest
+6 -22
View File
@@ -519,7 +519,7 @@ MainWindow* MainWindow::instance()
void MainWindow::closeEvent(QCloseEvent* event)
{
gSettings.Save();
gSettings.Save(true);
AzFramework::SystemCursorState currentCursorState;
bool isInGameMode = false;
@@ -708,14 +708,10 @@ void MainWindow::InitActions()
.SetShortcut(QKeySequence::Undo)
.SetReserved()
.SetStatusTip(tr("Undo last operation"))
//.SetMenu(new QMenu("FIXME"))
.SetApplyHoverEffect()
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateUndo);
am->AddAction(ID_REDO, tr("&Redo"))
.SetShortcut(AzQtComponents::RedoKeySequence)
.SetReserved()
//.SetMenu(new QMenu("FIXME"))
.SetApplyHoverEffect()
.SetStatusTip(tr("Redo last undo operation"))
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateRedo);
@@ -731,7 +727,6 @@ void MainWindow::InitActions()
// Modify actions
am->AddAction(AzToolsFramework::EditModeMove, tr("Move"))
.SetIcon(Style::icon("Move"))
.SetApplyHoverEffect()
.SetShortcut(tr("1"))
.SetToolTip(tr("Move (1)"))
.SetCheckable(true)
@@ -757,7 +752,6 @@ void MainWindow::InitActions()
});
am->AddAction(AzToolsFramework::EditModeRotate, tr("Rotate"))
.SetIcon(Style::icon("Translate"))
.SetApplyHoverEffect()
.SetShortcut(tr("2"))
.SetToolTip(tr("Rotate (2)"))
.SetCheckable(true)
@@ -783,7 +777,6 @@ void MainWindow::InitActions()
});
am->AddAction(AzToolsFramework::EditModeScale, tr("Scale"))
.SetIcon(Style::icon("Scale"))
.SetApplyHoverEffect()
.SetShortcut(tr("3"))
.SetToolTip(tr("Scale (3)"))
.SetCheckable(true)
@@ -808,7 +801,6 @@ void MainWindow::InitActions()
am->AddAction(AzToolsFramework::SnapToGrid, tr("Snap to grid"))
.SetIcon(Style::icon("Grid"))
.SetApplyHoverEffect()
.SetShortcut(tr("G"))
.SetToolTip(tr("Snap to grid (G)"))
.SetStatusTip(tr("Toggles snap to grid"))
@@ -821,7 +813,6 @@ void MainWindow::InitActions()
am->AddAction(AzToolsFramework::SnapAngle, tr("Snap angle"))
.SetIcon(Style::icon("Angle"))
.SetApplyHoverEffect()
.SetStatusTip(tr("Snap angle"))
.SetCheckable(true)
.RegisterUpdateCallback([](QAction* action) {
@@ -961,7 +952,6 @@ void MainWindow::InitActions()
.SetShortcut(tr("Ctrl+P"))
.SetToolTip(tr("Simulate (Ctrl+P)"))
.SetStatusTip(tr("Enable processing of Physics and AI."))
.SetApplyHoverEffect()
.SetCheckable(true)
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnSwitchPhysicsUpdate);
am->AddAction(ID_GAME_SYNCPLAYER, tr("Move Player and Camera Separately")).SetCheckable(true)
@@ -1051,8 +1041,7 @@ void MainWindow::InitActions()
// Editors Toolbar actions
am->AddAction(ID_OPEN_ASSET_BROWSER, tr("Asset browser"))
.SetToolTip(tr("Open Asset Browser"))
.SetApplyHoverEffect();
.SetToolTip(tr("Open Asset Browser"));
AZ::EBusReduceResult<bool, AZStd::logical_or<bool>> emfxEnabled(false);
using AnimationRequestBus = AzToolsFramework::EditorAnimationSystemRequestsBus;
@@ -1062,8 +1051,7 @@ void MainWindow::InitActions()
{
QAction* action = am->AddAction(ID_OPEN_EMOTIONFX_EDITOR, tr("Animation Editor"))
.SetToolTip(tr("Open Animation Editor"))
.SetIcon(QIcon(":/EMotionFX/EMFX_icon_32x32.png"))
.SetApplyHoverEffect();
.SetIcon(QIcon(":/EMotionFX/EMFX_icon_32x32.png"));
QObject::connect(action, &QAction::triggered, this, []() {
QtViewPaneManager::instance()->OpenPane(LyViewPane::AnimationEditor);
});
@@ -1071,12 +1059,10 @@ void MainWindow::InitActions()
am->AddAction(ID_OPEN_AUDIO_CONTROLS_BROWSER, tr("Audio Controls Editor"))
.SetToolTip(tr("Open Audio Controls Editor"))
.SetIcon(Style::icon("Audio"))
.SetApplyHoverEffect();
.SetIcon(Style::icon("Audio"));
am->AddAction(ID_OPEN_UICANVASEDITOR, tr(LyViewPane::UiEditor))
.SetToolTip(tr("Open UI Editor"))
.SetApplyHoverEffect();
.SetToolTip(tr("Open UI Editor"));
// Edit Mode Toolbar Actions
am->AddAction(IDC_SELECTION_MASK, tr("Selected Object Types"));
@@ -1089,12 +1075,10 @@ void MainWindow::InitActions()
// Object Toolbar Actions
am->AddAction(ID_GOTO_SELECTED, tr("Go to selected object"))
.SetIcon(Style::icon("select_object"))
.SetApplyHoverEffect()
.Connect(&QAction::triggered, this, &MainWindow::OnGotoSelected);
// Misc Toolbar Actions
am->AddAction(ID_OPEN_SUBSTANCE_EDITOR, tr("Open Substance Editor"))
.SetApplyHoverEffect();
am->AddAction(ID_OPEN_SUBSTANCE_EDITOR, tr("Open Substance Editor"));
}
void MainWindow::InitToolActionHandlers()
+1 -1
View File
@@ -1649,7 +1649,7 @@ QString CBaseObject::GetTypeName() const
}
QString name;
name.append(className.mid(0, className.length() - subClassName.length()));
name.append(className.midRef(0, className.length() - subClassName.length()));
return name;
}
+3 -3
View File
@@ -592,11 +592,11 @@ void CEntityObject::AdjustLightProperties(CVarBlockPtr& properties, const char*
if (IVariable* pCastShadowVarLegacy = FindVariableInSubBlock(properties, pSubBlockVar, "bCastShadow"))
{
pCastShadowVarLegacy->SetFlags(pCastShadowVarLegacy->GetFlags() | IVariable::UI_INVISIBLE);
if (pCastShadowVarLegacy->GetDisplayValue()[0] != '0')
const QString zeroPrefix("0");
if (!pCastShadowVarLegacy->GetDisplayValue().startsWith(zeroPrefix))
{
bCastShadowLegacy = true;
pCastShadowVarLegacy->SetDisplayValue("0");
pCastShadowVarLegacy->SetDisplayValue(zeroPrefix);
}
}
+1 -1
View File
@@ -828,7 +828,7 @@ void CObjectManager::ShowLastHiddenObject()
{
uint64 mostRecentID = CBaseObject::s_invalidHiddenID;
CBaseObject* mostRecentObject = nullptr;
for (auto it : m_objects)
for (const auto& it : m_objects)
{
CBaseObject* obj = it.second;
@@ -10,7 +10,6 @@
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/RTTI/AttributeReader.h>
@@ -57,6 +56,7 @@
#include <AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx>
#include <AzToolsFramework/UI/Layer/NameConflictWarning.hxx>
#include <AzToolsFramework/ViewportSelection/EditorHelpers.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <MathConversion.h>
#include <Atom/RPI.Public/ViewportContext.h>
@@ -1394,13 +1394,13 @@ void SandboxIntegrationManager::ContextMenu_NewEntity()
{
AZ::Vector3 worldPosition = AZ::Vector3::CreateZero();
CViewport* view = GetIEditor()->GetViewManager()->GetGameViewport();
// If we don't have a viewport active to aid in placement, the object
// will be created at the origin.
if (view)
if (CViewport* view = GetIEditor()->GetViewManager()->GetGameViewport())
{
const QPoint viewPoint(static_cast<int>(m_contextMenuViewPoint.GetX()), static_cast<int>(m_contextMenuViewPoint.GetY()));
worldPosition = view->GetHitLocation(viewPoint);
worldPosition = AzToolsFramework::FindClosestPickIntersection(
view->GetViewportId(), AzFramework::ScreenPointFromVector2(m_contextMenuViewPoint), AzToolsFramework::EditorPickRayLength,
GetDefaultEntityPlacementDistance());
}
CreateNewEntityAtPosition(worldPosition);
@@ -1675,6 +1675,12 @@ void SandboxIntegrationManager::GoToEntitiesInViewports(const AzToolsFramework::
if (auto viewportContext = viewportContextManager->GetViewportContextById(viewIndex))
{
const AZ::Transform cameraTransform = viewportContext->GetCameraTransform();
// do not attempt to interpolate to where we currently are
if (cameraTransform.GetTranslation().IsClose(center))
{
continue;
}
const AZ::Vector3 forward = (center - cameraTransform.GetTranslation()).GetNormalized();
// move camera 25% further back than required
@@ -2640,7 +2640,7 @@ QSize OutlinerItemDelegate::sizeHint(const QStyleOptionViewItem& option, const Q
m_cachedBoundingRectOfTallCharacter = QRect();
};
QTimer::singleShot(0, resetFunction);
QTimer::singleShot(0, this, resetFunction);
}
// And add 8 to it gives the outliner roughly the visible spacing we're looking for.
@@ -121,6 +121,18 @@ namespace
SortEntityChildrenRecursively(childId, comparer);
}
}
QModelIndex nextIndexForTree(bool direction, OutlinerTreeView *tree, QModelIndex current)
{
if (direction)
{
return tree->indexAbove(current);
}
else
{
return tree->indexBelow(current);
}
}
}
OutlinerWidget::OutlinerWidget(QWidget* pParent, Qt::WindowFlags flags)
@@ -891,9 +903,7 @@ void OutlinerWidget::DoSelectSliceRootNextToSelection(bool isTraversalUpwards)
return;
}
AZStd::function<QModelIndex(QModelIndex)> getNextIdxFunction =
AZStd::bind(isTraversalUpwards ? &QTreeView::indexAbove : &QTreeView::indexBelow, treeView, AZStd::placeholders::_1);
QModelIndex nextIdx = getNextIdxFunction(currentIdx);
QModelIndex nextIdx = nextIndexForTree(isTraversalUpwards,treeView,currentIdx);
bool foundSliceRoot = false;
while (nextIdx.isValid() && !foundSliceRoot)
@@ -904,7 +914,7 @@ void OutlinerWidget::DoSelectSliceRootNextToSelection(bool isTraversalUpwards)
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
foundSliceRoot, &AzToolsFramework::ToolsApplicationRequests::IsSliceRootEntity, currentEntityId);
nextIdx = getNextIdxFunction(currentIdx);
nextIdx = nextIndexForTree(isTraversalUpwards, treeView, currentIdx);
}
if (foundSliceRoot)
@@ -934,13 +944,10 @@ void OutlinerWidget::DoSelectEdgeSliceRoot(bool shouldSelectTopMostSlice)
}
QModelIndex currentIdx;
AZStd::function<QModelIndex(QModelIndex)> getNextIdxFunction;
if (shouldSelectTopMostSlice)
{
currentIdx = itemModel->index(0, OutlinerListModel::ColumnName);
getNextIdxFunction =
AZStd::bind(&QTreeView::indexBelow, treeView, AZStd::placeholders::_1);
}
else
{
@@ -949,9 +956,6 @@ void OutlinerWidget::DoSelectEdgeSliceRoot(bool shouldSelectTopMostSlice)
{
currentIdx = itemModel->index(itemModel->rowCount(currentIdx) - 1, OutlinerListModel::ColumnName, currentIdx);
}
getNextIdxFunction =
AZStd::bind(&QTreeView::indexAbove, treeView, AZStd::placeholders::_1);
}
QModelIndex nextIdx = currentIdx;
@@ -964,7 +968,7 @@ void OutlinerWidget::DoSelectEdgeSliceRoot(bool shouldSelectTopMostSlice)
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
foundSliceRoot, &AzToolsFramework::ToolsApplicationRequests::IsSliceRootEntity, currentEntityId);
nextIdx = getNextIdxFunction(currentIdx);
nextIdx = nextIndexForTree(shouldSelectTopMostSlice,treeView,currentIdx);
} while (nextIdx.isValid() && !foundSliceRoot);
if (foundSliceRoot)
@@ -1416,7 +1420,10 @@ void OutlinerWidget::SortContent()
}
m_entitiesToSort.clear();
auto comparer = AZStd::bind(&CompareEntitiesForSorting, AZStd::placeholders::_1, AZStd::placeholders::_2, m_sortMode);
auto comparer = [sortMode = m_sortMode](AZ::EntityId left, AZ::EntityId right) -> bool
{
return CompareEntitiesForSorting(left, right, sortMode);
};
for (const AZ::EntityId& entityId : parentsToSort)
{
SortEntityChildren(entityId, comparer);
@@ -1433,7 +1440,10 @@ void OutlinerWidget::OnSortModeChanged(EntityOutliner::DisplaySortMode sortMode)
if (sortMode != EntityOutliner::DisplaySortMode::Manually)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
auto comparer = AZStd::bind(&CompareEntitiesForSorting, AZStd::placeholders::_1, AZStd::placeholders::_2, sortMode);
auto comparer = [sortMode = m_sortMode](AZ::EntityId left, AZ::EntityId right) -> bool
{
return CompareEntitiesForSorting(left, right, sortMode);
};
SortEntityChildrenRecursively(AZ::EntityId(), comparer);
}
+6 -4
View File
@@ -471,7 +471,7 @@ void SEditorSettings::LoadValue(const char* sSection, const char* sKey, ESystemC
}
//////////////////////////////////////////////////////////////////////////
void SEditorSettings::Save()
void SEditorSettings::Save(bool isEditorClosing)
{
QString strStringPlaceholder;
@@ -638,14 +638,16 @@ void SEditorSettings::Save()
// --- Settings Registry values
// Prefab System UI
AzFramework::ApplicationRequests::Bus::Broadcast(
&AzFramework::ApplicationRequests::SetPrefabSystemEnabled, prefabSystem);
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::SetPrefabSystemEnabled, prefabSystem);
AzToolsFramework::Prefab::PrefabLoaderInterface* prefabLoaderInterface =
AZ::Interface<AzToolsFramework::Prefab::PrefabLoaderInterface>::Get();
prefabLoaderInterface->SetSaveAllPrefabsPreference(levelSaveSettings.saveAllPrefabsPreference);
SaveSettingsRegistryFile();
if (!isEditorClosing)
{
SaveSettingsRegistryFile();
}
}
//////////////////////////////////////////////////////////////////////////
+1 -1
View File
@@ -267,7 +267,7 @@ struct SANDBOX_API SEditorSettings
AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
SEditorSettings();
~SEditorSettings() = default;
void Save();
void Save(bool isEditorClosing = false);
void Load();
void LoadCloudSettings();
+26 -210
View File
@@ -14,14 +14,19 @@
// Qt
#include <QPainter>
// AzCore
#include <AzCore/Console/IConsole.h>
// AzQtComponents
#include <AzQtComponents/DragAndDrop/ViewportDragAndDrop.h>
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
// Editor
#include "Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h"
#include "ViewManager.h"
#include "Include/ITransformManipulator.h"
#include "Include/HitContext.h"
@@ -32,22 +37,35 @@
#include "GameEngine.h"
#include "Settings.h"
#ifdef LoadCursor
#undef LoadCursor
#endif
AZ_CVAR(
float,
ed_defaultEntityPlacementDistance,
10.0f,
nullptr,
AZ::ConsoleFunctorFlags::Null,
"The default distance to place an entity from the camera if no intersection is found");
float GetDefaultEntityPlacementDistance()
{
return ed_defaultEntityPlacementDistance;
}
//////////////////////////////////////////////////////////////////////
// Viewport drag and drop support
//////////////////////////////////////////////////////////////////////
void QtViewport::BuildDragDropContext(AzQtComponents::ViewportDragContext& context, const QPoint& pt)
void QtViewport::BuildDragDropContext(
AzQtComponents::ViewportDragContext& context, const AzFramework::ViewportId viewportId, const QPoint& point)
{
context.m_hitLocation = AZ::Vector3::CreateZero();
context.m_hitLocation = GetHitLocation(pt);
context.m_hitLocation = AzToolsFramework::FindClosestPickIntersection(
viewportId, AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(point), AzToolsFramework::EditorPickRayLength,
GetDefaultEntityPlacementDistance());
}
void QtViewport::dragEnterEvent(QDragEnterEvent* event)
{
if (!GetIEditor()->GetGameEngine()->IsLevelLoaded())
@@ -66,7 +84,7 @@ void QtViewport::dragEnterEvent(QDragEnterEvent* event)
// new bus-based way of doing it (install a listener!)
using namespace AzQtComponents;
ViewportDragContext context;
BuildDragDropContext(context, event->pos());
BuildDragDropContext(context, GetViewportId(), event->pos());
DragAndDropEventsBus::Event(DragAndDropContexts::EditorViewport, &DragAndDropEvents::DragEnter, event, context);
}
}
@@ -89,7 +107,7 @@ void QtViewport::dragMoveEvent(QDragMoveEvent* event)
// new bus-based way of doing it (install a listener!)
using namespace AzQtComponents;
ViewportDragContext context;
BuildDragDropContext(context, event->pos());
BuildDragDropContext(context, GetViewportId(), event->pos());
DragAndDropEventsBus::Event(DragAndDropContexts::EditorViewport, &DragAndDropEvents::DragMove, event, context);
}
}
@@ -112,7 +130,7 @@ void QtViewport::dropEvent(QDropEvent* event)
{
// new bus-based way of doing it (install a listener!)
ViewportDragContext context;
BuildDragDropContext(context, event->pos());
BuildDragDropContext(context, GetViewportId(), event->pos());
DragAndDropEventsBus::Event(DragAndDropContexts::EditorViewport, &DragAndDropEvents::Drop, event, context);
}
}
@@ -340,13 +358,6 @@ void QtViewport::resizeEvent(QResizeEvent* event)
Update();
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::leaveEvent(QEvent* event)
{
QWidget::leaveEvent(event);
MouseCallback(eMouseLeave, QPoint(), Qt::KeyboardModifiers(), Qt::MouseButtons());
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::paintEvent([[maybe_unused]] QPaintEvent* event)
{
@@ -581,63 +592,7 @@ void QtViewport::keyReleaseEvent(QKeyEvent* event)
OnKeyUp(nativeKey, 1, event->nativeModifiers());
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnLButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
{
// Save the mouse down position
m_cMouseDownPos = point;
if (MouseCallback(eMouseLDown, point, modifiers))
{
return;
}
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnLButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point)
{
// Check Edit Tool.
MouseCallback(eMouseLUp, point, modifiers);
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
{
MouseCallback(eMouseRDown, point, modifiers);
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnRButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point)
{
MouseCallback(eMouseRUp, point, modifiers);
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnMButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
{
// Check Edit Tool.
MouseCallback(eMouseMDown, point, modifiers);
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnMButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point)
{
// Move the viewer to the mouse location.
// Check Edit Tool.
MouseCallback(eMouseMUp, point, modifiers);
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnMButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point)
{
MouseCallback(eMouseMDblClick, point, modifiers);
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnMouseMove(Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint& point)
{
MouseCallback(eMouseMove, point, modifiers, buttons);
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnSetCursor()
@@ -696,44 +651,6 @@ void QtViewport::OnDragSelectRectangle(const QRect& rect, bool bNormalizeRect)
GetIEditor()->SetStatusText(szNewStatusText);
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnLButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point)
{
if (GetIEditor()->IsInGameMode())
{
// Ignore double clicks while in game.
return;
}
MouseCallback(eMouseLDblClick, point, modifiers);
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnRButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point)
{
MouseCallback(eMouseRDblClick, point, modifiers);
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnKeyDown([[maybe_unused]] UINT nChar, [[maybe_unused]] UINT nRepCnt, [[maybe_unused]] UINT nFlags)
{
if (GetIEditor()->IsInGameMode())
{
// Ignore key downs while in game.
return;
}
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::OnKeyUp([[maybe_unused]] UINT nChar, [[maybe_unused]] UINT nRepCnt, [[maybe_unused]] UINT nFlags)
{
if (GetIEditor()->IsInGameMode())
{
// Ignore key downs while in game.
return;
}
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::SetCurrentCursor(const QCursor& hCursor, const QString& cursorString)
{
@@ -1119,29 +1036,6 @@ bool QtViewport::HitTest(const QPoint& point, HitContext& hitInfo)
return false;
}
AZ::Vector3 QtViewport::GetHitLocation(const QPoint& point)
{
Vec3 pos = Vec3(ZERO);
HitContext hit;
if (HitTest(point, hit))
{
pos = hit.raySrc + hit.rayDir * hit.dist;
pos = SnapToGrid(pos);
}
else
{
bool hitTerrain;
pos = ViewToWorld(point, &hitTerrain);
if (hitTerrain)
{
pos.z = GetIEditor()->GetTerrainElevation(pos.x, pos.y);
}
pos = SnapToGrid(pos);
}
return AZ::Vector3(pos.x, pos.y, pos.z);
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::SetZoomFactor(float fZoomFactor)
{
@@ -1315,84 +1209,6 @@ bool QtViewport::GetAdvancedSelectModeFlag()
return m_bAdvancedSelectMode;
}
//////////////////////////////////////////////////////////////////////////
bool QtViewport::MouseCallback(EMouseEvent event, const QPoint& point, Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons)
{
AZ_PROFILE_FUNCTION(Editor);
// Ignore any mouse events in game mode.
if (GetIEditor()->IsInGameMode())
{
return true;
}
// We must ignore mouse events when we are in the middle of an assert.
// Reason: If we have an assert called from an engine module under the editor, if we call this function,
// it may call the engine again and cause a deadlock.
// Concrete example: CryPhysics called from Trackview causing an assert, and moving the cursor over the viewport
// would cause the editor to freeze as it calls CryPhysics again for a raycast while it didn't release the lock.
if (gEnv->pSystem->IsAssertDialogVisible())
{
return true;
}
//////////////////////////////////////////////////////////////////////////
// Hit test gizmo objects.
//////////////////////////////////////////////////////////////////////////
bool bAltClick = (modifiers & Qt::AltModifier);
bool bCtrlClick = (modifiers & Qt::ControlModifier);
bool bShiftClick = (modifiers & Qt::ShiftModifier);
int flags = (bCtrlClick ? MK_CONTROL : 0) |
(bShiftClick ? MK_SHIFT : 0) |
((buttons& Qt::LeftButton) ? MK_LBUTTON : 0) |
((buttons& Qt::MiddleButton) ? MK_MBUTTON : 0) |
((buttons& Qt::RightButton) ? MK_RBUTTON : 0);
switch (event)
{
case eMouseMove:
if (m_nLastUpdateFrame == m_nLastMouseMoveFrame)
{
// If mouse move event generated in the same frame, ignore it.
return false;
}
m_nLastMouseMoveFrame = m_nLastUpdateFrame;
// Skip the marker position update if anything is selected, since it is only used
// by the info bar which doesn't show the marker when there is an active selection.
// This helps a performance issue when calling ViewToWorld (which calls RayWorldIntersection)
// on every mouse movement becomes very expensive in scenes with large amounts of entities.
CSelectionGroup* selection = GetIEditor()->GetSelection();
if (!(buttons & Qt::RightButton) /* && m_nLastUpdateFrame != m_nLastMouseMoveFrame*/ && (selection && selection->IsEmpty()))
{
//m_nLastMouseMoveFrame = m_nLastUpdateFrame;
Vec3 pos = ViewToWorld(point);
GetIEditor()->SetMarkerPosition(pos);
}
break;
}
QPoint tempPoint(point.x(), point.y());
//////////////////////////////////////////////////////////////////////////
// Handle viewport manipulators.
//////////////////////////////////////////////////////////////////////////
if (!bAltClick)
{
ITransformManipulator* pManipulator = GetIEditor()->GetTransformManipulator();
if (pManipulator)
{
if (pManipulator->MouseCallback(this, event, tempPoint, flags))
{
return true;
}
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void QtViewport::ProcessRenderLisneters(DisplayContext& rstDisplayContext)
{
+20 -23
View File
@@ -6,13 +6,12 @@
*
*/
// Description : interface for the CViewport class.
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzFramework/Viewport/ViewportId.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h>
#include <AzToolsFramework/ViewportUi/ViewportUiManager.h>
#include <Cry_Color.h>
@@ -88,6 +87,9 @@ enum EStdCursor
STD_CURSOR_LAST,
};
//! The default distance an entity is placed from the camera if there is no intersection
SANDBOX_API float GetDefaultEntityPlacementDistance();
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
class SANDBOX_API CViewport
: public IDisplayViewport
@@ -201,7 +203,6 @@ public:
//! Performs hit testing of 2d point in view to find which object hit.
virtual bool HitTest(const QPoint& point, HitContext& hitInfo) = 0;
virtual AZ::Vector3 GetHitLocation(const QPoint& point) = 0;
virtual void MakeConstructionPlane(int axis) = 0;
@@ -432,7 +433,6 @@ public:
//! Performs hit testing of 2d point in view to find which object hit.
bool HitTest(const QPoint& point, HitContext& hitInfo) override;
AZ::Vector3 GetHitLocation(const QPoint& point) override;
//! Do 2D hit testing of line in world space.
// pToCameraDistance is an optional output parameter in which distance from the camera to the line is returned.
@@ -522,9 +522,6 @@ protected:
void setRenderOverlayVisible(bool);
bool isRenderOverlayVisible() const;
// called to process mouse callback inside the viewport.
virtual bool MouseCallback(EMouseEvent event, const QPoint& point, Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons = Qt::NoButton);
void ProcessRenderLisneters(DisplayContext& rstDisplayContext);
void mousePressEvent(QMouseEvent* event) override;
@@ -535,29 +532,29 @@ protected:
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
void leaveEvent(QEvent* event) override;
void paintEvent(QPaintEvent* event) override;
virtual void OnMouseMove(Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint& point);
virtual void OnMouseWheel(Qt::KeyboardModifiers modifiers, short zDelta, const QPoint& pt);
virtual void OnLButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point);
virtual void OnLButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point);
virtual void OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point);
virtual void OnRButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point);
virtual void OnMButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point);
virtual void OnMButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point);
virtual void OnMButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point);
virtual void OnLButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point);
virtual void OnRButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point);
virtual void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
virtual void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);
virtual void OnMouseMove(Qt::KeyboardModifiers, Qt::MouseButtons, const QPoint&) {}
virtual void OnMouseWheel(Qt::KeyboardModifiers, short zDelta, const QPoint&);
virtual void OnLButtonDown(Qt::KeyboardModifiers, const QPoint&) {}
virtual void OnLButtonUp(Qt::KeyboardModifiers, const QPoint&) {}
virtual void OnRButtonDown(Qt::KeyboardModifiers, const QPoint&) {}
virtual void OnRButtonUp(Qt::KeyboardModifiers, const QPoint&) {}
virtual void OnMButtonDblClk(Qt::KeyboardModifiers, const QPoint&) {}
virtual void OnMButtonDown(Qt::KeyboardModifiers, const QPoint&) {}
virtual void OnMButtonUp(Qt::KeyboardModifiers, const QPoint&) {}
virtual void OnLButtonDblClk(Qt::KeyboardModifiers, const QPoint&) {}
virtual void OnRButtonDblClk(Qt::KeyboardModifiers, const QPoint&) {}
virtual void OnKeyDown([[maybe_unused]] UINT nChar, [[maybe_unused]] UINT nRepCnt, [[maybe_unused]] UINT nFlags) {}
virtual void OnKeyUp([[maybe_unused]] UINT nChar, [[maybe_unused]] UINT nRepCnt, [[maybe_unused]] UINT nFlags) {}
#if defined(AZ_PLATFORM_WINDOWS)
void OnRawInput(UINT wParam, HRAWINPUT lParam);
#endif
void OnSetCursor();
virtual void BuildDragDropContext(AzQtComponents::ViewportDragContext& context, const QPoint& pt);
virtual void BuildDragDropContext(
AzQtComponents::ViewportDragContext& context, AzFramework::ViewportId viewportId, const QPoint& point);
void dragEnterEvent(QDragEnterEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override;
void dragLeaveEvent(QDragLeaveEvent* event) override;
+33 -23
View File
@@ -8,13 +8,15 @@
#include "ViewportManipulatorController.h"
#include <AzCore/Script/ScriptTimePoint.h>
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzCore/Script/ScriptTimePoint.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <QApplication>
@@ -87,8 +89,14 @@ namespace SandboxEditor
}
using InteractionBus = AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
using namespace AzToolsFramework::ViewportInteraction;
using AzFramework::InputChannel;
using AzToolsFramework::ViewportInteraction::KeyboardModifier;
using AzToolsFramework::ViewportInteraction::MouseButton;
using AzToolsFramework::ViewportInteraction::MouseEvent;
using AzToolsFramework::ViewportInteraction::MouseInteraction;
using AzToolsFramework::ViewportInteraction::MouseInteractionEvent;
using AzToolsFramework::ViewportInteraction::ProjectedViewportRay;
using AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus;
bool interactionHandled = false;
float wheelDelta = 0.0f;
@@ -117,16 +125,13 @@ namespace SandboxEditor
aznumeric_cast<int>(position->m_normalizedPosition.GetX() * windowSize.m_width),
aznumeric_cast<int>(position->m_normalizedPosition.GetY() * windowSize.m_height));
m_mouseInteraction.m_mousePick.m_screenCoordinates = screenPoint;
AZStd::optional<ProjectedViewportRay> ray;
ProjectedViewportRay ray{};
ViewportInteractionRequestBus::EventResult(
ray, GetViewportId(), &ViewportInteractionRequestBus::Events::ViewportScreenToWorldRay, screenPoint);
if (ray.has_value())
{
m_mouseInteraction.m_mousePick.m_rayOrigin = ray.value().origin;
m_mouseInteraction.m_mousePick.m_rayDirection = ray.value().direction;
}
m_mouseInteraction.m_mousePick.m_rayOrigin = ray.origin;
m_mouseInteraction.m_mousePick.m_rayDirection = ray.direction;
m_mouseInteraction.m_mousePick.m_screenCoordinates = screenPoint;
}
eventType = MouseEvent::Move;
@@ -152,7 +157,7 @@ namespace SandboxEditor
// Only insert the double click timing once we're done processing events, to avoid a false IsDoubleClick positive
if (finishedProcessingEvents)
{
m_pendingDoubleClicks[mouseButton] = m_curTime;
m_pendingDoubleClicks[mouseButton] = { m_currentTime, m_mouseInteraction.m_mousePick.m_screenCoordinates };
}
eventType = MouseEvent::Down;
}
@@ -160,8 +165,8 @@ namespace SandboxEditor
else if (state == InputChannel::State::Ended)
{
// If we've actually logged a mouse down event, forward a mouse up event.
// This prevents corner cases like the context menu thinking it should be opened even though no one clicked in this viewport,
// due to RenderViewportWidget ensuring all controllers get InputChannel::State::Ended events.
// This prevents corner cases like the context menu thinking it should be opened even though no one clicked in this
// viewport, due to RenderViewportWidget ensuring all controllers get InputChannel::State::Ended events.
if (m_mouseInteraction.m_mouseButtons.m_mouseButtons & mouseButtonValue)
{
// Erase the button from our state if we're done processing events.
@@ -246,17 +251,22 @@ namespace SandboxEditor
void ViewportManipulatorControllerInstance::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event)
{
m_curTime = event.m_time;
m_currentTime = event.m_time;
}
bool ViewportManipulatorControllerInstance::IsDoubleClick(AzToolsFramework::ViewportInteraction::MouseButton button) const
{
auto clickIt = m_pendingDoubleClicks.find(button);
if (clickIt == m_pendingDoubleClicks.end())
if (auto clickIt = m_pendingDoubleClicks.find(button); clickIt != m_pendingDoubleClicks.end())
{
return false;
const double doubleClickThresholdMilliseconds = qApp->doubleClickInterval();
const bool insideTimeThreshold =
(m_currentTime.GetMilliseconds() - clickIt->second.m_time.GetMilliseconds()) < doubleClickThresholdMilliseconds;
const bool insideDistanceThreshold =
AzFramework::ScreenVectorLength(clickIt->second.m_position - m_mouseInteraction.m_mousePick.m_screenCoordinates) <
AzFramework::DefaultMouseMoveDeadZone;
return insideTimeThreshold && insideDistanceThreshold;
}
const double doubleClickThresholdMilliseconds = qApp->doubleClickInterval();
return (m_curTime.GetMilliseconds() - clickIt->second.GetMilliseconds()) < doubleClickThresholdMilliseconds;
return false;
}
} //namespace SandboxEditor
} // namespace SandboxEditor
+10 -2
View File
@@ -39,8 +39,16 @@ namespace SandboxEditor
static bool IsMouseMove(const AzFramework::InputChannel& inputChannel);
static AzToolsFramework::ViewportInteraction::KeyboardModifier GetKeyboardModifier(const AzFramework::InputChannel& inputChannel);
//! Represents the time and location of a click.
struct ClickEvent
{
AZ::ScriptTimePoint m_time;
AzFramework::ScreenPoint m_position;
};
AzToolsFramework::ViewportInteraction::MouseInteraction m_mouseInteraction;
AZStd::unordered_map<AzToolsFramework::ViewportInteraction::MouseButton, AZ::ScriptTimePoint> m_pendingDoubleClicks;
AZ::ScriptTimePoint m_curTime;
AZStd::unordered_map<AzToolsFramework::ViewportInteraction::MouseButton, ClickEvent> m_pendingDoubleClicks;
AZ::ScriptTimePoint m_currentTime;
};
} // namespace SandboxEditor
+414 -417
View File
@@ -14,453 +14,450 @@
#include <AzCore/std/parallel/lock.h>
#include <AzCore/std/string/conversions.h>
namespace AZ
namespace AZ::Data
{
namespace Data
AssetFilterInfo::AssetFilterInfo(const AssetId& id, const AssetType& assetType, AssetLoadBehavior loadBehavior)
: m_assetId(id)
, m_assetType(assetType)
, m_loadBehavior(loadBehavior)
{
AssetFilterInfo::AssetFilterInfo(const AssetId& id, const AssetType& assetType, AssetLoadBehavior loadBehavior)
: m_assetId(id)
, m_assetType(assetType)
, m_loadBehavior(loadBehavior)
}
AssetFilterInfo::AssetFilterInfo(const Asset<AssetData>& asset)
: m_assetId(asset.GetId())
, m_assetType(asset.GetType())
, m_loadBehavior(asset.GetAutoLoadBehavior())
{
}
AssetId AssetId::CreateString(AZStd::string_view input)
{
size_t separatorIdx = input.find(':');
if (separatorIdx == AZStd::string_view::npos)
{
return AssetId();
}
AssetFilterInfo::AssetFilterInfo(const Asset<AssetData>& asset)
: m_assetId(asset.GetId())
, m_assetType(asset.GetType())
, m_loadBehavior(asset.GetAutoLoadBehavior())
AssetId assetId;
assetId.m_guid = Uuid::CreateString(input.data(), separatorIdx);
if (assetId.m_guid.IsNull())
{
return AssetId();
}
assetId.m_subId = strtoul(&input[separatorIdx + 1], nullptr, 16);
AssetId AssetId::CreateString(AZStd::string_view input)
return assetId;
}
void AssetId::Reflect(AZ::ReflectContext* context)
{
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
size_t separatorIdx = input.find(':');
if (separatorIdx == AZStd::string_view::npos)
{
return AssetId();
}
AssetId assetId;
assetId.m_guid = Uuid::CreateString(input.data(), separatorIdx);
if (assetId.m_guid.IsNull())
{
return AssetId();
}
assetId.m_subId = strtoul(&input[separatorIdx + 1], nullptr, 16);
return assetId;
serializeContext->Class<Data::AssetId>()
->Version(1)
->Field("guid", &Data::AssetId::m_guid)
->Field("subId", &Data::AssetId::m_subId)
;
}
void AssetId::Reflect(AZ::ReflectContext* context)
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
{
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
serializeContext->Class<Data::AssetId>()
->Version(1)
->Field("guid", &Data::AssetId::m_guid)
->Field("subId", &Data::AssetId::m_subId)
;
}
behaviorContext->Class<Data::AssetId>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Asset")
->Attribute(AZ::Script::Attributes::Module, "asset")
->Constructor()
->Constructor<const Uuid&, u32>()
->Method("CreateString", &Data::AssetId::CreateString)
->Method("IsValid", &Data::AssetId::IsValid)
->Attribute(AZ::Script::Attributes::Alias, "is_valid")
->Method("ToString", [](const Data::AssetId* self) { return self->ToString<AZStd::string>(); })
->Attribute(AZ::Script::Attributes::Alias, "to_string")
->Method("IsEqual", [](const Data::AssetId& self, const Data::AssetId& other) { return self == other; })
->Attribute(AZ::Script::Attributes::Alias, "is_equal")
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)
;
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
{
behaviorContext->Class<Data::AssetId>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Asset")
->Attribute(AZ::Script::Attributes::Module, "asset")
->Constructor()
->Constructor<const Uuid&, u32>()
->Method("CreateString", &Data::AssetId::CreateString)
->Method("IsValid", &Data::AssetId::IsValid)
->Attribute(AZ::Script::Attributes::Alias, "is_valid")
->Method("ToString", [](const Data::AssetId* self) { return self->ToString<AZStd::string>(); })
->Attribute(AZ::Script::Attributes::Alias, "to_string")
->Method("IsEqual", [](const Data::AssetId& self, const Data::AssetId& other) { return self == other; })
->Attribute(AZ::Script::Attributes::Alias, "is_equal")
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)
;
behaviorContext->Class<Data::AssetInfo>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Asset")
->Attribute(AZ::Script::Attributes::Module, "asset")
->Property("assetId", BehaviorValueGetter(&Data::AssetInfo::m_assetId), nullptr)
->Property("assetType", BehaviorValueGetter(&Data::AssetInfo::m_assetType), nullptr)
->Property("sizeBytes", BehaviorValueGetter(&Data::AssetInfo::m_sizeBytes), nullptr)
->Property("relativePath", BehaviorValueGetter(&Data::AssetInfo::m_relativePath), nullptr)
;
}
}
behaviorContext->Class<Data::AssetInfo>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Asset")
->Attribute(AZ::Script::Attributes::Module, "asset")
->Property("assetId", BehaviorValueGetter(&Data::AssetInfo::m_assetId), nullptr)
->Property("assetType", BehaviorValueGetter(&Data::AssetInfo::m_assetType), nullptr)
->Property("sizeBytes", BehaviorValueGetter(&Data::AssetInfo::m_sizeBytes), nullptr)
->Property("relativePath", BehaviorValueGetter(&Data::AssetInfo::m_relativePath), nullptr)
;
}
namespace AssetInternal
{
Asset<AssetData> FindOrCreateAsset(const AssetId& id, const AssetType& type, AssetLoadBehavior assetReferenceLoadBehavior)
{
return AssetManager::Instance().FindOrCreateAsset(id, type, assetReferenceLoadBehavior);
}
namespace AssetInternal
Asset<AssetData> GetAsset(const AssetId& id, const AssetType& type, AssetLoadBehavior assetReferenceLoadBehavior,
const AssetLoadParameters& loadParams)
{
Asset<AssetData> FindOrCreateAsset(const AssetId& id, const AssetType& type, AssetLoadBehavior assetReferenceLoadBehavior)
{
return AssetManager::Instance().FindOrCreateAsset(id, type, assetReferenceLoadBehavior);
}
Asset<AssetData> GetAsset(const AssetId& id, const AssetType& type, AssetLoadBehavior assetReferenceLoadBehavior,
const AssetLoadParameters& loadParams)
{
return AssetManager::Instance().GetAsset(id, type, assetReferenceLoadBehavior, loadParams);
}
AssetData::AssetStatus BlockUntilLoadComplete(const Asset<AssetData>& asset)
{
return AssetManager::Instance().BlockUntilLoadComplete(asset);
}
void UpdateAssetInfo(AssetId& id, AZStd::string& assetHint)
{
// it is possible that the assetID given is legacy / old and we have a new assetId we can use instead for it.
// in that case, upgrade the AssetID to the new one, so that future saves are in the new format.
// this function should only be invoked if the feature is turned on in the asset manager as it can be (slightly) expensive
if ((!AssetManager::IsReady()) || (!AssetManager::Instance().GetAssetInfoUpgradingEnabled()))
{
return;
}
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, id);
if (assetInfo.m_assetId.IsValid())
{
id = assetInfo.m_assetId;
if (!assetInfo.m_relativePath.empty())
{
assetHint = assetInfo.m_relativePath;
}
}
}
bool ReloadAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior)
{
AssetManager::Instance().ReloadAsset(assetData->GetId(), assetReferenceLoadBehavior);
return true;
}
bool SaveAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior)
{
AssetManager::Instance().SaveAsset({ assetData, assetReferenceLoadBehavior });
return true;
}
Asset<AssetData> GetAssetData(const AssetId& id, AssetLoadBehavior assetReferenceLoadBehavior)
{
if (AssetManager::IsReady())
{
AZStd::lock_guard<AZStd::recursive_mutex> assetLock(AssetManager::Instance().m_assetMutex);
auto it = AssetManager::Instance().m_assets.find(id);
if (it != AssetManager::Instance().m_assets.end())
{
return { it->second, assetReferenceLoadBehavior };
}
}
return {};
}
AssetId ResolveAssetId(const AssetId& id)
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, id);
if (assetInfo.m_assetId.IsValid())
{
return assetInfo.m_assetId;
}
else
{
return id;
}
}
return AssetManager::Instance().GetAsset(id, type, assetReferenceLoadBehavior, loadParams);
}
AssetData::~AssetData()
AssetData::AssetStatus BlockUntilLoadComplete(const Asset<AssetData>& asset)
{
UnregisterWithHandler();
return AssetManager::Instance().BlockUntilLoadComplete(asset);
}
void AssetData::Reflect(AZ::ReflectContext* context)
void UpdateAssetInfo(AssetId& id, AZStd::string& assetHint)
{
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
// it is possible that the assetID given is legacy / old and we have a new assetId we can use instead for it.
// in that case, upgrade the AssetID to the new one, so that future saves are in the new format.
// this function should only be invoked if the feature is turned on in the asset manager as it can be (slightly) expensive
if ((!AssetManager::IsReady()) || (!AssetManager::Instance().GetAssetInfoUpgradingEnabled()))
{
serializeContext->Class<AZ::Data::AssetData>()
->Version(1)
;
}
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
{
behaviorContext->Class<AssetData>("AssetData")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Asset")
->Attribute(AZ::Script::Attributes::Module, "asset")
->Method("IsReady", &AssetData::IsReady)
->Attribute(AZ::Script::Attributes::Alias, "is_ready")
->Method("IsError", &AssetData::IsError)
->Attribute(AZ::Script::Attributes::Alias, "is_error")
->Method("IsLoading", &AssetData::IsLoading)
->Attribute(AZ::Script::Attributes::Alias, "is_loading")
->Method("GetId", &AssetData::GetId)
->Attribute(AZ::Script::Attributes::Alias, "get_id")
->Method("GetUseCount", &AssetData::GetUseCount)
->Attribute(AZ::Script::Attributes::Alias, "get_use_count")
;
}
}
void AssetData::Acquire()
{
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
AcquireWeak();
++m_useCount;
}
void AssetData::Release()
{
AZ_Assert(m_useCount > 0, "Usecount is already 0!");
if (m_useCount.fetch_sub(1) == 1)
{
if (AssetManager::IsReady())
{
AssetManager::Instance().OnAssetUnused(this);
}
else
{
AZ_Assert(false, "Attempting to release asset after AssetManager has been destroyed!");
}
}
ReleaseWeak();
}
void AssetData::AcquireWeak()
{
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
++m_weakUseCount;
}
void AssetData::ReleaseWeak()
{
AZ_Assert(m_weakUseCount > 0, "WeakUseCount is already 0");
AssetId assetId = m_assetId;
int creationToken = m_creationToken;
AssetType assetType = GetType();
bool removeFromHash = IsRegisterReadonlyAndShareable();
// default creation token implies that the asset was not created by the asset manager and therefore it cannot be in the asset map.
removeFromHash = creationToken == s_defaultCreationToken ? false : removeFromHash;
if (m_weakUseCount.fetch_sub(1) == 1)
{
if (AssetManager::IsReady())
{
AssetManager::Instance().ReleaseAsset(this, assetId, assetType, removeFromHash, creationToken);
}
else
{
AZ_Assert(false, "Attempting to release asset after AssetManager has been destroyed!");
}
}
}
bool AssetData::IsLoading(bool includeQueued) const
{
auto curStatus = GetStatus();
return(curStatus == AssetStatus::Loading || curStatus == AssetStatus::LoadedPreReady || curStatus==AssetStatus::StreamReady ||
(includeQueued && curStatus == AssetStatus::Queued));
}
void AssetData::RegisterWithHandler(AssetHandler* handler)
{
if (!handler)
{
AZ_Error("AssetData", false, "No handler to register with");
return;
}
m_registeredHandler = handler;
}
void AssetData::UnregisterWithHandler()
{
if (m_registeredHandler)
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, id);
if (assetInfo.m_assetId.IsValid())
{
m_registeredHandler = nullptr;
}
}
bool AssetData::GetFlag(const AssetDataFlags& checkFlag) const
{
return m_flags[aznumeric_cast<AZStd::size_t>(checkFlag)];
}
void AssetData::SetFlag(const AssetDataFlags& checkFlag, bool setValue)
{
m_flags.set(aznumeric_cast<AZStd::size_t>(checkFlag), setValue);
}
bool AssetData::GetRequeue() const
{
return GetFlag(AssetDataFlags::Requeue);
}
void AssetData::SetRequeue(bool requeue)
{
SetFlag(AssetDataFlags::Requeue, requeue);
}
void AssetBusCallbacks::SetCallbacks(const AssetReadyCB& readyCB, const AssetMovedCB& movedCB, const AssetReloadedCB& reloadedCB,
const AssetSavedCB& savedCB, const AssetUnloadedCB& unloadedCB, const AssetErrorCB& errorCB, const AssetCanceledCB& cancelCB)
{
m_onAssetReadyCB = readyCB;
m_onAssetMovedCB = movedCB;
m_onAssetReloadedCB = reloadedCB;
m_onAssetSavedCB = savedCB;
m_onAssetUnloadedCB = unloadedCB;
m_onAssetErrorCB = errorCB;
m_onAssetCanceledCB = cancelCB;
}
void AssetBusCallbacks::ClearCallbacks()
{
SetCallbacks(AssetBusCallbacks::AssetReadyCB(),
AssetBusCallbacks::AssetMovedCB(),
AssetBusCallbacks::AssetReloadedCB(),
AssetBusCallbacks::AssetSavedCB(),
AssetBusCallbacks::AssetUnloadedCB(),
AssetBusCallbacks::AssetErrorCB(),
AssetBusCallbacks::AssetCanceledCB());
}
void AssetBusCallbacks::SetOnAssetReadyCallback(const AssetReadyCB& readyCB)
{
m_onAssetReadyCB = readyCB;
}
void AssetBusCallbacks::SetOnAssetMovedCallback(const AssetMovedCB& movedCB)
{
m_onAssetMovedCB = movedCB;
}
void AssetBusCallbacks::SetOnAssetReloadedCallback(const AssetReloadedCB& reloadedCB)
{
m_onAssetReloadedCB = reloadedCB;
}
void AssetBusCallbacks::SetOnAssetSavedCallback(const AssetSavedCB& savedCB)
{
m_onAssetSavedCB = savedCB;
}
void AssetBusCallbacks::SetOnAssetUnloadedCallback(const AssetUnloadedCB& unloadedCB)
{
m_onAssetUnloadedCB = unloadedCB;
}
void AssetBusCallbacks::SetOnAssetErrorCallback(const AssetErrorCB& errorCB)
{
m_onAssetErrorCB = errorCB;
}
void AssetBusCallbacks::SetOnAssetCanceledCallback(const AssetCanceledCB& cancelCB)
{
m_onAssetCanceledCB = cancelCB;
}
void AssetBusCallbacks::OnAssetReady(Asset<AssetData> asset)
{
if (m_onAssetReadyCB)
{
m_onAssetReadyCB(asset, *this);
}
}
void AssetBusCallbacks::OnAssetMoved(Asset<AssetData> asset, void* oldDataPointer)
{
if (m_onAssetMovedCB)
{
m_onAssetMovedCB(asset, oldDataPointer, *this);
}
}
void AssetBusCallbacks::OnAssetReloaded(Asset<AssetData> asset)
{
if (m_onAssetReloadedCB)
{
m_onAssetReloadedCB(asset, *this);
}
}
void AssetBusCallbacks::OnAssetSaved(Asset<AssetData> asset, bool isSuccessful)
{
if (m_onAssetSavedCB)
{
m_onAssetSavedCB(asset, isSuccessful, *this);
}
}
void AssetBusCallbacks::OnAssetUnloaded(const AssetId assetId, const AssetType assetType)
{
if (m_onAssetUnloadedCB)
{
m_onAssetUnloadedCB(assetId, assetType, *this);
}
}
void AssetBusCallbacks::OnAssetError(Asset<AssetData> asset)
{
if (m_onAssetErrorCB)
{
m_onAssetErrorCB(asset, *this);
}
}
void AssetBusCallbacks::OnAssetCanceled(const AssetId assetId)
{
if (m_onAssetCanceledCB)
{
m_onAssetCanceledCB(assetId, *this);
}
}
/*static*/ bool AssetFilterNoAssetLoading([[maybe_unused]] const AssetFilterInfo& filterInfo)
{
return false;
}
namespace ProductDependencyInfo
{
AZ::Data::AssetLoadBehavior LoadBehaviorFromFlags(const ProductDependencyFlags& dependencyFlags)
{
AZ::u8 loadBehaviorValue = 0;
for (AZ::u8 thisFlag = aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorLow);
thisFlag <= aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorHigh); ++thisFlag)
id = assetInfo.m_assetId;
if (!assetInfo.m_relativePath.empty())
{
if (dependencyFlags[thisFlag])
{
loadBehaviorValue |= (1 << thisFlag);
}
assetHint = assetInfo.m_relativePath;
}
return static_cast<AZ::Data::AssetLoadBehavior>(loadBehaviorValue);
}
ProductDependencyFlags CreateFlags(AZ::Data::AssetLoadBehavior autoLoadBehavior)
{
AZ::Data::ProductDependencyInfo::ProductDependencyFlags returnFlags;
AZ::u8 loadBehavior = aznumeric_caster(autoLoadBehavior);
for (AZ::u8 thisFlag = aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorLow);
thisFlag <= aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorHigh); ++thisFlag)
{
if (loadBehavior & (1 << thisFlag))
{
returnFlags[thisFlag] = true;
}
}
return returnFlags;
}
}
} // namespace Data
} // namespace AZ
bool ReloadAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior)
{
AssetManager::Instance().ReloadAsset(assetData->GetId(), assetReferenceLoadBehavior);
return true;
}
bool SaveAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior)
{
AssetManager::Instance().SaveAsset({ assetData, assetReferenceLoadBehavior });
return true;
}
Asset<AssetData> GetAssetData(const AssetId& id, AssetLoadBehavior assetReferenceLoadBehavior)
{
if (AssetManager::IsReady())
{
AZStd::lock_guard<AZStd::recursive_mutex> assetLock(AssetManager::Instance().m_assetMutex);
auto it = AssetManager::Instance().m_assets.find(id);
if (it != AssetManager::Instance().m_assets.end())
{
return { it->second, assetReferenceLoadBehavior };
}
}
return {};
}
AssetId ResolveAssetId(const AssetId& id)
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, id);
if (assetInfo.m_assetId.IsValid())
{
return assetInfo.m_assetId;
}
else
{
return id;
}
}
}
AssetData::~AssetData()
{
UnregisterWithHandler();
}
void AssetData::Reflect(AZ::ReflectContext* context)
{
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
serializeContext->Class<AZ::Data::AssetData>()
->Version(1)
;
}
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
{
behaviorContext->Class<AssetData>("AssetData")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Asset")
->Attribute(AZ::Script::Attributes::Module, "asset")
->Method("IsReady", &AssetData::IsReady)
->Attribute(AZ::Script::Attributes::Alias, "is_ready")
->Method("IsError", &AssetData::IsError)
->Attribute(AZ::Script::Attributes::Alias, "is_error")
->Method("IsLoading", &AssetData::IsLoading)
->Attribute(AZ::Script::Attributes::Alias, "is_loading")
->Method("GetId", &AssetData::GetId)
->Attribute(AZ::Script::Attributes::Alias, "get_id")
->Method("GetUseCount", &AssetData::GetUseCount)
->Attribute(AZ::Script::Attributes::Alias, "get_use_count")
;
}
}
void AssetData::Acquire()
{
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
AcquireWeak();
++m_useCount;
}
void AssetData::Release()
{
AZ_Assert(m_useCount > 0, "Usecount is already 0!");
if (m_useCount.fetch_sub(1) == 1)
{
if (AssetManager::IsReady())
{
AssetManager::Instance().OnAssetUnused(this);
}
else
{
AZ_Assert(false, "Attempting to release asset after AssetManager has been destroyed!");
}
}
ReleaseWeak();
}
void AssetData::AcquireWeak()
{
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
++m_weakUseCount;
}
void AssetData::ReleaseWeak()
{
AZ_Assert(m_weakUseCount > 0, "WeakUseCount is already 0");
AssetId assetId = m_assetId;
int creationToken = m_creationToken;
AssetType assetType = GetType();
bool removeFromHash = IsRegisterReadonlyAndShareable();
// default creation token implies that the asset was not created by the asset manager and therefore it cannot be in the asset map.
removeFromHash = creationToken == s_defaultCreationToken ? false : removeFromHash;
if (m_weakUseCount.fetch_sub(1) == 1)
{
if (AssetManager::IsReady())
{
AssetManager::Instance().ReleaseAsset(this, assetId, assetType, removeFromHash, creationToken);
}
else
{
AZ_Assert(false, "Attempting to release asset after AssetManager has been destroyed!");
}
}
}
bool AssetData::IsLoading(bool includeQueued) const
{
auto curStatus = GetStatus();
return(curStatus == AssetStatus::Loading || curStatus == AssetStatus::LoadedPreReady || curStatus==AssetStatus::StreamReady ||
(includeQueued && curStatus == AssetStatus::Queued));
}
void AssetData::RegisterWithHandler(AssetHandler* handler)
{
if (!handler)
{
AZ_Error("AssetData", false, "No handler to register with");
return;
}
m_registeredHandler = handler;
}
void AssetData::UnregisterWithHandler()
{
if (m_registeredHandler)
{
m_registeredHandler = nullptr;
}
}
bool AssetData::GetFlag(const AssetDataFlags& checkFlag) const
{
return m_flags[aznumeric_cast<AZStd::size_t>(checkFlag)];
}
void AssetData::SetFlag(const AssetDataFlags& checkFlag, bool setValue)
{
m_flags.set(aznumeric_cast<AZStd::size_t>(checkFlag), setValue);
}
bool AssetData::GetRequeue() const
{
return GetFlag(AssetDataFlags::Requeue);
}
void AssetData::SetRequeue(bool requeue)
{
SetFlag(AssetDataFlags::Requeue, requeue);
}
void AssetBusCallbacks::SetCallbacks(const AssetReadyCB& readyCB, const AssetMovedCB& movedCB, const AssetReloadedCB& reloadedCB,
const AssetSavedCB& savedCB, const AssetUnloadedCB& unloadedCB, const AssetErrorCB& errorCB, const AssetCanceledCB& cancelCB)
{
m_onAssetReadyCB = readyCB;
m_onAssetMovedCB = movedCB;
m_onAssetReloadedCB = reloadedCB;
m_onAssetSavedCB = savedCB;
m_onAssetUnloadedCB = unloadedCB;
m_onAssetErrorCB = errorCB;
m_onAssetCanceledCB = cancelCB;
}
void AssetBusCallbacks::ClearCallbacks()
{
SetCallbacks(AssetBusCallbacks::AssetReadyCB(),
AssetBusCallbacks::AssetMovedCB(),
AssetBusCallbacks::AssetReloadedCB(),
AssetBusCallbacks::AssetSavedCB(),
AssetBusCallbacks::AssetUnloadedCB(),
AssetBusCallbacks::AssetErrorCB(),
AssetBusCallbacks::AssetCanceledCB());
}
void AssetBusCallbacks::SetOnAssetReadyCallback(const AssetReadyCB& readyCB)
{
m_onAssetReadyCB = readyCB;
}
void AssetBusCallbacks::SetOnAssetMovedCallback(const AssetMovedCB& movedCB)
{
m_onAssetMovedCB = movedCB;
}
void AssetBusCallbacks::SetOnAssetReloadedCallback(const AssetReloadedCB& reloadedCB)
{
m_onAssetReloadedCB = reloadedCB;
}
void AssetBusCallbacks::SetOnAssetSavedCallback(const AssetSavedCB& savedCB)
{
m_onAssetSavedCB = savedCB;
}
void AssetBusCallbacks::SetOnAssetUnloadedCallback(const AssetUnloadedCB& unloadedCB)
{
m_onAssetUnloadedCB = unloadedCB;
}
void AssetBusCallbacks::SetOnAssetErrorCallback(const AssetErrorCB& errorCB)
{
m_onAssetErrorCB = errorCB;
}
void AssetBusCallbacks::SetOnAssetCanceledCallback(const AssetCanceledCB& cancelCB)
{
m_onAssetCanceledCB = cancelCB;
}
void AssetBusCallbacks::OnAssetReady(Asset<AssetData> asset)
{
if (m_onAssetReadyCB)
{
m_onAssetReadyCB(asset, *this);
}
}
void AssetBusCallbacks::OnAssetMoved(Asset<AssetData> asset, void* oldDataPointer)
{
if (m_onAssetMovedCB)
{
m_onAssetMovedCB(asset, oldDataPointer, *this);
}
}
void AssetBusCallbacks::OnAssetReloaded(Asset<AssetData> asset)
{
if (m_onAssetReloadedCB)
{
m_onAssetReloadedCB(asset, *this);
}
}
void AssetBusCallbacks::OnAssetSaved(Asset<AssetData> asset, bool isSuccessful)
{
if (m_onAssetSavedCB)
{
m_onAssetSavedCB(asset, isSuccessful, *this);
}
}
void AssetBusCallbacks::OnAssetUnloaded(const AssetId assetId, const AssetType assetType)
{
if (m_onAssetUnloadedCB)
{
m_onAssetUnloadedCB(assetId, assetType, *this);
}
}
void AssetBusCallbacks::OnAssetError(Asset<AssetData> asset)
{
if (m_onAssetErrorCB)
{
m_onAssetErrorCB(asset, *this);
}
}
void AssetBusCallbacks::OnAssetCanceled(const AssetId assetId)
{
if (m_onAssetCanceledCB)
{
m_onAssetCanceledCB(assetId, *this);
}
}
/*static*/ bool AssetFilterNoAssetLoading([[maybe_unused]] const AssetFilterInfo& filterInfo)
{
return false;
}
namespace ProductDependencyInfo
{
AZ::Data::AssetLoadBehavior LoadBehaviorFromFlags(const ProductDependencyFlags& dependencyFlags)
{
AZ::u8 loadBehaviorValue = 0;
for (AZ::u8 thisFlag = aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorLow);
thisFlag <= aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorHigh); ++thisFlag)
{
if (dependencyFlags[thisFlag])
{
loadBehaviorValue |= (1 << thisFlag);
}
}
return static_cast<AZ::Data::AssetLoadBehavior>(loadBehaviorValue);
}
ProductDependencyFlags CreateFlags(AZ::Data::AssetLoadBehavior autoLoadBehavior)
{
AZ::Data::ProductDependencyInfo::ProductDependencyFlags returnFlags;
AZ::u8 loadBehavior = aznumeric_caster(autoLoadBehavior);
for (AZ::u8 thisFlag = aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorLow);
thisFlag <= aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorHigh); ++thisFlag)
{
if (loadBehavior & (1 << thisFlag))
{
returnFlags[thisFlag] = true;
}
}
return returnFlags;
}
}
} // namespace AZ::Data
File diff suppressed because it is too large Load Diff
@@ -24,8 +24,8 @@ namespace AZ
// AssetContainer loads an asset and all of its dependencies as a collection which is parallellized as much as possible.
// With the container, the data will all load in parallel. Dependent asset loads will still obey the expected rules
// where PreLoad assets will emit OnAssetReady before the parent does, and QueueLoad assets will emit OnAssetReady in
// no guaranteed order. However, the OnAssetContainerReady signals will not emit until all PreLoad and QueueLoad assets
// where PreLoad assets will emit OnAssetReady before the parent does, and QueueLoad assets will emit OnAssetReady in
// no guaranteed order. However, the OnAssetContainerReady signals will not emit until all PreLoad and QueueLoad assets
// are ready. NoLoad dependencies are not loaded by default but can be loaded along with their dependencies using the
// same rules as above by using the LoadAll dependency rule.
class AssetContainer :
@@ -36,7 +36,7 @@ namespace AZ
AZ_CLASS_ALLOCATOR(AssetContainer, SystemAllocator, 0);
AssetContainer() = default;
AssetContainer(Asset<AssetData> asset, const AssetLoadParameters& loadParams);
~AssetContainer();
@@ -81,6 +81,10 @@ namespace AZ
// AssetLoadBus
void OnAssetDataLoaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
protected:
virtual AZStd::vector<AZStd::pair<AssetInfo, Asset<AssetData>>> CreateAndQueueDependentAssets(
const AZStd::vector<AssetInfo>& dependencyInfoList, const AssetLoadParameters& loadParamsCopyWithNoLoadingFilter);
// Waiting assets are those which have not yet signalled ready. In the case of PreLoad dependencies the data may have completed the load cycle but
// the Assets aren't considered "Ready" yet if there are PreLoad dependencies still loading and will still be in the list until the point that asset and
// All of its preload dependencies have been loaded, when it signals OnAssetReady
@@ -97,7 +101,7 @@ namespace AZ
void AddDependency(Asset<AssetData>&& addDependency);
// Add a "graph section" to our list of dependencies. This checks the catalog for all Pre and Queue load assets which are dependents of the requested asset and kicks off loads
// NoLoads which are encounted are placed in another list and can be loaded on demand with the LoadDependency call.
// NoLoads which are encounted are placed in another list and can be loaded on demand with the LoadDependency call.
void AddDependentAssets(Asset<AssetData> rootAsset, const AssetLoadParameters& loadParams);
// If "PreLoad" assets are found in the graph these are cached and tracked with both OnAssetReady and OnAssetDataLoaded messages.
@@ -117,7 +121,7 @@ namespace AZ
// duringInit if we're coming from the checkReady method - containers that start ready don't need to signal
void HandleReadyAsset(AZ::Data::Asset<AZ::Data::AssetData> asset);
// Optimization to save the lookup in the dependencies map
// Optimization to save the lookup in the dependencies map
AssetInternal::WeakAsset<AssetData> m_rootAsset;
// The root asset id is stored here semi-redundantly on initialization so that we can still refer to it even if the
@@ -136,7 +140,7 @@ namespace AZ
AZStd::atomic_bool m_finalNotificationSent{false};
mutable AZStd::recursive_mutex m_preloadMutex;
// AssetId -> List of assets it is still waiting on
// AssetId -> List of assets it is still waiting on
PreloadAssetListType m_preloadList;
// AssetId -> List of assets waiting on it
@@ -12,207 +12,204 @@
#include <AzCore/Serialization/Json/StackedString.h>
#include <AzCore/Memory/SystemAllocator.h>
namespace AZ
namespace AZ::Data
{
namespace Data
AZ_CLASS_ALLOCATOR_IMPL(AssetJsonSerializer, SystemAllocator, 0);
JsonSerializationResult::Result AssetJsonSerializer::Load(void* outputValue, const Uuid& /*outputValueTypeId*/,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
AZ_CLASS_ALLOCATOR_IMPL(AssetJsonSerializer, SystemAllocator, 0);
namespace JSR = JsonSerializationResult;
JsonSerializationResult::Result AssetJsonSerializer::Load(void* outputValue, const Uuid& /*outputValueTypeId*/,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
switch (inputValue.GetType())
{
namespace JSR = JsonSerializationResult;
case rapidjson::kObjectType:
return LoadAsset(outputValue, inputValue, context);
case rapidjson::kArrayType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kStringType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kTrueType: // fall through
case rapidjson::kNumberType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Asset<T> can only be read from an object.");
switch (inputValue.GetType())
default:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for Asset<T>.");
}
}
JsonSerializationResult::Result AssetJsonSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& /*valueTypeId*/, JsonSerializerContext& context)
{
namespace JSR = JsonSerializationResult;
const Asset<AssetData>* instance = reinterpret_cast<const Asset<AssetData>*>(inputValue);
const Asset<AssetData>* defaultInstance = reinterpret_cast<const Asset<AssetData>*>(defaultValue);
JSR::ResultCode result(JSR::Tasks::WriteValue);
{
ScopedContextPath subPathId(context, "m_assetId");
const auto* id = &instance->GetId();
const auto* defaultId = defaultInstance ? &defaultInstance->GetId() : nullptr;
rapidjson::Value assetIdValue;
result = ContinueStoring(assetIdValue, id, defaultId, azrtti_typeid<AssetId>(), context);
if (result.GetOutcome() == JSR::Outcomes::Success || result.GetOutcome() == JSR::Outcomes::PartialDefaults)
{
case rapidjson::kObjectType:
return LoadAsset(outputValue, inputValue, context);
case rapidjson::kArrayType: // fall through
case rapidjson::kNullType: // fall through
case rapidjson::kStringType: // fall through
case rapidjson::kFalseType: // fall through
case rapidjson::kTrueType: // fall through
case rapidjson::kNumberType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Asset<T> can only be read from an object.");
default:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for Asset<T>.");
if (!outputValue.IsObject())
{
outputValue.SetObject();
}
outputValue.AddMember(rapidjson::StringRef("assetId"), AZStd::move(assetIdValue), context.GetJsonAllocator());
}
}
JsonSerializationResult::Result AssetJsonSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& /*valueTypeId*/, JsonSerializerContext& context)
{
namespace JSR = JsonSerializationResult;
const AZ::Data::AssetLoadBehavior autoLoadBehavior = instance->GetAutoLoadBehavior();
const AZ::Data::AssetLoadBehavior defaultAutoLoadBehavior = defaultInstance ?
defaultInstance->GetAutoLoadBehavior() : AZ::Data::AssetLoadBehavior::Default;
const Asset<AssetData>* instance = reinterpret_cast<const Asset<AssetData>*>(inputValue);
const Asset<AssetData>* defaultInstance = reinterpret_cast<const Asset<AssetData>*>(defaultValue);
JSR::ResultCode result(JSR::Tasks::WriteValue);
{
ScopedContextPath subPathId(context, "m_assetId");
const auto* id = &instance->GetId();
const auto* defaultId = defaultInstance ? &defaultInstance->GetId() : nullptr;
rapidjson::Value assetIdValue;
result = ContinueStoring(assetIdValue, id, defaultId, azrtti_typeid<AssetId>(), context);
if (result.GetOutcome() == JSR::Outcomes::Success || result.GetOutcome() == JSR::Outcomes::PartialDefaults)
{
if (!outputValue.IsObject())
{
outputValue.SetObject();
}
outputValue.AddMember(rapidjson::StringRef("assetId"), AZStd::move(assetIdValue), context.GetJsonAllocator());
}
}
{
const AZ::Data::AssetLoadBehavior autoLoadBehavior = instance->GetAutoLoadBehavior();
const AZ::Data::AssetLoadBehavior defaultAutoLoadBehavior = defaultInstance ?
defaultInstance->GetAutoLoadBehavior() : AZ::Data::AssetLoadBehavior::Default;
result.Combine(
ContinueStoringToJsonObjectField(outputValue, "loadBehavior",
&autoLoadBehavior, &defaultAutoLoadBehavior,
azrtti_typeid<Data::AssetLoadBehavior>(), context));
}
{
ScopedContextPath subPathHint(context, "m_assetHint");
const AZStd::string* hint = &instance->GetHint();
const AZStd::string defaultHint;
rapidjson::Value assetHintValue;
JSR::ResultCode resultHint = ContinueStoring(assetHintValue, hint, &defaultHint, azrtti_typeid<AZStd::string>(), context);
if (resultHint.GetOutcome() == JSR::Outcomes::Success || resultHint.GetOutcome() == JSR::Outcomes::PartialDefaults)
{
if (!outputValue.IsObject())
{
outputValue.SetObject();
}
outputValue.AddMember(rapidjson::StringRef("assetHint"), AZStd::move(assetHintValue), context.GetJsonAllocator());
}
result.Combine(resultHint);
}
return context.Report(result,
result.GetProcessing() == JSR::Processing::Completed ? "Successfully stored Asset<T>." : "Failed to store Asset<T>.");
result.Combine(
ContinueStoringToJsonObjectField(outputValue, "loadBehavior",
&autoLoadBehavior, &defaultAutoLoadBehavior,
azrtti_typeid<Data::AssetLoadBehavior>(), context));
}
JsonSerializationResult::Result AssetJsonSerializer::LoadAsset(void* outputValue, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult;
Asset<AssetData>* instance = reinterpret_cast<Asset<AssetData>*>(outputValue);
AssetId id;
JSR::ResultCode result(JSR::Tasks::ReadField);
SerializedAssetTracker* assetTracker =
context.GetMetadata().Find<SerializedAssetTracker>();
ScopedContextPath subPathHint(context, "m_assetHint");
const AZStd::string* hint = &instance->GetHint();
const AZStd::string defaultHint;
rapidjson::Value assetHintValue;
JSR::ResultCode resultHint = ContinueStoring(assetHintValue, hint, &defaultHint, azrtti_typeid<AZStd::string>(), context);
if (resultHint.GetOutcome() == JSR::Outcomes::Success || resultHint.GetOutcome() == JSR::Outcomes::PartialDefaults)
{
Data::AssetLoadBehavior loadBehavior = instance->GetAutoLoadBehavior();
result =
ContinueLoadingFromJsonObjectField(&loadBehavior,
azrtti_typeid<Data::AssetLoadBehavior>(),
inputValue, "loadBehavior", context);
instance->SetAutoLoadBehavior(loadBehavior);
if (!outputValue.IsObject())
{
outputValue.SetObject();
}
outputValue.AddMember(rapidjson::StringRef("assetHint"), AZStd::move(assetHintValue), context.GetJsonAllocator());
}
result.Combine(resultHint);
}
auto it = inputValue.FindMember("assetId");
if (it != inputValue.MemberEnd())
return context.Report(result,
result.GetProcessing() == JSR::Processing::Completed ? "Successfully stored Asset<T>." : "Failed to store Asset<T>.");
}
JsonSerializationResult::Result AssetJsonSerializer::LoadAsset(void* outputValue, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult;
Asset<AssetData>* instance = reinterpret_cast<Asset<AssetData>*>(outputValue);
AssetId id;
JSR::ResultCode result(JSR::Tasks::ReadField);
SerializedAssetTracker* assetTracker =
context.GetMetadata().Find<SerializedAssetTracker>();
{
Data::AssetLoadBehavior loadBehavior = instance->GetAutoLoadBehavior();
result =
ContinueLoadingFromJsonObjectField(&loadBehavior,
azrtti_typeid<Data::AssetLoadBehavior>(),
inputValue, "loadBehavior", context);
instance->SetAutoLoadBehavior(loadBehavior);
}
auto it = inputValue.FindMember("assetId");
if (it != inputValue.MemberEnd())
{
ScopedContextPath subPath(context, "assetId");
result.Combine(ContinueLoading(&id, azrtti_typeid<AssetId>(), it->value, context));
if (!id.m_guid.IsNull())
{
ScopedContextPath subPath(context, "assetId");
result.Combine(ContinueLoading(&id, azrtti_typeid<AssetId>(), it->value, context));
if (!id.m_guid.IsNull())
*instance = AssetManager::Instance().FindOrCreateAsset(id, instance->GetType(), instance->GetAutoLoadBehavior());
if (!instance->GetId().IsValid())
{
*instance = AssetManager::Instance().FindOrCreateAsset(id, instance->GetType(), instance->GetAutoLoadBehavior());
if (!instance->GetId().IsValid())
{
// If the asset failed to be created, FindOrCreateAsset returns an asset instance with a null
// id. To preserve the asset id in the source json, reset the asset to an empty one, but with
// the right id.
const auto loadBehavior = instance->GetAutoLoadBehavior();
*instance = Asset<AssetData>(id, instance->GetType());
instance->SetAutoLoadBehavior(loadBehavior);
}
// If the asset failed to be created, FindOrCreateAsset returns an asset instance with a null
// id. To preserve the asset id in the source json, reset the asset to an empty one, but with
// the right id.
const auto loadBehavior = instance->GetAutoLoadBehavior();
*instance = Asset<AssetData>(id, instance->GetType());
instance->SetAutoLoadBehavior(loadBehavior);
}
result.Combine(context.Report(result, "Successfully created Asset<T> with id."));
}
else if (result.GetProcessing() == JSR::Processing::Completed)
{
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
"Null Asset<T> created."));
}
else
{
result.Combine(context.Report(result, "Failed to retrieve asset id for Asset<T>."));
}
result.Combine(context.Report(result, "Successfully created Asset<T> with id."));
}
else if (result.GetProcessing() == JSR::Processing::Completed)
{
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
"Null Asset<T> created."));
}
else
{
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
"The asset id is missing, so there's not enough information to create an Asset<T>."));
}
it = inputValue.FindMember("assetHint");
if (it != inputValue.MemberEnd())
{
ScopedContextPath subPath(context, "assetHint");
AZStd::string hint;
result.Combine(ContinueLoading(&hint, azrtti_typeid<AZStd::string>(), it->value, context));
instance->SetHint(AZStd::move(hint));
}
else
{
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
"The asset hint is missing for Asset<T>, so it will be left empty."));
}
if (assetTracker)
{
assetTracker->FixUpAsset(*instance);
assetTracker->AddAsset(*instance);
}
bool success = result.GetOutcome() <= JSR::Outcomes::PartialSkip;
bool defaulted = result.GetOutcome() == JSR::Outcomes::DefaultsUsed || result.GetOutcome() == JSR::Outcomes::PartialDefaults;
AZStd::string_view message =
success ? "Successfully loaded information and created instance of Asset<T>." :
defaulted ? "A default id was provided for Asset<T>, so no instance could be created." :
"Not enough information was available to create an instance of Asset<T> or data was corrupted.";
return context.Report(result, message);
}
void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback)
{
m_assetFixUpCallback = AZStd::move(assetFixUpCallback);
}
void SerializedAssetTracker::FixUpAsset(Asset<AssetData>& asset)
{
if (m_assetFixUpCallback)
{
m_assetFixUpCallback(asset);
result.Combine(context.Report(result, "Failed to retrieve asset id for Asset<T>."));
}
}
void SerializedAssetTracker::AddAsset(Asset<AssetData> asset)
else
{
m_serializedAssets.emplace_back(asset);
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
"The asset id is missing, so there's not enough information to create an Asset<T>."));
}
const AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets() const
it = inputValue.FindMember("assetHint");
if (it != inputValue.MemberEnd())
{
return m_serializedAssets;
ScopedContextPath subPath(context, "assetHint");
AZStd::string hint;
result.Combine(ContinueLoading(&hint, azrtti_typeid<AZStd::string>(), it->value, context));
instance->SetHint(AZStd::move(hint));
}
else
{
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
"The asset hint is missing for Asset<T>, so it will be left empty."));
}
AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets()
if (assetTracker)
{
return m_serializedAssets;
assetTracker->FixUpAsset(*instance);
assetTracker->AddAsset(*instance);
}
} // namespace Data
} // namespace AZ
bool success = result.GetOutcome() <= JSR::Outcomes::PartialSkip;
bool defaulted = result.GetOutcome() == JSR::Outcomes::DefaultsUsed || result.GetOutcome() == JSR::Outcomes::PartialDefaults;
AZStd::string_view message =
success ? "Successfully loaded information and created instance of Asset<T>." :
defaulted ? "A default id was provided for Asset<T>, so no instance could be created." :
"Not enough information was available to create an instance of Asset<T> or data was corrupted.";
return context.Report(result, message);
}
void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback)
{
m_assetFixUpCallback = AZStd::move(assetFixUpCallback);
}
void SerializedAssetTracker::FixUpAsset(Asset<AssetData>& asset)
{
if (m_assetFixUpCallback)
{
m_assetFixUpCallback(asset);
}
}
void SerializedAssetTracker::AddAsset(Asset<AssetData> asset)
{
m_serializedAssets.emplace_back(asset);
}
const AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets() const
{
return m_serializedAssets;
}
AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets()
{
return m_serializedAssets;
}
} // namespace AZ::Data
File diff suppressed because it is too large Load Diff
@@ -169,14 +169,14 @@ namespace AZ
/// Register handler with the system for a particular asset type.
/// A handler should be registered for each asset type it handles.
/// Please note that all the handlers are registered just once during app startup from the main thread
/// and therefore this is not a thread safe method and should not be invoked from different threads.
/// and therefore this is not a thread safe method and should not be invoked from different threads.
void RegisterHandler(AssetHandler* handler, const AssetType& assetType);
/// Unregister handler from the asset system.
/// Please note that all the handlers are unregistered just once during app shutdown from the main thread
/// and therefore this is not a thread safe method and should not be invoked from different threads.
void UnregisterHandler(AssetHandler* handler);
// @}
// @{ Asset catalog management
/// Register a catalog with the system for a particular asset type.
/// A catalog should be registered for each asset type it is responsible for.
@@ -295,7 +295,7 @@ namespace AZ
/**
* Old 'legacy' assetIds and asset hints can be automatically replaced with new ones during deserialize / assignment.
* This operation can be somewhat costly, and its only useful if the program subsequently re-saves the files its loading so that
* the asset hints and assetIds actually persist. Thus, it can be disabled in situations where you know you are not going to be
* the asset hints and assetIds actually persist. Thus, it can be disabled in situations where you know you are not going to be
* saving over or creating new source files (for example builders/background apps)
* By default, it is enabled.
*/
@@ -316,7 +316,7 @@ namespace AZ
* This method must be invoked before you start unregistering handlers manually and shutting down the asset manager.
* This method ensures that all jobs in flight are either canceled or completed.
* This method is automatically called in the destructor but if you are unregistering handlers manually,
* you must invoke it yourself.
* you must invoke it yourself.
*/
void PrepareShutDown();
@@ -366,7 +366,7 @@ namespace AZ
/**
* Creates a new shared AssetContainer with an optional loadFilter
* **/
AZStd::shared_ptr<AssetContainer> CreateAssetContainer(Asset<AssetData> asset, const AssetLoadParameters& loadParams = AssetLoadParameters{}) const;
virtual AZStd::shared_ptr<AssetContainer> CreateAssetContainer(Asset<AssetData> asset, const AssetLoadParameters& loadParams = AssetLoadParameters{}) const;
/**
@@ -452,7 +452,7 @@ namespace AZ
// Variant of RegisterAssetLoading used for jobs which have been queued and need to verify the status of the asset
// before loading in order to prevent cases where a load is queued, then a blocking load goes through, then the queued
// before loading in order to prevent cases where a load is queued, then a blocking load goes through, then the queued
// load is processed. This validation step leaves the loaded (And potentially modified) data as is in that case.
bool ValidateAndRegisterAssetLoading(const Asset<AssetData>& asset);
@@ -482,7 +482,7 @@ namespace AZ
* the blocking. That will result in a single thread deadlock.
*
* If you need to queue work, the logic needs to be similar to this:
*
*
AssetHandler::LoadResult MyAssetHandler::LoadAssetData(const Asset<AssetData>& asset, AZStd::shared_ptr<AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB)
{
@@ -496,13 +496,13 @@ namespace AZ
}
else
{
// queue job to load asset in thread identified by m_loadingThreadId
// queue job to load asset in thread identified by m_loadingThreadId
auto* queuedJob = QueueLoadingOnOtherThread(...);
// block waiting for queued job to complete
queuedJob->BlockUntilComplete();
}
.
.
.
@@ -525,7 +525,7 @@ namespace AZ
//! Result from LoadAssetData - it either finished loading, didn't finish and is waiting for more data, or had an error.
enum class LoadResult : u8
{
Error, // The provided data failed to load correctly
MoreDataRequired, // The provided data loaded correctly, but more data is required to finish the asset load
LoadComplete // The provided data loaded correctly, and the asset has been created
@@ -10,6 +10,7 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/bitset.h>
#include <AzCore/Outcome/Outcome.h>
@@ -129,7 +130,8 @@ namespace AZ
/// Remove a catalog from our delta list and rebuild the catalog from remaining items
virtual bool RemoveDeltaCatalog(AZStd::shared_ptr<AzFramework::AssetRegistry> /*deltaCatalog*/) { return true; }
/// Creates a manifest with the given DeltaCatalog name
virtual bool CreateBundleManifest(const AZStd::string& /*deltaCatalogPath*/, const AZStd::vector<AZStd::string>& /*dependentBundleNames*/, const AZStd::string& /*fileDirectory*/, int /*bundleVersion*/, const AZStd::vector<AZStd::string>& /*levelDirs*/) { return false; }
virtual bool CreateBundleManifest(const AZStd::string& /*deltaCatalogPath*/, const AZStd::vector<AZStd::string>& /*dependentBundleNames*/,
const AZStd::string& /*fileDirectory*/, int /*bundleVersion*/, const AZStd::vector<AZ::IO::Path>& /*levelDirs*/) { return false; }
/// Creates an instance of a registry containing info for just the specified files, and writes it out to a file at the specified path
virtual bool CreateDeltaCatalog(const AZStd::vector<AZStd::string>& /*files*/, const AZStd::string& /*filePath*/) { return false; }
@@ -10,292 +10,289 @@
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/std/containers/fixed_vector.h>
namespace AZ
namespace AZ::EntityUtils
{
namespace EntityUtils
//=========================================================================
// Reflect
//=========================================================================
void Reflect(ReflectContext* context)
{
//=========================================================================
// Reflect
//=========================================================================
void Reflect(ReflectContext* context)
if (auto serializeContext = azrtti_cast<SerializeContext*>(context))
{
if (auto serializeContext = azrtti_cast<SerializeContext*>(context))
serializeContext->Class<SerializableEntityContainer>()->
Version(1)->
Field("Entities", &SerializableEntityContainer::m_entities);
}
}
struct StackDataType
{
const SerializeContext::ClassData* m_classData;
const SerializeContext::ClassElement* m_elementData;
void* m_dataPtr;
bool m_isModifiedContainer;
};
//=========================================================================
// EnumerateEntityIds
//=========================================================================
void EnumerateEntityIds(const void* classPtr, const Uuid& classUuid, const EntityIdVisitor& visitor, SerializeContext* context)
{
AZ_PROFILE_FUNCTION(AzCore);
if (!context)
{
context = GetApplicationSerializeContext();
if (!context)
{
serializeContext->Class<SerializableEntityContainer>()->
Version(1)->
Field("Entities", &SerializableEntityContainer::m_entities);
AZ_Error("Serialization", false, "No serialize context provided! Failed to get component application default serialize context! ComponentApp is not started or input serialize context should not be null!");
return;
}
}
AZStd::vector<const SerializeContext::ClassData*> parentStack;
parentStack.reserve(30);
auto beginCB = [ &](void* ptr, const SerializeContext::ClassData* classData, const SerializeContext::ClassElement* elementData) -> bool
{
(void)elementData;
struct StackDataType
if (classData->m_typeId == SerializeTypeInfo<EntityId>::GetUuid())
{
// determine if this is entity ref or just entityId (please refer to the function documentation for more info)
bool isEntityId = false;
if (!parentStack.empty() && parentStack.back()->m_typeId == SerializeTypeInfo<Entity>::GetUuid())
{
// our parent in the entity (currently entity has only one EntityId member, but we can check the offset for future proof
AZ_Assert(elementData && strcmp(elementData->m_name, "Id") == 0, "class Entity, should have only ONE EntityId member, the actual entity id!");
isEntityId = true;
}
EntityId* entityIdPtr = (elementData->m_flags & SerializeContext::ClassElement::FLG_POINTER) ?
*reinterpret_cast<EntityId**>(ptr) : reinterpret_cast<EntityId*>(ptr);
visitor(*entityIdPtr, isEntityId, elementData);
}
parentStack.push_back(classData);
return true;
};
auto endCB = [ &]() -> bool
{
parentStack.pop_back();
return true;
};
SerializeContext::EnumerateInstanceCallContext callContext(
beginCB,
endCB,
context,
SerializeContext::ENUM_ACCESS_FOR_READ,
nullptr
);
context->EnumerateInstanceConst(
&callContext,
classPtr,
classUuid,
nullptr,
nullptr
);
}
//=========================================================================
// GetApplicationSerializeContext
//=========================================================================
SerializeContext* GetApplicationSerializeContext()
{
SerializeContext* context = nullptr;
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
return context;
}
//=========================================================================
// FindFirstDerivedComponent
//=========================================================================
Component* FindFirstDerivedComponent(const Entity* entity, const Uuid& typeId)
{
for (AZ::Component* component : entity->GetComponents())
{
const SerializeContext::ClassData* m_classData;
const SerializeContext::ClassElement* m_elementData;
void* m_dataPtr;
bool m_isModifiedContainer;
if (azrtti_istypeof(typeId, component))
{
return component;
}
}
return nullptr;
}
Component* FindFirstDerivedComponent(EntityId entityId, const Uuid& typeId)
{
Entity* entity{};
ComponentApplicationBus::BroadcastResult(entity, &ComponentApplicationRequests::FindEntity, entityId);
return entity ? FindFirstDerivedComponent(entity, typeId) : nullptr;
}
//=========================================================================
// FindDerivedComponents
//=========================================================================
Entity::ComponentArrayType FindDerivedComponents(const Entity* entity, const Uuid& typeId)
{
Entity::ComponentArrayType result;
for (AZ::Component* component : entity->GetComponents())
{
if (azrtti_istypeof(typeId, component))
{
result.push_back(component);
}
}
return result;
}
Entity::ComponentArrayType FindDerivedComponents(EntityId entityId, const Uuid& typeId)
{
Entity* entity{};
ComponentApplicationBus::BroadcastResult(entity, &ComponentApplicationRequests::FindEntity, entityId);
return entity ? FindDerivedComponents(entity, typeId) : Entity::ComponentArrayType();
}
bool EnumerateBaseRecursive(SerializeContext* context, const EnumerateBaseRecursiveVisitor& baseClassVisitor, const TypeId& typeToExamine)
{
AZ_Assert(context, "CheckDeclaresSerializeBaseClass called with no serialize context.");
if (!context)
{
return false;
}
AZStd::fixed_vector<TypeId, 64> knownBaseClasses = { typeToExamine }; // avoid allocating heap here if possible. 64 types are 64*sizeof(Uuid) which is only 1k.
bool foundBaseClass = false;
auto enumerateBaseVisitor = [&baseClassVisitor, &knownBaseClasses](const AZ::SerializeContext::ClassData* classData, const TypeId& examineTypeId)
{
if (!classData)
{
return false;
}
if (AZStd::find(knownBaseClasses.begin(), knownBaseClasses.end(), classData->m_typeId) == knownBaseClasses.end())
{
if (knownBaseClasses.size() == 64)
{
// this should be pretty unlikely since a single class would have to have many other classes in its heirarchy
// and it'd all have to be basically in one layer, as we are popping as we explore.
AZ_WarningOnce("EntityUtils", false, "While trying to find a base class, all available slots were consumed. consider increasing the size of knownBaseClasses.\n");
// we cannot continue any further, assume we did not find it.
return false;
}
knownBaseClasses.push_back(classData->m_typeId);
}
return baseClassVisitor(classData, examineTypeId);
};
//=========================================================================
// EnumerateEntityIds
//=========================================================================
void EnumerateEntityIds(const void* classPtr, const Uuid& classUuid, const EntityIdVisitor& visitor, SerializeContext* context)
while (!knownBaseClasses.empty() && !foundBaseClass)
{
AZ_PROFILE_FUNCTION(AzCore);
TypeId toExamine = knownBaseClasses.back();
knownBaseClasses.pop_back();
if (!context)
{
context = GetApplicationSerializeContext();
if (!context)
{
AZ_Error("Serialization", false, "No serialize context provided! Failed to get component application default serialize context! ComponentApp is not started or input serialize context should not be null!");
return;
}
}
AZStd::vector<const SerializeContext::ClassData*> parentStack;
parentStack.reserve(30);
auto beginCB = [ &](void* ptr, const SerializeContext::ClassData* classData, const SerializeContext::ClassElement* elementData) -> bool
{
(void)elementData;
if (classData->m_typeId == SerializeTypeInfo<EntityId>::GetUuid())
{
// determine if this is entity ref or just entityId (please refer to the function documentation for more info)
bool isEntityId = false;
if (!parentStack.empty() && parentStack.back()->m_typeId == SerializeTypeInfo<Entity>::GetUuid())
{
// our parent in the entity (currently entity has only one EntityId member, but we can check the offset for future proof
AZ_Assert(elementData && strcmp(elementData->m_name, "Id") == 0, "class Entity, should have only ONE EntityId member, the actual entity id!");
isEntityId = true;
}
EntityId* entityIdPtr = (elementData->m_flags & SerializeContext::ClassElement::FLG_POINTER) ?
*reinterpret_cast<EntityId**>(ptr) : reinterpret_cast<EntityId*>(ptr);
visitor(*entityIdPtr, isEntityId, elementData);
}
parentStack.push_back(classData);
return true;
};
auto endCB = [ &]() -> bool
{
parentStack.pop_back();
return true;
};
SerializeContext::EnumerateInstanceCallContext callContext(
beginCB,
endCB,
context,
SerializeContext::ENUM_ACCESS_FOR_READ,
nullptr
);
context->EnumerateInstanceConst(
&callContext,
classPtr,
classUuid,
nullptr,
nullptr
);
context->EnumerateBase(enumerateBaseVisitor, toExamine);
}
//=========================================================================
// GetApplicationSerializeContext
//=========================================================================
SerializeContext* GetApplicationSerializeContext()
{
SerializeContext* context = nullptr;
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
return context;
}
return foundBaseClass;
}
//=========================================================================
// FindFirstDerivedComponent
//=========================================================================
Component* FindFirstDerivedComponent(const Entity* entity, const Uuid& typeId)
bool CheckIfClassIsDeprecated(SerializeContext* context, const TypeId& typeToExamine)
{
bool isDeprecated = false;
auto classVisitorFn = [&isDeprecated](const AZ::SerializeContext::ClassData* classData, const TypeId& /*rttiBase*/)
{
for (AZ::Component* component : entity->GetComponents())
{
if (azrtti_istypeof(typeId, component))
{
return component;
}
}
return nullptr;
}
Component* FindFirstDerivedComponent(EntityId entityId, const Uuid& typeId)
{
Entity* entity{};
ComponentApplicationBus::BroadcastResult(entity, &ComponentApplicationRequests::FindEntity, entityId);
return entity ? FindFirstDerivedComponent(entity, typeId) : nullptr;
}
//=========================================================================
// FindDerivedComponents
//=========================================================================
Entity::ComponentArrayType FindDerivedComponents(const Entity* entity, const Uuid& typeId)
{
Entity::ComponentArrayType result;
for (AZ::Component* component : entity->GetComponents())
{
if (azrtti_istypeof(typeId, component))
{
result.push_back(component);
}
}
return result;
}
Entity::ComponentArrayType FindDerivedComponents(EntityId entityId, const Uuid& typeId)
{
Entity* entity{};
ComponentApplicationBus::BroadcastResult(entity, &ComponentApplicationRequests::FindEntity, entityId);
return entity ? FindDerivedComponents(entity, typeId) : Entity::ComponentArrayType();
}
bool EnumerateBaseRecursive(SerializeContext* context, const EnumerateBaseRecursiveVisitor& baseClassVisitor, const TypeId& typeToExamine)
{
AZ_Assert(context, "CheckDeclaresSerializeBaseClass called with no serialize context.");
if (!context)
// Stop iterating once we stop receiving SerializeContext::ClassData*.
if (!classData)
{
return false;
}
AZStd::fixed_vector<TypeId, 64> knownBaseClasses = { typeToExamine }; // avoid allocating heap here if possible. 64 types are 64*sizeof(Uuid) which is only 1k.
bool foundBaseClass = false;
auto enumerateBaseVisitor = [&baseClassVisitor, &knownBaseClasses](const AZ::SerializeContext::ClassData* classData, const TypeId& examineTypeId)
{
if (!classData)
{
return false;
}
if (AZStd::find(knownBaseClasses.begin(), knownBaseClasses.end(), classData->m_typeId) == knownBaseClasses.end())
{
if (knownBaseClasses.size() == 64)
{
// this should be pretty unlikely since a single class would have to have many other classes in its heirarchy
// and it'd all have to be basically in one layer, as we are popping as we explore.
AZ_WarningOnce("EntityUtils", false, "While trying to find a base class, all available slots were consumed. consider increasing the size of knownBaseClasses.\n");
// we cannot continue any further, assume we did not find it.
return false;
}
knownBaseClasses.push_back(classData->m_typeId);
}
return baseClassVisitor(classData, examineTypeId);
};
while (!knownBaseClasses.empty() && !foundBaseClass)
{
TypeId toExamine = knownBaseClasses.back();
knownBaseClasses.pop_back();
context->EnumerateBase(enumerateBaseVisitor, toExamine);
}
return foundBaseClass;
}
bool CheckIfClassIsDeprecated(SerializeContext* context, const TypeId& typeToExamine)
{
bool isDeprecated = false;
auto classVisitorFn = [&isDeprecated](const AZ::SerializeContext::ClassData* classData, const TypeId& /*rttiBase*/)
{
// Stop iterating once we stop receiving SerializeContext::ClassData*.
if (!classData)
{
return false;
}
// Stop iterating if we've found that the class is deprecated
if (classData->IsDeprecated())
{
isDeprecated = true;
return false;
}
return true; // keep iterating
};
// Check if the type is deprecated
const AZ::SerializeContext::ClassData* classData = context->FindClassData(typeToExamine);
// Stop iterating if we've found that the class is deprecated
if (classData->IsDeprecated())
{
return true;
}
// Check if any of its bases are deprecated
EnumerateBaseRecursive(context, classVisitorFn, typeToExamine);
return isDeprecated;
}
bool CheckDeclaresSerializeBaseClass(SerializeContext* context, const TypeId& typeToFind, const TypeId& typeToExamine)
{
AZ_Assert(context, "CheckDeclaresSerializeBaseClass called with no serialize context.");
if (!context)
{
isDeprecated = true;
return false;
}
bool foundBaseClass = false;
auto baseClassVisitorFn = [&typeToFind, &foundBaseClass](const AZ::SerializeContext::ClassData* reflectedBase, const TypeId& /*rttiBase*/)
{
if (!reflectedBase)
{
foundBaseClass = false;
return false; // stop iterating
}
return true; // keep iterating
};
foundBaseClass = (reflectedBase->m_typeId == typeToFind);
if (foundBaseClass)
{
return false; // we have a base, stop iterating
}
return true; // keep iterating
};
EnumerateBaseRecursive(context, baseClassVisitorFn, typeToExamine);
return foundBaseClass;
}
bool RemoveDuplicateServicesOfAndAfterIterator(
const ComponentDescriptor::DependencyArrayType::iterator& iterator,
ComponentDescriptor::DependencyArrayType& providedServiceArray,
const Entity* entity)
// Check if the type is deprecated
const AZ::SerializeContext::ClassData* classData = context->FindClassData(typeToExamine);
if (classData->IsDeprecated())
{
// Build types that strip out AZ_Warnings will complain that entity is unused without this.
(void)entity;
if (iterator == providedServiceArray.end())
{
return false;
}
bool duplicateFound = false;
for (ComponentDescriptor::DependencyArrayType::iterator duplicateCheckIter = AZStd::next(iterator);
duplicateCheckIter != providedServiceArray.end();)
{
if (*iterator == *duplicateCheckIter)
{
AZ_Warning("Entity", false, "Duplicate service %d found on entity %s [%s]",
*duplicateCheckIter,
entity ? entity->GetName().c_str() : "Entity not provided",
entity ? entity->GetId().ToString().c_str() : "");
duplicateCheckIter = providedServiceArray.erase(duplicateCheckIter);
duplicateFound = true;
}
else
{
++duplicateCheckIter;
}
}
return duplicateFound;
return true;
}
} // namespace EntityUtils
} // namespace AZ
// Check if any of its bases are deprecated
EnumerateBaseRecursive(context, classVisitorFn, typeToExamine);
return isDeprecated;
}
bool CheckDeclaresSerializeBaseClass(SerializeContext* context, const TypeId& typeToFind, const TypeId& typeToExamine)
{
AZ_Assert(context, "CheckDeclaresSerializeBaseClass called with no serialize context.");
if (!context)
{
return false;
}
bool foundBaseClass = false;
auto baseClassVisitorFn = [&typeToFind, &foundBaseClass](const AZ::SerializeContext::ClassData* reflectedBase, const TypeId& /*rttiBase*/)
{
if (!reflectedBase)
{
foundBaseClass = false;
return false; // stop iterating
}
foundBaseClass = (reflectedBase->m_typeId == typeToFind);
if (foundBaseClass)
{
return false; // we have a base, stop iterating
}
return true; // keep iterating
};
EnumerateBaseRecursive(context, baseClassVisitorFn, typeToExamine);
return foundBaseClass;
}
bool RemoveDuplicateServicesOfAndAfterIterator(
const ComponentDescriptor::DependencyArrayType::iterator& iterator,
ComponentDescriptor::DependencyArrayType& providedServiceArray,
const Entity* entity)
{
// Build types that strip out AZ_Warnings will complain that entity is unused without this.
(void)entity;
if (iterator == providedServiceArray.end())
{
return false;
}
bool duplicateFound = false;
for (ComponentDescriptor::DependencyArrayType::iterator duplicateCheckIter = AZStd::next(iterator);
duplicateCheckIter != providedServiceArray.end();)
{
if (*iterator == *duplicateCheckIter)
{
AZ_Warning("Entity", false, "Duplicate service %d found on entity %s [%s]",
*duplicateCheckIter,
entity ? entity->GetName().c_str() : "Entity not provided",
entity ? entity->GetId().ToString().c_str() : "");
duplicateCheckIter = providedServiceArray.erase(duplicateCheckIter);
duplicateFound = true;
}
else
{
++duplicateCheckIter;
}
}
return duplicateFound;
}
} // namespace AZ::EntityUtils
@@ -57,6 +57,7 @@ void ZStd::StartCompressor(unsigned int compressionLevel)
ZSTD_customMem customAlloc;
customAlloc.customAlloc = reinterpret_cast<ZSTD_allocFunction>(&AllocateMem);
customAlloc.customFree = &FreeMem;
customAlloc.opaque = nullptr;
AZ_UNUSED(compressionLevel);
m_streamCompression = (ZSTD_createCStream_advanced(customAlloc));
@@ -14,323 +14,313 @@
#include <AzCore/std/containers/map.h>
#include <AzCore/std/smart_ptr/make_shared.h>
namespace AZ
namespace AZ::Debug
{
namespace Debug
namespace
{
namespace
struct AssetTreeNode;
// Per-thread data that needs to be stored.
struct ThreadData
{
struct AssetTreeNode;
// Per-thread data that needs to be stored.
struct ThreadData
{
AZStd::vector<AssetTreeNodeBase*, AZStdAssetTrackingAllocator> m_currentAssetStack;
};
// Access thread data through a virtual function to ensure that the same thread-local data is being shared across DLLs.
// Otherwise, the thread_local variables are replicated across DLLs that link the AzCore library, and you'll get a
// different version in each module.
class ThreadDataProvider
{
public:
virtual ThreadData& GetThreadData() = 0;
};
}
class AssetTrackingImpl final :
public ThreadDataProvider
{
public:
AZ_TYPE_INFO(AssetTrackingImpl, "{01E2A099-3523-40BE-80E0-E0ADD861BEE1}");
AZ_CLASS_ALLOCATOR(AssetTrackingImpl, OSAllocator, 0);
AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable);
~AssetTrackingImpl();
void AssetBegin(const char* id, const char* file, int line);
void AssetAttach(void* otherAllocation, const char* file, int line);
void AssetEnd();
ThreadData& GetThreadData() override;
private:
static EnvironmentVariable<AssetTrackingImpl*>& GetEnvironmentVariable();
static AssetTrackingImpl* GetSharedInstance();
static ThreadData& GetSharedThreadData();
using PrimaryAssets = AZStd::unordered_map<AssetTrackingId, AssetPrimaryInfo, AZStd::hash<AssetTrackingId>, AZStd::equal_to<AssetTrackingId>, AZStdAssetTrackingAllocator>;
using ThreadData = ThreadData;
using mutex_type = AZStd::mutex;
using lock_type = AZStd::lock_guard<mutex_type>;
mutex_type m_mutex;
PrimaryAssets m_primaryAssets;
AssetTreeNodeBase* m_assetRoot = nullptr;
AssetAllocationTableBase* m_allocationTable = nullptr;
bool m_performingAnalysis = false;
friend class AssetTracking;
friend class AssetTracking::Scope;
AZStd::vector<AssetTreeNodeBase*, AZStdAssetTrackingAllocator> m_currentAssetStack;
};
// Access thread data through a virtual function to ensure that the same thread-local data is being shared across DLLs.
// Otherwise, the thread_local variables are replicated across DLLs that link the AzCore library, and you'll get a
// different version in each module.
class ThreadDataProvider
{
public:
virtual ThreadData& GetThreadData() = 0;
};
}
}
class AssetTrackingImpl final :
public ThreadDataProvider
{
public:
AZ_TYPE_INFO(AssetTrackingImpl, "{01E2A099-3523-40BE-80E0-E0ADD861BEE1}");
AZ_CLASS_ALLOCATOR(AssetTrackingImpl, OSAllocator, 0);
AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable);
~AssetTrackingImpl();
void AssetBegin(const char* id, const char* file, int line);
void AssetAttach(void* otherAllocation, const char* file, int line);
void AssetEnd();
ThreadData& GetThreadData() override;
private:
static EnvironmentVariable<AssetTrackingImpl*>& GetEnvironmentVariable();
static AssetTrackingImpl* GetSharedInstance();
static ThreadData& GetSharedThreadData();
using PrimaryAssets = AZStd::unordered_map<AssetTrackingId, AssetPrimaryInfo, AZStd::hash<AssetTrackingId>, AZStd::equal_to<AssetTrackingId>, AZStdAssetTrackingAllocator>;
using ThreadData = ThreadData;
using mutex_type = AZStd::mutex;
using lock_type = AZStd::lock_guard<mutex_type>;
mutex_type m_mutex;
PrimaryAssets m_primaryAssets;
AssetTreeNodeBase* m_assetRoot = nullptr;
AssetAllocationTableBase* m_allocationTable = nullptr;
bool m_performingAnalysis = false;
friend class AssetTracking;
friend class AssetTracking::Scope;
};
///////////////////////////////////////////////////////////////////////////////
// AssetTrackingImpl methods
///////////////////////////////////////////////////////////////////////////////
namespace AZ
{
namespace Debug
AssetTrackingImpl::AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable) :
m_assetRoot(&assetTree->GetRoot()),
m_allocationTable(allocationTable)
{
AssetTrackingImpl::AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable) :
m_assetRoot(&assetTree->GetRoot()),
m_allocationTable(allocationTable)
{
AZ_Assert(!GetSharedInstance(), "Only one AssetTrackingImpl can exist!");
AZ_Assert(!GetSharedInstance(), "Only one AssetTrackingImpl can exist!");
GetEnvironmentVariable().Set(this);
AllocatorManager::Instance().EnterProfilingMode();
GetEnvironmentVariable().Set(this);
AllocatorManager::Instance().EnterProfilingMode();
}
AssetTrackingImpl::~AssetTrackingImpl()
{
AllocatorManager::Instance().ExitProfilingMode();
GetEnvironmentVariable().Reset();
}
void AssetTrackingImpl::AssetBegin(const char* id, const char* file, int line)
{
// In the future it may be desirable to organize assets based on where in code the asset was entered into.
// For now these are ignored.
AZ_UNUSED(file);
AZ_UNUSED(line);
using namespace Internal;
AssetTrackingId assetId(id);
auto& threadData = GetSharedThreadData();
AssetTreeNodeBase* parentAsset = threadData.m_currentAssetStack.empty() ? nullptr : threadData.m_currentAssetStack.back();
AssetTreeNodeBase* childAsset;
AssetPrimaryInfo* assetPrimaryInfo;
if (!parentAsset)
{
parentAsset = m_assetRoot;
}
AssetTrackingImpl::~AssetTrackingImpl()
{
AllocatorManager::Instance().ExitProfilingMode();
GetEnvironmentVariable().Reset();
}
lock_type lock(m_mutex);
void AssetTrackingImpl::AssetBegin(const char* id, const char* file, int line)
{
// In the future it may be desirable to organize assets based on where in code the asset was entered into.
// For now these are ignored.
AZ_UNUSED(file);
AZ_UNUSED(line);
// Locate or create the primary record for this asset
auto primaryItr = m_primaryAssets.find(assetId);
using namespace Internal;
AssetTrackingId assetId(id);
auto& threadData = GetSharedThreadData();
AssetTreeNodeBase* parentAsset = threadData.m_currentAssetStack.empty() ? nullptr : threadData.m_currentAssetStack.back();
AssetTreeNodeBase* childAsset;
AssetPrimaryInfo* assetPrimaryInfo;
if (!parentAsset)
if (primaryItr != m_primaryAssets.end())
{
parentAsset = m_assetRoot;
}
{
lock_type lock(m_mutex);
// Locate or create the primary record for this asset
auto primaryItr = m_primaryAssets.find(assetId);
if (primaryItr != m_primaryAssets.end())
{
assetPrimaryInfo = &primaryItr->second;
}
else
{
auto insertResult = m_primaryAssets.emplace(assetId, AssetPrimaryInfo());
assetPrimaryInfo = &insertResult.first->second;
assetPrimaryInfo->m_id = &insertResult.first->first;
}
// Add this asset to the stack for this thread's context
childAsset = parentAsset->FindOrAddChild(assetId, assetPrimaryInfo);
}
threadData.m_currentAssetStack.push_back(childAsset);
}
void AssetTrackingImpl::AssetAttach(void* otherAllocation, const char* file, int line)
{
AZ_UNUSED(file);
AZ_UNUSED(line);
using namespace Internal;
AssetTreeNodeBase* assetInfo = m_allocationTable->FindAllocation(otherAllocation);
// We will push back a nullptr if there is no asset, this is necessary to balance the call to AssetEnd()
GetSharedThreadData().m_currentAssetStack.push_back(assetInfo);
}
void AssetTrackingImpl::AssetEnd()
{
AZ_Assert(!GetSharedThreadData().m_currentAssetStack.empty(), "AssetEnd() called without matching AssetBegin() or AssetAttach. Use the AZ_ASSET_NAMED_SCOPE and AZ_ASSET_ATTACH_TO_SCOPE macros to avoid this!");
GetSharedThreadData().m_currentAssetStack.pop_back();
}
AssetTrackingImpl* AssetTrackingImpl::GetSharedInstance()
{
auto environmentVariable = GetEnvironmentVariable();
if(environmentVariable)
{
return *environmentVariable;
}
return nullptr;
}
ThreadData& AssetTrackingImpl::GetSharedThreadData()
{
// Cast to the base type so our virtual call doesn't get optimized away. We require GetThreadData() to be executed in the same DLL every time.
return static_cast<ThreadDataProvider*>(GetSharedInstance())->GetThreadData();
}
AssetTrackingImpl::ThreadData& AssetTrackingImpl::GetThreadData()
{
static thread_local ThreadData* data = nullptr;
static thread_local typename AZStd::aligned_storage_t<sizeof(ThreadData), alignof(ThreadData)> storage;
if (!data)
{
data = new (&storage) ThreadData;
}
return *data;
}
EnvironmentVariable<AssetTrackingImpl*>& AssetTrackingImpl::GetEnvironmentVariable()
{
static EnvironmentVariable<AssetTrackingImpl*> assetTrackingImpl = Environment::CreateVariable<AssetTrackingImpl*>(AzTypeInfo<AssetTrackingImpl*>::Name());
return assetTrackingImpl;
}
///////////////////////////////////////////////////////////////////////////////
// AssetTracking::Scope functions
///////////////////////////////////////////////////////////////////////////////
AssetTracking::Scope AssetTracking::Scope::ScopeFromAssetId(const char* file, int line, const char* fmt, ...)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
static const int BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
va_list args;
va_start(args, fmt);
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
va_end(args);
impl->AssetBegin(buffer, file, line);
}
return Scope();
}
AssetTracking::Scope AssetTracking::Scope::ScopeFromAttachment(void* attachTo, const char* file, int line)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetAttach(attachTo, file, line);
}
return Scope();
}
AssetTracking::Scope::~Scope()
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetEnd();
}
}
AssetTracking::Scope::Scope()
{
}
///////////////////////////////////////////////////////////////////////////////
// AssetTracking functions
///////////////////////////////////////////////////////////////////////////////
void AssetTracking::EnterScopeByAssetId(const char* file, int line, const char* fmt, ...)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
static const int BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
va_list args;
va_start(args, fmt);
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
va_end(args);
impl->AssetBegin(buffer, file, line);
}
}
void AssetTracking::EnterScopeByAttachment(void* attachTo, const char* file, int line)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetAttach(attachTo, file, line);
}
}
void AssetTracking::ExitScope()
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetEnd();
}
}
const char* AssetTracking::GetDebugScope()
{
// Output debug information about the current asset scope in the current thread.
// Do not use in production code.
#ifndef RELEASE
static const int BUFFER_SIZE = 1024;
static char buffer[BUFFER_SIZE];
const auto& assetStack = AssetTrackingImpl::GetSharedInstance()->GetThreadData().m_currentAssetStack;
if (assetStack.empty())
{
azsnprintf(buffer, BUFFER_SIZE, "<none>");
assetPrimaryInfo = &primaryItr->second;
}
else
{
char* pos = buffer;
for (auto itr = assetStack.rbegin(); itr != assetStack.rend(); ++itr)
{
pos += azsnprintf(pos, BUFFER_SIZE - (pos - buffer), "%s\n", (*itr)->GetAssetPrimaryInfo()->m_id->m_id.c_str());
if (pos >= buffer + BUFFER_SIZE)
{
break;
}
}
auto insertResult = m_primaryAssets.emplace(assetId, AssetPrimaryInfo());
assetPrimaryInfo = &insertResult.first->second;
assetPrimaryInfo->m_id = &insertResult.first->first;
}
return buffer;
#else
return "";
#endif
// Add this asset to the stack for this thread's context
childAsset = parentAsset->FindOrAddChild(assetId, assetPrimaryInfo);
}
AssetTracking::AssetTracking(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable)
threadData.m_currentAssetStack.push_back(childAsset);
}
void AssetTrackingImpl::AssetAttach(void* otherAllocation, const char* file, int line)
{
AZ_UNUSED(file);
AZ_UNUSED(line);
using namespace Internal;
AssetTreeNodeBase* assetInfo = m_allocationTable->FindAllocation(otherAllocation);
// We will push back a nullptr if there is no asset, this is necessary to balance the call to AssetEnd()
GetSharedThreadData().m_currentAssetStack.push_back(assetInfo);
}
void AssetTrackingImpl::AssetEnd()
{
AZ_Assert(!GetSharedThreadData().m_currentAssetStack.empty(), "AssetEnd() called without matching AssetBegin() or AssetAttach. Use the AZ_ASSET_NAMED_SCOPE and AZ_ASSET_ATTACH_TO_SCOPE macros to avoid this!");
GetSharedThreadData().m_currentAssetStack.pop_back();
}
AssetTrackingImpl* AssetTrackingImpl::GetSharedInstance()
{
auto environmentVariable = GetEnvironmentVariable();
if(environmentVariable)
{
m_impl.reset(aznew AssetTrackingImpl(assetTree, allocationTable));
return *environmentVariable;
}
AssetTracking::~AssetTracking()
return nullptr;
}
ThreadData& AssetTrackingImpl::GetSharedThreadData()
{
// Cast to the base type so our virtual call doesn't get optimized away. We require GetThreadData() to be executed in the same DLL every time.
return static_cast<ThreadDataProvider*>(GetSharedInstance())->GetThreadData();
}
AssetTrackingImpl::ThreadData& AssetTrackingImpl::GetThreadData()
{
static thread_local ThreadData* data = nullptr;
static thread_local typename AZStd::aligned_storage_t<sizeof(ThreadData), alignof(ThreadData)> storage;
if (!data)
{
data = new (&storage) ThreadData;
}
AssetTreeNodeBase* AssetTracking::GetCurrentThreadAsset() const
{
const auto& assetStack = m_impl->GetThreadData().m_currentAssetStack;
AssetTreeNodeBase* result = assetStack.empty() ? nullptr : assetStack.back();
return *data;
}
return result;
EnvironmentVariable<AssetTrackingImpl*>& AssetTrackingImpl::GetEnvironmentVariable()
{
static EnvironmentVariable<AssetTrackingImpl*> assetTrackingImpl = Environment::CreateVariable<AssetTrackingImpl*>(AzTypeInfo<AssetTrackingImpl*>::Name());
return assetTrackingImpl;
}
///////////////////////////////////////////////////////////////////////////////
// AssetTracking::Scope functions
///////////////////////////////////////////////////////////////////////////////
AssetTracking::Scope AssetTracking::Scope::ScopeFromAssetId(const char* file, int line, const char* fmt, ...)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
static const int BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
va_list args;
va_start(args, fmt);
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
va_end(args);
impl->AssetBegin(buffer, file, line);
}
return Scope();
}
AssetTracking::Scope AssetTracking::Scope::ScopeFromAttachment(void* attachTo, const char* file, int line)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetAttach(attachTo, file, line);
}
return Scope();
}
AssetTracking::Scope::~Scope()
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetEnd();
}
}
AssetTracking::Scope::Scope()
{
}
///////////////////////////////////////////////////////////////////////////////
// AssetTracking functions
///////////////////////////////////////////////////////////////////////////////
void AssetTracking::EnterScopeByAssetId(const char* file, int line, const char* fmt, ...)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
static const int BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
va_list args;
va_start(args, fmt);
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
va_end(args);
impl->AssetBegin(buffer, file, line);
}
}
void AssetTracking::EnterScopeByAttachment(void* attachTo, const char* file, int line)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetAttach(attachTo, file, line);
}
}
void AssetTracking::ExitScope()
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetEnd();
}
}
const char* AssetTracking::GetDebugScope()
{
// Output debug information about the current asset scope in the current thread.
// Do not use in production code.
#ifndef RELEASE
static const int BUFFER_SIZE = 1024;
static char buffer[BUFFER_SIZE];
const auto& assetStack = AssetTrackingImpl::GetSharedInstance()->GetThreadData().m_currentAssetStack;
if (assetStack.empty())
{
azsnprintf(buffer, BUFFER_SIZE, "<none>");
}
else
{
char* pos = buffer;
for (auto itr = assetStack.rbegin(); itr != assetStack.rend(); ++itr)
{
pos += azsnprintf(pos, BUFFER_SIZE - (pos - buffer), "%s\n", (*itr)->GetAssetPrimaryInfo()->m_id->m_id.c_str());
if (pos >= buffer + BUFFER_SIZE)
{
break;
}
}
}
return buffer;
#else
return "";
#endif
}
AssetTracking::AssetTracking(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable)
{
m_impl.reset(aznew AssetTrackingImpl(assetTree, allocationTable));
}
AssetTracking::~AssetTracking()
{
}
AssetTreeNodeBase* AssetTracking::GetCurrentThreadAsset() const
{
const auto& assetStack = m_impl->GetThreadData().m_currentAssetStack;
AssetTreeNodeBase* result = assetStack.empty() ? nullptr : assetStack.back();
return result;
}
} // namespace AzFramework

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