Merge branch 'development' into Atom/dmcdiar/ATOM-16896

This commit is contained in:
dmcdiarmid-ly
2021-12-09 17:21:47 -07:00
72 changed files with 2020 additions and 749 deletions
@@ -14,30 +14,44 @@ from scene_api.scene_data import SceneGraphName
def log_exception_traceback():
"""
Outputs an exception stacktrace.
"""
"""Outputs an exception stacktrace."""
data = traceback.format_exc()
logger = logging.getLogger('python')
logger.error(data)
def sanitize_name_for_disk(name: str):
"""
Removes illegal filename characters from a string.
def sanitize_name_for_disk(name: str) -> str:
"""Removes illegal filename characters from a string.
Parameters
----------
name :
String to clean.
Returns
-------
str
Name with illegal characters removed.
:param name: String to clean.
:return: Name with illegal characters removed.
"""
return "".join(char for char in name if char not in "|<>:\"/?*\\")
def get_mesh_node_names(scene_graph: sceneData.SceneGraph) -> Tuple[List[SceneGraphName], List[str]]:
"""
Returns a tuple of all the mesh nodes as well as all the node paths
"""Returns a tuple of all the mesh nodes as well as all the node paths
Parameters
----------
scene_graph :
Scene graph to search
Returns
-------
Tuple[List[SceneGraphName], List[str]]
Tuple of [Mesh Nodes, All Node Paths]
:param scene_graph: Scene graph to search
:return: Tuple of [Mesh Nodes, All Node Paths]
"""
import azlmbr.scene as sceneApi
import azlmbr.scene.graph
@@ -8,7 +8,7 @@
import azlmbr.bus
import azlmbr.math
from scene_api.scene_data import PrimitiveShape, DecompositionMode
from scene_api.scene_data import PrimitiveShape, DecompositionMode, ColorChannel, TangentSpaceSource, TangentSpaceMethod
from scene_helpers import *
@@ -71,6 +71,7 @@ def add_physx_meshes(scene_manifest: sceneData.SceneManifest, source_file_name:
triangle = scene_manifest.add_physx_triangle_mesh_group(source_file_name + "_triangle", False, True, True, True, True, True)
scene_manifest.physx_mesh_group_add_selected_unselected_nodes(triangle, [first_mesh], all_except_first_mesh)
def update_manifest(scene):
import uuid, os
import azlmbr.scene.graph
@@ -114,10 +115,11 @@ def update_manifest(scene):
if node != mesh_path:
scene_manifest.mesh_group_unselect_node(mesh_group, node)
scene_manifest.mesh_group_add_cloth_rule(mesh_group, mesh_path, "Col0", 1, "Col0", 2, "Col0", 2, 3)
scene_manifest.mesh_group_add_cloth_rule(mesh_group, mesh_path, "Col0", ColorChannel.GREEN, "Col0",
ColorChannel.BLUE, "Col0", ColorChannel.BLUE, ColorChannel.ALPHA)
scene_manifest.mesh_group_add_advanced_mesh_rule(mesh_group, True, False, True, "Col0")
scene_manifest.mesh_group_add_skin_rule(mesh_group, 3, 0.002)
scene_manifest.mesh_group_add_tangent_rule(mesh_group, 1, 0)
scene_manifest.mesh_group_add_tangent_rule(mesh_group, TangentSpaceSource.MIKKT_GENERATION, TangentSpaceMethod.TSPACE_BASIC)
# Create an editor entity
entity_id = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "CreateEditorReadyEntity", mesh_group_name)
@@ -140,12 +140,16 @@ class TestHelper:
# make sure the server launcher is running
waiter.wait_for(lambda: process_utils.process_exists("AutomatedTesting.ServerLauncher", ignore_extensions=True), timeout=5.0, exc=AssertionError("AutomatedTesting.ServerLauncher has NOT launched!"), interval=1.0)
# make sure the editor connects to the editor-server and sends the level data packet
wait_for_critical_expected_line("MultiplayerEditorConnection: Editor-server activation has found and connected to the editor.", section_tracer.prints, 15.0)
wait_for_critical_expected_line("Editor is sending the editor-server the level data packet.", section_tracer.prints, 5.0)
# make sure the editor finally connects to the editor-server network simulation
wait_for_critical_expected_line("Logger: Editor Server completed receiving the editor's level assets, responding to Editor...", section_tracer.prints, 5.0)
wait_for_critical_expected_line("Editor-server ready. Editor has successfully connected to the editor-server's network simulation.", section_tracer.prints, 5.0)
wait_for_critical_unexpected_line(f"MultiplayerSystemComponent: SpawnDefaultPlayerPrefab failed. Missing sv_defaultPlayerSpawnAsset at path '{sv_default_player_spawn_asset.lower()}'.", section_tracer.prints, 0.5)
TestHelper.wait_for_condition(lambda : multiplayer.PythonEditorFuncs_is_in_game_mode(), 5.0)
Report.critical_result(msgtuple_success_fail, multiplayer.PythonEditorFuncs_is_in_game_mode())
@@ -178,7 +178,7 @@ class TestAutomationBase:
@staticmethod
def _kill_ly_processes(include_asset_processor=True):
LY_PROCESSES = [
'Editor', 'Profiler', 'RemoteConsole',
'Editor', 'Profiler', 'RemoteConsole', 'AutomatedTesting.ServerLauncher'
]
AP_PROCESSES = [
'AssetProcessor', 'AssetProcessorBatch', 'AssetBuilder', 'CrySCompileServer',
@@ -5,7 +5,7 @@
"ClassData": {
"m_scriptCanvas": {
"Id": {
"id": 20239954977260
"id": 11859291537220
},
"Name": "AutoComponent_NetworkInput",
"Components": {
@@ -81,7 +81,7 @@
"m_nodes": [
{
"Id": {
"id": 20265724781036
"id": 11872176439108
},
"Name": "SC-Node(NotEqualTo)",
"Components": {
@@ -232,7 +232,7 @@
},
{
"Id": {
"id": 20257134846444
"id": 11893651275588
},
"Name": "SC-Node(NotEqualTo)",
"Components": {
@@ -383,7 +383,7 @@
},
{
"Id": {
"id": 20278609682924
"id": 11897946242884
},
"Name": "SC-Node(Print)",
"Components": {
@@ -423,16 +423,16 @@
}
}
],
"m_format": "AutoComponent_NetworkInput ProcessInput called!",
"m_format": "AutoComponent_NetworkInput ProcessInput called!\n",
"m_unresolvedString": [
"AutoComponent_NetworkInput ProcessInput called!"
"AutoComponent_NetworkInput ProcessInput called!\n"
]
}
}
},
{
"Id": {
"id": 20244249944556
"id": 11885061340996
},
"Name": "SC-Node(CreateFromValues)",
"Components": {
@@ -571,7 +571,7 @@
},
{
"Id": {
"id": 20252839879148
"id": 11867881471812
},
"Name": "EBusEventHandler",
"Components": {
@@ -862,7 +862,7 @@
},
{
"Id": {
"id": 20248544911852
"id": 11863586504516
},
"Name": "SC-Node(ExtractProperty)",
"Components": {
@@ -1003,7 +1003,7 @@
},
{
"Id": {
"id": 20270019748332
"id": 11880766373700
},
"Name": "SC-Node(Print)",
"Components": {
@@ -1043,16 +1043,16 @@
}
}
],
"m_format": "AutoComponent_NetworkInput received bad fwdback!",
"m_format": "AutoComponent_NetworkInput received bad fwdback!\n",
"m_unresolvedString": [
"AutoComponent_NetworkInput received bad fwdback!"
"AutoComponent_NetworkInput received bad fwdback!\n"
]
}
}
},
{
"Id": {
"id": 20274314715628
"id": 11889356308292
},
"Name": "SC-Node(Print)",
"Components": {
@@ -1092,16 +1092,16 @@
}
}
],
"m_format": "AutoComponent_NetworkInput received bad leftright!",
"m_format": "AutoComponent_NetworkInput received bad leftright!\n",
"m_unresolvedString": [
"AutoComponent_NetworkInput received bad leftright!"
"AutoComponent_NetworkInput received bad leftright!\n"
]
}
}
},
{
"Id": {
"id": 20261429813740
"id": 11876471406404
},
"Name": "SC-Node(Print)",
"Components": {
@@ -1141,9 +1141,9 @@
}
}
],
"m_format": "AutoComponent_NetworkInput CreateInput called!",
"m_format": "AutoComponent_NetworkInput CreateInput called!\n",
"m_unresolvedString": [
"AutoComponent_NetworkInput CreateInput called!"
"AutoComponent_NetworkInput CreateInput called!\n"
]
}
}
@@ -1152,7 +1152,7 @@
"m_connections": [
{
"Id": {
"id": 20282904650220
"id": 11902241210180
},
"Name": "srcEndpoint=(NetworkTestPlayerComponentBusHandler Handler: ExecutionSlot:CreateInput), destEndpoint=(Print: In)",
"Components": {
@@ -1161,7 +1161,7 @@
"Id": 3586317167340048684,
"sourceEndpoint": {
"nodeId": {
"id": 20252839879148
"id": 11867881471812
},
"slotId": {
"m_id": "{B831AC60-7641-4B74-9829-26A3576B4766}"
@@ -1169,7 +1169,7 @@
},
"targetEndpoint": {
"nodeId": {
"id": 20261429813740
"id": 11876471406404
},
"slotId": {
"m_id": "{2B6DB3BC-AA87-4280-B4C3-42C1EE17CBA3}"
@@ -1180,7 +1180,7 @@
},
{
"Id": {
"id": 20287199617516
"id": 11906536177476
},
"Name": "srcEndpoint=(NetworkTestPlayerComponentBusHandler Handler: ExecutionSlot:CreateInput), destEndpoint=(CreateFromValues: In)",
"Components": {
@@ -1189,7 +1189,7 @@
"Id": 15956251897822268937,
"sourceEndpoint": {
"nodeId": {
"id": 20252839879148
"id": 11867881471812
},
"slotId": {
"m_id": "{B831AC60-7641-4B74-9829-26A3576B4766}"
@@ -1197,7 +1197,7 @@
},
"targetEndpoint": {
"nodeId": {
"id": 20244249944556
"id": 11885061340996
},
"slotId": {
"m_id": "{514CDDAA-290F-4758-B28F-4003E719E635}"
@@ -1208,7 +1208,7 @@
},
{
"Id": {
"id": 20291494584812
"id": 11910831144772
},
"Name": "srcEndpoint=(CreateFromValues: Result: NetworkTestPlayerComponentNetworkInput), destEndpoint=(NetworkTestPlayerComponentBusHandler Handler: Result: NetworkTestPlayerComponentNetworkInput)",
"Components": {
@@ -1217,7 +1217,7 @@
"Id": 3864080489501353126,
"sourceEndpoint": {
"nodeId": {
"id": 20244249944556
"id": 11885061340996
},
"slotId": {
"m_id": "{90E52F81-54E0-4C63-9881-B661FB5D87D1}"
@@ -1225,7 +1225,7 @@
},
"targetEndpoint": {
"nodeId": {
"id": 20252839879148
"id": 11867881471812
},
"slotId": {
"m_id": "{ADF9B366-8324-4C1E-B601-C059DA70FDDE}"
@@ -1236,7 +1236,7 @@
},
{
"Id": {
"id": 20295789552108
"id": 11915126112068
},
"Name": "srcEndpoint=(NetworkTestPlayerComponentBusHandler Handler: ExecutionSlot:ProcessInput), destEndpoint=(Print: In)",
"Components": {
@@ -1245,7 +1245,7 @@
"Id": 8628095809445337119,
"sourceEndpoint": {
"nodeId": {
"id": 20252839879148
"id": 11867881471812
},
"slotId": {
"m_id": "{4C8F2908-12B0-4C35-8468-31D3D4DF36AA}"
@@ -1253,7 +1253,7 @@
},
"targetEndpoint": {
"nodeId": {
"id": 20278609682924
"id": 11897946242884
},
"slotId": {
"m_id": "{D994D58A-DBF1-4929-B779-F0D2CBAD2F0D}"
@@ -1264,7 +1264,7 @@
},
{
"Id": {
"id": 20300084519404
"id": 11919421079364
},
"Name": "srcEndpoint=(NetworkTestPlayerComponentBusHandler Handler: ExecutionSlot:ProcessInput), destEndpoint=(Extract Properties: In)",
"Components": {
@@ -1273,7 +1273,7 @@
"Id": 10621112306443381493,
"sourceEndpoint": {
"nodeId": {
"id": 20252839879148
"id": 11867881471812
},
"slotId": {
"m_id": "{4C8F2908-12B0-4C35-8468-31D3D4DF36AA}"
@@ -1281,7 +1281,7 @@
},
"targetEndpoint": {
"nodeId": {
"id": 20248544911852
"id": 11863586504516
},
"slotId": {
"m_id": "{C80C50EE-F216-4F44-B107-6B35354AFD52}"
@@ -1292,7 +1292,7 @@
},
{
"Id": {
"id": 20304379486700
"id": 11923716046660
},
"Name": "srcEndpoint=(NetworkTestPlayerComponentBusHandler Handler: NetworkTestPlayerComponentNetworkInput), destEndpoint=(Extract Properties: Source)",
"Components": {
@@ -1301,7 +1301,7 @@
"Id": 14013500888143163469,
"sourceEndpoint": {
"nodeId": {
"id": 20252839879148
"id": 11867881471812
},
"slotId": {
"m_id": "{8F69FA2E-28D8-4DF1-A4B5-AEF3985095C5}"
@@ -1309,7 +1309,7 @@
},
"targetEndpoint": {
"nodeId": {
"id": 20248544911852
"id": 11863586504516
},
"slotId": {
"m_id": "{D387C800-352B-4B01-8765-4F4B40DF45CB}"
@@ -1320,7 +1320,7 @@
},
{
"Id": {
"id": 20308674453996
"id": 11928011013956
},
"Name": "srcEndpoint=(Extract Properties: Out), destEndpoint=(Not Equal To (!=): In)",
"Components": {
@@ -1329,7 +1329,7 @@
"Id": 14597948098713219792,
"sourceEndpoint": {
"nodeId": {
"id": 20248544911852
"id": 11863586504516
},
"slotId": {
"m_id": "{C69C098D-D667-4DC7-85E5-AFD119727D94}"
@@ -1337,7 +1337,7 @@
},
"targetEndpoint": {
"nodeId": {
"id": 20257134846444
"id": 11893651275588
},
"slotId": {
"m_id": "{AE7E453C-15DE-47D2-954A-05C3895B7AC6}"
@@ -1348,7 +1348,7 @@
},
{
"Id": {
"id": 20312969421292
"id": 11932305981252
},
"Name": "srcEndpoint=(Extract Properties: FwdBack: Number), destEndpoint=(Not Equal To (!=): Value A)",
"Components": {
@@ -1357,7 +1357,7 @@
"Id": 14915522756837814768,
"sourceEndpoint": {
"nodeId": {
"id": 20248544911852
"id": 11863586504516
},
"slotId": {
"m_id": "{0C03D491-DE25-46C2-BF09-14769FA49FDB}"
@@ -1365,7 +1365,7 @@
},
"targetEndpoint": {
"nodeId": {
"id": 20257134846444
"id": 11893651275588
},
"slotId": {
"m_id": "{57EBC15B-452E-49B3-8BD3-4FBBB07F1F14}"
@@ -1376,7 +1376,7 @@
},
{
"Id": {
"id": 20317264388588
"id": 11936600948548
},
"Name": "srcEndpoint=(Extract Properties: Out), destEndpoint=(Not Equal To (!=): In)",
"Components": {
@@ -1385,7 +1385,7 @@
"Id": 6510282773353837676,
"sourceEndpoint": {
"nodeId": {
"id": 20248544911852
"id": 11863586504516
},
"slotId": {
"m_id": "{C69C098D-D667-4DC7-85E5-AFD119727D94}"
@@ -1393,7 +1393,7 @@
},
"targetEndpoint": {
"nodeId": {
"id": 20265724781036
"id": 11872176439108
},
"slotId": {
"m_id": "{AE7E453C-15DE-47D2-954A-05C3895B7AC6}"
@@ -1404,7 +1404,7 @@
},
{
"Id": {
"id": 20321559355884
"id": 11940895915844
},
"Name": "srcEndpoint=(Extract Properties: LeftRight: Number), destEndpoint=(Not Equal To (!=): Value A)",
"Components": {
@@ -1413,7 +1413,7 @@
"Id": 16150645152204311425,
"sourceEndpoint": {
"nodeId": {
"id": 20248544911852
"id": 11863586504516
},
"slotId": {
"m_id": "{4C13F9EF-60BF-4AD1-8FA9-66F46455411C}"
@@ -1421,7 +1421,7 @@
},
"targetEndpoint": {
"nodeId": {
"id": 20265724781036
"id": 11872176439108
},
"slotId": {
"m_id": "{57EBC15B-452E-49B3-8BD3-4FBBB07F1F14}"
@@ -1432,7 +1432,7 @@
},
{
"Id": {
"id": 20325854323180
"id": 11945190883140
},
"Name": "srcEndpoint=(Not Equal To (!=): True), destEndpoint=(Print: In)",
"Components": {
@@ -1441,7 +1441,7 @@
"Id": 3322355580364572639,
"sourceEndpoint": {
"nodeId": {
"id": 20257134846444
"id": 11893651275588
},
"slotId": {
"m_id": "{AC364E17-A9A1-42DB-A29D-7B2D666E4287}"
@@ -1449,7 +1449,7 @@
},
"targetEndpoint": {
"nodeId": {
"id": 20270019748332
"id": 11880766373700
},
"slotId": {
"m_id": "{733AA75D-022C-45E9-9D0F-3EF9A1633ADC}"
@@ -1460,7 +1460,7 @@
},
{
"Id": {
"id": 20330149290476
"id": 11949485850436
},
"Name": "srcEndpoint=(Not Equal To (!=): True), destEndpoint=(Print: In)",
"Components": {
@@ -1469,7 +1469,7 @@
"Id": 1975626970668030308,
"sourceEndpoint": {
"nodeId": {
"id": 20265724781036
"id": 11872176439108
},
"slotId": {
"m_id": "{AC364E17-A9A1-42DB-A29D-7B2D666E4287}"
@@ -1477,7 +1477,7 @@
},
"targetEndpoint": {
"nodeId": {
"id": 20274314715628
"id": 11889356308292
},
"slotId": {
"m_id": "{733AA75D-022C-45E9-9D0F-3EF9A1633ADC}"
@@ -1498,16 +1498,16 @@
"GraphCanvasData": [
{
"Key": {
"id": 20239954977260
"id": 11859291537220
},
"Value": {
"ComponentData": {
"{5F84B500-8C45-40D1-8EFC-A5306B241444}": {
"$type": "SceneComponentSaveData",
"ViewParams": {
"Scale": 1.0097068678919363,
"AnchorX": 1086.4539794921875,
"AnchorY": 198.07728576660156
"Scale": 0.8416459517191037,
"AnchorX": -80.7940673828125,
"AnchorY": -622.589599609375
}
}
}
@@ -1515,38 +1515,7 @@
},
{
"Key": {
"id": 20244249944556
},
"Value": {
"ComponentData": {
"{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
"$type": "NodeSaveData"
},
"{328FF15C-C302-458F-A43D-E1794DE0904E}": {
"$type": "GeneralNodeTitleComponentSaveData",
"PaletteOverride": "MethodNodeTitlePalette"
},
"{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
"$type": "GeometrySaveData",
"Position": [
740.0,
100.0
]
},
"{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
"$type": "StylingComponentSaveData",
"SubStyle": ".method"
},
"{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
"$type": "PersistentIdComponentSaveData",
"PersistentId": "{7A7C96CB-4B5A-48DD-A0AD-0094A113549B}"
}
}
}
},
{
"Key": {
"id": 20248544911852
"id": 11863586504516
},
"Value": {
"ComponentData": {
@@ -1576,7 +1545,7 @@
},
{
"Key": {
"id": 20252839879148
"id": 11867881471812
},
"Value": {
"ComponentData": {
@@ -1613,67 +1582,7 @@
},
{
"Key": {
"id": 20257134846444
},
"Value": {
"ComponentData": {
"{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
"$type": "NodeSaveData"
},
"{328FF15C-C302-458F-A43D-E1794DE0904E}": {
"$type": "GeneralNodeTitleComponentSaveData",
"PaletteOverride": "MathNodeTitlePalette"
},
"{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
"$type": "GeometrySaveData",
"Position": [
1040.0,
320.0
]
},
"{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
"$type": "StylingComponentSaveData"
},
"{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
"$type": "PersistentIdComponentSaveData",
"PersistentId": "{71AB4748-41F4-4FEA-874C-F54037236F31}"
}
}
}
},
{
"Key": {
"id": 20261429813740
},
"Value": {
"ComponentData": {
"{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
"$type": "NodeSaveData"
},
"{328FF15C-C302-458F-A43D-E1794DE0904E}": {
"$type": "GeneralNodeTitleComponentSaveData",
"PaletteOverride": "StringNodeTitlePalette"
},
"{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
"$type": "GeometrySaveData",
"Position": [
740.0,
-100.0
]
},
"{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
"$type": "StylingComponentSaveData"
},
"{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
"$type": "PersistentIdComponentSaveData",
"PersistentId": "{26E363EE-F35A-4096-88EF-DF907A809894}"
}
}
}
},
{
"Key": {
"id": 20265724781036
"id": 11872176439108
},
"Value": {
"ComponentData": {
@@ -1703,7 +1612,37 @@
},
{
"Key": {
"id": 20270019748332
"id": 11876471406404
},
"Value": {
"ComponentData": {
"{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
"$type": "NodeSaveData"
},
"{328FF15C-C302-458F-A43D-E1794DE0904E}": {
"$type": "GeneralNodeTitleComponentSaveData",
"PaletteOverride": "StringNodeTitlePalette"
},
"{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
"$type": "GeometrySaveData",
"Position": [
740.0,
-100.0
]
},
"{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
"$type": "StylingComponentSaveData"
},
"{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
"$type": "PersistentIdComponentSaveData",
"PersistentId": "{26E363EE-F35A-4096-88EF-DF907A809894}"
}
}
}
},
{
"Key": {
"id": 11880766373700
},
"Value": {
"ComponentData": {
@@ -1733,7 +1672,38 @@
},
{
"Key": {
"id": 20274314715628
"id": 11885061340996
},
"Value": {
"ComponentData": {
"{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
"$type": "NodeSaveData"
},
"{328FF15C-C302-458F-A43D-E1794DE0904E}": {
"$type": "GeneralNodeTitleComponentSaveData",
"PaletteOverride": "MethodNodeTitlePalette"
},
"{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
"$type": "GeometrySaveData",
"Position": [
740.0,
100.0
]
},
"{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
"$type": "StylingComponentSaveData",
"SubStyle": ".method"
},
"{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
"$type": "PersistentIdComponentSaveData",
"PersistentId": "{7A7C96CB-4B5A-48DD-A0AD-0094A113549B}"
}
}
}
},
{
"Key": {
"id": 11889356308292
},
"Value": {
"ComponentData": {
@@ -1763,7 +1733,37 @@
},
{
"Key": {
"id": 20278609682924
"id": 11893651275588
},
"Value": {
"ComponentData": {
"{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
"$type": "NodeSaveData"
},
"{328FF15C-C302-458F-A43D-E1794DE0904E}": {
"$type": "GeneralNodeTitleComponentSaveData",
"PaletteOverride": "MathNodeTitlePalette"
},
"{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
"$type": "GeometrySaveData",
"Position": [
1040.0,
320.0
]
},
"{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
"$type": "StylingComponentSaveData"
},
"{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
"$type": "PersistentIdComponentSaveData",
"PersistentId": "{71AB4748-41F4-4FEA-874C-F54037236F31}"
}
}
}
},
{
"Key": {
"id": 11897946242884
},
"Value": {
"ComponentData": {
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d674eac2070ed0028ceff1e84692c9cf1f69db2192c2295b6d714670ccd50308
size 8936
oid sha256:82a4ffbffeee43ea4ae293080e838bbc501fe9c7c20febc2e56e745451c95df3
size 9221
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8a2c9ad554554eba1021abe0baf0b3455da1aaab2bb8331eb240f79c89031636
size 5202
oid sha256:3ef03f338cd867860068b5bd343f66fa9bda4f3de1662badf552f05716da35ed
size 5161
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:982f085cfb17ce957cd1534e89a6fb5c76bcbe6936caec214ecd422b0a5dbe7b
size 5214
oid sha256:bf48b69c0cc2599581bd3d931d8adc6faba5d78d950c2c0946c19ec7ba7b6215
size 5190
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3361ba7aa2faa53421d8a92ed0bb2e1747e766d93c0315484a3c85b74a6494c3
size 8820
oid sha256:be53bb087c53874577dad8f1fa084698c27fbad827f0ed7dc50927fb4c9d8884
size 7748
@@ -12,6 +12,7 @@
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
@@ -63,6 +64,11 @@ namespace DisplaySettingsPythonBindingsUnitTests
m_app.Start(appDesc);
m_app.RegisterComponentDescriptor(AzToolsFramework::DisplaySettingsComponent::CreateDescriptor());
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
}
void TearDown() override
@@ -243,7 +243,7 @@ namespace AZ
if (StringFunc::StartsWith(curr->m_name, command, false))
{
AZLOG_INFO("- %s : %s\n", curr->m_name, curr->m_desc);
AZLOG_INFO("- %s : %s", curr->m_name, curr->m_desc);
if (commandSubset.size() < MaxConsoleCommandPlusArgsLength)
{
@@ -433,29 +433,29 @@ namespace AZ
{
if ((curr->GetFlags() & requiredSet) != requiredSet)
{
AZLOG_WARN("%s failed required set flag check\n", curr->m_name);
AZLOG_WARN("%s failed required set flag check", curr->m_name);
continue;
}
if ((curr->GetFlags() & requiredClear) != ConsoleFunctorFlags::Null)
{
AZLOG_WARN("%s failed required clear flag check\n", curr->m_name);
AZLOG_WARN("%s failed required clear flag check", curr->m_name);
continue;
}
if ((curr->GetFlags() & ConsoleFunctorFlags::IsCheat) != ConsoleFunctorFlags::Null)
{
AZLOG_WARN("%s is marked as a cheat\n", curr->m_name);
AZLOG_WARN("%s is marked as a cheat", curr->m_name);
}
if ((curr->GetFlags() & ConsoleFunctorFlags::IsDeprecated) != ConsoleFunctorFlags::Null)
{
AZLOG_WARN("%s is marked as deprecated\n", curr->m_name);
AZLOG_WARN("%s is marked as deprecated", curr->m_name);
}
if ((curr->GetFlags() & ConsoleFunctorFlags::NeedsReload) != ConsoleFunctorFlags::Null)
{
AZLOG_WARN("Changes to %s will only take effect after level reload\n", curr->m_name);
AZLOG_WARN("Changes to %s will only take effect after level reload", curr->m_name);
}
// Letting this intentionally fall-through, since in editor we can register common variables multiple times
@@ -468,7 +468,7 @@ namespace AZ
{
CVarFixedString value;
curr->GetValue(value);
AZLOG_INFO("> %s : %s\n", curr->GetName(), value.empty() ? "<empty>" : value.c_str());
AZLOG_INFO("> %s : %s", curr->GetName(), value.empty() ? "<empty>" : value.c_str());
}
flags = curr->GetFlags();
}
@@ -119,25 +119,21 @@ namespace AZ
void LoggerSystemComponent::LogInternalV(LogLevel level, const char* format, const char* file, const char* function, int32_t line, va_list args)
{
constexpr AZStd::size_t MaxLogBufferSize = 1000;
char buffer[MaxLogBufferSize];
auto buffer = AZStd::fixed_string<MaxLogBufferSize>::format_arg(format, args);
m_logEvent.Signal(level, buffer.c_str(), file, function, line);
buffer += '\n';
const AZStd::size_t length = azvsnprintf(buffer, MaxLogBufferSize, format, args);
buffer[AZStd::min<AZStd::size_t>(length + 1, MaxLogBufferSize - 1)] = '\0';
m_logEvent.Signal(level, buffer, file, function, line);
// Force a new-line before calling the AZ::Debug::Trace functions, as they assume a newline is present
buffer[AZStd::min<AZStd::size_t>(length + 1, MaxLogBufferSize - 2)] = '\n';
switch (level)
{
case LogLevel::Warn:
AZ_Warning("Logger", true, buffer);
AZ_Warning("Logger", true, buffer.c_str());
break;
case LogLevel::Error:
AZ_Error("Logger", true, buffer);
AZ_Error("Logger", true, buffer.c_str());
break;
default:
// Catch all else with trace
AZ::Debug::Trace::Output("Logger", buffer);
AZ::Debug::Trace::Output("Logger", buffer.c_str());
break;
}
}
@@ -14,7 +14,6 @@
#define AZ_TRAIT_UNIT_TEST_DILLER_TRIGGER_EVENT_COUNT 100000
#define AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_FRAMEPROFILER_TEST true
#define AZ_TRAIT_DISABLE_FAILED_FRAMEWORK_TESTS true
@@ -158,16 +158,16 @@ namespace AzToolsFramework
bool success = true;
AZStd::vector<char> fileBuffer;
const AZ::IO::Path workingPath{ dirToArchive };
const AZ::IO::FixedMaxPath workingPath{ dirToArchive };
for (const auto& fileName : foundFiles.GetValue())
{
bool thisSuccess = false;
AZ::IO::PathView relativePath = AZ::IO::PathView{ fileName }.LexicallyRelative(workingPath);
AZ::IO::FixedMaxPath relativePath = AZ::IO::FixedMaxPath{ fileName }.LexicallyRelative(workingPath);
AZ::IO::FixedMaxPath fullPath = (workingPath / relativePath);
AZ::IO::Path fullPath = (workingPath / relativePath);
if (ArchiveUtils::ReadFile(fullPath, AZ::IO::OpenMode::ModeRead, fileBuffer))
if (ArchiveUtils::ReadFile(static_cast<AZ::IO::PathView>(fullPath), AZ::IO::OpenMode::ModeRead, fileBuffer))
{
int result = archive->UpdateFile(
relativePath.Native(), fileBuffer.data(), fileBuffer.size(), s_compressionMethod,
@@ -11,6 +11,7 @@
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/Input/Buses/Notifications/InputChannelNotificationBus.h>
#include <AzFramework/Input/Buses/Notifications/InputTextNotificationBus.h>
#include <AzFramework/Input/Buses/Requests/InputChannelRequestBus.h>
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
@@ -24,6 +25,30 @@
namespace AzToolsFramework
{
static bool HandleTextEvent(QEvent::Type eventType, Qt::Key key, QString keyText, bool isAutoRepeat)
{
bool textConsumed = false;
if (key == Qt::Key_Backspace)
{
keyText = "\b";
}
if (!keyText.isEmpty())
{
// key events are first sent as shortcuts, if accepted they are then re-sent as traditional key
// down events. dispatching the key event as text during a shortcut (and auto-repeat press)
// ensures all printable keys a fair chance at being consumed before processing elsewhere
if (eventType == QEvent::Type::ShortcutOverride || (eventType == QEvent::Type::KeyPress && isAutoRepeat))
{
AzFramework::InputTextNotificationBus::Broadcast(
&AzFramework::InputTextNotifications::OnInputTextEvent, AZStd::string(keyText.toUtf8().data()), textConsumed);
}
}
return textConsumed;
}
void QtEventToAzInputMapper::InitializeKeyMappings()
{
// This assumes modifier keys (ctrl/shift/alt) map to the left control/shift/alt keys as Qt provides no way to disambiguate
@@ -194,6 +219,7 @@ namespace AzToolsFramework
// Install a global event filter to ensure we don't miss mouse and key release events.
QApplication::instance()->installEventFilter(this);
AzFramework::InputChannelNotificationBus::Handler::BusConnect();
}
bool QtEventToAzInputMapper::HandlesInputEvent(const AzFramework::InputChannel& channel) const
@@ -317,6 +343,19 @@ namespace AzToolsFramework
return false;
}
AZ::s32 QtEventToAzInputMapper::GetPriority() const
{
return AzFramework::InputChannelEventListener::GetPriorityLast();
}
void QtEventToAzInputMapper::OnInputChannelEvent(const AzFramework::InputChannel& inputChannel, bool& hasBeenConsumed)
{
if (m_enabled && hasBeenConsumed)
{
m_lastConsumedInputChannelIdCrc32 = inputChannel.GetInputChannelId().GetNameCrc32();
}
}
void QtEventToAzInputMapper::NotifyUpdateChannelIfNotIdle(const AzFramework::InputChannel* channel, QEvent* event)
{
if (channel->GetState() != AzFramework::InputChannel::State::Idle)
@@ -357,6 +396,9 @@ namespace AzToolsFramework
if (buttonChannel)
{
// reset the consumed event cache so the chain of calls from UpdateState below can properly update it, if necessary
m_lastConsumedInputChannelIdCrc32 = 0;
if (mouseEvent->type() != QEvent::Type::MouseButtonRelease)
{
buttonChannel->UpdateState(true);
@@ -366,7 +408,16 @@ namespace AzToolsFramework
buttonChannel->UpdateState(false);
}
NotifyUpdateChannelIfNotIdle(buttonChannel, mouseEvent);
if (m_lastConsumedInputChannelIdCrc32 == buttonChannel->GetInputChannelId().GetNameCrc32())
{
// a standard az-input handler consumed the event so mark it as such
mouseEvent->accept();
}
else
{
// only notify if not consumed elsewhere
NotifyUpdateChannelIfNotIdle(buttonChannel, mouseEvent);
}
}
}
}
@@ -408,16 +459,24 @@ namespace AzToolsFramework
void QtEventToAzInputMapper::HandleKeyEvent(QKeyEvent* keyEvent)
{
// Ignore key repeat events, they're unrelated to actual physical button presses.
const Qt::Key key = static_cast<Qt::Key>(keyEvent->key());
const QEvent::Type eventType = keyEvent->type();
// special handling for text events in edit mode
if (HandleTextEvent(eventType, key, keyEvent->text(), keyEvent->isAutoRepeat()))
{
keyEvent->accept();
return;
}
// Ignore key repeat events for non-text, they're unrelated to actual physical button presses.
if (keyEvent->isAutoRepeat())
{
return;
}
const Qt::Key key = static_cast<Qt::Key>(keyEvent->key());
// For ShortcutEvent, only continue processing if we're in the HighPriorityKeys set.
if (keyEvent->type() != QEvent::Type::ShortcutOverride || m_highPriorityKeys.find(key) != m_highPriorityKeys.end())
if (eventType != QEvent::Type::ShortcutOverride || m_highPriorityKeys.find(key) != m_highPriorityKeys.end())
{
if (auto keyIt = m_keyMappings.find(key); keyIt != m_keyMappings.end())
{
@@ -425,7 +484,7 @@ namespace AzToolsFramework
if (keyChannel)
{
if (keyEvent->type() == QEvent::Type::KeyPress || keyEvent->type() == QEvent::Type::ShortcutOverride)
if (eventType == QEvent::Type::KeyPress || eventType == QEvent::Type::ShortcutOverride)
{
keyChannel->UpdateState(true);
}
@@ -451,8 +510,22 @@ namespace AzToolsFramework
{
wheelAngle = angleDelta.y();
}
// reset the consumed event cache so the chain of calls from ProcessRawInputEvent below can properly update it, if necessary
m_lastConsumedInputChannelIdCrc32 = 0;
cursorZChannel->ProcessRawInputEvent(aznumeric_cast<float>(wheelAngle));
NotifyUpdateChannelIfNotIdle(cursorZChannel, wheelEvent);
if (m_lastConsumedInputChannelIdCrc32 == cursorZChannel->GetInputChannelId().GetNameCrc32())
{
// a standard az-input handler consumed the event so mark it as such
wheelEvent->accept();
}
else
{
// only notify if not consumed elsewhere
NotifyUpdateChannelIfNotIdle(cursorZChannel, wheelEvent);
}
}
void QtEventToAzInputMapper::ClearInputChannels(QEvent* event)
@@ -17,7 +17,7 @@
#include <AzFramework/Input/Channels/InputChannelDigitalWithSharedPosition2D.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Input/Events/InputChannelEventListener.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <QEvent>
@@ -34,7 +34,9 @@ namespace AzToolsFramework
{
//! Maps events from the Qt input system to synthetic InputChannels in AzFramework
//! that can be used by AzFramework::ViewportControllers.
class QtEventToAzInputMapper final : public QObject
class QtEventToAzInputMapper final
: public QObject
, public AzFramework::InputChannelNotificationBus::Handler
{
Q_OBJECT
@@ -69,6 +71,11 @@ namespace AzToolsFramework
//! \param event The underlying Qt event that triggered this change, if applicable.
void InputChannelUpdated(const AzFramework::InputChannel* channel, QEvent* event);
protected:
// AzFramework::InputChannelNotificationBus overrides ...
AZ::s32 GetPriority() const override;
void OnInputChannelEvent(const AzFramework::InputChannel& inputChannel, bool& hasBeenConsumed) override;
private:
// Gets an input channel of the specified type by ID.
template<class TInputChannel>
@@ -161,6 +168,8 @@ namespace AzToolsFramework
AZStd::unordered_set<Qt::Key> m_highPriorityKeys;
// A lookup table for AZ input channel ID -> physical input channel on our mouse or keyboard device.
AZStd::unordered_map<AzFramework::InputChannelId, AzFramework::InputChannel*> m_channels;
// The crc32 of the last consumed input event's channel id.
AZ::Crc32 m_lastConsumedInputChannelIdCrc32 = 0;
// Where the mouse cursor was at the last cursor event.
QPoint m_previousGlobalCursorPosition;
// The source widget to map events from, used to calculate the relative mouse position within the widget bounds.
@@ -150,7 +150,7 @@ namespace AzToolsFramework
return result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success;
}
// some assets may come in from the JSON serialzier with no AssetID, but have an asset hint
// some assets may come in from the JSON serializer with no AssetID, but have an asset hint
// this attempts to fix up the assets using the assetHint field
void FixUpInvalidAssets(AZ::Data::Asset<AZ::Data::AssetData>& asset)
{
@@ -56,6 +56,40 @@ namespace UnitTest
QApplication::sendEvent(widget, &mouseMoveEvent);
}
void MouseScroll(QWidget* widget, QPoint localEventPosition, QPoint wheelDelta,
Qt::MouseButtons mouseButtons, Qt::KeyboardModifiers keyboardModifiers)
{
const QPoint globalEventPos = widget->mapToGlobal(localEventPosition);
const QPoint zero = QPoint();
QWheelEvent wheelEventBegin(globalEventPos, zero, zero, wheelDelta, mouseButtons, keyboardModifiers, Qt::ScrollBegin, false);
QApplication::sendEvent(widget, &wheelEventBegin);
QWheelEvent wheelEventUpdate(globalEventPos, zero, zero, wheelDelta, mouseButtons, keyboardModifiers, Qt::ScrollUpdate, false);
QApplication::sendEvent(widget, &wheelEventUpdate);
QWheelEvent wheelEventEnd(globalEventPos, zero, zero, zero, mouseButtons, keyboardModifiers, Qt::ScrollEnd, false);
QApplication::sendEvent(widget, &wheelEventEnd);
}
AZStd::string QtKeyToAzString(Qt::Key key, Qt::KeyboardModifiers modifiers)
{
QKeySequence keySequence = QKeySequence(key);
QString keyText = keySequence.toString();
// QKeySequence seems to uppercase alpha keys regardless of shift-modifier
if (modifiers == Qt::NoModifier && keyText.isUpper())
{
keyText = keyText.toLower();
}
else if (modifiers != Qt::ShiftModifier)
{
keyText = QString();
}
return AZStd::string(keyText.toUtf8().data());
}
bool TestWidget::eventFilter(QObject* watched, QEvent* event)
{
AZ_UNUSED(watched);
@@ -77,6 +77,20 @@ namespace UnitTest
/// @param mouseButton The button to be held during the move.
void MouseMove(QWidget* widget, const QPoint& initialPosition, const QPoint& mouseDelta, Qt::MouseButton mouseButton = Qt::NoButton);
/// Performs a full series (begin, update, end) of mouse wheel events on the provided widget.
/// @param widget The widget to perform the mouse wheel events on.
/// @param localEventPosition The position of the mouse relative to the widget (will be remapped to a global position internally).
/// @param wheelDelta How far to move the mouse (note: mouseDelta may be zero and the mouse will only be moved to initialPosition).
/// @param mouseButtons Optional mouse buttons to include during the wheel events, defaults to Qt::NoButton
/// @param keyboardModifiers Optional keyboard modifiers to include during the wheel events, defaults to Qt::NoModifier
void MouseScroll(QWidget* widget, QPoint localEventPosition, QPoint wheelDelta,
Qt::MouseButtons mouseButtons = Qt::NoButton, Qt::KeyboardModifiers keyboardModifiers = Qt::NoModifier);
/// Convert a Qt::Key + optional modifiers to the printable text of the key sequence
/// @param key The widget to perform the mouse wheel event on.
/// @param modifiers Optional keyboard modifiers to include during the wheel events, defaults to Qt::NoModifier
AZStd::string QtKeyToAzString(Qt::Key key, Qt::KeyboardModifiers modifiers = Qt::NoModifier);
/// Test widget to store QActions generated by EditorTransformComponentSelection.
class TestWidget : public QWidget
{
@@ -2504,6 +2504,29 @@ namespace AzToolsFramework
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
// do not create manipulators for the container entity of the focused prefab.
if (auto prefabFocusPublicInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabFocusPublicInterface>::Get())
{
AzFramework::EntityContextId editorEntityContextId = GetEntityContextId();
if (AZ::EntityId focusRoot = prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
focusRoot.IsValid())
{
m_selectedEntityIds.erase(focusRoot);
}
}
// do not create manipulators for any entities marked as read only
if (auto readOnlyEntityPublicInterface = AZ::Interface<ReadOnlyEntityPublicInterface>::Get())
{
AZStd::erase_if(
m_selectedEntityIds,
[readOnlyEntityPublicInterface](auto entityId)
{
return readOnlyEntityPublicInterface->IsReadOnly(entityId);
}
);
}
// note: create/destroy pattern to be addressed
DestroyManipulators(m_entityIdManipulators);
CreateEntityIdManipulators();
@@ -3586,10 +3609,9 @@ namespace AzToolsFramework
debugDisplay.SetLineWidth(1.0f);
const float labelOffset = ed_viewportGizmoAxisLabelOffset;
const float screenScale = GetScreenDisplayScaling(viewportId);
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize;
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize;
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize;
// draw the label of of each axis for the gizmo
const float labelSize = ed_viewportGizmoAxisLabelSize;
@@ -3636,29 +3658,6 @@ namespace AzToolsFramework
m_selectedEntityIds.clear();
m_selectedEntityIds.reserve(selectedEntityIds.size());
AZStd::copy(selectedEntityIds.begin(), selectedEntityIds.end(), AZStd::inserter(m_selectedEntityIds, m_selectedEntityIds.end()));
// Do not create manipulators for the container entity of the focused prefab.
if (auto prefabFocusPublicInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabFocusPublicInterface>::Get())
{
AzFramework::EntityContextId editorEntityContextId = GetEntityContextId();
if (AZ::EntityId focusRoot = prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
focusRoot.IsValid())
{
m_selectedEntityIds.erase(focusRoot);
}
}
// Do not create manipulators for any entities marked as read only
if (auto readOnlyEntityPublicInterface = AZ::Interface<ReadOnlyEntityPublicInterface>::Get())
{
AZStd::erase_if(
m_selectedEntityIds,
[readOnlyEntityPublicInterface](auto entityId)
{
return readOnlyEntityPublicInterface->IsReadOnly(entityId);
}
);
}
}
void EditorTransformComponentSelection::OnTransformChanged(
@@ -69,12 +69,12 @@ namespace UnitTest
QString GetArchiveFolderName()
{
return "Archive";
return "archive";
}
QString GetExtractFolderName()
{
return "Extracted";
return "extracted";
}
void CreateArchiveFolder(QString archiveFolderName, QStringList fileList)
@@ -90,7 +90,7 @@ namespace UnitTest
QString CreateArchiveListTextFile()
{
QString listFilePath = QDir(m_tempDir.GetDirectory()).absoluteFilePath("FileList.txt");
QString listFilePath = QDir(m_tempDir.GetDirectory()).absoluteFilePath("filelist.txt");
QString textContent = CreateArchiveFileList().join("\n");
EXPECT_TRUE(CreateDummyFile(listFilePath, textContent));
return listFilePath;
@@ -151,11 +151,7 @@ namespace UnitTest
UnitTest::ScopedTemporaryDirectory m_tempDir;
};
#if AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
TEST_F(ArchiveComponentTest, DISABLED_CreateArchive_FilesAtThreeDepths_ArchiveCreated)
#else
TEST_F(ArchiveComponentTest, CreateArchive_FilesAtThreeDepths_ArchiveCreated)
#endif // AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
{
EXPECT_TRUE(m_tempDir.IsValid());
CreateArchiveFolder();
@@ -167,11 +163,7 @@ namespace UnitTest
EXPECT_TRUE(createResult);
}
#if AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
TEST_F(ArchiveComponentTest, DISABLED_ListFilesInArchive_FilesAtThreeDepths_FilesFound)
#else
TEST_F(ArchiveComponentTest, ListFilesInArchive_FilesAtThreeDepths_FilesFound)
#endif // AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
{
EXPECT_TRUE(m_tempDir.IsValid());
CreateArchiveFolder();
@@ -190,11 +182,7 @@ namespace UnitTest
EXPECT_EQ(fileList.size(), 6);
}
#if AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
TEST_F(ArchiveComponentTest, DISABLED_CreateDeltaCatalog_AssetsNotRegistered_Failure)
#else
TEST_F(ArchiveComponentTest, CreateDeltaCatalog_AssetsNotRegistered_Failure)
#endif // AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
{
QStringList fileList = CreateArchiveFileList();
@@ -213,11 +201,7 @@ namespace UnitTest
EXPECT_EQ(catalogCreated, false);
}
#if AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
TEST_F(ArchiveComponentTest, DISABLED_AddFilesToArchive_FromListFile_Success)
#else
TEST_F(ArchiveComponentTest, AddFilesToArchive_FromListFile_Success)
#endif // AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
{
QString listFile = CreateArchiveListTextFile();
CreateArchiveFolder(GetArchiveFolderName(), CreateArchiveFileList());
@@ -233,11 +217,7 @@ namespace UnitTest
EXPECT_TRUE(result);
}
#if AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
TEST_F(ArchiveComponentTest, DISABLED_ExtractArchive_AllFiles_Success)
#else
TEST_F(ArchiveComponentTest, ExtractArchive_AllFiles_Success)
#endif // AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
{
CreateArchiveFolder();
AZ_TEST_START_TRACE_SUPPRESSION;
@@ -264,11 +244,7 @@ namespace UnitTest
}
}
#if AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
TEST_F(ArchiveComponentTest, DISABLED_CreateDeltaCatalog_ArchiveWithoutCatalogAssetsRegistered_Success)
#else
TEST_F(ArchiveComponentTest, CreateDeltaCatalog_ArchiveWithoutCatalogAssetsRegistered_Success)
#endif // AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
{
QStringList fileList = CreateArchiveFileList();
@@ -0,0 +1,515 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzToolsFramework/Input/QtEventToAzInputMapper.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <AzFramework/Input/Events/InputTextEventListener.h>
namespace UnitTest
{
static bool IsMouseButton(const AzFramework::InputChannelId& inputChannelId)
{
const auto& buttons = AzFramework::InputDeviceMouse::Button::All;
const auto& it = AZStd::find(buttons.cbegin(), buttons.cend(), inputChannelId);
return it != buttons.cend();
}
class QtEventToAzInputMapperFixture
: public AllocatorsTestFixture
, public AzFramework::InputChannelNotificationBus::Handler
, public AzFramework::InputTextNotificationBus::Handler
{
public:
static inline constexpr QSize WidgetSize = QSize(1920, 1080);
static inline constexpr int TestDeviceIdSeed = 4321;
void SetUp() override
{
AllocatorsTestFixture::SetUp();
m_rootWidget = AZStd::make_unique<QWidget>();
m_rootWidget->setFixedSize(WidgetSize);
m_rootWidget->move(0, 0);
m_inputChannelMapper = AZStd::make_unique<AzToolsFramework::QtEventToAzInputMapper>(m_rootWidget.get(), TestDeviceIdSeed);
// listen for events signaled from QtEventToAzInputMapper and forward to the controller list
QObject::connect(m_inputChannelMapper.get(), &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, m_rootWidget.get(),
[this]([[maybe_unused]] const AzFramework::InputChannel* inputChannel, QEvent* event)
{
const QEvent::Type eventType = event->type();
if (eventType == QEvent::Type::MouseButtonPress ||
eventType == QEvent::Type::MouseButtonRelease ||
eventType == QEvent::Type::MouseButtonDblClick)
{
m_signalEvents.push_back(QtEventInfo(static_cast<QMouseEvent*>(event)));
event->accept();
}
else if (eventType == QEvent::Type::Wheel)
{
m_signalEvents.push_back(QtEventInfo(static_cast<QWheelEvent*>(event)));
event->accept();
}
else if (eventType == QEvent::Type::KeyPress ||
eventType == QEvent::Type::KeyRelease ||
eventType == QEvent::Type::ShortcutOverride)
{
m_signalEvents.push_back(QtEventInfo(static_cast<QKeyEvent*>(event)));
event->accept();
}
});
}
void TearDown() override
{
m_inputChannelMapper.reset();
m_rootWidget.reset();
AllocatorsTestFixture::TearDown();
}
void OnInputChannelEvent(const AzFramework::InputChannel& inputChannel, bool& hasBeenConsumed) override
{
AZ_Assert(hasBeenConsumed == false, "Unexpected input event consumed elsewhere during QtEventToAzInputMapper tests");
const AzFramework::InputChannelId& inputChannelId = inputChannel.GetInputChannelId();
const AzFramework::InputDeviceId& inputDeviceId = inputChannel.GetInputDevice().GetInputDeviceId();
if (AzFramework::InputDeviceMouse::IsMouseDevice(inputDeviceId))
{
if (IsMouseButton(inputChannelId))
{
m_azChannelEvents.push_back(AzEventInfo(inputChannel));
hasBeenConsumed = m_captureAzEvents;
}
else if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Z)
{
m_azChannelEvents.push_back(AzEventInfo(inputChannel));
hasBeenConsumed = m_captureAzEvents;
}
}
else if (AzFramework::InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId))
{
m_azChannelEvents.push_back(AzEventInfo(inputChannel));
hasBeenConsumed = m_captureAzEvents;
}
}
void OnInputTextEvent(const AZStd::string& textUtf8, bool& hasBeenConsumed) override
{
AZ_Assert(hasBeenConsumed == false, "Unexpected text event consumed elsewhere during QtEventToAzInputMapper tests");
m_azTextEvents.push_back(textUtf8);
hasBeenConsumed = m_captureTextEvents;
}
// simple structure for caching minimal QtEvent data necessary for testing
struct QtEventInfo
{
explicit QtEventInfo(QMouseEvent* mouseEvent)
: m_eventType(mouseEvent->type())
, m_button(mouseEvent->button())
{
}
explicit QtEventInfo(QWheelEvent* mouseWheelEvent)
: m_eventType(mouseWheelEvent->type())
, m_scrollPhase(mouseWheelEvent->phase())
{
}
explicit QtEventInfo(QKeyEvent* keyEvent)
: m_eventType(keyEvent->type())
, m_key(keyEvent->key())
{
}
QEvent::Type m_eventType{ QEvent::None };
Qt::MouseButton m_button{ Qt::NoButton };
Qt::ScrollPhase m_scrollPhase{ Qt::NoScrollPhase };
int m_key{ 0 };
};
// simple structure for caching minimal AzInput event data necessary for testing
struct AzEventInfo
{
AzEventInfo() = delete;
explicit AzEventInfo(const AzFramework::InputChannel& inputChannel)
: m_inputChannelId(inputChannel.GetInputChannelId())
, m_isActive(inputChannel.IsActive())
{
}
AzFramework::InputChannelId m_inputChannelId;
bool m_isActive;
};
AZStd::unique_ptr<QWidget> m_rootWidget;
AZStd::unique_ptr<AzToolsFramework::QtEventToAzInputMapper> m_inputChannelMapper;
AZStd::vector<QtEventInfo> m_signalEvents;
AZStd::vector<AzEventInfo> m_azChannelEvents;
AZStd::vector<AZStd::string> m_azTextEvents;
bool m_captureAzEvents{ false };
bool m_captureTextEvents{ false };
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Qt event forwarding through the internal signal handler test
TEST_F(QtEventToAzInputMapperFixture, MouseWheel_NoAzHandlers_ReceivedThreeSignalAndZeroAzChannelEvents)
{
// setup
const QPoint mouseEventPos = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
const QPoint scrollDelta = QPoint(10, 10);
MouseScroll(m_rootWidget.get(), mouseEventPos, scrollDelta);
// qt validation
ASSERT_EQ(m_signalEvents.size(), 3);
EXPECT_EQ(m_signalEvents[0].m_eventType, QEvent::Type::Wheel);
EXPECT_EQ(m_signalEvents[0].m_scrollPhase, Qt::ScrollBegin);
EXPECT_EQ(m_signalEvents[1].m_eventType, QEvent::Type::Wheel);
EXPECT_EQ(m_signalEvents[1].m_scrollPhase, Qt::ScrollUpdate);
EXPECT_EQ(m_signalEvents[2].m_eventType, QEvent::Type::Wheel);
EXPECT_EQ(m_signalEvents[2].m_scrollPhase, Qt::ScrollEnd);
// az validation
EXPECT_EQ(m_azChannelEvents.size(), 0);
}
// Qt event to AzInput event conversion test
TEST_F(QtEventToAzInputMapperFixture, MouseWheel_AzHandlerNotCaptured_ReceivedThreeSignalAndThreeAzChannelEvents)
{
// setup
const AzFramework::InputChannelId mouseWheelId = AzFramework::InputDeviceMouse::Movement::Z;
const char* mouseWheelChannelName = mouseWheelId.GetName();
AzFramework::InputChannelNotificationBus::Handler::BusConnect();
m_captureAzEvents = false;
const QPoint mouseEventPos = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
const QPoint scrollDelta = QPoint(10, 10);
MouseScroll(m_rootWidget.get(), mouseEventPos, scrollDelta);
// qt validation
ASSERT_EQ(m_signalEvents.size(), 3);
EXPECT_EQ(m_signalEvents[0].m_eventType, QEvent::Type::Wheel);
EXPECT_EQ(m_signalEvents[0].m_scrollPhase, Qt::ScrollBegin);
EXPECT_EQ(m_signalEvents[1].m_eventType, QEvent::Type::Wheel);
EXPECT_EQ(m_signalEvents[1].m_scrollPhase, Qt::ScrollUpdate);
EXPECT_EQ(m_signalEvents[2].m_eventType, QEvent::Type::Wheel);
EXPECT_EQ(m_signalEvents[2].m_scrollPhase, Qt::ScrollEnd);
// az validation
ASSERT_EQ(m_azChannelEvents.size(), 3);
EXPECT_STREQ(m_azChannelEvents[0].m_inputChannelId.GetName(), mouseWheelChannelName);
EXPECT_STREQ(m_azChannelEvents[1].m_inputChannelId.GetName(), mouseWheelChannelName);
EXPECT_STREQ(m_azChannelEvents[2].m_inputChannelId.GetName(), mouseWheelChannelName);
// cleanup
AzFramework::InputChannelNotificationBus::Handler::BusDisconnect();
}
// AzInput event handler consumption test
TEST_F(QtEventToAzInputMapperFixture, MouseWheel_AzHandlerCaptured_ReceivedZeroSignalAndThreeAzChannelEvents)
{
// setup
const AzFramework::InputChannelId mouseWheelId = AzFramework::InputDeviceMouse::Movement::Z;
const char* mouseWheelChannelName = mouseWheelId.GetName();
AzFramework::InputChannelNotificationBus::Handler::BusConnect();
m_captureAzEvents = true;
const QPoint mouseEventPos = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
const QPoint scrollDelta = QPoint(10, 10);
MouseScroll(m_rootWidget.get(), mouseEventPos, scrollDelta);
// qt validation
EXPECT_EQ(m_signalEvents.size(), 0);
// az validation
ASSERT_EQ(m_azChannelEvents.size(), 3);
EXPECT_STREQ(m_azChannelEvents[0].m_inputChannelId.GetName(), mouseWheelChannelName);
EXPECT_STREQ(m_azChannelEvents[1].m_inputChannelId.GetName(), mouseWheelChannelName);
EXPECT_STREQ(m_azChannelEvents[2].m_inputChannelId.GetName(), mouseWheelChannelName);
// cleanup
AzFramework::InputChannelNotificationBus::Handler::BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct MouseButtonIdsParam
{
Qt::MouseButton m_qt;
AzFramework::InputChannelId m_az;
};
class MouseButtonParamQtEventToAzInputMapperFixture
: public QtEventToAzInputMapperFixture
, public ::testing::WithParamInterface<MouseButtonIdsParam>
{
};
// Qt event forwarding through the internal signal handler test
TEST_P(MouseButtonParamQtEventToAzInputMapperFixture, MouseClick_NoAzHandlers_ReceivedTwoSignalAndZeroAzChannelEvents)
{
// setup
const MouseButtonIdsParam mouseButtonIds = GetParam();
const QPoint mouseEventPos = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
QTest::mouseClick(m_rootWidget.get(), mouseButtonIds.m_qt, Qt::NoModifier, mouseEventPos);
// qt validation
ASSERT_EQ(m_signalEvents.size(), 2);
EXPECT_EQ(m_signalEvents[0].m_eventType, QEvent::Type::MouseButtonPress);
EXPECT_EQ(m_signalEvents[0].m_button, mouseButtonIds.m_qt);
EXPECT_EQ(m_signalEvents[1].m_eventType, QEvent::Type::MouseButtonRelease);
EXPECT_EQ(m_signalEvents[1].m_button, mouseButtonIds.m_qt);
// az validation
EXPECT_EQ(m_azChannelEvents.size(), 0);
}
// Qt event to AzInput event conversion test
TEST_P(MouseButtonParamQtEventToAzInputMapperFixture, MouseClick_AzHandlerNotCaptured_ReceivedTwoSignalAndTwoAzChannelEvents)
{
// setup
const MouseButtonIdsParam mouseButtonIds = GetParam();
AzFramework::InputChannelNotificationBus::Handler::BusConnect();
m_captureAzEvents = false;
const QPoint mouseEventPos = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
QTest::mouseClick(m_rootWidget.get(), mouseButtonIds.m_qt, Qt::NoModifier, mouseEventPos);
// qt validation
ASSERT_EQ(m_signalEvents.size(), 2);
EXPECT_EQ(m_signalEvents[0].m_eventType, QEvent::Type::MouseButtonPress);
EXPECT_EQ(m_signalEvents[0].m_button, mouseButtonIds.m_qt);
EXPECT_EQ(m_signalEvents[1].m_eventType, QEvent::Type::MouseButtonRelease);
EXPECT_EQ(m_signalEvents[1].m_button, mouseButtonIds.m_qt);
// az validation
ASSERT_EQ(m_azChannelEvents.size(), 2);
EXPECT_STREQ(m_azChannelEvents[0].m_inputChannelId.GetName(), mouseButtonIds.m_az.GetName());
EXPECT_TRUE(m_azChannelEvents[0].m_isActive);
EXPECT_STREQ(m_azChannelEvents[1].m_inputChannelId.GetName(), mouseButtonIds.m_az.GetName());
EXPECT_FALSE(m_azChannelEvents[1].m_isActive);
// cleanup
AzFramework::InputChannelNotificationBus::Handler::BusDisconnect();
}
// AzInput event handler consumption test
TEST_P(MouseButtonParamQtEventToAzInputMapperFixture, MouseClick_AzHandlerCaptured_ReceivedZeroSignalAndTwoAzChannelEvents)
{
// setup
const MouseButtonIdsParam mouseButtonIds = GetParam();
AzFramework::InputChannelNotificationBus::Handler::BusConnect();
m_captureAzEvents = true;
const QPoint mouseEventPos = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
QTest::mouseClick(m_rootWidget.get(), mouseButtonIds.m_qt, Qt::NoModifier, mouseEventPos);
// qt validation
EXPECT_EQ(m_signalEvents.size(), 0);
// az validation
ASSERT_EQ(m_azChannelEvents.size(), 2);
EXPECT_STREQ(m_azChannelEvents[0].m_inputChannelId.GetName(), mouseButtonIds.m_az.GetName());
EXPECT_TRUE(m_azChannelEvents[0].m_isActive);
EXPECT_STREQ(m_azChannelEvents[1].m_inputChannelId.GetName(), mouseButtonIds.m_az.GetName());
EXPECT_FALSE(m_azChannelEvents[1].m_isActive);
// cleanup
AzFramework::InputChannelNotificationBus::Handler::BusDisconnect();
}
INSTANTIATE_TEST_CASE_P(All, MouseButtonParamQtEventToAzInputMapperFixture,
testing::Values(
MouseButtonIdsParam{ Qt::MouseButton::LeftButton, AzFramework::InputDeviceMouse::Button::Left },
MouseButtonIdsParam{ Qt::MouseButton::RightButton, AzFramework::InputDeviceMouse::Button::Right },
MouseButtonIdsParam{ Qt::MouseButton::MiddleButton, AzFramework::InputDeviceMouse::Button::Middle }
),
[](const ::testing::TestParamInfo<MouseButtonIdsParam>& info)
{
return info.param.m_az.GetName();
}
);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct KeyEventIdsParam
{
Qt::Key m_qt;
AzFramework::InputChannelId m_az;
};
class PrintableKeyEventParamQtEventToAzInputMapperFixture
: public QtEventToAzInputMapperFixture
, public ::testing::WithParamInterface<KeyEventIdsParam>
{
};
// Qt event forwarding through the internal signal handler test
TEST_P(PrintableKeyEventParamQtEventToAzInputMapperFixture, KeyClick_NoAzHandlers_ReceivedTwoSignalAndZeroAzEvents)
{
// setup
const KeyEventIdsParam keyEventIds = GetParam();
const Qt::KeyboardModifiers modifiers = Qt::NoModifier;
QTest::keyClick(m_rootWidget.get(), keyEventIds.m_qt, modifiers);
// qt validation
ASSERT_EQ(m_signalEvents.size(), 2);
EXPECT_EQ(m_signalEvents[0].m_eventType, QEvent::Type::KeyPress);
EXPECT_EQ(m_signalEvents[0].m_key, keyEventIds.m_qt);
EXPECT_EQ(m_signalEvents[1].m_eventType, QEvent::Type::KeyRelease);
EXPECT_EQ(m_signalEvents[1].m_key, keyEventIds.m_qt);
// az validation
EXPECT_EQ(m_azChannelEvents.size(), 0);
EXPECT_EQ(m_azTextEvents.size(), 0);
}
// Qt event to AzInput event conversion test
TEST_P(PrintableKeyEventParamQtEventToAzInputMapperFixture, KeyClick_AzHandlersNotCaptured_ReceivedTwoSignalAndThreeAzEvents)
{
// setup
const KeyEventIdsParam keyEventIds = GetParam();
const Qt::KeyboardModifiers modifiers = Qt::NoModifier;
AZStd::string keyAsText = QtKeyToAzString(keyEventIds.m_qt, modifiers);
AzFramework::InputChannelNotificationBus::Handler::BusConnect();
m_captureAzEvents = false;
AzFramework::InputTextNotificationBus::Handler::BusConnect();
m_captureAzEvents = false;
QTest::keyClick(m_rootWidget.get(), keyEventIds.m_qt, modifiers);
// qt validation
ASSERT_EQ(m_signalEvents.size(), 2);
EXPECT_EQ(m_signalEvents[0].m_eventType, QEvent::Type::KeyPress);
EXPECT_EQ(m_signalEvents[0].m_key, keyEventIds.m_qt);
EXPECT_EQ(m_signalEvents[1].m_eventType, QEvent::Type::KeyRelease);
EXPECT_EQ(m_signalEvents[1].m_key, keyEventIds.m_qt);
// az validation
ASSERT_EQ(m_azTextEvents.size(), 1);
EXPECT_STREQ(m_azTextEvents[0].c_str(), keyAsText.c_str());
ASSERT_EQ(m_azChannelEvents.size(), 2);
EXPECT_STREQ(m_azChannelEvents[0].m_inputChannelId.GetName(), keyEventIds.m_az.GetName());
EXPECT_TRUE(m_azChannelEvents[0].m_isActive);
EXPECT_STREQ(m_azChannelEvents[1].m_inputChannelId.GetName(), keyEventIds.m_az.GetName());
EXPECT_FALSE(m_azChannelEvents[1].m_isActive);
// cleanup
AzFramework::InputTextNotificationBus::Handler::BusDisconnect();
AzFramework::InputChannelNotificationBus::Handler::BusDisconnect();
}
INSTANTIATE_TEST_CASE_P(All, PrintableKeyEventParamQtEventToAzInputMapperFixture,
testing::Values(
KeyEventIdsParam{ Qt::Key_0, AzFramework::InputDeviceKeyboard::Key::Alphanumeric0 },
KeyEventIdsParam{ Qt::Key_1, AzFramework::InputDeviceKeyboard::Key::Alphanumeric1 },
KeyEventIdsParam{ Qt::Key_2, AzFramework::InputDeviceKeyboard::Key::Alphanumeric2 },
KeyEventIdsParam{ Qt::Key_3, AzFramework::InputDeviceKeyboard::Key::Alphanumeric3 },
KeyEventIdsParam{ Qt::Key_4, AzFramework::InputDeviceKeyboard::Key::Alphanumeric4 },
KeyEventIdsParam{ Qt::Key_5, AzFramework::InputDeviceKeyboard::Key::Alphanumeric5 },
KeyEventIdsParam{ Qt::Key_6, AzFramework::InputDeviceKeyboard::Key::Alphanumeric6 },
KeyEventIdsParam{ Qt::Key_7, AzFramework::InputDeviceKeyboard::Key::Alphanumeric7 },
KeyEventIdsParam{ Qt::Key_8, AzFramework::InputDeviceKeyboard::Key::Alphanumeric8 },
KeyEventIdsParam{ Qt::Key_9, AzFramework::InputDeviceKeyboard::Key::Alphanumeric9 },
KeyEventIdsParam{ Qt::Key_A, AzFramework::InputDeviceKeyboard::Key::AlphanumericA },
KeyEventIdsParam{ Qt::Key_B, AzFramework::InputDeviceKeyboard::Key::AlphanumericB },
KeyEventIdsParam{ Qt::Key_C, AzFramework::InputDeviceKeyboard::Key::AlphanumericC },
KeyEventIdsParam{ Qt::Key_D, AzFramework::InputDeviceKeyboard::Key::AlphanumericD },
KeyEventIdsParam{ Qt::Key_E, AzFramework::InputDeviceKeyboard::Key::AlphanumericE },
KeyEventIdsParam{ Qt::Key_F, AzFramework::InputDeviceKeyboard::Key::AlphanumericF },
KeyEventIdsParam{ Qt::Key_G, AzFramework::InputDeviceKeyboard::Key::AlphanumericG },
KeyEventIdsParam{ Qt::Key_H, AzFramework::InputDeviceKeyboard::Key::AlphanumericH },
KeyEventIdsParam{ Qt::Key_I, AzFramework::InputDeviceKeyboard::Key::AlphanumericI },
KeyEventIdsParam{ Qt::Key_J, AzFramework::InputDeviceKeyboard::Key::AlphanumericJ },
KeyEventIdsParam{ Qt::Key_K, AzFramework::InputDeviceKeyboard::Key::AlphanumericK },
KeyEventIdsParam{ Qt::Key_L, AzFramework::InputDeviceKeyboard::Key::AlphanumericL },
KeyEventIdsParam{ Qt::Key_M, AzFramework::InputDeviceKeyboard::Key::AlphanumericM },
KeyEventIdsParam{ Qt::Key_N, AzFramework::InputDeviceKeyboard::Key::AlphanumericN },
KeyEventIdsParam{ Qt::Key_O, AzFramework::InputDeviceKeyboard::Key::AlphanumericO },
KeyEventIdsParam{ Qt::Key_P, AzFramework::InputDeviceKeyboard::Key::AlphanumericP },
KeyEventIdsParam{ Qt::Key_Q, AzFramework::InputDeviceKeyboard::Key::AlphanumericQ },
KeyEventIdsParam{ Qt::Key_R, AzFramework::InputDeviceKeyboard::Key::AlphanumericR },
KeyEventIdsParam{ Qt::Key_S, AzFramework::InputDeviceKeyboard::Key::AlphanumericS },
KeyEventIdsParam{ Qt::Key_T, AzFramework::InputDeviceKeyboard::Key::AlphanumericT },
KeyEventIdsParam{ Qt::Key_U, AzFramework::InputDeviceKeyboard::Key::AlphanumericU },
KeyEventIdsParam{ Qt::Key_V, AzFramework::InputDeviceKeyboard::Key::AlphanumericV },
KeyEventIdsParam{ Qt::Key_W, AzFramework::InputDeviceKeyboard::Key::AlphanumericW },
KeyEventIdsParam{ Qt::Key_X, AzFramework::InputDeviceKeyboard::Key::AlphanumericX },
KeyEventIdsParam{ Qt::Key_Y, AzFramework::InputDeviceKeyboard::Key::AlphanumericY },
KeyEventIdsParam{ Qt::Key_Z, AzFramework::InputDeviceKeyboard::Key::AlphanumericZ },
// these may need to be special cased due to the printable text conversion
//KeyEventIdsParam{ Qt::Key_Space, AzFramework::InputDeviceKeyboard::Key::EditSpace },
//KeyEventIdsParam{ Qt::Key_Tab, AzFramework::InputDeviceKeyboard::Key::EditTab },
KeyEventIdsParam{ Qt::Key_Apostrophe, AzFramework::InputDeviceKeyboard::Key::PunctuationApostrophe },
KeyEventIdsParam{ Qt::Key_Backslash, AzFramework::InputDeviceKeyboard::Key::PunctuationBackslash },
KeyEventIdsParam{ Qt::Key_BracketLeft, AzFramework::InputDeviceKeyboard::Key::PunctuationBracketL },
KeyEventIdsParam{ Qt::Key_BracketRight, AzFramework::InputDeviceKeyboard::Key::PunctuationBracketR },
KeyEventIdsParam{ Qt::Key_Comma, AzFramework::InputDeviceKeyboard::Key::PunctuationComma },
KeyEventIdsParam{ Qt::Key_Equal, AzFramework::InputDeviceKeyboard::Key::PunctuationEquals },
KeyEventIdsParam{ Qt::Key_hyphen, AzFramework::InputDeviceKeyboard::Key::PunctuationHyphen },
KeyEventIdsParam{ Qt::Key_Period, AzFramework::InputDeviceKeyboard::Key::PunctuationPeriod },
KeyEventIdsParam{ Qt::Key_Semicolon, AzFramework::InputDeviceKeyboard::Key::PunctuationSemicolon },
KeyEventIdsParam{ Qt::Key_Slash, AzFramework::InputDeviceKeyboard::Key::PunctuationSlash },
KeyEventIdsParam{ Qt::Key_QuoteLeft, AzFramework::InputDeviceKeyboard::Key::PunctuationTilde }
),
[](const ::testing::TestParamInfo<KeyEventIdsParam>& info)
{
return info.param.m_az.GetName();
}
);
} // namespace UnitTest
+10 -6
View File
@@ -6,18 +6,17 @@
*
*/
#include <AzTest/AzTest.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/ComponentApplicationLifecycle.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzTest/AzTest.h>
#include <QApplication>
using namespace AZ;
// Handle asserts
class ToolsFrameworkHook
: public AZ::Test::ITestEnvironment
@@ -25,12 +24,12 @@ class ToolsFrameworkHook
public:
void SetupEnvironment() override
{
AllocatorInstance<SystemAllocator>::Create();
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
}
void TeardownEnvironment() override
{
AllocatorInstance<SystemAllocator>::Destroy();
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
}
};
@@ -38,12 +37,17 @@ AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv)
{
::testing::InitGoogleMock(&argc, argv);
QApplication app(argc, argv);
auto styleManager = AZStd::make_unique< AzQtComponents::StyleManager>(&app);
auto styleManager = AZStd::make_unique<AzQtComponents::StyleManager>(&app);
AZ::IO::FixedMaxPath engineRootPath;
{
AZ::ComponentApplication componentApplication(argc, argv);
auto settingsRegistry = AZ::SettingsRegistry::Get();
settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
AZ::ComponentApplicationLifecycle::RegisterEvent(*settingsRegistry, "SystemComponentsDeactivated");
AZ::ComponentApplicationLifecycle::RegisterEvent(*settingsRegistry, "ConsoleUnavailable");
AZ::ComponentApplicationLifecycle::RegisterEvent(*settingsRegistry, "SettingsRegistryUnavailable");
AZ::ComponentApplicationLifecycle::RegisterEvent(*settingsRegistry, "SystemAllocatorPendingDestruction");
}
styleManager->initialize(&app, engineRootPath);
AZ::Test::printUnusedParametersWarning(argc, argv);
@@ -176,7 +176,6 @@ namespace UnitTest
TEST_F(PrefabFixupTest, Test_LoadInstanceFromPrefabDom_Overload3)
{
Instance instance;
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> referencedAssets;
Instance::EntityList entityList;
(PrefabDomUtils::LoadInstanceFromPrefabDom(instance, entityList, m_prefabDom));
@@ -48,6 +48,7 @@ set(FILES
FocusMode/EditorFocusModeSelectionTests.cpp
FocusMode/EditorFocusModeTests.cpp
GenericComponentWrapperTest.cpp
Input/QtEventToAzInputMapperTests.cpp
InstanceDataHierarchy.cpp
IntegerPrimtitiveTestConfig.h
LogLines.cpp
+1 -1
View File
@@ -1080,7 +1080,7 @@ AZ_POP_DISABLE_WARNING
{
m_pUserCallback->OnInitProgress("Initializing additional systems...");
}
AZ_Printf(AZ_TRACE_SYSTEM_WINDOW, "Initializing additional systems");
AZ_Printf(AZ_TRACE_SYSTEM_WINDOW, "Initializing additional systems\n");
InlineInitializationProcessing("CSystem::Init AIInit");
@@ -10,12 +10,7 @@
{
"Name": "DepthStencilTextureInput",
"SlotType": "Input",
"ScopeAttachmentUsage": "Shader",
"ImageViewDesc": {
"AspectFlags": [
"Depth"
]
}
"ScopeAttachmentUsage": "Shader"
},
{
"Name": "NormalInput",
@@ -74,7 +74,7 @@ struct TileLightData
bool Light_IsInsideBin(uint package, uint bin)
{
return (package & (1 << bin)) != 0;
return (package & (1u << bin)) != 0;
}
uint PackLightIndexWithBinMask(uint ind, uint bins)
@@ -130,7 +130,7 @@ uint NVLC_GetBin(const float viewZ, const TileLightData data)
const float zFarCoordSystemAdjusted = data.zFar * RH_COORD_SYSTEM_REVERSE;
float f = saturate( (abs(viewZCoordSystemAdjusted) - zNearCoordSystemAdjusted) / (zFarCoordSystemAdjusted - zNearCoordSystemAdjusted) );
float bin = min(f, 0.999999) * float(1 << data.logMaxBins);
float bin = min(f, 0.999999) * float(1u << data.logMaxBins);
return uint(bin);
}
@@ -54,6 +54,8 @@ void ApplyDecal(uint currDecalIndex, inout Surface surface)
localPos = mul(decalRot, localPos);
float3 decalUVW = localPos * rcp(decal.m_halfSize);
[branch]
if(decalUVW.x >= -1.0f && decalUVW.x <= 1.0f &&
decalUVW.y >= -1.0f && decalUVW.y <= 1.0f &&
decalUVW.z >= -1.0f && decalUVW.z <= 1.0f)
@@ -72,6 +74,7 @@ void ApplyDecal(uint currDecalIndex, inout Surface surface)
float2 normalMap = 0;
// Each texture array handles a size permutation.
// e.g. it could be that tex array 0 handles 256x256 and tex array 1 handles 512x64, etc.
[branch]
switch(textureArrayIndex)
{
case 0:
@@ -127,9 +127,9 @@ namespace AZ
static void Reflect(AZ::ReflectContext* context);
ImageSubresourceLayoutPlaced() = default;
ImageSubresourceLayoutPlaced(const ImageSubresourceLayout& subresourceLayout, size_t offset);
ImageSubresourceLayoutPlaced(const ImageSubresourceLayout& subresourceLayout, uint32_t offset);
size_t m_offset = 0;
uint32_t m_offset = 0;
};
/**
@@ -40,78 +40,94 @@ namespace AZ
public:
FrameGraphAttachmentDatabase() = default;
/// Clears the database back to an empty state.
//! Clears the database back to an empty state.
void Clear();
/// Imports an image into the database.
//! Imports an image into the database.
ResultCode ImportImage(const AttachmentId& attachmentId, Ptr<Image> image);
/// Imports a swapchain into the database.
//! Imports a swapchain into the database.
ResultCode ImportSwapChain(const AttachmentId& attachmentId, Ptr<SwapChain> swapChain);
/// Imports a buffer into the database.
//! Imports a buffer into the database.
ResultCode ImportBuffer(const AttachmentId& attachmentId, Ptr<Buffer> buffer);
/// Creates a transient image and inserts it into the database.
//! Creates a transient image and inserts it into the database.
ResultCode CreateTransientImage(const TransientImageDescriptor& descriptor);
/// Creates a transient buffer and inserts it into the database.
//! Creates a transient buffer and inserts it into the database.
ResultCode CreateTransientBuffer(const TransientBufferDescriptor& descriptor);
/// Finds the attachment associated with \param attachmentId and returns its image descriptor.
//! Finds the attachment associated with \param attachmentId and returns its image descriptor.
ImageDescriptor GetImageDescriptor(const AttachmentId& attachmentId) const;
/// Finds the attachment associated with \param attachmentId and returns its buffer descriptor.
//! Finds the attachment associated with \param attachmentId and returns its buffer descriptor.
BufferDescriptor GetBufferDescriptor(const AttachmentId& attachmentId) const;
/// Returns whether the attachment exists in the database.
//! Returns whether the attachment exists in the database.
bool IsAttachmentValid(const AttachmentId& attachmentId) const;
/// Finds an attachment associated with \param attachmentId.
//! Finds an attachment associated with \param attachmentId.
const FrameAttachment* FindAttachment(const AttachmentId& attachmentId) const;
FrameAttachment* FindAttachment(const AttachmentId& attachmentId);
/// Finds an attachment associated with \param attachmentId and attempts to cast
/// to the requested type. Will return null if the type is not compatible, or the
/// attachment was not found.
//! Finds an attachment associated with \param attachmentId and attempts to cast
//! to the requested type. Will return null if the type is not compatible, or the
//! attachment was not found.
template <typename AttachmentType>
const AttachmentType* FindAttachment(const AttachmentId& attachmentId) const;
template <typename AttachmentType>
AttachmentType* FindAttachment(const AttachmentId& attachmentId);
/// Returns the full list of attachments.
//! Returns the full list of attachments.
const AZStd::vector<FrameAttachment*>& GetAttachments() const;
/// Returns the full list of image attachments.
//! Returns the full list of image attachments.
const AZStd::vector<ImageFrameAttachment*>& GetImageAttachments() const;
/// Returns the full list of buffer attachments.
//! Returns the full list of buffer attachments.
const AZStd::vector<BufferFrameAttachment*>& GetBufferAttachments() const;
/// Returns the transient swap chain attachments registered in the graph.
//! Returns the transient swap chain attachments registered in the graph.
const AZStd::vector<SwapChainFrameAttachment*>& GetSwapChainAttachments() const;
/// Returns the imported image attachments registered in the graph.
//! Returns the imported image attachments registered in the graph.
const AZStd::vector<ImageFrameAttachment*>& GetImportedImageAttachments() const;
/// Returns the imported buffer attachments registered in the graph.
//! Returns the imported buffer attachments registered in the graph.
const AZStd::vector<BufferFrameAttachment*>& GetImportedBufferAttachments() const;
/// Returns the transient image attachments registered in the graph.
//! Returns the transient image attachments registered in the graph.
const AZStd::vector<ImageFrameAttachment*>& GetTransientImageAttachments() const;
/// Returns the transient buffer attachments registered in the graph.
//! Returns the transient buffer attachments registered in the graph.
const AZStd::vector<BufferFrameAttachment*>& GetTransientBufferAttachments() const;
/// Finds the list of scope attachments used by a scope for the given attachment.
//! Finds the list of scope attachments used by a scope for the given attachment.
const ScopeAttachmentPtrList* FindScopeAttachmentList(const ScopeId& scopeId, const AttachmentId& attachmentId) const;
/// Finds the scope attachment used by a scope for the given attachment. If multiple scope attachments are used for the
/// same attachment (like binding multiple mips of a texture), the index parameter will specify which one to select.
const ScopeAttachment* FindScopeAttachment(const ScopeId& scopeId, const AttachmentId& attachmentId, size_t index = 0) const;
//! Finds the scope attachment used by a scope for the given attachment
const ScopeAttachment* FindScopeAttachment(const ScopeId& scopeId, const AttachmentId& attachmentId) const;
/// Returns the full list of scope attachments.
//! Finds the scope attachment used by a scope for the given attachment. If multiple scope image attachments are used for the
//! same attachment, provide ScopeAttachmentUsage (in case attachments are merged) and
//! ImageViewDescriptor (in case the attachments are different based on view, i.e different mips or aspect of a texture) to ensure
//! that the correct scope attachment is returned.
const ScopeAttachment* FindScopeAttachment(
const ScopeId& scopeId,
const AttachmentId& attachmentId,
const ImageViewDescriptor& imageViewDescriptor,
const RHI::ScopeAttachmentUsage attachmentUsage) const;
//! Finds the scope attachment used by a scope for the given attachment. If multiple scope attachments are used for the same attachment
//! provide attachmentUsage to ensure that the correct scope attachment is returned
const ScopeAttachment* FindScopeAttachment(
const ScopeId& scopeId,
const AttachmentId& attachmentId,
const RHI::ScopeAttachmentUsage attachmentUsage) const;
//! Returns the full list of scope attachments.
const ScopeAttachmentPtrList& GetScopeAttachments() const;
template <typename ScopeAttachmentType, typename... Args>
@@ -120,8 +136,8 @@ namespace AZ
FrameAttachment& attachment,
Args&&... arguments);
/// Emplaces a use of a resource pool by a specific scope. Returns the ScopeId of the most recent use of the pool or en empty
/// ScopeId if this is the first use.
//! Emplaces a use of a resource pool by a specific scope. Returns the ScopeId of the most recent use of the pool or en empty
//! ScopeId if this is the first use.
ScopeId EmplaceResourcePoolUse(ResourcePool& pool, ScopeId scopeId);
private:
@@ -8,6 +8,7 @@
#pragma once
#include <Atom/RHI.Reflect/AttachmentId.h>
#include <Atom/RHI.Reflect/AttachmentEnums.h>
#include <Atom/RHI.Reflect/ScopeId.h>
namespace AZ
@@ -19,15 +20,15 @@ namespace AZ
class BufferView;
class Image;
class ImageView;
class ScopeAttachment;
struct BufferDescriptor;
struct ImageDescriptor;
struct ImageViewDescriptor;
/**
* FrameGraphCompileContext provides access to compiled image and buffer views
* associated with the provided scope id, along with other query methods for
* accessing attachment resource data. This information can be used to
* compile ShaderResourceGroups.
*/
//! FrameGraphCompileContext provides access to compiled image and buffer views
//! associated with the provided scope id, along with other query methods for
//! accessing attachment resource data. This information can be used to
//! compile ShaderResourceGroups.
class FrameGraphCompileContext
{
public:
@@ -37,31 +38,46 @@ namespace AZ
const ScopeId& scopeId,
const FrameGraphAttachmentDatabase& attachmentDatabase);
/// Returns the scope id associated with this context.
//! Returns the scope id associated with this context.
const ScopeId& GetScopeId() const;
/// Returns whether the given attachment id is valid within the current frame.
//! Returns whether the given attachment id is valid within the current frame.
bool IsAttachmentValid(const AttachmentId& attachmentId) const;
/// Returns the number of scope attachments used by the current scope for the given attachment
//! Returns the number of scope attachments used by the current scope for the given attachment
const size_t GetScopeAttachmentCount(const AttachmentId& attachmentId) const;
/// Returns the buffer view associated with usage on the current scope.
const BufferView* GetBufferView(const AttachmentId& attachmentId, size_t index = 0) const;
//! Returns the buffer view associated with the scope attachment.
const BufferView* GetBufferView(const ScopeAttachment* scopeAttachment) const;
/// Returns the buffer associated with usage on the current scope.
//! Returns the buffer view associated with the attachmentId.
const BufferView* GetBufferView(const AttachmentId& attachmentId) const;
//! Returns the buffer view associated with attachmentId and the attachmentUsage on the current scope.
const BufferView* GetBufferView(const AttachmentId& attachmentId, RHI::ScopeAttachmentUsage attachmentUsage) const;
//! Returns the buffer associated with attachmentId.
const Buffer* GetBuffer(const AttachmentId& attachmentId) const;
/// Returns the image view associated with usage on the current scope.
const ImageView* GetImageView(const AttachmentId& attachmentId, size_t index = 0) const;
//! Returns the image view associated with the scope attachment
const ImageView* GetImageView(const ScopeAttachment* scopeAttacment) const;
/// Returns the image associated with usage on the current scope.
//! Returns the image view associated with attachmentId, attachmentUsage and imageViewDescriptor on the current scope.
const ImageView* GetImageView(
const AttachmentId& attachmentId,
const ImageViewDescriptor& imageViewDescriptor,
const RHI::ScopeAttachmentUsage attachmentUsage) const;
//! Returns the image view associated with the attachmentId.
const ImageView* GetImageView(const AttachmentId& attachmentId) const;
//! Returns the image associated with the attachmentId.
const Image* GetImage(const AttachmentId& attachmentId) const;
/// Returns the buffer descriptor for the given attachment id.
//! Returns the buffer descriptor for the given attachment id.
BufferDescriptor GetBufferDescriptor(const AttachmentId& attachmentId) const;
/// Returns the image descriptor for the given attachment id.
//! Returns the image descriptor for the given attachment id.
ImageDescriptor GetImageDescriptor(const AttachmentId& attachmentId) const;
private:
@@ -142,9 +142,6 @@ namespace AZ
}
}
// [GFX TODO][ATOM-1669]: Review if it's needed to validate
// overlapping of ranges.
return true;
}
@@ -124,7 +124,7 @@ namespace AZ
, m_blockElementHeight{blockElementHeight}
{}
ImageSubresourceLayoutPlaced::ImageSubresourceLayoutPlaced(const ImageSubresourceLayout& subresourceLayout, size_t offset)
ImageSubresourceLayoutPlaced::ImageSubresourceLayoutPlaced(const ImageSubresourceLayout& subresourceLayout, uint32_t offset)
: ImageSubresourceLayout(subresourceLayout)
, m_offset{offset}
{}
@@ -209,7 +209,11 @@ namespace AZ
return nullptr;
}
const ScopeAttachment* FrameGraphAttachmentDatabase::FindScopeAttachment(const ScopeId& scopeId, const AttachmentId& attachmentId, size_t index) const
const ScopeAttachment* FrameGraphAttachmentDatabase::FindScopeAttachment(
const ScopeId& scopeId,
const AttachmentId& attachmentId,
const ImageViewDescriptor& imageViewDescriptor,
const RHI::ScopeAttachmentUsage attachmentUsage) const
{
const ScopeAttachmentPtrList* scopeAttachmentList = FindScopeAttachmentList(scopeId, attachmentId);
if (!scopeAttachmentList)
@@ -217,21 +221,93 @@ namespace AZ
return nullptr;
}
if (index >= scopeAttachmentList->size())
if (scopeAttachmentList->size() > 1)
{
AZ_Error("AttachmentDatabase", false,
"Attempting to access scope attachment [%d], but list only has [%d] elements. ScopeId: [%s]. AttachmentId: [%s]",
index,
scopeAttachmentList->size(),
scopeId.GetCStr(),
attachmentId.GetCStr());
//Find the attachment with the same view and usage
auto findIter = AZStd::find_if(scopeAttachmentList->begin(), scopeAttachmentList->end(), [&](const ScopeAttachment* scopeAttacment)
{
const ImageScopeAttachment* imageAttachment = azrtti_cast<const ImageScopeAttachment*>(scopeAttacment);
bool isSameView = imageAttachment->GetDescriptor().m_imageViewDescriptor.IsSameSubResource(imageViewDescriptor);
if (isSameView)
{
AZStd::vector<ScopeAttachmentUsageAndAccess> usageAndAccessVec = imageAttachment->GetUsageAndAccess();
auto usageAccessIter = AZStd::find_if(usageAndAccessVec.begin(), usageAndAccessVec.end(), [&](const ScopeAttachmentUsageAndAccess usageAndAccess)
{
return usageAndAccess.m_usage == attachmentUsage;
});
return usageAccessIter != usageAndAccessVec.end();
}
return false;
});
if (findIter != scopeAttachmentList->end())
{
return *findIter;
}
AZ_Error("AttachmentDatabase", false, "Couldnt find ScopeAttachment %s with the same view and usage for scope %s", attachmentId.GetCStr(), scopeId.GetCStr());
return nullptr;
}
else
{
return (*scopeAttachmentList)[0];
}
}
const ScopeAttachment* FrameGraphAttachmentDatabase::FindScopeAttachment(
const ScopeId& scopeId,
const AttachmentId& attachmentId,
const RHI::ScopeAttachmentUsage attachmentUsage) const
{
const ScopeAttachmentPtrList* scopeAttachmentList = FindScopeAttachmentList(scopeId, attachmentId);
if (!scopeAttachmentList)
{
return nullptr;
}
return (*scopeAttachmentList)[index];
}
//More than one entry indicates that the same attachment is used multiple times in a scope.
if (scopeAttachmentList->size() > 1)
{
//Find the attachment with the same usage
auto findIter = AZStd::find_if(scopeAttachmentList->begin(), scopeAttachmentList->end(), [&](const ScopeAttachment* scopeAttacment)
{
AZStd::vector<ScopeAttachmentUsageAndAccess> usageAndAccessVec = scopeAttacment->GetUsageAndAccess();
auto usageAccessIter = AZStd::find_if(usageAndAccessVec.begin(), usageAndAccessVec.end(), [&](const ScopeAttachmentUsageAndAccess usageAndAccess)
{
return usageAndAccess.m_usage == attachmentUsage;
});
return usageAccessIter != usageAndAccessVec.end();
});
if (findIter != scopeAttachmentList->end())
{
return *findIter;
}
AZ_Error("AttachmentDatabase", false, "Couldnt find ScopeAttachment %s with the same view and usage for scope %s", attachmentId.GetCStr(), scopeId.GetCStr());
return nullptr;
}
else
{
return (*scopeAttachmentList)[0];
}
}
const ScopeAttachment* FrameGraphAttachmentDatabase::FindScopeAttachment(const ScopeId& scopeId, const AttachmentId& attachmentId) const
{
const ScopeAttachmentPtrList* scopeAttachmentList = FindScopeAttachmentList(scopeId, attachmentId);
if (!scopeAttachmentList)
{
return nullptr;
}
AZ_Error( "AttachmentDatabase", scopeAttachmentList->size() > 0, "Couldnt fine Scopeattachment %s for scope %s", attachmentId.GetCStr(), scopeId.GetCStr());
return (*scopeAttachmentList)[0];
}
const AZStd::vector<ImageFrameAttachment*>& FrameGraphAttachmentDatabase::GetImageAttachments() const
{
return m_imageAttachments;
@@ -40,9 +40,8 @@ namespace AZ
return 0;
}
const BufferView* FrameGraphCompileContext::GetBufferView(const AttachmentId& attachmentId, size_t index) const
const BufferView* FrameGraphCompileContext::GetBufferView(const ScopeAttachment* scopeAttacment) const
{
const ScopeAttachment* scopeAttacment = m_attachmentDatabase->FindScopeAttachment(m_scopeId, attachmentId, index);
const BufferScopeAttachment* attachment = azrtti_cast<const BufferScopeAttachment*>(scopeAttacment);
if (!attachment)
{
@@ -51,6 +50,18 @@ namespace AZ
return attachment->GetBufferView();
}
const BufferView* FrameGraphCompileContext::GetBufferView(const AttachmentId& attachmentId) const
{
const ScopeAttachment* scopeAttacment = m_attachmentDatabase->FindScopeAttachment(m_scopeId, attachmentId);
return GetBufferView(scopeAttacment);
}
const BufferView* FrameGraphCompileContext::GetBufferView(const AttachmentId& attachmentId, const RHI::ScopeAttachmentUsage attachmentUsage) const
{
const ScopeAttachment* scopeAttacment = m_attachmentDatabase->FindScopeAttachment(m_scopeId, attachmentId, attachmentUsage);
return GetBufferView(scopeAttacment);
}
const Buffer* FrameGraphCompileContext::GetBuffer(const AttachmentId& attachmentId) const
{
const BufferView* bufferView = GetBufferView(attachmentId);
@@ -61,9 +72,8 @@ namespace AZ
return nullptr;
}
const ImageView* FrameGraphCompileContext::GetImageView(const AttachmentId& attachmentId, size_t index) const
const ImageView* FrameGraphCompileContext::GetImageView(const ScopeAttachment* scopeAttacment) const
{
const ScopeAttachment* scopeAttacment = m_attachmentDatabase->FindScopeAttachment(m_scopeId, attachmentId, index);
const ImageScopeAttachment* attachment = azrtti_cast<const ImageScopeAttachment*>(scopeAttacment);
if (!attachment)
{
@@ -72,6 +82,18 @@ namespace AZ
return attachment->GetImageView();
}
const ImageView* FrameGraphCompileContext::GetImageView(const AttachmentId& attachmentId, const ImageViewDescriptor& imageViewDescriptor, RHI::ScopeAttachmentUsage attachmentUsage) const
{
const ScopeAttachment* scopeAttacment = m_attachmentDatabase->FindScopeAttachment(m_scopeId, attachmentId, imageViewDescriptor, attachmentUsage);
return GetImageView(scopeAttacment);
}
const ImageView* FrameGraphCompileContext::GetImageView(const AttachmentId& attachmentId) const
{
const ScopeAttachment* scopeAttacment = m_attachmentDatabase->FindScopeAttachment(m_scopeId, attachmentId);
return GetImageView(scopeAttacment);
}
const Image* FrameGraphCompileContext::GetImage(const AttachmentId& attachmentId) const
{
const ImageView* imageView = GetImageView(attachmentId);
@@ -112,6 +112,8 @@ namespace AZ
{
infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE);
//Un-comment this if you want to break on warnings too
//infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE);
}
}
+1 -1
View File
@@ -72,7 +72,7 @@ namespace AZ
{
const RHI::ImageDescriptor& imageDescriptor = GetDescriptor();
size_t byteOffset = 0;
uint32_t byteOffset = 0;
if (subresourceLayouts)
{
@@ -376,7 +376,7 @@ namespace AZ
void Image::GetSubresourceLayoutsInternal(const RHI::ImageSubresourceRange& subresourceRange, RHI::ImageSubresourceLayoutPlaced* subresourceLayouts, size_t* totalSizeInBytes) const
{
const RHI::ImageDescriptor& imageDescriptor = GetDescriptor();
size_t byteOffset = 0;
uint32_t byteOffset = 0;
const uint32_t offsetAligment = 4;
for (uint16_t arraySlice = subresourceRange.m_arraySliceMin; arraySlice <= subresourceRange.m_mipSliceMax; ++arraySlice)
{
@@ -398,7 +398,7 @@ namespace AZ
layout.m_size = subresourceLayout.m_size;
}
byteOffset = RHI::AlignUp(byteOffset + static_cast<uint64_t>(subresourceLayout.m_bytesPerImage) * subresourceLayout.m_size.m_depth, offsetAligment);
byteOffset = RHI::AlignUp(byteOffset + subresourceLayout.m_bytesPerImage * subresourceLayout.m_size.m_depth, offsetAligment);
}
}
@@ -54,8 +54,8 @@ namespace AZ
}
m_descriptorSetAllocator = RHI::Ptr<DescriptorSetAllocator>(aznew DescriptorSetAllocator);
// [GFX_TODO] ATOM-679 Set a proper pool size.
const uint32_t descriptorSetsPerPool = 100;
// [GFX_TODO] ATOM-16891 - Refactor Descriptor management system
const uint32_t descriptorSetsPerPool = 20;
DescriptorSetAllocator::Descriptor allocatorDescriptor;
allocatorDescriptor.m_device = &device;
allocatorDescriptor.m_layout = m_descriptorSetLayout.get();
@@ -37,7 +37,7 @@ namespace AZ
ResourcePoolAssetType m_poolType = ResourcePoolAssetType::Unknown;
AZStd::string m_poolName = "Unknown";
size_t m_budgetInBytes = 0;
uint32_t m_budgetInBytes = 0;
// Configuration for buffer pool
RHI::HeapMemoryLevel m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
@@ -276,7 +276,8 @@ namespace AZ
{
inputIndex = imageIndex;
}
const RHI::ImageView* imageView = context.GetImageView(attachment->GetAttachmentId(), binding.m_attachmentUsageIndex);
const RHI::ImageView* imageView =
context.GetImageView(attachment->GetAttachmentId(), binding.m_unifiedScopeDesc.GetImageViewDescriptor(), binding.m_scopeAttachmentUsage);
if (binding.m_shaderImageDimensionsNameIndex.HasName())
{
@@ -315,7 +316,7 @@ namespace AZ
{
inputIndex = bufferIndex;
}
const RHI::BufferView* bufferView = context.GetBufferView(attachment->GetAttachmentId(), binding.m_attachmentUsageIndex);
const RHI::BufferView* bufferView = context.GetBufferView(attachment->GetAttachmentId(), binding.m_scopeAttachmentUsage);
m_shaderResourceGroup->SetBufferView(RHI::ShaderInputBufferIndex(inputIndex), bufferView, arrayIndex);
++bufferIndex;
}
@@ -112,12 +112,12 @@ namespace UnitTest
: AZ::RHI::ShaderStageFunction(shaderStage)
{}
void SetIndex(size_t index)
void SetIndex(uint32_t index)
{
m_index = index;
}
size_t m_index;
int32_t m_index;
ShaderByteCode m_byteCode;
@@ -1362,8 +1362,9 @@ namespace AZ::AtomBridge
// if 2d draw need to project pos to screen first
AzFramework::TextDrawParameters params;
AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext();
const auto dpiScaleFactor = viewportContext->GetDpiScalingFactor();
params.m_drawViewportId = viewportContext->GetId(); // get the viewport ID so default viewport works
params.m_position = AZ::Vector3(x, y, 1.0f);
params.m_position = AZ::Vector3(x * dpiScaleFactor, y * dpiScaleFactor, 1.0f);
params.m_color = m_rendState.m_color;
params.m_scale = AZ::Vector2(size);
params.m_hAlign = center ? AzFramework::TextHorizontalAlignment::Center : AzFramework::TextHorizontalAlignment::Left; //! Horizontal text alignment
@@ -254,7 +254,7 @@ namespace BarrierInput
static bool barrierBye([[maybe_unused]]BarrierClient* pContext, [[maybe_unused]]int* pArgs, [[maybe_unused]]Stream* pStream, [[maybe_unused]]int streamLeft)
{
AZLOG_INFO("BarrierClient: Server said bye. Disconnecting\n");
AZLOG_INFO("BarrierClient: Server said bye. Disconnecting");
return false;
}
@@ -284,7 +284,7 @@ namespace BarrierInput
const char* packetStart = stream.GetData();
if (packetLength > streamLength)
{
AZLOG_INFO("BarrierClient: Packet overruns buffer (Packet Length: %d Buffer Length: %d), probably lots of data on clipboard?\n", packetLength, streamLength);
AZLOG_INFO("BarrierClient: Packet overruns buffer (Packet Length: %d Buffer Length: %d), probably lots of data on clipboard?", packetLength, streamLength);
return false;
}
@@ -377,7 +377,7 @@ namespace BarrierInput
const int lengthReceived = AZ::AzSock::Recv(m_socket, stream.GetBuffer(), stream.GetBufferSize(), 0);
if (lengthReceived <= 0)
{
AZLOG_INFO("BarrierClient: Receive failed, reconnecting.\n");
AZLOG_INFO("BarrierClient: Receive failed, reconnecting.");
connected = false;
continue;
}
@@ -386,7 +386,7 @@ namespace BarrierInput
stream.SetLength(lengthReceived);
if (!ProcessPackets(this, stream))
{
AZLOG_INFO("BarrierClient: Packet processing failed, reconnecting.\n");
AZLOG_INFO("BarrierClient: Packet processing failed, reconnecting.");
connected = false;
continue;
}
+42 -38
View File
@@ -401,34 +401,37 @@ bool ImGuiManager::OnInputChannelEventFiltered(const InputChannel& inputChannel)
const InputChannelId& inputChannelId = inputChannel.GetInputChannelId();
const InputDeviceId& inputDeviceId = inputChannel.GetInputDevice().GetInputDeviceId();
// Handle Keyboard Hotkeys
if (InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId) && inputChannel.IsStateBegan())
{
// Cycle through ImGui Menu Bar States on Home button press
if (inputChannelId == InputDeviceKeyboard::Key::NavigationHome)
{
ToggleThroughImGuiVisibleState();
}
bool consumeEvent = false;
// Cycle through Standalone Editor Window States
if (inputChannel.GetInputChannelId() == InputDeviceKeyboard::Key::NavigationEnd)
{
if (gEnv->IsEditor() && m_editorWindowState == DisplayState::Hidden)
{
ImGuiUpdateListenerBus::Broadcast(&IImGuiUpdateListener::OnOpenEditorWindow);
}
else
{
m_editorWindowState = m_editorWindowState == DisplayState::Visible
? DisplayState::VisibleNoMouse
: DisplayState::Visible;
}
}
}
// Handle Keyboard Modifier Keys
// Handle Keyboard Inputs
if (InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId))
{
// Handle Keyboard Hotkeys
if (inputChannel.IsStateBegan())
{
// Cycle through ImGui Menu Bar States on Home button press
if (inputChannelId == InputDeviceKeyboard::Key::NavigationHome)
{
ToggleThroughImGuiVisibleState();
}
// Cycle through Standalone Editor Window States
if (inputChannel.GetInputChannelId() == InputDeviceKeyboard::Key::NavigationEnd)
{
if (gEnv->IsEditor() && m_editorWindowState == DisplayState::Hidden)
{
ImGuiUpdateListenerBus::Broadcast(&IImGuiUpdateListener::OnOpenEditorWindow);
}
else
{
m_editorWindowState = m_editorWindowState == DisplayState::Visible
? DisplayState::VisibleNoMouse
: DisplayState::Visible;
}
}
}
// Handle Keyboard Modifier Keys
if (inputChannelId == InputDeviceKeyboard::Key::ModifierShiftL
|| inputChannelId == InputDeviceKeyboard::Key::ModifierShiftR)
{
@@ -454,7 +457,7 @@ bool ImGuiManager::OnInputChannelEventFiltered(const InputChannel& inputChannel)
}
// Handle Controller Inputs
if (InputDeviceGamepad::IsGamepadDevice(inputDeviceId))
else if (InputDeviceGamepad::IsGamepadDevice(inputDeviceId))
{
// Only pipe in Controller Nav Inputs when at least 1 of the two controller modes are enabled.
if (m_controllerModeFlags)
@@ -496,32 +499,30 @@ bool ImGuiManager::OnInputChannelEventFiltered(const InputChannel& inputChannel)
{
ToggleThroughImGuiVisibleState();
}
// If we have the Discrete Input Mode Enabled.. and we are in the Visible State, then consume input here
if (m_enableDiscreteInputMode && m_clientMenuBarState == DisplayState::Visible)
{
return true;
}
return false;
}
// Handle Mouse Inputs
if (InputDeviceMouse::IsMouseDevice(inputDeviceId))
else if (InputDeviceMouse::IsMouseDevice(inputDeviceId))
{
const int mouseButtonIndex = GetAzMouseButtonIndex(inputChannelId);
if (0 <= mouseButtonIndex && mouseButtonIndex < AZ_ARRAY_SIZE(io.MouseDown))
{
io.MouseDown[mouseButtonIndex] = inputChannel.IsActive();
// only consume the event during edit mode in the editor so the viewport doesn't also respond to it
consumeEvent = gEnv->IsEditing() && io.WantCaptureMouse;
}
else if (inputChannelId == InputDeviceMouse::Movement::Z)
{
io.MouseWheel = inputChannel.GetValue() / static_cast<float>(IMGUI_WHEEL_DELTA);
// only consume the event during edit mode in the editor so the viewport doesn't also respond to it
consumeEvent = gEnv->IsEditing() && io.WantCaptureMouse;
}
}
// Handle Touch Inputs
if (InputDeviceTouch::IsTouchDevice(inputDeviceId))
else if (InputDeviceTouch::IsTouchDevice(inputDeviceId))
{
const int touchIndex = GetAzTouchIndex(inputChannelId);
if (0 <= touchIndex && touchIndex < AZ_ARRAY_SIZE(io.MouseDown))
@@ -542,7 +543,7 @@ bool ImGuiManager::OnInputChannelEventFiltered(const InputChannel& inputChannel)
}
// Handle Virtual Keyboard Inputs
if (InputDeviceVirtualKeyboard::IsVirtualKeyboardDevice(inputDeviceId))
else if (InputDeviceVirtualKeyboard::IsVirtualKeyboardDevice(inputDeviceId))
{
if (inputChannelId == AzFramework::InputDeviceVirtualKeyboard::Command::EditEnter)
{
@@ -554,13 +555,16 @@ bool ImGuiManager::OnInputChannelEventFiltered(const InputChannel& inputChannel)
if (m_clientMenuBarState == DisplayState::Visible
|| m_editorWindowState == DisplayState::Visible)
{
// If we have the Discrete Input Mode Enabled.. then consume the input here.
// If we have the Discrete Input Mode Enabled.. then consume the input here.
if (m_enableDiscreteInputMode)
{
return true;
}
return consumeEvent;
}
// don't allow event capturing when ImGui isn't active
return false;
}
@@ -81,7 +81,7 @@ namespace Multiplayer
else
{
m_networkEditorInterface->SendReliablePacket(editorServerToEditorConnectionId, MultiplayerEditorPackets::EditorServerReadyForLevelData());
AZ_Printf("MultiplayerEditorConnection", "Editor-server activation has found and connected to the editor.")
AZ_Printf("MultiplayerEditorConnection", "Editor-server activation has found and connected to the editor.\n")
}
}
@@ -1135,9 +1135,15 @@ namespace Multiplayer
// make sure the player prefab path is lowercase (how it's stored in the cache folder)
auto sv_defaultPlayerSpawnAssetLowerCase = static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset);
AZStd::to_lower(sv_defaultPlayerSpawnAssetLowerCase.begin(), sv_defaultPlayerSpawnAssetLowerCase.end());
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAssetLowerCase).c_str()));
PrefabEntityId playerPrefabEntityId(AZ::Name(sv_defaultPlayerSpawnAssetLowerCase.c_str()));
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate);
AZ_Warning(
"MultiplayerSystemComponent", !entityList.empty(),
"SpawnDefaultPlayerPrefab failed. Missing sv_defaultPlayerSpawnAsset at path '%s'.\n",
sv_defaultPlayerSpawnAssetLowerCase.c_str())
for (NetworkEntityHandle subEntity : entityList)
{
subEntity.Activate();
@@ -324,7 +324,8 @@ namespace Profiler
// Gets called when region ends and all data is set
void CpuTimingLocalStorage::AddCachedRegion(const CachedTimeRegion& timeRegionCached)
{
if (m_hitSizeLimitMap[timeRegionCached.m_groupRegionName.m_regionName])
if (auto iter = m_hitSizeLimitMap.find(timeRegionCached.m_groupRegionName.m_regionName);
iter != m_hitSizeLimitMap.end() && iter->second)
{
return;
}
@@ -15,6 +15,7 @@
#include <AzCore/Debug/ProfilerBus.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/JSON/filereadstream.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/JsonSerializationResult.h>
@@ -375,6 +376,7 @@ namespace Profiler
DrawTable();
}
ImGui::EndChild();
}
void ImGuiCpuProfiler::DrawFilePicker()
@@ -597,8 +599,9 @@ namespace Profiler
DrawFrameBoundaries();
// Draw an invisible button to capture inputs
ImGui::InvisibleButton("Timeline Input", { ImGui::GetWindowContentRegionWidth(), baseRow * RowHeight });
// Draw an invisible button to capture inputs and make sure it has a non-zero height
ImGui::InvisibleButton("Timeline Input",
{ ImGui::GetWindowContentRegionWidth(), AZ::GetMax(baseRow, decltype(baseRow){1}) * RowHeight });
// Controls
ImGuiIO& io = ImGui::GetIO();
@@ -643,7 +646,9 @@ namespace Profiler
}
}
}
ImGui::EndChild();
ImGui::EndChild(); // "Timeline"
ImGui::EndChild(); // "Options and Statistics"
}
void ImGuiCpuProfiler::CacheCpuTimingStatistics()
@@ -7,13 +7,13 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
import typing
import json
import azlmbr.scene as sceneApi
from enum import Enum, IntEnum
from enum import IntEnum
# Wraps the AZ.SceneAPI.Containers.SceneGraph.NodeIndex internal class
class SceneGraphNodeIndex:
def __init__(self, sceneGraphNodeIndex) -> None:
self.nodeIndex = sceneGraphNodeIndex
def __init__(self, scene_graph_node_index) -> None:
self.nodeIndex = scene_graph_node_index
def as_number(self):
return self.nodeIndex.AsNumber()
@@ -29,9 +29,9 @@ class SceneGraphNodeIndex:
# Wraps AZ.SceneAPI.Containers.SceneGraph.Name internal class
class SceneGraphName():
def __init__(self, sceneGraphName) -> None:
self.name = sceneGraphName
class SceneGraphName:
def __init__(self, scene_graph_name) -> None:
self.name = scene_graph_name
def get_path(self) -> str:
return self.name.GetPath()
@@ -41,9 +41,9 @@ class SceneGraphName():
# Wraps AZ.SceneAPI.Containers.SceneGraph class
class SceneGraph():
def __init__(self, sceneGraphInstance) -> None:
self.sceneGraph = sceneGraphInstance
class SceneGraph:
def __init__(self, scene_graph_instance) -> None:
self.sceneGraph = scene_graph_instance
@classmethod
def is_valid_name(cls, name):
@@ -96,53 +96,161 @@ class SceneGraph():
return self.sceneGraph.GetNodeContent(node)
class ColorChannel(IntEnum):
RED = 0
""" Red color channel """
GREEN = 1
""" Green color channel """
BLUE = 2
""" Blue color channel """
ALPHA = 3
""" Alpha color channel """
class TangentSpaceSource(IntEnum):
SCENE = 0
""" Extract the tangents and bitangents directly from the source scene file. """
MIKKT_GENERATION = 1
""" Use MikkT algorithm to generate tangents """
class TangentSpaceMethod(IntEnum):
TSPACE = 0
""" Generates the tangents and bitangents with their true magnitudes which can be used for relief mapping effects.
It calculates the 'real' bitangent which may not be perpendicular to the tangent.
However, both, the tangent and bitangent are perpendicular to the vertex normal.
"""
TSPACE_BASIC = 1
""" Calculates unit vector tangents and bitangents at pixel/vertex level which are sufficient for basic normal mapping. """
class PrimitiveShape(IntEnum):
BEST_FIT = 0
""" The algorithm will determine which of the shapes fits best. """
SPHERE = 1
""" Sphere shape """
BOX = 2
""" Box shape """
CAPSULE = 3
""" Capsule shape """
class DecompositionMode(IntEnum):
VOXEL = 0
""" Voxel-based approximate convex decomposition """
TETRAHEDRON = 1
""" Tetrahedron-based approximate convex decomposition """
# Contains a dictionary to contain and export AZ.SceneAPI.Containers.SceneManifest
class SceneManifest():
class SceneManifest:
def __init__(self):
self.manifest = {'values': []}
def add_mesh_group(self, name: str) -> dict:
meshGroup = {}
meshGroup['$type'] = '{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup'
meshGroup['name'] = name
meshGroup['nodeSelectionList'] = {'selectedNodes': [], 'unselectedNodes': []}
meshGroup['rules'] = {'rules': [{'$type': 'MaterialRule'}]}
self.manifest['values'].append(meshGroup)
return meshGroup
"""Adds a Mesh Group to the scene manifest.
Parameters
----------
name :
Name of the mesh group. This will become a file on disk and be usable as a Mesh in the editor.
Returns
-------
dict
Newly created mesh group.
"""
mesh_group = {
'$type': '{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup',
'name': name,
'nodeSelectionList': {'selectedNodes': [], 'unselectedNodes': []},
'rules': {'rules': [{'$type': 'MaterialRule'}]}
}
self.manifest['values'].append(mesh_group)
return mesh_group
def add_prefab_group(self, name: str, id: str, json: dict) -> dict:
prefabGroup = {}
prefabGroup['$type'] = '{99FE3C6F-5B55-4D8B-8013-2708010EC715} PrefabGroup'
prefabGroup['name'] = name
prefabGroup['id'] = id
prefabGroup['prefabDomData'] = json
self.manifest['values'].append(prefabGroup)
return prefabGroup
"""Adds a Prefab Group to the scene manifest. This will become a file on disk and be usable as a ProceduralPrefab in the editor.
Parameters
----------
name :
Name of the prefab.
id :
Unique ID for this prefab group.
json :
The prefab template data.
Returns
-------
dict
The newly created Prefab group
"""
prefab_group = {
'$type': '{99FE3C6F-5B55-4D8B-8013-2708010EC715} PrefabGroup',
'name': name,
'id': id,
'prefabDomData': json
}
self.manifest['values'].append(prefab_group)
return prefab_group
def mesh_group_select_node(self, mesh_group: dict, node_name: str) -> None:
"""Adds a node as a selected node.
Parameters
----------
mesh_group :
Mesh group to apply the selection to.
node_name :
Path of the node.
"""
mesh_group['nodeSelectionList']['selectedNodes'].append(node_name)
def mesh_group_unselect_node(self, mesh_group: dict, node_name: str) -> None:
"""Adds a node as an unselected node.
Parameters
----------
mesh_group :
Mesh group to apply the selection to.
node_name :
Path of the node.
"""
mesh_group['nodeSelectionList']['unselectedNodes'].append(node_name)
def mesh_group_add_advanced_coordinate_system(self, mesh_group: dict, origin_node_name: str, translation: object,
rotation: object, scale: float) -> None:
def mesh_group_add_advanced_coordinate_system(self, mesh_group: dict,
origin_node_name: str = '',
translation: typing.Optional[object] = None,
rotation: typing.Optional[object] = None,
scale: float = 1.0) -> None:
"""Adds an Advanced Coordinate System rule which modifies the target coordinate system,
applying a transformation to all data (transforms and vertex data if it exists).
Parameters
----------
mesh_group :
Mesh group to add the Advanced Coordinate System rule to.
origin_node_name :
Path of the node to use as the origin.
translation :
Moves the group along the given vector.
rotation :
Sets the orientation offset of the processed mesh in degrees. Rotates the group after translation.
scale :
Sets the scale offset of the processed mesh.
"""
origin_rule = {
'$type': 'CoordinateSystemRule',
'useAdvancedData': True,
'originNodeName': '' if origin_node_name is None else origin_node_name
'originNodeName': self.__default_or_value(origin_node_name, '')
}
if translation is not None:
origin_rule['translation'] = translation
@@ -153,31 +261,57 @@ class SceneManifest():
mesh_group['rules']['rules'].append(origin_rule)
def mesh_group_add_comment(self, mesh_group: dict, comment: str) -> None:
commentRule = {
"""Adds a Comment rule.
Parameters
----------
mesh_group :
Mesh group to add the comment rule to.
comment :
Text for the comment rule.
"""
comment_rule = {
'$type': 'CommentRule',
'comment': comment
}
mesh_group['rules']['rules'].append(commentRule)
mesh_group['rules']['rules'].append(comment_rule)
def __default_or_value(self, val, default):
return default if val is None else val
def mesh_group_add_cloth_rule(self, mesh_group: dict, cloth_node_name: str,
inverse_masses_stream_name: str, inverse_masses_channel: int,
motion_constraints_stream_name: str, motion_constraints_channel: int,
backstop_stream_name: str, backstop_offset_channel: int,
backstop_radius_channel: int) -> None:
"""
Adds a Cloth rule. 0 = Red, 1 = Green, 2 = Blue, 3 = Alpha
:param mesh_group: Mesh Group to add the cloth rule to
:param cloth_node_name: Name of the node that the rule applies to
:param inverse_masses_stream_name: Name of the color stream to use for inverse masses
:param inverse_masses_channel: Color channel (index) for inverse masses
:param motion_constraints_stream_name: Name of the color stream to use for motion constraints
:param motion_constraints_channel: Color channel (index) for motion constraints
:param backstop_stream_name: Name of the color stream to use for backstop
:param backstop_offset_channel: Color channel (index) for backstop offset value
:param backstop_radius_channel: Color chnanel (index) for backstop radius value
def mesh_group_add_cloth_rule(self, mesh_group: dict,
cloth_node_name: str,
inverse_masses_stream_name: typing.Optional[str],
inverse_masses_channel: typing.Optional[ColorChannel],
motion_constraints_stream_name: typing.Optional[str],
motion_constraints_channel: typing.Optional[ColorChannel],
backstop_stream_name: typing.Optional[str],
backstop_offset_channel: typing.Optional[ColorChannel],
backstop_radius_channel: typing.Optional[ColorChannel]) -> None:
"""Adds a Cloth rule.
Parameters
----------
mesh_group :
Mesh Group to add the cloth rule to
cloth_node_name :
Name of the node that the rule applies to
inverse_masses_stream_name :
Name of the color stream to use for inverse masses
inverse_masses_channel :
Color channel (index) for inverse masses
motion_constraints_stream_name :
Name of the color stream to use for motion constraints
motion_constraints_channel :
Color channel (index) for motion constraints
backstop_stream_name :
Name of the color stream to use for backstop
backstop_offset_channel :
Color channel (index) for backstop offset value
backstop_radius_channel :
Color channel (index) for backstop radius value
"""
cloth_rule = {
'$type': 'ClothRule',
@@ -186,22 +320,31 @@ class SceneManifest():
}
if inverse_masses_channel is not None:
cloth_rule['inverseMassesChannel'] = inverse_masses_channel
cloth_rule['inverseMassesChannel'] = int(inverse_masses_channel)
cloth_rule['motionConstraintsStreamName'] = self.__default_or_value(motion_constraints_stream_name, 'Default: 1.0')
if motion_constraints_channel is not None:
cloth_rule['motionConstraintsChannel'] = motion_constraints_channel
cloth_rule['motionConstraintsChannel'] = int(motion_constraints_channel)
cloth_rule['backstopStreamName'] = self.__default_or_value(backstop_stream_name, 'None')
if backstop_offset_channel is not None:
cloth_rule['backstopOffsetChannel'] = backstop_offset_channel
cloth_rule['backstopOffsetChannel'] = int(backstop_offset_channel)
if backstop_radius_channel is not None:
cloth_rule['backstopRadiusChannel'] = backstop_radius_channel
cloth_rule['backstopRadiusChannel'] = int(backstop_radius_channel)
mesh_group['rules']['rules'].append(cloth_rule)
def mesh_group_add_lod_rule(self, mesh_group: dict) -> dict:
"""
Adds an LOD rule
:param mesh_group: Mesh Group to add the rule to
:return: LOD rule
"""Adds an LOD rule.
Parameters
----------
mesh_group :
Mesh Group to add the rule to.
Returns
-------
dict
LOD rule.
"""
lod_rule = {
'$type': '{6E796AC8-1484-4909-860A-6D3F22A7346F} LodRule',
@@ -212,47 +355,76 @@ class SceneManifest():
return lod_rule
def lod_rule_add_lod(self, lod_rule: dict) -> dict:
"""
Adds an LOD level to the LOD rule. Nodes are added in order. The first node added represents LOD1, 2nd LOD2, etc
:param lod_rule: LOD rule to add the LOD level to
:return: LOD level
"""Adds an LOD level to the LOD rule. Nodes are added in order. The first node added represents LOD1, 2nd LOD2, etc.
Parameters
----------
lod_rule :
LOD rule to add the LOD level to.
Returns
-------
dict
LOD level.
"""
lod = {'selectedNodes': [], 'unselectedNodes': []}
lod_rule['nodeSelectionList'].append(lod)
return lod
def lod_select_node(self, lod: dict, selected_node: str) -> None:
"""
Adds a node as a selected node
:param lod: LOD level to add the node to
:param selected_node: Path of the node
"""Adds a node as a selected node.
Parameters
----------
lod :
LOD level to add the node to.
selected_node :
Path of the node.
"""
lod['selectedNodes'].append(selected_node)
def lod_unselect_node(self, lod: dict, unselected_node: str) -> None:
"""
Adds a node as an unselected node
:param lod: LOD rule to add the node to
:param unselected_node: Path of the node
"""Adds a node as an unselected node.
Parameters
----------
lod :
LOD rule to add the node to.
unselected_node :
Path of the node.
"""
lod['unselectedNodes'].append(unselected_node)
def mesh_group_add_advanced_mesh_rule(self, mesh_group: dict, use_32bit_vertices: bool, merge_meshes: bool,
use_custom_normals: bool,
vertex_color_stream: str) -> None:
"""
Adds an Advanced Mesh rule
:param mesh_group: Mesh Group to add the rule to
:param use_32bit_vertices: False = 16bit vertex position precision. True = 32bit vertex position precision
:param merge_meshes: Merge all meshes into a single mesh
:param use_custom_normals: True = use normals from DCC tool. False = average normals
:param vertex_color_stream: Color stream name to use for Vertex Coloring
def mesh_group_add_advanced_mesh_rule(self, mesh_group: dict,
use_32bit_vertices: bool = False,
merge_meshes: bool = True,
use_custom_normals: bool = True,
vertex_color_stream: typing.Optional[str] = None) -> None:
"""Adds an Advanced Mesh rule.
Parameters
----------
mesh_group :
Mesh Group to add the rule to.
use_32bit_vertices :
False = 16bit vertex position precision. True = 32bit vertex position precision.
merge_meshes :
Merge all meshes into a single mesh.
use_custom_normals :
True = use normals from DCC tool. False = average normals.
vertex_color_stream :
Color stream name to use for Vertex Coloring.
"""
rule = {
'$type': 'StaticMeshAdvancedRule',
'use32bitVertices': self.__default_or_value(use_32bit_vertices, False),
'mergeMeshes': self.__default_or_value(merge_meshes, True),
'useCustomNormals': self.__default_or_value(use_custom_normals, True)
'use32bitVertices': use_32bit_vertices,
'mergeMeshes': merge_meshes,
'useCustomNormals': use_custom_normals
}
if vertex_color_stream is not None:
@@ -260,37 +432,51 @@ class SceneManifest():
mesh_group['rules']['rules'].append(rule)
def mesh_group_add_skin_rule(self, mesh_group: dict, max_weights_per_vertex: int, weight_threshold: float) -> None:
"""
Adds a Skin rule
:param mesh_group: Mesh Group to add the rule to
:param max_weights_per_vertex: Max number of joints that can influence a vertex
:param weight_threshold: Weight values below this value will be treated as 0
def mesh_group_add_skin_rule(self, mesh_group: dict, max_weights_per_vertex: int = 4, weight_threshold: float = 0.001) -> None:
"""Adds a Skin rule.
Parameters
----------
mesh_group :
Mesh Group to add the rule to.
max_weights_per_vertex :
Max number of joints that can influence a vertex.
weight_threshold :
Weight values below this value will be treated as 0.
"""
rule = {
'$type': 'SkinRule',
'maxWeightsPerVertex': self.__default_or_value(max_weights_per_vertex, 4),
'weightThreshold': self.__default_or_value(weight_threshold, 0.001)
'maxWeightsPerVertex': max_weights_per_vertex,
'weightThreshold': weight_threshold
}
mesh_group['rules']['rules'].append(rule)
def mesh_group_add_tangent_rule(self, mesh_group: dict, tangent_space: int, tspace_method: int) -> None:
"""
Adds a Tangent rule to control tangent space generation
:param mesh_group: Mesh Group to add the rule to
:param tangent_space: Tangent space source. 0 = Scene, 1 = MikkT Tangent Generation
:param tspace_method: MikkT Generation method. 0 = TSpace, 1 = TSpaceBasic
def mesh_group_add_tangent_rule(self, mesh_group: dict,
tangent_space: TangentSpaceSource = TangentSpaceSource.SCENE,
tspace_method: TangentSpaceMethod = TangentSpaceMethod.TSPACE) -> None:
"""Adds a Tangent rule to control tangent space generation.
Parameters
----------
mesh_group :
Mesh Group to add the rule to.
tangent_space :
Tangent space source. 0 = Scene, 1 = MikkT Tangent Generation.
tspace_method :
MikkT Generation method. 0 = TSpace, 1 = TSpaceBasic.
"""
rule = {
'$type': 'TangentsRule',
'tangentSpace': self.__default_or_value(tangent_space, 1),
'tSpaceMethod': self.__default_or_value(tspace_method, 0)
'tangentSpace': int(tangent_space),
'tSpaceMethod': int(tspace_method)
}
mesh_group['rules']['rules'].append(rule)
def __add_physx_base_mesh_group(self, name: str, physics_material: typing.Optional[str]) -> dict:
def __add_physx_base_mesh_group(self, name: str, physics_material: typing.Optional[str] = None) -> dict:
import azlmbr.math
group = {
'$type': '{5B03C8E6-8CEE-4DA0-A7FA-CD88689DD45B} MeshGroup',
@@ -314,7 +500,9 @@ class SceneManifest():
return group
def add_physx_triangle_mesh_group(self, name: str, merge_meshes: bool = True, weld_vertices: bool = False,
def add_physx_triangle_mesh_group(self, name: str,
merge_meshes: bool = True,
weld_vertices: bool = False,
disable_clean_mesh: bool = False,
force_32bit_indices: bool = False,
suppress_triangle_mesh_remap_table: bool = False,
@@ -322,26 +510,42 @@ class SceneManifest():
mesh_weld_tolerance: float = 0.0,
num_tris_per_leaf: int = 4,
physics_material: typing.Optional[str] = None) -> dict:
"""
Adds a Triangle type PhysX Mesh Group to the scene.
"""Adds a Triangle type PhysX Mesh Group to the scene.
Parameters
----------
name :
Name of the mesh group.
merge_meshes :
When true, all selected nodes will be merged into a single collision mesh.
weld_vertices :
When true, mesh welding is performed. Clean mesh must be enabled.
disable_clean_mesh :
When true, mesh cleaning is disabled. This makes cooking faster.
force_32bit_indices :
When true, 32-bit indices will always be created regardless of triangle count.
suppress_triangle_mesh_remap_table :
When true, the face remap table is not created.
This saves a significant amount of memory, but the SDK will not be able to provide the remap
information for internal mesh triangles returned by collisions, sweeps or raycasts hits.
build_triangle_adjacencies :
When true, the triangle adjacency information is created.
mesh_weld_tolerance :
If mesh welding is enabled, this controls the distance at
which vertices are welded. If mesh welding is not enabled, this value defines the
acceptance distance for mesh validation. Provided no two vertices are within this
distance, the mesh is considered to be clean. If not, a warning will be emitted.
num_tris_per_leaf :
Mesh cooking hint for max triangles per leaf limit. Fewer triangles per leaf
produces larger meshes with better runtime performance and worse cooking performance.
physics_material :
Configure which physics material to use.
Returns
-------
dict
The newly created mesh group.
:param name: Name of the mesh group.
:param merge_meshes: When true, all selected nodes will be merged into a single collision mesh.
:param weld_vertices: When true, mesh welding is performed. Clean mesh must be enabled.
:param disable_clean_mesh: When true, mesh cleaning is disabled. This makes cooking faster.
:param force_32bit_indices: When true, 32-bit indices will always be created regardless of triangle count.
:param suppress_triangle_mesh_remap_table: When true, the face remap table is not created.
This saves a significant amount of memory, but the SDK will not be able to provide the remap
information for internal mesh triangles returned by collisions, sweeps or raycasts hits.
:param build_triangle_adjacencies: When true, the triangle adjacency information is created.
:param mesh_weld_tolerance: If mesh welding is enabled, this controls the distance at
which vertices are welded. If mesh welding is not enabled, this value defines the
acceptance distance for mesh validation. Provided no two vertices are within this
distance, the mesh is considered to be clean. If not, a warning will be emitted.
:param num_tris_per_leaf: Mesh cooking hint for max triangles per leaf limit. Fewer triangles per leaf
produces larger meshes with better runtime performance and worse cooking performance.
:param physics_material: Configure which physics material to use.
:return: The newly created mesh group.
"""
group = self.__add_physx_base_mesh_group(name, physics_material)
group["export method"] = 0
@@ -367,32 +571,49 @@ class SceneManifest():
gauss_map_limit: int = 32,
build_gpu_data: bool = False,
physics_material: typing.Optional[str] = None) -> dict:
"""
Adds a Convex type PhysX Mesh Group to the scene.
"""Adds a Convex type PhysX Mesh Group to the scene.
Parameters
----------
name :
Name of the mesh group.
area_test_epsilon :
If the area of a triangle of the hull is below this value, the triangle will be
rejected. This test is done only if Check Zero Area Triangles is used.
plane_tolerance :
The value is used during hull construction. When a new point is about to be added
to the hull it gets dropped when the point is closer to the hull than the planeTolerance.
use_16bit_indices :
Denotes the use of 16-bit vertex indices in Convex triangles or polygons.
check_zero_area_triangles :
Checks and removes almost zero-area triangles during convex hull computation.
The rejected area size is specified in Area Test Epsilon.
quantize_input :
Quantizes the input vertices using the k-means clustering.
use_plane_shifting :
Enables plane shifting vertex limit algorithm. Plane shifting is an alternative
algorithm for the case when the computed hull has more vertices than the specified vertex
limit.
shift_vertices :
Convex hull input vertices are shifted to be around origin to provide better
computation stability
gauss_map_limit :
Vertex limit beyond which additional acceleration structures are computed for each
convex mesh. Increase that limit to reduce memory usage. Computing the extra structures
all the time does not guarantee optimal performance.
build_gpu_data :
When true, additional information required for GPU-accelerated rigid body
simulation is created. This can increase memory usage and cooking times for convex meshes
and triangle meshes. Convex hulls are created with respect to GPU simulation limitations.
Vertex limit is set to 64 and vertex limit per face is internally set to 32.
physics_material :
Configure which physics material to use.
Returns
-------
dict
The newly created mesh group.
:param name: Name of the mesh group.
:param area_test_epsilon: If the area of a triangle of the hull is below this value, the triangle will be
rejected. This test is done only if Check Zero Area Triangles is used.
:param plane_tolerance: The value is used during hull construction. When a new point is about to be added
to the hull it gets dropped when the point is closer to the hull than the planeTolerance.
:param use_16bit_indices: Denotes the use of 16-bit vertex indices in Convex triangles or polygons.
:param check_zero_area_triangles: Checks and removes almost zero-area triangles during convex hull computation.
The rejected area size is specified in Area Test Epsilon.
:param quantize_input: Quantizes the input vertices using the k-means clustering.
:param use_plane_shifting: Enables plane shifting vertex limit algorithm. Plane shifting is an alternative
algorithm for the case when the computed hull has more vertices than the specified vertex
limit.
:param shift_vertices: Convex hull input vertices are shifted to be around origin to provide better
computation stability
:param gauss_map_limit: Vertex limit beyond which additional acceleration structures are computed for each
convex mesh. Increase that limit to reduce memory usage. Computing the extra structures
all the time does not guarantee optimal performance.
:param build_gpu_data: When true, additional information required for GPU-accelerated rigid body
simulation is created. This can increase memory usage and cooking times for convex meshes
and triangle meshes. Convex hulls are created with respect to GPU simulation limitations.
Vertex limit is set to 64 and vertex limit per face is internally set to 32.
:param physics_material: Configure which physics material to use.
:return: The newly created mesh group.
"""
group = self.__add_physx_base_mesh_group(name, physics_material)
group["export method"] = 1
@@ -414,17 +635,27 @@ class SceneManifest():
primitive_shape_target: PrimitiveShape = PrimitiveShape.BEST_FIT,
volume_term_coefficient: float = 0.0,
physics_material: typing.Optional[str] = None) -> dict:
"""
Adds a Primitive Shape type PhysX Mesh Group to the scene
"""Adds a Primitive Shape type PhysX Mesh Group to the scene
Parameters
----------
name :
Name of the mesh group.
primitive_shape_target :
The shape that should be fitted to this mesh. If BEST_FIT is selected, the
algorithm will determine which of the shapes fits best.
volume_term_coefficient :
This parameter controls how aggressively the primitive fitting algorithm will try
to minimize the volume of the fitted primitive. A value of 0 (no volume minimization) is
recommended for most meshes, especially those with moderate to high vertex counts.
physics_material :
Configure which physics material to use.
Returns
-------
dict
The newly created mesh group.
:param name: Name of the mesh group.
:param primitive_shape_target: The shape that should be fitted to this mesh. If BEST_FIT is selected, the
algorithm will determine which of the shapes fits best.
:param volume_term_coefficient: This parameter controls how aggressively the primitive fitting algorithm will try
to minimize the volume of the fitted primitive. A value of 0 (no volume minimization) is
recommended for most meshes, especially those with moderate to high vertex counts.
:param physics_material: Configure which physics material to use.
:return: The newly created mesh group.
"""
group = self.__add_physx_base_mesh_group(name, physics_material)
group["export method"] = 2
@@ -447,26 +678,40 @@ class SceneManifest():
convex_hull_downsampling: int = 4,
pca: bool = False,
project_hull_vertices: bool = True) -> None:
"""
Enables and configures mesh decomposition for a PhysX Mesh Group.
"""Enables and configures mesh decomposition for a PhysX Mesh Group.
Only valid for convex or primitive mesh types.
:param mesh_group: Mesh group to configure decomposition for.
:param max_convex_hulls: Controls the maximum number of hulls to generate.
:param max_num_vertices_per_convex_hull: Controls the maximum number of triangles per convex hull.
:param concavity: Maximum concavity of each approximate convex hull.
:param resolution: Maximum number of voxels generated during the voxelization stage.
:param mode: Select voxel-based approximate convex decomposition or tetrahedron-based
approximate convex decomposition.
:param alpha: Controls the bias toward clipping along symmetry planes.
:param beta: Controls the bias toward clipping along revolution axes.
:param min_volume_per_convex_hull: Controls the adaptive sampling of the generated convex hulls.
:param plane_downsampling: Controls the granularity of the search for the best clipping plane.
:param convex_hull_downsampling: Controls the precision of the convex hull generation process
during the clipping plane selection stage.
:param pca: Enable or disable normalizing the mesh before applying the convex decomposition.
:param project_hull_vertices: Project the output convex hull vertices onto the original source mesh to increase
the floating point accuracy of the results.
Parameters
----------
mesh_group :
Mesh group to configure decomposition for.
max_convex_hulls :
Controls the maximum number of hulls to generate.
max_num_vertices_per_convex_hull :
Controls the maximum number of triangles per convex hull.
concavity :
Maximum concavity of each approximate convex hull.
resolution :
Maximum number of voxels generated during the voxelization stage.
mode :
Select voxel-based approximate convex decomposition or tetrahedron-based
approximate convex decomposition.
alpha :
Controls the bias toward clipping along symmetry planes.
beta :
Controls the bias toward clipping along revolution axes.
min_volume_per_convex_hull :
Controls the adaptive sampling of the generated convex hulls.
plane_downsampling :
Controls the granularity of the search for the best clipping plane.
convex_hull_downsampling :
Controls the precision of the convex hull generation process
during the clipping plane selection stage.
pca :
Enable or disable normalizing the mesh before applying the convex decomposition.
project_hull_vertices :
Project the output convex hull vertices onto the original source mesh to increase
the floating point accuracy of the results.
"""
mesh_group['DecomposeMeshes'] = True
mesh_group['ConvexDecompositionParams'] = {
@@ -485,41 +730,54 @@ class SceneManifest():
}
def physx_mesh_group_add_selected_node(self, mesh_group: dict, node: str) -> None:
"""
Adds a node to the selected nodes list
"""Adds a node to the selected nodes list
:param mesh_group: Mesh group to add to.
:param node: Node path to add.
Parameters
----------
mesh_group :
Mesh group to add to.
node :
Node path to add.
"""
mesh_group['NodeSelectionList']['selectedNodes'].append(node)
def physx_mesh_group_add_unselected_node(self, mesh_group: dict, node: str) -> None:
"""
Adds a node to the unselected nodes list
"""Adds a node to the unselected nodes list
:param mesh_group: Mesh group to add to.
:param node: Node path to add.
Parameters
----------
mesh_group :
Mesh group to add to.
node :
Node path to add.
"""
mesh_group['NodeSelectionList']['unselectedNodes'].append(node)
def physx_mesh_group_add_selected_unselected_nodes(self, mesh_group: dict, selected: typing.List[str],
unselected: typing.List[str]) -> None:
"""
Adds a set of nodes to the selected/unselected node lists
"""Adds a set of nodes to the selected/unselected node lists
:param mesh_group: Mesh group to add to.
:param selected: List of node paths to add to the selected list.
:param unselected: List of node paths to add to the unselected list.
Parameters
----------
mesh_group :
Mesh group to add to.
selected :
List of node paths to add to the selected list.
unselected :
List of node paths to add to the unselected list.
"""
mesh_group['NodeSelectionList']['selectedNodes'].extend(selected)
mesh_group['NodeSelectionList']['unselectedNodes'].extend(unselected)
def physx_mesh_group_add_comment(self, mesh_group: dict, comment: str) -> None:
"""
Adds a comment rule
"""Adds a comment rule
:param mesh_group: Mesh group to add the rule to.
:param comment: Comment string.
Parameters
----------
mesh_group :
Mesh group to add the rule to.
comment :
Comment string.
"""
rule = {
"$type": "CommentRule",
@@ -281,7 +281,8 @@ namespace ScriptCanvasBuilder
}
else
{
if (AzFramework::StringFunc::Find(fileNameOnly, s_unitTestParseErrorPrefix) != AZStd::string::npos)
if (!ScriptCanvas::Grammar::g_processingErrorsForUnitTestsEnabled
&& AzFramework::StringFunc::Find(fileNameOnly, s_unitTestParseErrorPrefix) != AZStd::string::npos)
{
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
}
@@ -431,16 +431,6 @@ namespace ScriptCanvasEditor
AzToolsFramework::ToolsApplicationNotificationBus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree_NewContent);
}
void EditorScriptCanvasComponent::OnStartPlayInEditor()
{
ScriptCanvas::Execution::PerformanceStatisticsEBus::Broadcast(&ScriptCanvas::Execution::PerformanceStatisticsBus::ClearSnaphotStatistics);
}
void EditorScriptCanvasComponent::OnStopPlayInEditor()
{
AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequests::GarbageCollect);
}
void EditorScriptCanvasComponent::SetAssetId(const SourceHandle& assetId)
{
if (m_sourceHandle.Describe() != assetId.Describe())
@@ -190,7 +190,7 @@ namespace ScriptCanvasEditor
{
scriptCanvasSlot = eventHandler->GetSlot(slotId);
int& index = (scriptCanvasSlot->IsData() && scriptCanvasSlot->IsInput()) ? paramIndex : outputIndex;
int& index = (scriptCanvasSlot && scriptCanvasSlot->IsData() && scriptCanvasSlot->IsInput()) ? paramIndex : outputIndex;
if (scriptCanvasSlot && scriptCanvasSlot->IsVisible())
{
@@ -93,9 +93,7 @@ namespace ScriptCanvasEditor
//=====================================================================
// EditorEntityContextNotificationBus
void OnStartPlayInEditor() override;
void OnStopPlayInEditor() override;
protected:
enum class SourceChangeDescription : AZ::u8
@@ -102,9 +102,6 @@ namespace ScriptCanvasEditor
void Log(const char* format, ...);
private:
bool m_verbose = true;
StateMachine* m_stateMachine;
};
@@ -363,7 +360,7 @@ namespace ScriptCanvasEditor
template <typename Traits>
void ScriptCanvasEditor::State<Traits>::Log(const char* format, ...)
{
if (m_verbose)
if (m_stateMachine->GetVerbose())
{
char sBuffer[2048];
va_list ArgList;
@@ -34,6 +34,7 @@
#include <ScriptCanvas/Core/Datum.h>
#include <ScriptCanvas/Data/DataRegistry.h>
#include <ScriptCanvas/Libraries/Libraries.h>
#include <ScriptCanvas/PerformanceStatisticsBus.h>
#include <ScriptCanvas/Variable/VariableCore.h>
#include <ScriptCanvas/View/EditCtrls/GenericLineEditCtrl.h>
@@ -338,10 +339,20 @@ namespace ScriptCanvasEditor
}
};
openers.push_back({ "O3DE_ScriptCanvasEditor", "Open In Script Canvas Editor...", QIcon(), scriptCanvasEditorCallback });
openers.push_back({ "O3DE_ScriptCanvasEditor", "Open In Script Canvas Editor...", QIcon(ScriptCanvasAssetDescription().GetIconPathImpl()), scriptCanvasEditorCallback });
}
}
void SystemComponent::OnStartPlayInEditor()
{
ScriptCanvas::Execution::PerformanceStatisticsEBus::Broadcast(&ScriptCanvas::Execution::PerformanceStatisticsBus::ClearSnaphotStatistics);
}
void SystemComponent::OnStopPlayInEditor()
{
AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequests::GarbageCollect);
}
void SystemComponent::OnUserSettingsActivated()
{
PopulateEditorCreatableTypes();
@@ -23,6 +23,7 @@
#include <Editor/View/Windows/Tools/UpgradeTool/Model.h>
#include <ScriptCanvas/Bus/ScriptCanvasBus.h>
#include <ScriptCanvas/Bus/ScriptCanvasExecutionBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
namespace ScriptCanvasEditor
{
@@ -36,6 +37,8 @@ namespace ScriptCanvasEditor
, private AZ::Data::AssetBus::MultiHandler
, private AzToolsFramework::AssetSeedManagerRequests::Bus::Handler
, private AzToolsFramework::EditorContextMenuBus::Handler
, private AzToolsFramework::EditorEntityContextNotificationBus::Handler
{
public:
AZ_COMPONENT(SystemComponent, "{1DE7A120-4371-4009-82B5-8140CB1D7B31}");
@@ -97,7 +100,12 @@ namespace ScriptCanvasEditor
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
protected:
void OnStartPlayInEditor() override;
void OnStopPlayInEditor() override;
private:
SystemComponent(const SystemComponent&) = delete;
@@ -1880,10 +1880,6 @@ namespace ScriptCanvasEditor
void MainWindow::OnFileOpen()
{
AZ::SerializeContext* serializeContext = nullptr;
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
AZ_Assert(serializeContext, "Failed to acquire application serialize context.");
AZStd::string assetRoot;
{
AZStd::array<char, AZ::IO::MaxPathLength> assetRootChar;
@@ -1892,21 +1888,9 @@ namespace ScriptCanvasEditor
}
AZStd::string assetPath = AZStd::string::format("%s/scriptcanvas", assetRoot.c_str());
AZ::EBusAggregateResults<AZStd::vector<AZStd::string>> fileFilters;
AssetRegistryRequestBus::BroadcastResult(fileFilters, &AssetRegistryRequests::GetAssetHandlerFileFilters);
QString filter;
AZStd::set<AZStd::string> filterSet;
auto aggregateFilters = fileFilters.values;
for (auto aggregateFilters2 : fileFilters.values)
{
for (const AZStd::string& fileFilter : aggregateFilters2)
{
filterSet.insert(fileFilter);
}
}
AZStd::set<AZStd::string> filterSet { ".scriptcanvas" };
QStringList nameFilters;
@@ -188,6 +188,11 @@ namespace ScriptCanvasEditor
OnButtonPressUpgradeImplementation(info);
}
void Controller::OnUpgradeDependencyWaitInterval([[maybe_unused]] const SourceHandle& info)
{
AddLogEntries();
}
void Controller::OnUpgradeModificationBegin([[maybe_unused]] const ModifyConfiguration& config, const SourceHandle& info)
{
for (auto* item : FindTableItems(info))
@@ -210,6 +215,8 @@ namespace ScriptCanvasEditor
else
{
VE_LOG("Failed to modify %s: %s", result.asset.Path().c_str(), result.errorMessage.data());
AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data()
, false, "Failed to modify %s: %s", result.asset.Path().c_str(), result.errorMessage.data());
}
for (auto* item : FindTableItems(info))
@@ -93,9 +93,10 @@ namespace ScriptCanvasEditor
( const ModifyConfiguration& config
, const AZStd::vector<SourceHandle>& assets
, const AZStd::vector<size_t>& sortedOrder) override;
void OnUpgradeDependencyWaitInterval(const SourceHandle& info) override;
void OnUpgradeModificationBegin(const ModifyConfiguration& config, const SourceHandle& info) override;
void OnUpgradeModificationEnd(const ModifyConfiguration& config, const SourceHandle& info, ModificationResult result) override;
void SetLoggingPreferences();
void SetSpinnerIsBusy(bool isBusy);
void SetRowBusy(int index);
@@ -145,6 +145,7 @@ namespace ScriptCanvasEditor
}
Idle();
RestoreSettings();
}
void Model::OnScanComplete()
@@ -161,6 +162,7 @@ namespace ScriptCanvasEditor
return;
}
CacheSettings();
m_state = State::Scanning;
m_log.Activate();
m_keepEditorAlive = AZStd::make_unique<EditorKeepAlive>();
@@ -22,6 +22,7 @@ namespace ScriptCanvasEditor
SourceHandle modifySingleAsset;
bool backupGraphBeforeModification = false;
bool successfulDependencyUpgradeRequired = true;
AZ::s32 perDependencyWaitSecondsMax = 20;
};
struct ModificationResult
@@ -98,6 +99,7 @@ namespace ScriptCanvasEditor
( const ModifyConfiguration& config
, const AZStd::vector<SourceHandle>& assets
, const AZStd::vector<size_t>& sortedOrder) = 0;
virtual void OnUpgradeDependencyWaitInterval(const SourceHandle& info) = 0;
virtual void OnUpgradeModificationBegin(const ModifyConfiguration& config, const SourceHandle& info) = 0;
virtual void OnUpgradeModificationEnd(const ModifyConfiguration& config, const SourceHandle& info, ModificationResult result) = 0;
};
@@ -30,38 +30,93 @@ namespace ScriptCanvasEditor
AZ_Assert(m_config.modification, "No modification function provided");
ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeBegin, modification, m_assets);
AZ::SystemTickBus::Handler::BusConnect();
AzFramework::AssetSystemInfoBus::Handler::BusConnect();
m_result.asset = m_assets[GetCurrentIndex()];
}
size_t Modifier::GetCurrentIndex() const
Modifier::~Modifier()
{
return m_state == State::GatheringDependencies
? m_assetIndex
: m_dependencyOrderedAssetIndicies[m_assetIndex];
AzFramework::AssetSystemInfoBus::Handler::BusDisconnect();
}
AZStd::unordered_set<size_t>& Modifier::GetOrCreateDependencyIndexSet()
bool Modifier::AllDependenciesCleared(const AZStd::unordered_set<size_t>& dependencies) const
{
auto iter = m_dependencies.find(m_assetIndex);
if (iter == m_dependencies.end())
for (auto index : dependencies)
{
iter = m_dependencies.insert_or_assign(m_assetIndex, AZStd::unordered_set<size_t>()).first;
SourceHandle dependency = m_assets[index];
CompleteDescriptionInPlace(dependency);
if (dependency.Id().IsNull() || !m_assetsCompletedByAP.contains(dependency.Id()))
{
return false;
}
}
return iter->second;
return true;
}
const ModificationResults& Modifier::GetResult() const
bool Modifier::AnyDependenciesFailed(const AZStd::unordered_set<size_t>& dependencies) const
{
return m_results;
for (auto index : dependencies)
{
SourceHandle dependency = m_assets[index];
CompleteDescriptionInPlace(dependency);
if (dependency.Id().IsNull() || m_assetsFailedByAP.contains(dependency.Id()))
{
return true;
}
}
return false;
}
void Modifier::AssetCompilationSuccess([[maybe_unused]] const AZStd::string& assetPath)
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
m_successNotifications.insert(assetPath);
}
void Modifier::AssetCompilationFailed(const AZStd::string& assetPath)
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
m_failureNotifications.insert(assetPath);
}
AZStd::sys_time_t Modifier::CalculateRemainingWaitTime(const AZStd::unordered_set<size_t>& dependencies) const
{
auto maxSeconds = AZStd::chrono::seconds(dependencies.size() * m_config.perDependencyWaitSecondsMax);
auto waitedSeconds = AZStd::chrono::seconds(AZStd::chrono::system_clock::now() - m_waitTimeStamp);
return (maxSeconds - waitedSeconds).count();
}
void Modifier::CheckDependencies()
{
ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeModificationBegin, m_config, m_result.asset);
if (auto dependencies = GetDependencies(GetCurrentIndex()); dependencies != nullptr && !dependencies->empty())
{
VE_LOG
( "dependencies found for %s, update will wait for the AP to finish processing them"
, m_result.asset.Path().c_str());
m_waitTimeStamp = AZStd::chrono::system_clock::now();
m_waitLogTimeStamp = AZStd::chrono::system_clock::time_point{};
m_modifyState = ModifyState::WaitingForDependencyProcessing;
}
else
{
m_modifyState = ModifyState::StartModification;
}
}
void Modifier::GatherDependencies()
{
AZ::SerializeContext* serializeContext{};
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
AZ_Assert(serializeContext, "SerializeContext is required to enumerate dependent assets in the ScriptCanvas file");
LoadAsset();
bool anyFailures = false;
if (m_result.asset.Get() && m_result.asset.Mod()->GetGraphData())
@@ -101,7 +156,7 @@ namespace ScriptCanvasEditor
, nullptr))
{
anyFailures = true;
VE_LOG("Modifier: ERROR - Failed to gather dependencies from graph data: %s"
VE_LOG("Modifier: ERROR - Failed to gather dependencies from graph data: %s"
, m_result.asset.Path().c_str())
}
}
@@ -111,16 +166,52 @@ namespace ScriptCanvasEditor
VE_LOG("Modifier: ERROR - Failed to load asset %s for modification, even though it scanned properly"
, m_result.asset.Path().c_str());
}
ModelNotificationsBus::Broadcast
( &ModelNotificationsTraits::OnUpgradeDependenciesGathered
, m_result.asset
, anyFailures ? Result::Failure : Result::Success);
}
ReleaseCurrentAsset();
size_t Modifier::GetCurrentIndex() const
{
return m_state == State::GatheringDependencies
? m_assetIndex
: m_dependencyOrderedAssetIndicies[m_assetIndex];
}
// Flush asset database events to ensure no asset references are held by closures queued on Ebuses.
AZ::Data::AssetManager::Instance().DispatchEvents();
const AZStd::unordered_set<size_t>* Modifier::GetDependencies(size_t index) const
{
auto iter = m_dependencies.find(index);
return iter != m_dependencies.end() ? &iter->second : nullptr;
}
AZStd::unordered_set<size_t>& Modifier::GetOrCreateDependencyIndexSet()
{
auto iter = m_dependencies.find(m_assetIndex);
if (iter == m_dependencies.end())
{
iter = m_dependencies.insert_or_assign(m_assetIndex, AZStd::unordered_set<size_t>()).first;
}
return iter->second;
}
const ModificationResults& Modifier::GetResult() const
{
return m_results;
}
void Modifier::InitializeResult()
{
m_result = {};
if (m_assetIndex != m_assets.size())
{
m_result.asset = m_assets[GetCurrentIndex()];
CompleteDescriptionInPlace(m_result.asset);
m_attemptedAssets.insert(m_result.asset.Id());
}
}
void Modifier::LoadAsset()
@@ -144,7 +235,7 @@ namespace ScriptCanvasEditor
}
else if (m_result.asset.Describe() != result.asset.Describe())
{
ReportModificationError("Received modifiction complete notification for different result");
ReportModificationError("Received modification complete notification for different result");
}
else
{
@@ -154,9 +245,6 @@ namespace ScriptCanvasEditor
void Modifier::ModifyCurrentAsset()
{
m_result = {};
m_result.asset = m_assets[GetCurrentIndex()];
ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeModificationBegin, m_config, m_result.asset);
LoadAsset();
if (m_result.asset.IsGraphValid())
@@ -171,50 +259,17 @@ namespace ScriptCanvasEditor
}
}
void Modifier::ModifyNextAsset()
void Modifier::NextAsset()
{
ModelNotificationsBus::Broadcast
( &ModelNotificationsTraits::OnUpgradeModificationEnd, m_config, m_result.asset, m_result);
ModificationNotificationsBus::Handler::BusDisconnect();
m_modifyState = ModifyState::Idle;
ReleaseCurrentAsset();
++m_assetIndex;
m_result = {};
InitializeResult();
}
void Modifier::ReleaseCurrentAsset()
void Modifier::NextModification()
{
m_result.asset = m_result.asset.Describe();
}
void Modifier::ReportModificationError(AZStd::string_view report)
{
m_result.errorMessage = report;
m_results.m_failures.push_back({ m_result.asset.Describe(), report });
ModifyNextAsset();
}
void Modifier::ReportModificationSuccess()
{
m_result.asset = m_result.asset.Describe();
m_results.m_successes.push_back({ m_result.asset.Describe(), {} });
ModifyNextAsset();
}
void Modifier::ReportSaveResult()
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
m_fileSaver.reset();
if (m_fileSaveResult.fileSaveError.empty())
{
ReportModificationSuccess();
}
else
{
ReportModificationError(m_fileSaveResult.fileSaveError);
}
ModelNotificationsBus::Broadcast( &ModelNotificationsTraits::OnUpgradeModificationEnd, m_config, m_result.asset, m_result);
ModificationNotificationsBus::Handler::BusDisconnect();
NextAsset();
m_fileSaveResult = {};
m_modifyState = ModifyState::Idle;
}
@@ -224,7 +279,7 @@ namespace ScriptCanvasEditor
if (!result.tempFileRemovalError.empty())
{
VE_LOG
( "Temporary file not removed for %s: %s"
("Temporary file not removed for %s: %s"
, m_result.asset.Path().c_str()
, result.tempFileRemovalError.c_str());
}
@@ -252,6 +307,78 @@ namespace ScriptCanvasEditor
AZ::SystemTickBus::ExecuteQueuedEvents();
}
void Modifier::ProcessNotifications()
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
for (const auto& assetPath : m_successNotifications)
{
VE_LOG("received AssetCompilationSuccess: %s", assetPath.c_str());
SourceHandle sourceHandle(nullptr, {}, assetPath.c_str());
CompleteDescriptionInPlace(sourceHandle);
if (m_attemptedAssets.contains(sourceHandle.Id()))
{
m_assetsCompletedByAP.insert(sourceHandle.Id());
}
}
m_successNotifications.clear();
for (const auto& assetPath : m_failureNotifications)
{
VE_LOG("received AssetCompilationFailed: %s", assetPath.c_str());
SourceHandle sourceHandle(nullptr, {}, assetPath.c_str());
CompleteDescriptionInPlace(sourceHandle);
if (m_attemptedAssets.contains(sourceHandle.Id()))
{
m_assetsFailedByAP.insert(sourceHandle.Id());
}
}
m_failureNotifications.clear();
}
void Modifier::ReleaseCurrentAsset()
{
m_result.asset = m_result.asset.Describe();
// Flush asset database events to ensure no asset references are held by closures queued on Ebuses.
AZ::Data::AssetManager::Instance().DispatchEvents();
}
void Modifier::ReportModificationError(AZStd::string_view report)
{
m_result.errorMessage = report;
m_results.m_failures.push_back({ m_result.asset.Describe(), report });
m_assetsFailedByAP.insert(m_result.asset.Id());
NextModification();
}
void Modifier::ReportModificationSuccess()
{
// \note DO NOT put asset into the m_assetsCompletedByAP here. That can only be done when the message is received by the AP
m_results.m_successes.push_back({ m_result.asset.Describe(), {} });
AzFramework::AssetSystemRequestBus::Broadcast(
&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetByUuid, m_result.asset.Id());
NextModification();
}
void Modifier::ReportSaveResult()
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
m_fileSaver.reset();
if (m_fileSaveResult.fileSaveError.empty())
{
ReportModificationSuccess();
}
else
{
ReportModificationError(m_fileSaveResult.fileSaveError);
}
}
void Modifier::SaveModifiedGraph(const ModificationResult& result)
{
m_modifyState = ModifyState::Saving;
@@ -316,49 +443,87 @@ namespace ScriptCanvasEditor
m_assetIndex = 0;
m_state = State::ModifyingGraphs;
InitializeResult();
}
else
{
GatherDependencies();
ReleaseCurrentAsset();
++m_assetIndex;
NextAsset();
}
}
void Modifier::TickUpdateGraph()
{
if (m_assetIndex == m_assets.size())
{
VE_LOG("Modifier: Complete.");
AZ::SystemTickBus::Handler::BusDisconnect();
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
if (m_onComplete)
switch (m_modifyState)
{
case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::Idle:
if (m_assetIndex == m_assets.size())
{
m_onComplete();
VE_LOG("Modifier: Complete.");
AZ::SystemTickBus::Handler::BusDisconnect();
if (m_onComplete)
{
m_onComplete();
}
}
else
{
CheckDependencies();
}
break;
case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::WaitingForDependencyProcessing:
WaitForDependencies();
break;
case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::StartModification:
ModifyCurrentAsset();
break;
case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::ReportResult:
ReportSaveResult();
break;
default:
break;
}
else
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
}
switch (m_modifyState)
{
case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::Idle:
ModifyCurrentAsset();
break;
case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::ReportResult:
ReportSaveResult();
break;
default:
break;
}
void Modifier::WaitForDependencies()
{
const AZ::s32 LogPeriodSeconds = 5;
ProcessNotifications();
auto dependencies = GetDependencies(GetCurrentIndex());
if (dependencies == nullptr || dependencies->empty() || AllDependenciesCleared(*dependencies))
{
m_modifyState = ModifyState::StartModification;
}
else if (AnyDependenciesFailed(*dependencies))
{
ReportModificationError("A required dependency failed to update, graph cannot update.");
}
else if (AZStd::chrono::seconds(CalculateRemainingWaitTime(*dependencies)).count() < 0)
{
ReportModificationError("Dependency update time has taken too long, aborting modification.");
}
else if (AZStd::chrono::seconds(AZStd::chrono::system_clock::now() - m_waitLogTimeStamp).count() > LogPeriodSeconds)
{
m_waitLogTimeStamp = AZStd::chrono::system_clock::now();
AZ_TracePrintf
( ScriptCanvas::k_VersionExplorerWindow.data()
, "Waiting for dependencies for %d more seconds: %s"
, AZStd::chrono::seconds(CalculateRemainingWaitTime(*dependencies)).count()
, m_result.asset.Path().c_str());
ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeDependencyWaitInterval, m_result.asset);
}
}
const AZStd::unordered_set<size_t>* Modifier::Sorter::GetDependencies(size_t index) const
{
auto iter = modifier->m_dependencies.find(index);
return iter != modifier->m_dependencies.end() ? &iter->second : nullptr;
return modifier->GetDependencies(index);
}
void Modifier::Sorter::Sort()
@@ -379,7 +544,7 @@ namespace ScriptCanvasEditor
if (markedTemporary.contains(index))
{
AZ_Error
(ScriptCanvas::k_VersionExplorerWindow.data()
( ScriptCanvas::k_VersionExplorerWindow.data()
, false
, "Modifier: Dependency sort has failed during, circular dependency detected for Asset: %s"
, modifier->m_result.asset.Path().c_str());
@@ -9,6 +9,7 @@
#pragma once
#include <AzCore/Component/TickBus.h>
#include <AzFramework/Asset/AssetSystemBus.h>
#include <Editor/View/Windows/Tools/UpgradeTool/FileSaver.h>
#include <Editor/View/Windows/Tools/UpgradeTool/ModelTraits.h>
#include <ScriptCanvas/Core/Core.h>
@@ -17,9 +18,10 @@ namespace ScriptCanvasEditor
{
namespace VersionExplorer
{
class Modifier
: private AZ::SystemTickBus::Handler
, private ModificationNotificationsBus::Handler
class Modifier final
: public AZ::SystemTickBus::Handler
, public ModificationNotificationsBus::Handler
, public AzFramework::AssetSystemInfoBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(Modifier, AZ::SystemAllocator, 0);
@@ -29,6 +31,8 @@ namespace ScriptCanvasEditor
, AZStd::vector<SourceHandle>&& assets
, AZStd::function<void()> onComplete);
virtual ~Modifier();
const ModificationResults& GetResult() const;
ModificationResults&& TakeResult();
@@ -56,6 +60,8 @@ namespace ScriptCanvasEditor
enum class ModifyState
{
Idle,
WaitingForDependencyProcessing,
StartModification,
InProgress,
Saving,
ReportResult
@@ -76,30 +82,49 @@ namespace ScriptCanvasEditor
// dependency indices by asset info index (only exist if graphs have them)
AZStd::unordered_map<size_t, AZStd::unordered_set<size_t>> m_dependencies;
AZStd::unordered_map<AZ::Uuid, size_t> m_assetInfoIndexById;
AZStd::vector<size_t> m_failures;
ModifyConfiguration m_config;
ModificationResult m_result;
ModificationResults m_results;
AZStd::unique_ptr<FileSaver> m_fileSaver;
FileSaveResult m_fileSaveResult;
// m_attemptedAssets is assets attempted to be processed by modification, as opposed to
// those processed by the AP as a result of one of their dependencies being processed.
AZStd::unordered_set<AZ::Uuid> m_attemptedAssets;
AZStd::unordered_set<AZ::Uuid> m_assetsCompletedByAP;
AZStd::unordered_set<AZ::Uuid> m_assetsFailedByAP;
AZStd::chrono::system_clock::time_point m_waitLogTimeStamp;
AZStd::chrono::system_clock::time_point m_waitTimeStamp;
AZStd::unordered_set<AZStd::string> m_successNotifications;
AZStd::unordered_set<AZStd::string> m_failureNotifications;
size_t GetCurrentIndex() const;
bool AllDependenciesCleared(const AZStd::unordered_set<size_t>& dependencies) const;
bool AnyDependenciesFailed(const AZStd::unordered_set<size_t>& dependencies) const;
void AssetCompilationSuccess(const AZStd::string& assetPath) override;
void AssetCompilationFailed(const AZStd::string& assetPath) override;
AZStd::sys_time_t CalculateRemainingWaitTime(const AZStd::unordered_set<size_t>& dependencies) const;
void CheckDependencies();
void GatherDependencies();
size_t GetCurrentIndex() const;
const AZStd::unordered_set<size_t>* GetDependencies(size_t index) const;
AZStd::unordered_set<size_t>& GetOrCreateDependencyIndexSet();
void InitializeResult();
void LoadAsset();
void ModifyCurrentAsset();
void ModifyNextAsset();
void ModificationComplete(const ModificationResult& result) override;
void ModifyCurrentAsset();
void NextAsset();
void NextModification();
void OnFileSaveComplete(const FileSaveResult& result);
void OnSystemTick() override;
void ProcessNotifications();
void ReleaseCurrentAsset();
void ReportModificationError(AZStd::string_view report);
void ReportModificationSuccess();
void ReportSaveResult();
void SaveModifiedGraph(const ModificationResult& result);
void SortGraphsByDependencies();
void OnFileSaveComplete(const FileSaveResult& result);
void OnSystemTick() override;
void TickGatherDependencies();
void TickUpdateGraph();
void WaitForDependencies();
};
}
}
@@ -84,6 +84,7 @@ namespace ScriptCanvas
}
}
return true;
}
@@ -503,20 +503,21 @@ namespace ScriptCanvas
void InitializeInterpretedStatics(RuntimeData& runtimeData)
{
if (!runtimeData.m_areStaticsInitialized)
AZ_Error("ScriptCanvas", !runtimeData.m_areStaticsInitialized, "ScriptCanvas runtime data already initalized");
{
runtimeData.m_areStaticsInitialized = true;
for (auto& dependency : runtimeData.m_requiredAssets)
{
InitializeInterpretedStatics(dependency.Get()->GetData());
if (!dependency.Get()->GetData().m_areStaticsInitialized)
{
InitializeInterpretedStatics(dependency.Get()->GetData());
}
}
#if defined(AZ_PROFILE_BUILD) || defined(AZ_DEBUG_BUILD)
Execution::InitializeFromLuaStackFunctions(const_cast<Grammar::DebugSymbolMap&>(runtimeData.m_debugMap));
#endif
AZ_WarningOnce("ScriptCanvas", !runtimeData.m_areStaticsInitialized, "ScriptCanvas runtime data already initalized");
if (runtimeData.RequiresStaticInitialization())
{
AZ::ScriptLoadResult result{};
@@ -49,7 +49,10 @@ namespace ScriptCanvas
, config.asset.GetId().ToString<AZStd::string>().data());
#endif
Execution::InitializeInterpretedStatics(runtimeAsset->GetData());
if (!runtimeAsset->GetData().m_areStaticsInitialized)
{
Execution::InitializeInterpretedStatics(runtimeAsset->GetData());
}
}
void ExecutionStateInterpreted::ClearLuaRegistryIndex()
@@ -2384,9 +2384,8 @@ namespace ScriptCanvas
AZ_TracePrintf("ScriptCanvas", "%s", pretty.data());
AZ_TracePrintf("ScriptCanvas", "SubgraphInterface:");
AZ_TracePrintf("ScriptCanvas", ToString(m_subgraphInterface).data());
AZ_TracePrintf("Script Canvas", "Parse Duration: %8.4f ms\n", m_parseDuration / 1000.0);
}
AZ_TracePrintf("Script Canvas", "Parse Duration: %8.4f ms\n", m_parseDuration / 1000.0);
}
}
}
@@ -15,6 +15,7 @@ namespace ScriptCanvas
AZ_CVAR(bool, g_disableParseOnGraphValidation, false, {}, AZ::ConsoleFunctorFlags::Null, "In case parsing the graph is interfering with opening a graph, disable parsing on validation");
AZ_CVAR(bool, g_printAbstractCodeModel, true, {}, AZ::ConsoleFunctorFlags::Null, "Print out the Abstract Code Model at the end of parsing for debug purposes.");
AZ_CVAR(bool, g_printAbstractCodeModelAtPrefabTime, false, {}, AZ::ConsoleFunctorFlags::Null, "Print out the Abstract Code Model at the end of parsing (at prefab time) for debug purposes.");
AZ_CVAR(bool, g_processingErrorsForUnitTestsEnabled, false, {}, AZ::ConsoleFunctorFlags::Null, "Enable AP processing errors on parse failure for unit tests.");
AZ_CVAR(bool, g_saveRawTranslationOuputToFile, true, {}, AZ::ConsoleFunctorFlags::Null, "Save out the raw result of translation for debug purposes.");
AZ_CVAR(bool, g_saveRawTranslationOuputToFileAtPrefabTime, false, {}, AZ::ConsoleFunctorFlags::Null, "Save out the raw result of translation (at prefab time) for debug purposes.");
@@ -245,6 +245,7 @@ namespace ScriptCanvas
AZ_CVAR_EXTERNED(bool, g_disableParseOnGraphValidation);
AZ_CVAR_EXTERNED(bool, g_printAbstractCodeModel);
AZ_CVAR_EXTERNED(bool, g_printAbstractCodeModelAtPrefabTime);
AZ_CVAR_EXTERNED(bool, g_processingErrorsForUnitTestsEnabled);
AZ_CVAR_EXTERNED(bool, g_saveRawTranslationOuputToFile);
AZ_CVAR_EXTERNED(bool, g_saveRawTranslationOuputToFileAtPrefabTime);
+41 -15
View File
@@ -76,11 +76,11 @@ def palRm(path) {
def palRmDir(path) {
if (env.IS_UNIX) {
sh label: "Removing ${path}",
script: "rm -rf ${path}"
script: "if [ -d ${path} ]; then rm -rf ${path}; fi"
} else {
def win_path = path.replace('/','\\')
bat label: "Removing ${win_path}",
script: "rd /s /q ${win_path}"
script: "IF exist ${win_path} rd /s /q ${win_path}"
}
}
@@ -406,10 +406,6 @@ def ExportTestResults(Map options, String platform, String type, String workspac
def o3deroot = "${workspace}/${ENGINE_REPOSITORY_NAME}"
dir("${o3deroot}/${params.OUTPUT_DIRECTORY}") {
junit testResults: "Testing/**/*.xml"
palRmDir("Testing")
// Recreate test runner xml directories that need to be pre generated
palMkdir("Testing/Pytest")
palMkdir("Testing/Gtest")
}
}
}
@@ -461,9 +457,26 @@ def UploadAPLogs(String platformName, String jobName, String workspace, Map para
}
}
def PostBuildCommonSteps(String workspace, boolean mount = true) {
echo 'Starting post-build common steps...'
def UploadTestArtifacts(String workspace, String outputDirectory) {
catchError(message: "Error archiving test artifacts (this won't fail the build)", buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
def cmakeBuildDir = [workspace, ENGINE_REPOSITORY_NAME, outputDirectory].join('/')
echo "Uploading Test Artifacts: ${cmakeBuildDir}/Testing"
ArchiveArtifactsOnS3("${cmakeBuildDir}/Testing", "test_artifacts", true)
}
}
def PostBuildCommonSteps(String workspace, Map params, boolean mount = true) {
echo 'Starting post-build common steps...'
if (params && params.containsKey('OUTPUT_DIRECTORY')){
dir([workspace, ENGINE_REPOSITORY_NAME, params.OUTPUT_DIRECTORY].join('/')){
// Clean up Testing directory
palRmDir("Testing")
// Recreate test runner xml directories that need to be pre generated to prevent race condition on incremental runs
palMkdir("Testing/Pytest")
palMkdir("Testing/Gtest")
}
}
if (mount) {
def pythonCmd = ''
if(env.IS_UNIX) pythonCmd = 'sudo -E python3 -u '
@@ -532,10 +545,18 @@ def CreateUploadAPLogsStage(String platformName, String jobName, String workspac
}
}
def CreateTeardownStage(Map environmentVars) {
def CreateUploadTestArtifactStage(String jobName, String workspace, String outputDirectory) {
return {
stage("${jobName}_upload_test_artifacts") {
UploadTestArtifacts(workspace, outputDirectory)
}
}
}
def CreateTeardownStage(Map environmentVars, Map params) {
return {
stage('Teardown') {
PostBuildCommonSteps(environmentVars['WORKSPACE'], environmentVars['MOUNT_VOLUME'])
PostBuildCommonSteps(environmentVars['WORKSPACE'], params, environmentVars['MOUNT_VOLUME'])
}
}
}
@@ -552,6 +573,7 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar
}
withEnv(GetEnvStringList(envVars)) {
def build_job_name = build_job.key
def params = platform.value.build_types[build_job_name].PARAMETERS
try {
CreateSetupStage(pipelineConfig, snapshot, repositoryName, projectName, pipelineName, branchName, platform.key, build_job.key, envVars, onlyMountEBSVolume).call()
@@ -559,6 +581,7 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar
pipelineEnvVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, build_job.value.PIPELINE_ENV ?: EMPTY_JSON, pipelineName)
build_job.value.steps.each { build_step ->
build_job_name = build_step
params = platform.value.build_types[build_job_name].PARAMETERS
// This addition of maps makes it that the right operand will override entries if they overlap with the left operand
envVars = pipelineEnvVars + GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, platform.value.build_types[build_step].PIPELINE_ENV ?: EMPTY_JSON, pipelineName)
try {
@@ -586,16 +609,18 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar
if (build_job_name.toLowerCase().contains('asset') && env.IS_UPLOAD_AP_LOGS?.toBoolean()) {
CreateUploadAPLogsStage(platform.key, build_job_name, envVars['WORKSPACE'], platform.value.build_types[build_job_name].PARAMETERS).call()
}
// Upload test artifacts only on builds that failed and ran test suites
if (env.IS_UPLOAD_TEST_ARTIFACTS?.toBoolean() && params.containsKey('CMAKE_TARGET') && params.CMAKE_TARGET.contains("TEST_SUITE")) {
CreateUploadTestArtifactStage(build_job_name, envVars['WORKSPACE'], params.OUTPUT_DIRECTORY).call()
}
// All other errors will be raised outside the retry block
currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE'
currentException = e.toString()
}
finally {
def params = platform.value.build_types[build_job_name].PARAMETERS
if (env.MARS_REPO && params && params.containsKey('TEST_METRICS') && params.TEST_METRICS == 'True') {
def output_directory = params.OUTPUT_DIRECTORY
def configuration = params.CONFIGURATION
CreateTestMetricsStage(pipelineConfig, branchName, envVars, build_job_name, output_directory, configuration).call()
CreateTestMetricsStage(pipelineConfig, branchName, envVars, build_job_name, params.OUTPUT_DIRECTORY, params.CONFIGURATION).call()
}
if (params && params.containsKey('TEST_RESULTS') && params.TEST_RESULTS == 'True') {
CreateExportTestResultsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call()
@@ -603,7 +628,7 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar
if (params && params.containsKey('TEST_SCREENSHOTS') && params.TEST_SCREENSHOTS == 'True' && currentResult == 'FAILURE') {
CreateExportTestScreenshotsStage(pipelineConfig, branchName, platform.key, build_job_name, envVars, params).call()
}
CreateTeardownStage(envVars).call()
CreateTeardownStage(envVars, params).call()
}
}
}
@@ -854,6 +879,7 @@ finally {
"build_number": env.BUILD_NUMBER,
"repository_name": env.REPOSITORY_NAME,
"branch_name": env.BRANCH_NAME,
"pipeline_name": GetRunningPipelineName(env.JOB_NAME),
"build_result": "${currentBuild.currentResult}",
"build_failure": buildFailure,
"recreate_volume": env.RECREATE_VOLUME,